something else i just noticed - i don't believe the behavior of the win32
conditions will be the same as the unix/pthread conditions.  

specifically, with the win32 conditions, if a condition is signalled and
there are no waiting threads, the next thread that waits on the condition
will have that signal effectively applied to it.  in other words, if thread
1 signals a condition, then thread 2 waits on that condition, thread 2 will
not block.  however, i believe this is not the case with pthreads.  with
pthreads, thread 2 would block in this situation, i think.  again, not an
expert in these matters :)

if i'm correct here, i believe the problem could be solved by only calling
SetEvent in signal/broadcast if cond->num_waiting > 0.

i've attached a patch.

-kevin.


> 
> hi.  i just happened to be looking through the win32 implementation of
> apr_thread_cond_wait, and it seems to me there could be some 
> issues.  now,
> i'm not really an expert in this area, so i could be totally 
> off, but i
> figured it might be worthwhile to share my thoughts.  at the 
> very least, i
> might learn something :)
> 
> anyway, below is the implementation with my comments 
> sprinkled throughout:
> 
> 
> APR_DECLARE(apr_status_t) 
> apr_thread_cond_wait(apr_thread_cond_t *cond,
>                                                
> apr_thread_mutex_t *mutex)
> {
>     DWORD rv;
> 
>     while (1) {
>         WaitForSingleObject(cond->mutex, INFINITE);
>         cond->num_waiting++;
>         ReleaseMutex(cond->mutex);
> 
>         apr_thread_mutex_unlock(mutex);
>         rv = WaitForSingleObject(cond->event, INFINITE);
>         /* shouldn't cond->mutex be acquired here with:
>          *   WaitForSingleObject(cond->mutex, INFINITE);
>          * before decrementing cond->num_waiting and
>          * examining it later (when cond->signal_all is 1)??
>          */
>         cond->num_waiting--;
>         if (rv == WAIT_FAILED) {
>             /* if cond->mutex is acquired above, it should
>              * be released here.
>              */
>             return apr_get_os_error();
>         }
>         if (cond->signal_all) {
>             if (cond->num_waiting == 0) {
>                 /* shouldn't cond->signal_all be reset here with:
>                  *   cond->signal_all = 0;
>                  * ??  otherwise, a call to 
> apr_thread_cond_broadcast()
>                  * will result in subsequent calls to
> apr_thread_cond_signal()
>                  * appearing to be calls to 
> apr_thread_cond_broadcast().  i
>                  * suppose it's an unlikely scenario where for a given
> condition,
>                  * you'd use broadcast sometimes and signal 
> other times.
> not
>                  * totally unthinkable though.
>                  */
>                 ResetEvent(cond->event);
>             }
>             break;
>         }
>         if (cond->signalled) {
>             cond->signalled = 0;
>             ResetEvent(cond->event);
>             break;
>         }
>         ReleaseMutex(cond->mutex);
>     }
>     apr_thread_mutex_lock(mutex);
>     return APR_SUCCESS;
> }
> 

Index: thread_cond.c
===================================================================
RCS file: /home/cvspublic/apr/locks/win32/thread_cond.c,v
retrieving revision 1.5
diff -u -w -r1.5 thread_cond.c
--- thread_cond.c       2001/10/12 01:05:03     1.5
+++ thread_cond.c       2001/12/21 04:39:40
@@ -92,12 +92,32 @@
 
         apr_thread_mutex_unlock(mutex);
         rv = WaitForSingleObject(cond->event, INFINITE);
+        /* shouldn't cond->mutex be acquired here with:
+         *   WaitForSingleObject(cond->mutex, INFINITE);
+         * before decrementing cond->num_waiting and
+         * examining it later (when cond->signal_all is 1)??
+         */
+        WaitForSingleObject(cond->mutex, INFINITE);
         cond->num_waiting--;
         if (rv == WAIT_FAILED) {
+            /* if cond->mutex is acquired above, it should
+             * be released here.
+             */
+            ReleaseMutex(cond->mutex);
             return apr_get_os_error();
         }
         if (cond->signal_all) {
             if (cond->num_waiting == 0) {
+                /* shouldn't cond->signal_all be reset here with:
+                 *   cond->signal_all = 0;
+                 * ??  otherwise, a call to apr_thread_cond_broadcast()
+                 * will result in subsequent calls to apr_thread_cond_signal()
+                 * appearing to be calls to apr_thread_cond_broadcast().  i
+                 * suppose it's an unlikely scenario where for a given 
condition,
+                 * you'd use broadcast sometimes and signal other times.  not
+                 * totally unthinkable though.
+                 */
+                cond->signal_all = 0;
                 ResetEvent(cond->event);
             }
             break;
@@ -125,7 +145,9 @@
 
     WaitForSingleObject(cond->mutex, INFINITE);
     cond->signalled = 1;
+    if (cond->num_waiting > 0) {
     rv = SetEvent(cond->event);
+    }
     ReleaseMutex(cond->mutex);
     if (rv == 0) {
         return apr_get_os_error();
@@ -140,7 +162,9 @@
     WaitForSingleObject(cond->mutex, INFINITE);
     cond->signalled = 1;
     cond->signal_all = 1;
+    if (cond->num_waiting > 0) {
     rv = SetEvent(cond->event);
+    }
     ReleaseMutex(cond->mutex);
     if (rv == 0) {
         return apr_get_os_error();

Reply via email to