bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH]: update tls and lock tests, gl_cond_t WIN32 implementation


From: Bruno Haible
Subject: Re: [PATCH]: update tls and lock tests, gl_cond_t WIN32 implementation
Date: Thu, 16 Oct 2008 02:43:52 +0200
User-agent: KMail/1.5.4

Hi Yoann,

When looking in detail at your implementation of condition variables for Win32,
I find it has two major flaws:

  - Already mentioned in [1] is the fact that it can busy-loop. Namely,
    when a broadcast is done, all threads are woken up and loop until the
    event is reset, which happens when the last among the threads has woken
    up. The first thread will busy-loop waiting for the last one...

  - The signaling of a single thread is inefficient and non-scalable: It
    wakes up all threads, and only one among the threads is unblocked.
    The others are woken up for nothing.

What do other implementations do?

  - The one in GNOME glib [2] and the draft one I had written a few weeks ago
    notify each thread through an event, one event per thread. The only
    drawback is that the waiting threads are stored in an array, leading to
    a scalability problem again.

  - The one in pthreads-win32 [3] is based on a semaphore, and the broadcast
    of a condition variable is based on sem_post_multiple of the semaphore.
    The problem here is that semaphores are likely overkill because they are
    meant for synchronization of different processes, not just different
    threads [4].

Find here an adaption of the "one event per thread" idea, and the use of
linked lists rather than array to keep the removal an O(1) operation.
It passes the tests, and should be scalable. OK to apply?

I think in the long run we should make use of pthreads-win32 in some form,
but that's a different story. Paul Eggert and I came to no good conclusion
about it in January [5].

Bruno

[1] http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
[2] http://lists.gnu.org/archive/html/bug-gnulib/2008-10/msg00209.html
[3] http://sourceware.org/pthreads-win32/
[4] http://msdn.microsoft.com/en-us/library/ms685129(VS.85).aspx
[5] http://lists.gnu.org/archive/html/bug-gnulib/2008-01/msg00229.html


2008-10-15  Bruno Haible  <address@hidden>

        New implementation of condition variables for Win32.
        * lib/glthread/cond.h (struct gl_waitqueue_link): New type.
        (gl_linked_waitqueue_t): New type.
        (gl_cond_t): Use it.
        * lib/glthread/cond.c (struct gl_waitqueue_element): New type.
        (gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove,
        gl_waitqueue_notify_first, gl_waitqueue_notify_all): New functions.
        (glthread_cond_init_func, glthread_cond_wait_func,
        glthread_cond_timedwait_func, glthread_cond_signal_func,
        glthread_cond_broadcast_func, glthread_cond_destroy_func):
        Reimplemented on the basis of gl_linked_waitqueue_t.
        * lib/glthread/lock.h (gl_carray_waitqueue_t): Renamed from
        gl_waitqueue_t.
        (gl_rwlock_t): Update.
        * lib/glthread/lock.c (gl_waitqueue_t): Alias to gl_carray_waitqueue_t.

*** lib/glthread/cond.h.orig    2008-10-16 02:03:37.000000000 +0200
--- lib/glthread/cond.h 2008-10-15 04:10:05.000000000 +0200
***************
*** 279,296 ****
  
  /* -------------------------- gl_cond_t datatype -------------------------- */
  
  typedef struct
          {
            gl_spinlock_t guard; /* protects the initialization */
            CRITICAL_SECTION lock; /* protects the remaining fields */
!           HANDLE event; /* an event configured for manual reset */
!           unsigned int waiters_count; /* number of threads currently waiting 
*/
!           unsigned int release_count; /* number of threads that are currently
!                                          being notified but have not yet
!                                          acknowledged. Always
!                                          release_count <= waiters_count */
!           unsigned int wait_generation_count; /* incremented each time a 
signal
!                                                  or broadcast is performed */
          }
          gl_cond_t;
  # define gl_cond_define(STORAGECLASS, NAME) \
--- 279,299 ----
  
  /* -------------------------- gl_cond_t datatype -------------------------- */
  
