Thread Synchronization

I’m editing some of the .cc files and would like to know the preferred
method for performing thread synchronization in C++ code. In
particular, I
noticed that the set and get methods that are called on the
gr_oscope_guts
class originate from a different thread then that of process_samples,
and
this is causing problems for me.

Thanks,
Tom

On Tue, May 12, 2009 at 03:22:47PM -0400, Tom Lutz wrote:

I’m editing some of the .cc files and would like to know the preferred
method for performing thread synchronization in C++ code. In particular, I
noticed that the set and get methods that are called on the gr_oscope_guts
class originate from a different thread then that of process_samples, and
this is causing problems for me.

Thanks,
Tom

Please use the boost mutexs:

#include <boost/thread.hpp>

boost::mutex m; // the mutex

boost::mutex::scoped_lock guard(m) // the scoped guard

Eric

On Tue, May 12, 2009 at 4:09 PM, Eric B. [email protected] wrote:

Tom

Please use the boost mutexs:

#include <boost/thread.hpp>

boost::mutex m; // the mutex

boost::mutex::scoped_lock guard(m) // the scoped guard

Eric

Thanks a bunch!
Tom

On Wed, May 13, 2009 at 03:33:41PM -0400, Tom Lutz wrote:

Thread A (the GUI), inverts the boolean _update_record_count_internal to
signal the other thread to perform an action (via the
set_samples_per_output_record function)

Thread B (the data flow), compares the two booleans inside the
process_sample function and takes action if they are inequal. It inverts its
boolean after performing the operation.

This might work on x86 machines, but isn’t safe on machines
with weak memory ordering across processors (e.g., PPC, Itanium).
You’d need to introduce architecture specific memory barriers.

Can you put the mutex at a higher level (e.g., in the object that
has the gr_oscope_guts)?

Eric

Would it severely affect performance to create a
boost::mutex::scoped_lock
inside the function gr_oscope_guts::process_sample? It looks to me as
if
this would hurt performance because this function is called for every
single
sample the scope receives. If a boost::mutex is based on an OS
primitive
that requires a syscall…could be sloow.

The reason I’d like to place a mutex in this function is so that the
buffers
can be resized per user preference while the scope is running. In my
case,
I needed more than 2048 samples, so I’m working on making the scope with
a
configurable output record size (up to 2^21 samples per channel, or 8MiB
of
data per channel).

I currently have a thread-safe cue spock brow way of re-allocating the
buffers while the scope is running but it is not as clean as using a
mutex
(although it is faster, I’m sure). Here’s how my method works. Tell me
if
you think it is flawed or a hack-job…

Two booleans (private member vars of the gr_oscope_guts class) control
the
synchronization of the threads:
bool _update_record_count_internal;
bool _update_record_count_state_internal;

Thread A (the GUI), inverts the boolean _update_record_count_internal to
signal the other thread to perform an action (via the
set_samples_per_output_record function)

Thread B (the data flow), compares the two booleans inside the
process_sample function and takes action if they are inequal. It inverts
its
boolean after performing the operation.

------SNIP — Added this function to the gr_oscope_guts class------
bool gr_oscope_guts::set_samples_per_output_record(int record_size)
{
int tmp_record_size = record_size;
//if record_size is out of bounds, fail
if((record_size < MINIMUM_OUTPUT_RECORD_SIZE) || (record_size >
MAXIMUM_OUTPUT_RECORD_SIZE))
return false;
while(!(tmp_record_size&0x01)) // shift right until LSB is set, then
ensure the number is a power of 2
tmp_record_size = tmp_record_size>>1;
if(tmp_record_size^0x1) //see if any other bits are set. if so, it is
NOT
a power of 2
return false;
_new_record_count_internal = record_size;
_update_record_count_internal = !_update_record_count_internal; //do
the
actual re-allocation inside the process_sample function
}

----SNIP from beginning of gr_oscope_guts::process_sample(const
float*)-----
if(_update_record_count_state_internal !=
_update_record_count_internal)
{
set_samples_per_output_record_internal(_new_record_count_internal);
_update_record_count_state_internal =
!_update_record_count_state_internal;
}

The function _update_record_count_state_internal performs the actual
de/re-allocation of buffers.

By the way – what is the most graceful way of handling a failed memory
allocation?

Thanks in advance,
Tom

This might work on x86 machines, but isn’t safe on machines
with weak memory ordering across processors (e.g., PPC, Itanium).
You’d need to introduce architecture specific memory barriers.

Can you put the mutex at a higher level (e.g., in the object that
has the gr_oscope_guts)?

Eric

Wow, I totally didn’t think of this. Yes, I can place the mutex
inside gr_oscope_f, outside of a ‘for’ loop that processes a block of
samples (hopefully a block greater than 1).

-Tom