Hi,
In the example you give, you are basically protecting accesses to the
volatile global variable error with mutexes.
I don't believe that these mutexes are necessary. The accesses to the
variable error are already atomic, and the volatile keyword will ensure
that the compiler does not hold the variable in a register.
They might help your program work on all platforms by ensuring that the
appropriate memory barriers are inserted. However, that would be a
side-effect.
The problem I have with the code is that there is no synchronisation on
the passing of the barrier between the threads. The synchronisation
should be something like:
mutex_lock()
write to shared_buf
mutex_unlock()
That's the structure that you don't want another thread to read whilst
it is partially complete.
If you get that synchronisation correct, I expect that the
synchronisation on the error variable would also be attained.
Note that your definition of type_p is incorrect, it needs to be defined
as a pointer to a volatile variable. The fact that it is made to point
to the first elements in a volatile shared_buf does not transmit that
volatility onto the pointer. The code would probably work because in the
example you are immediately do a function call after the write - which
would ensure that the compiler writes the variable to memory (in case
the function reads it).
HTH,
Darryl.
On 03/21/09 12:06 AM, Bert Miemietz wrote:
> Hallo Darryl,
>
> after some more reading (your links were really interesting) I hope you don't
> mi
> nd
> if I come around with one more question. Perhaps the following code fragments
> can serve as an example:
>
> #define USE_MTX
> #define BSIZE 8192
>
> volatile int error;
> volatile char shared_buf[BSIZE] /* buffer for data transport */
> int *type_p /* describe type of data in buffer */
> type_p = (int *) shared_buf;
> pthread_mutex_t mtx;
>
> pthread_create(..., transmit_thread, ...);
> pthread_create(..., receive_thread, ...);
> pthread_create(..., consumer_thread, ...);
>
> transmit_thread {
> int error1;
>
> while (1) {
> #ifdef USE_MTX
> pthread_mutex_lock(&mtx);
> error1 = error;
> pthread_mutex_unlock(&mtx);
> #else
> error1 = error;
> #endif
> if (error1 != 0) break;
> if (transmit() != 0) { /* transmit yields any error */
> #ifdef USE_MTX
> pthread_mutex_lock(&mtx);
> error = 5;
> pthread_mutex_unlock(&mtx);
> #else
> error = 5;
> #endif
> }
> }
> }
>
> receive_thread {
> int error1;
>
> while (1) {
> #ifdef USE_MTX
> pthread_mutex_lock(&mtx);
> error1 = error;
> pthread_mutex_unlock(&mtx);
> #else
> error1 = error;
> #endif
> if (error1 != 0) break;
> if (receive_to_buf() != 0) { /* receive yields any error */
> #ifdef USE_MTX
> pthread_mutex_lock(&mtx);
> error = 5;
> pthread_mutex_unlock(&mtx);
> #else
> error = 5;
> #endif
> }
> else {
> *type_p = 10;
> wakeup_consumer_with_cv();
> }
> }
> }
>
>
> consumer_thread {
> int error1;
> waiting_on_cv();
> #ifdef USE_MTX
> pthread_mutex_lock(&mtx);
> error1 = error;
> pthread_mutex_unlock(&mtx);
> #else
> error1 = error;
> #endif
> if (error1 == 0) {
> if (*type_p == 10) {
> process_the_buffer();
> }
> }
> }
>
>
> I hope, I didn't do any simple mistakes in writing.
> The following is desired:
> - transmit and receive threads share the same communication
> and shall work in parallel
> - as soon as any error occurrs this would result in
> interrupting the threads in their transmit/receive functions
> The fact that this is due to an error shall be
> communicated by means of the variable error. A thread
> interrupted in transmit or receive shall never retry any
> transmit or receive
> - if the receive thread has filled the buffer it wakes up
> the consumer waiting on a cv. The consumer shall then
> find the correct data in the buffer (as long as error is 0).
>
> It is not worth discussing the purpose of the code. It is only
> an example to make clear my issue. If I really need a mutex
> or similar around any access to error (just to ensure flushing
> of the store buffers after an update) this leads to an ugly
> code instead of simply writing "while (error == 0)". Error is
> not calculated, it is simply assigned a value. Therefore I
> guess one could simply use "error = 5" without a mutex
> if only this got visible right away to all other threads.
> On the other hand: If I use the mutex would I still need
> the volatile keyword to ensure the compiler doesn't
> optimize away the access?
> And how can I be/make sure that data written to buffer
> by receive_thread is completely visible to consumer_thread
> after the latter is woken up with the cv?
> Perhaps all these questions can be condensed to the question
> if I need to define USE_MTX in the example to get a conforming
> and portable code that runs on any CPU (at least with Solaris).
>
> I hope very much that these issues are also of interest
> to other programmers.
> Thank you in advance for your patience and your assistance!
--
Darryl Gove
Compiler Performance Engineering
Blog: http://blogs.sun.com/d/
Book: http://www.sun.com/books/catalog/solaris_app_programming.xml