+ struct gl_waitqueue_link
+ {
+   struct gl_waitqueue_link *wql_next;
+   struct gl_waitqueue_link *wql_prev;
+ };
+ typedef struct
+         {
+           struct gl_waitqueue_link wq_list; /* circular list of waiting 
threads */
+         }
+         gl_linked_waitqueue_t;
  typedef struct
          {
            gl_spinlock_t guard; /* protects the initialization */
            CRITICAL_SECTION lock; /* protects the remaining fields */
!           gl_linked_waitqueue_t waiters; /* waiting threads */
          }
          gl_cond_t;
  # define gl_cond_define(STORAGECLASS, NAME) \
*** lib/glthread/cond.c.orig    2008-10-16 02:03:37.000000000 +0200
--- lib/glthread/cond.c 2008-10-16 01:36:03.000000000 +0200
***************
*** 79,101 ****
  
  /* -------------------------- gl_cond_t datatype -------------------------- */
  
! /* This implementation is based on the article
!    Douglas C. Schmidt, Irfan Pyarali
!    "Strategies for Implementing POSIX Condition Variables on Win32"
!    <http://www.cs.wustl.edu/~schmidt/win32-cv-1.html>  */
  
  int
  glthread_cond_init_func (gl_cond_t *cond)
  {
    InitializeCriticalSection (&cond->lock);
!   /* Create a manual-reset event.  */
!   cond->event = CreateEvent (NULL, TRUE, FALSE, NULL);
!   cond->waiters_count = 0;
!   cond->release_count = 0;
!   cond->wait_generation_count = 0;
  
    cond->guard.done = 1;
- 
    return 0;
  }
  
--- 79,221 ----
  
  /* -------------------------- gl_cond_t datatype -------------------------- */
  
! /* In this file, the waitqueues are implemented as linked lists.  */
! #define gl_waitqueue_t gl_linked_waitqueue_t
! 
! /* All links of a circular list, except the anchor, are of this type, carrying
!    a payload.  */
! struct gl_waitqueue_element
! {
!   struct gl_waitqueue_link link; /* must be the first field! */
!   HANDLE event; /* Waiting thread, represented by an event.
!                    This field is immutable once initialized. */
! };
! 
! static inline void
! gl_waitqueue_init (gl_waitqueue_t *wq)
! {
!   wq->wq_list.wql_next = &wq->wq_list;
!   wq->wq_list.wql_prev = &wq->wq_list;
! }
! 
! /* Enqueues the current thread, represented by an event, in a wait queue.
!    Returns NULL if an allocation failure occurs.  */
! static struct gl_waitqueue_element *
! gl_waitqueue_add (gl_waitqueue_t *wq)
! {
!   struct gl_waitqueue_element *elt;
!   HANDLE event;
! 
!   /* Allocate the memory for the waitqueue element on the heap, not on the
!      thread's stack.  If the thread exits unexpectedly, we prefer to leak
!      some memory rather than to access unavailable memory and crash.  */
!   elt =
!     (struct gl_waitqueue_element *)
!     malloc (sizeof (struct gl_waitqueue_element));
!   if (elt == NULL)
!     /* No more memory.  */
!     return NULL;
! 
!   /* Whether the created event is a manual-reset one or an auto-reset one,
!      does not matter, since we will wait on it only once.  */
!   event = CreateEvent (NULL, TRUE, FALSE, NULL);
!   if (event == INVALID_HANDLE_VALUE)
!     {
!       /* No way to allocate an event.  */
!       free (elt);
!       return NULL;
!     }
!   elt->event = event;
!   /* Insert elt at the end of the circular list.  */
!   (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
!   (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
!   return elt;
! }
! 
! /* Removes the current thread, represented by a 'struct gl_waitqueue_element 
*',
!    from a wait queue.
!    Returns true if is was found and removed, false if it was not present.  */
! static inline bool
! gl_waitqueue_remove (gl_waitqueue_t *wq, struct gl_waitqueue_element *elt)
! {
!   if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
!     {
!       /* Remove elt from the circular list.  */
!       struct gl_waitqueue_link *prev = elt->link.wql_prev;
!       struct gl_waitqueue_link *next = elt->link.wql_next;
!       prev->wql_next = next;
!       next->wql_prev = prev;
!       elt->link.wql_next = NULL;
!       elt->link.wql_prev = NULL;
!       return true;
!     }
!   else
!     return false;
! }
! 
! /* Notifies the first thread from a wait queue and dequeues it.  */
! static inline void
! gl_waitqueue_notify_first (gl_waitqueue_t *wq)
! {
!   if (wq->wq_list.wql_next != &wq->wq_list)
!     {
!       struct gl_waitqueue_element *elt =
!       (struct gl_waitqueue_element *) wq->wq_list.wql_next;
!       struct gl_waitqueue_link *prev;
!       struct gl_waitqueue_link *next;
! 
!       /* Remove elt from the circular list.  */
!       prev = &wq->wq_list; /* = elt->link.wql_prev; */
!       next = elt->link.wql_next;
!       prev->wql_next = next;
!       next->wql_prev = prev;
!       elt->link.wql_next = NULL;
!       elt->link.wql_prev = NULL;
! 
!       SetEvent (elt->event);
!       /* After the SetEvent, this thread cannot access *elt any more, because
!        the woken-up thread will quickly call  free (elt).  */
!     }
! }
! 
! /* Notifies all threads from a wait queue and dequeues them all.  */
! static inline void
! gl_waitqueue_notify_all (gl_waitqueue_t *wq)
! {
!   struct gl_waitqueue_link *l;
! 
!   for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
!     {
!       struct gl_waitqueue_element *elt = (struct gl_waitqueue_element *) l;
!       struct gl_waitqueue_link *prev;
!       struct gl_waitqueue_link *next;
! 
!       /* Remove elt from the circular list.  */
!       prev = &wq->wq_list; /* = elt->link.wql_prev; */
!       next = elt->link.wql_next;
!       prev->wql_next = next;
!       next->wql_prev = prev;
!       elt->link.wql_next = NULL;
!       elt->link.wql_prev = NULL;
! 
!       SetEvent (elt->event);
!       /* After the SetEvent, this thread cannot access *elt any more, because
!        the woken-up thread will quickly call  free (elt).  */
! 
!       l = next;
!     }
!   if (!(wq->wq_list.wql_next == &wq->wq_list
!         && wq->wq_list.wql_prev == &wq->wq_list))
!     abort ();
! }
  
  int
  glthread_cond_init_func (gl_cond_t *cond)
  {
    InitializeCriticalSection (&cond->lock);
!   gl_waitqueue_init (&cond->waiters);
  
    cond->guard.done = 1;
    return 0;
  }
  
***************
*** 115,189 ****
          Sleep (0);
      }
  
    {
!     unsigned old_generation_count;
!     HANDLE event;
! 
!     EnterCriticalSection (&cond->lock);
!     /* Increment waiters_count,
!        and get a copy of the current wait_generation_count.  */
!     cond->waiters_count++;
!     old_generation_count = cond->wait_generation_count;
!     event = cond->event;
      LeaveCriticalSection (&cond->lock);
! 
!     {
!       /* Now release the lock and let any other thread take it.  */
!       int err = glthread_lock_unlock (lock);
!       if (err != 0)
!       {
!         EnterCriticalSection (&cond->lock);
!         cond->waiters_count--;
!         LeaveCriticalSection (&cond->lock);
!         return err;
!       }
! 
        {
!       /* Wait until another thread signals this event.  */
        DWORD result;
  
!       for (;;)
          {
-           bool wait_done;
- 
-           result = WaitForSingleObject (event, INFINITE);
-           if (result != WAIT_OBJECT_0)
-             break;
-           /* Distingish intended from spurious wakeups.  */
            EnterCriticalSection (&cond->lock);
!           wait_done =
!             (cond->release_count > 0
!              && cond->wait_generation_count != old_generation_count);
            LeaveCriticalSection (&cond->lock);
!           if (wait_done)
!             break;
!         }
! 
!       /* Take the lock again.  It does not matter whether this is done
!          before or after the bookkeeping.  */
!       err = glthread_lock_lock (lock);
! 
!       /* Do the bookkeeping.  */
!       EnterCriticalSection (&cond->lock);
!       cond->waiters_count--;
!       if (result == WAIT_OBJECT_0)
!         {
!           /* The wait terminated because the event was signaled.
!              Acknowledge the receipt.  */
!           if (--cond->release_count == 0)
!             {
!               /* The last waiting thread to be notified has to reset
!                  the event.  */
!               ResetEvent (cond->event);
!             }
          }
!       LeaveCriticalSection (&cond->lock);
! 
!       return (err ? err :
!               result == WAIT_OBJECT_0 ? 0 :
!               /* WAIT_FAILED shouldn't happen */ EAGAIN);
        }
-     }
    }
  }
  
--- 235,284 ----
          Sleep (0);
      }
  
+   EnterCriticalSection (&cond->lock);
    {
!     struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
      LeaveCriticalSection (&cond->lock);
!     if (elt == NULL)
        {
!       /* Allocation failure.  Weird.  */
!       return EAGAIN;
!       }
!     else
!       {
!       HANDLE event = elt->event;
!       int err;
        DWORD result;
  
!       /* Now release the lock and let any other thread take it.  */
!       err = glthread_lock_unlock (lock);
!       if (err != 0)
          {
            EnterCriticalSection (&cond->lock);
!           gl_waitqueue_remove (&cond->waiters, elt);
            LeaveCriticalSection (&cond->lock);
!           CloseHandle (event);
!           free (elt);
!           return err;
          }
!       /* POSIX says:
!           "If another thread is able to acquire the mutex after the
!            about-to-block thread has released it, then a subsequent call to
!            pthread_cond_broadcast() or pthread_cond_signal() in that thread
!            shall behave as if it were issued after the about-to-block thread
!            has blocked."
!          This is fulfilled here, because the thread signalling is done
!          through SetEvent, not PulseEvent.  */
!       /* Wait until another thread signals this event.  */
!       result = WaitForSingleObject (event, INFINITE);
!       if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
!         abort ();
!       CloseHandle (event);
!       free (elt);
!       /* The thread which signalled the event already did the bookkeeping:
!          removed us from the waiters.  */
!       return glthread_lock_lock (lock);
        }
    }
  }
  
***************
*** 211,317 ****
          Sleep (0);
      }
  
    {
!     unsigned old_generation_count;
!     HANDLE event;
! 
!     EnterCriticalSection (&cond->lock);
!     /* Increment waiters_count,
!        and get a copy of the current wait_generation_count.  */
!     cond->waiters_count++;
!     old_generation_count = cond->wait_generation_count;
!     event = cond->event;
      LeaveCriticalSection (&cond->lock);
! 
!     {
!       /* Now release the lock and let any other thread take it.  */
!       int err = glthread_lock_unlock (lock);
!       if (err != 0)
!       {
!         EnterCriticalSection (&cond->lock);
!         cond->waiters_count--;
!         LeaveCriticalSection (&cond->lock);
!         return err;
!       }
! 
        {
!       /* Wait until another thread signals this event or until the abstime
!          passes.  */
        DWORD result;
  
!       for (;;)
          {
!           DWORD timeout;
!           bool wait_done;
! 
!           gettimeofday (&currtime, NULL);
!           if (currtime.tv_sec > abstime->tv_sec)
!             timeout = 0;
            else
              {
!               unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
!               timeout = seconds * 1000;
!               if (timeout / 1000 != seconds) /* overflow? */
!                 timeout = INFINITE;
                else
                  {
!                   long milliseconds =
!                     abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
!                   if (milliseconds >= 0)
!                     {
!                       timeout += milliseconds;
!                       if (timeout < milliseconds) /* overflow? */
!                         timeout = INFINITE;
!                     }
                    else
!                     {
!                       if (timeout >= - milliseconds)
!                         timeout -= (- milliseconds);
!                       else
!                         timeout = 0;
!                     }
                  }
              }
- 
-           result = WaitForSingleObject (event, timeout);
-           if (result != WAIT_OBJECT_0)
-             break;
-           /* Distingish intended from spurious wakeups.  */
-           EnterCriticalSection (&cond->lock);
-           wait_done =
-             (cond->release_count > 0
-              && cond->wait_generation_count != old_generation_count);
-           LeaveCriticalSection (&cond->lock);
-           if (wait_done)
-             break;
          }
! 
!       /* Take the lock again.  It does not matter whether this is done
!          before or after the bookkeeping.  */
!       err = glthread_lock_lock (lock);
! 
!       /* Do the bookkeeping.  */
!       EnterCriticalSection (&cond->lock);
!       cond->waiters_count--;
!       if (result == WAIT_OBJECT_0)
          {
!           /* The wait terminated because the event was signaled.
!              Acknowledge the receipt.  */
!           if (--cond->release_count == 0)
              {
!               /* The last waiting thread to be notified has to reset
!                  the event.  */
!               ResetEvent (cond->event);
              }
          }
!       LeaveCriticalSection (&cond->lock);
! 
        return (err ? err :
                result == WAIT_OBJECT_0 ? 0 :
                result == WAIT_TIMEOUT ? ETIMEDOUT :
                /* WAIT_FAILED shouldn't happen */ EAGAIN);
        }
-     }
    }
  }
  
--- 306,415 ----
          Sleep (0);
      }
  
+   EnterCriticalSection (&cond->lock);
    {
!     struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
      LeaveCriticalSection (&cond->lock);
!     if (elt == NULL)
        {
!       /* Allocation failure.  Weird.  */
!       return EAGAIN;
!       }
!     else
!       {
!       HANDLE event = elt->event;
!       int err;
!       DWORD timeout;
        DWORD result;
  
!       /* Now release the lock and let any other thread take it.  */
!       err = glthread_lock_unlock (lock);
!       if (err != 0)
          {
!           EnterCriticalSection (&cond->lock);
!           gl_waitqueue_remove (&cond->waiters, elt);
!           LeaveCriticalSection (&cond->lock);
!           CloseHandle (event);
!           free (elt);
!           return err;
!         }
!       /* POSIX says:
!           "If another thread is able to acquire the mutex after the
!            about-to-block thread has released it, then a subsequent call to
!            pthread_cond_broadcast() or pthread_cond_signal() in that thread
!            shall behave as if it were issued after the about-to-block thread
!            has blocked."
!          This is fulfilled here, because the thread signalling is done
!          through SetEvent, not PulseEvent.  */
!       /* Wait until another thread signals this event or until the abstime
!          passes.  */
!       gettimeofday (&currtime, NULL);
!       if (currtime.tv_sec > abstime->tv_sec)
!         timeout = 0;
!       else
!         {
!           unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
!           timeout = seconds * 1000;
!           if (timeout / 1000 != seconds) /* overflow? */
!             timeout = INFINITE;
            else
              {
!               long milliseconds =
!                 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
!               if (milliseconds >= 0)
!                 {
!                   timeout += milliseconds;
!                   if (timeout < milliseconds) /* overflow? */
!                     timeout = INFINITE;
!                 }
                else
                  {
!                   if (timeout >= - milliseconds)
!                     timeout -= (- milliseconds);
                    else
!                     timeout = 0;
                  }
              }
          }
!       result = WaitForSingleObject (event, timeout);
!       if (result == WAIT_FAILED)
!         abort ();
!       if (result == WAIT_TIMEOUT)
          {
!           EnterCriticalSection (&cond->lock);
!           if (gl_waitqueue_remove (&cond->waiters, elt))
              {
!               /* The event was not signaled between the WaitForSingleObject
!                  call and the EnterCriticalSection call.  */
!               if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
!                 abort ();
              }
+           else
+             {
+               /* The event was signaled between the WaitForSingleObject
+                  call and the EnterCriticalSection call.  */
+               if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
+                 abort ();
+               /* Produce the right return value.  */
+               result = WAIT_OBJECT_0;
+             }
+           LeaveCriticalSection (&cond->lock);
          }
!       else
!         {
!           /* The thread which signalled the event already did the
!              bookkeeping: removed us from the waiters.  */
!         }
!       CloseHandle (event);
!       free (elt);
!       /* Take the lock again.  It does not matter whether this is done
!          before or after the bookkeeping for WAIT_TIMEOUT.  */
!       err = glthread_lock_lock (lock);
        return (err ? err :
                result == WAIT_OBJECT_0 ? 0 :
                result == WAIT_TIMEOUT ? ETIMEDOUT :
                /* WAIT_FAILED shouldn't happen */ EAGAIN);
        }
    }
  }
  
***************
*** 324,341 ****
    EnterCriticalSection (&cond->lock);
    /* POSIX says:
        "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
!        have no effect if there are no threads currently blocked on cond."
!      Also, if  waiters_count == release_count > 0, all blocked threads will
!      be unblocked soon anyway; do nothing in this case as well.  */
!   if (cond->waiters_count > cond->release_count)
!     {
!       /* Signal the manual-reset event.  */
!       SetEvent (cond->event);
!       /* ... and reset it is soon as one of the currently waiting threads has
!        acknowledged the receipt of the signal.  */
!       cond->release_count++;
!       cond->wait_generation_count++;
!     }
    LeaveCriticalSection (&cond->lock);
  
    return 0;
--- 422,430 ----
    EnterCriticalSection (&cond->lock);
    /* POSIX says:
        "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
!        have no effect if there are no threads currently blocked on cond."  */
!   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
!     gl_waitqueue_notify_first (&cond->waiters);
    LeaveCriticalSection (&cond->lock);
  
    return 0;
***************
*** 350,365 ****
    EnterCriticalSection (&cond->lock);
    /* POSIX says:
        "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
!        have no effect if there are no threads currently blocked on cond."  */
!   if (cond->waiters_count > 0)
!     {
!       /* Signal the manual-reset event.  */
!       SetEvent (cond->event);
!       /* ... and reset it only after all currently waiting threads have
!        acknowledged the receipt of the signal.  */
!       cond->release_count = cond->waiters_count;
!       cond->wait_generation_count++;
!     }
    LeaveCriticalSection (&cond->lock);
  
    return 0;
--- 439,447 ----
    EnterCriticalSection (&cond->lock);
    /* POSIX says:
        "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
!        have no effect if there are no threads currently blocked on cond."
!      gl_waitqueue_notify_all is a nop in this case.  */
!   gl_waitqueue_notify_all (&cond->waiters);
    LeaveCriticalSection (&cond->lock);
  
    return 0;
***************
*** 370,378 ****
  {
    if (!cond->guard.done)
      return EINVAL;
!   if (cond->waiters_count > 0)
      return EBUSY;
-   CloseHandle (cond->event);
    DeleteCriticalSection (&cond->lock);
    cond->guard.done = 0;
    return 0;
--- 452,459 ----
  {
    if (!cond->guard.done)
      return EINVAL;
!   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
      return EBUSY;
    DeleteCriticalSection (&cond->lock);
    cond->guard.done = 0;
    return 0;
*** lib/glthread/lock.h.orig    2008-10-16 02:03:37.000000000 +0200
--- lib/glthread/lock.h 2008-10-15 02:48:13.000000000 +0200
***************
*** 675,687 ****
            unsigned int alloc; /* length of allocated array */
            unsigned int offset; /* index of first waiting thread in array */
          }
!         gl_waitqueue_t;
  typedef struct
          {
            gl_spinlock_t guard; /* protects the initialization */
            CRITICAL_SECTION lock; /* protects the remaining fields */
!           gl_waitqueue_t waiting_readers; /* waiting readers */
!           gl_waitqueue_t waiting_writers; /* waiting writers */
            int runcount; /* number of readers running, or -1 when a writer 
runs */
          }
          gl_rwlock_t;
--- 675,687 ----
            unsigned int alloc; /* length of allocated array */
            unsigned int offset; /* index of first waiting thread in array */
          }
!         gl_carray_waitqueue_t;
  typedef struct
          {
            gl_spinlock_t guard; /* protects the initialization */
            CRITICAL_SECTION lock; /* protects the remaining fields */
!           gl_carray_waitqueue_t waiting_readers; /* waiting readers */
!           gl_carray_waitqueue_t waiting_writers; /* waiting writers */
            int runcount; /* number of readers running, or -1 when a writer 
runs */
          }
          gl_rwlock_t;
*** lib/glthread/lock.c.orig    2008-10-16 02:03:37.000000000 +0200
--- lib/glthread/lock.c 2008-10-15 02:51:16.000000000 +0200
***************
*** 680,685 ****
--- 680,688 ----
  
  /* ------------------------- gl_rwlock_t datatype ------------------------- */
  
+ /* In this file, the waitqueues are implemented as circular arrays.  */
+ #define gl_waitqueue_t gl_carray_waitqueue_t
+ 
  static inline void
  gl_waitqueue_init (gl_waitqueue_t *wq)
  {





reply via email to

[Prev in Thread] Current Thread [Next in Thread]