New upstream version 2.3.4

This commit is contained in:
Jan Wagner 2023-10-18 07:29:37 +00:00
parent e7bdd1c6c6
commit de72f6f588
556 changed files with 90432 additions and 53391 deletions

View file

@ -1,22 +1,21 @@
/* Locking in multithreaded situations.
Copyright (C) 2005-2013 Free Software Foundation, Inc.
Copyright (C) 2005-2023 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
gthr-win32.h. */
Based on GCC's gthr-posix.h, gthr-posix95.h. */
#include <config.h>
@ -24,15 +23,267 @@
/* ========================================================================= */
#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
/* -------------------------- gl_lock_t datatype -------------------------- */
int
glthread_lock_init (gl_lock_t *lock)
{
if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
return ENOMEM;
lock->init_needed = 0;
return 0;
}
int
glthread_lock_lock (gl_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_lock (&lock->mutex) != thrd_success)
return EAGAIN;
return 0;
}
int
glthread_lock_unlock (gl_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_unlock (&lock->mutex) != thrd_success)
return EINVAL;
return 0;
}
int
glthread_lock_destroy (gl_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
mtx_destroy (&lock->mutex);
return 0;
}
/* ------------------------- gl_rwlock_t datatype ------------------------- */
int
glthread_rwlock_init (gl_rwlock_t *lock)
{
if (mtx_init (&lock->lock, mtx_plain) != thrd_success
|| cnd_init (&lock->waiting_readers) != thrd_success
|| cnd_init (&lock->waiting_writers) != thrd_success)
return ENOMEM;
lock->waiting_writers_count = 0;
lock->runcount = 0;
lock->init_needed = 0;
return 0;
}
int
glthread_rwlock_rdlock (gl_rwlock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_lock (&lock->lock) != thrd_success)
return EAGAIN;
/* Test whether only readers are currently running, and whether the runcount
field will not overflow, and whether no writer is waiting. The latter
condition is because POSIX recommends that "write locks shall take
precedence over read locks", to avoid "writer starvation". */
while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
{
/* This thread has to wait for a while. Enqueue it among the
waiting_readers. */
if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
{
mtx_unlock (&lock->lock);
return EINVAL;
}
}
lock->runcount++;
if (mtx_unlock (&lock->lock) != thrd_success)
return EINVAL;
return 0;
}
int
glthread_rwlock_wrlock (gl_rwlock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_lock (&lock->lock) != thrd_success)
return EAGAIN;
/* Test whether no readers or writers are currently running. */
while (!(lock->runcount == 0))
{
/* This thread has to wait for a while. Enqueue it among the
waiting_writers. */
lock->waiting_writers_count++;
if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
{
lock->waiting_writers_count--;
mtx_unlock (&lock->lock);
return EINVAL;
}
lock->waiting_writers_count--;
}
lock->runcount--; /* runcount becomes -1 */
if (mtx_unlock (&lock->lock) != thrd_success)
return EINVAL;
return 0;
}
int
glthread_rwlock_unlock (gl_rwlock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_lock (&lock->lock) != thrd_success)
return EAGAIN;
if (lock->runcount < 0)
{
/* Drop a writer lock. */
if (!(lock->runcount == -1))
{
mtx_unlock (&lock->lock);
return EINVAL;
}
lock->runcount = 0;
}
else
{
/* Drop a reader lock. */
if (!(lock->runcount > 0))
{
mtx_unlock (&lock->lock);
return EINVAL;
}
lock->runcount--;
}
if (lock->runcount == 0)
{
/* POSIX recommends that "write locks shall take precedence over read
locks", to avoid "writer starvation". */
if (lock->waiting_writers_count > 0)
{
/* Wake up one of the waiting writers. */
if (cnd_signal (&lock->waiting_writers) != thrd_success)
{
mtx_unlock (&lock->lock);
return EINVAL;
}
}
else
{
/* Wake up all waiting readers. */
if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
{
mtx_unlock (&lock->lock);
return EINVAL;
}
}
}
if (mtx_unlock (&lock->lock) != thrd_success)
return EINVAL;
return 0;
}
int
glthread_rwlock_destroy (gl_rwlock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
mtx_destroy (&lock->lock);
cnd_destroy (&lock->waiting_readers);
cnd_destroy (&lock->waiting_writers);
return 0;
}
/* --------------------- gl_recursive_lock_t datatype --------------------- */
int
glthread_recursive_lock_init (gl_recursive_lock_t *lock)
{
if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
return ENOMEM;
lock->init_needed = 0;
return 0;
}
int
glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_lock (&lock->mutex) != thrd_success)
return EAGAIN;
return 0;
}
int
glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
if (mtx_unlock (&lock->mutex) != thrd_success)
return EINVAL;
return 0;
}
int
glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
{
if (lock->init_needed)
call_once (&lock->init_once, lock->init_func);
mtx_destroy (&lock->mutex);
return 0;
}
/* -------------------------- gl_once_t datatype -------------------------- */
#endif
/* ========================================================================= */
#if USE_POSIX_THREADS
/* -------------------------- gl_lock_t datatype -------------------------- */
/* ------------------------- gl_rwlock_t datatype ------------------------- */
# if HAVE_PTHREAD_RWLOCK
# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
# if !defined PTHREAD_RWLOCK_INITIALIZER
# if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
# if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
/* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
int
glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
{
pthread_rwlockattr_t attributes;
int err;
err = pthread_rwlockattr_init (&attributes);
if (err != 0)
return err;
/* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
do this; see
http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
err = pthread_rwlockattr_setkind_np (&attributes,
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
if (err == 0)
err = pthread_rwlock_init(lock, &attributes);
/* pthread_rwlockattr_destroy always returns 0. It cannot influence the
return value. */
pthread_rwlockattr_destroy (&attributes);
return err;
}
# endif
# else
int
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
@ -152,11 +403,9 @@ glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
if (err != 0)
return err;
/* Test whether only readers are currently running, and whether the runcount
field will not overflow. */
/* POSIX says: "It is implementation-defined whether the calling thread
acquires the lock when a writer does not hold the lock and there are
writers blocked on the lock." Let's say, no: give the writers a higher
priority. */
field will not overflow, and whether no writer is waiting. The latter
condition is because POSIX recommends that "write locks shall take
precedence over read locks", to avoid "writer starvation". */
while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
{
/* This thread has to wait for a while. Enqueue it among the
@ -469,161 +718,25 @@ glthread_once_singlethreaded (pthread_once_t *once_control)
return 0;
}
#endif
/* ========================================================================= */
#if USE_PTH_THREADS
/* Use the GNU Pth threads library. */
/* -------------------------- gl_lock_t datatype -------------------------- */
/* ------------------------- gl_rwlock_t datatype ------------------------- */
/* --------------------- gl_recursive_lock_t datatype --------------------- */
/* -------------------------- gl_once_t datatype -------------------------- */
static void
glthread_once_call (void *arg)
{
void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
void (*initfunction) (void) = *gl_once_temp_addr;
initfunction ();
}
# if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
int
glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
glthread_once_multithreaded (pthread_once_t *once_control,
void (*init_function) (void))
{
void (*temp) (void) = initfunction;
return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
}
int
glthread_once_singlethreaded (pth_once_t *once_control)
{
/* We know that pth_once_t is an integer type. */
if (*once_control == PTH_ONCE_INIT)
int err = pthread_once (once_control, init_function);
if (err == ENOSYS)
{
/* First time use of once_control. Invert the marker. */
*once_control = ~ PTH_ONCE_INIT;
return 1;
/* This happens on FreeBSD 11: The pthread_once function in libc returns
ENOSYS. */
if (glthread_once_singlethreaded (once_control))
init_function ();
return 0;
}
else
return 0;
return err;
}
#endif
/* ========================================================================= */
#if USE_SOLARIS_THREADS
/* Use the old Solaris threads library. */
/* -------------------------- gl_lock_t datatype -------------------------- */
/* ------------------------- gl_rwlock_t datatype ------------------------- */
/* --------------------- gl_recursive_lock_t datatype --------------------- */
int
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
{
int err;
err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
if (err != 0)
return err;
lock->owner = (thread_t) 0;
lock->depth = 0;
return 0;
}
int
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
{
thread_t self = thr_self ();
if (lock->owner != self)
{
int err;
err = mutex_lock (&lock->mutex);
if (err != 0)
return err;
lock->owner = self;
}
if (++(lock->depth) == 0) /* wraparound? */
{
lock->depth--;
return EAGAIN;
}
return 0;
}
int
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
{
if (lock->owner != thr_self ())
return EPERM;
if (lock->depth == 0)
return EINVAL;
if (--(lock->depth) == 0)
{
lock->owner = (thread_t) 0;
return mutex_unlock (&lock->mutex);
}
else
return 0;
}
int
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
{
if (lock->owner != (thread_t) 0)
return EBUSY;
return mutex_destroy (&lock->mutex);
}
/* -------------------------- gl_once_t datatype -------------------------- */
int
glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
{
if (!once_control->inited)
{
int err;
/* Use the mutex to guarantee that if another thread is already calling
the initfunction, this thread waits until it's finished. */
err = mutex_lock (&once_control->mutex);
if (err != 0)
return err;
if (!once_control->inited)
{
once_control->inited = 1;
initfunction ();
}
return mutex_unlock (&once_control->mutex);
}
else
return 0;
}
int
glthread_once_singlethreaded (gl_once_t *once_control)
{
/* We know that gl_once_t contains an integer type. */
if (!once_control->inited)
{
/* First time use of once_control. Invert the marker. */
once_control->inited = ~ 0;
return 1;
}
else
return 0;
}
# endif
#endif
@ -631,427 +744,6 @@ glthread_once_singlethreaded (gl_once_t *once_control)
#if USE_WINDOWS_THREADS
/* -------------------------- gl_lock_t datatype -------------------------- */
void
glthread_lock_init_func (gl_lock_t *lock)
{
InitializeCriticalSection (&lock->lock);
lock->guard.done = 1;
}
int
glthread_lock_lock_func (gl_lock_t *lock)
{
if (!lock->guard.done)
{
if (InterlockedIncrement (&lock->guard.started) == 0)
/* This thread is the first one to need this lock. Initialize it. */
glthread_lock_init (lock);
else
/* Yield the CPU while waiting for another thread to finish
initializing this lock. */
while (!lock->guard.done)
Sleep (0);
}
EnterCriticalSection (&lock->lock);
return 0;
}
int
glthread_lock_unlock_func (gl_lock_t *lock)
{
if (!lock->guard.done)
return EINVAL;
LeaveCriticalSection (&lock->lock);
return 0;
}
int
glthread_lock_destroy_func (gl_lock_t *lock)
{
if (!lock->guard.done)
return EINVAL;
DeleteCriticalSection (&lock->lock);
lock->guard.done = 0;
return 0;
}
/* ------------------------- gl_rwlock_t datatype ------------------------- */
/* In this file, the waitqueues are implemented as circular arrays. */
#define gl_waitqueue_t gl_carray_waitqueue_t
static void
gl_waitqueue_init (gl_waitqueue_t *wq)
{
wq->array = NULL;
wq->count = 0;
wq->alloc = 0;
wq->offset = 0;
}
/* Enqueues the current thread, represented by an event, in a wait queue.
Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
static HANDLE
gl_waitqueue_add (gl_waitqueue_t *wq)
{
HANDLE event;
unsigned int index;
if (wq->count == wq->alloc)
{
unsigned int new_alloc = 2 * wq->alloc + 1;
HANDLE *new_array =
(HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
if (new_array == NULL)
/* No more memory. */
return INVALID_HANDLE_VALUE;
/* Now is a good opportunity to rotate the array so that its contents
starts at offset 0. */
if (wq->offset > 0)
{
unsigned int old_count = wq->count;
unsigned int old_alloc = wq->alloc;
unsigned int old_offset = wq->offset;
unsigned int i;
if (old_offset + old_count > old_alloc)
{
unsigned int limit = old_offset + old_count - old_alloc;
for (i = 0; i < limit; i++)
new_array[old_alloc + i] = new_array[i];
}
for (i = 0; i < old_count; i++)
new_array[i] = new_array[old_offset + i];
wq->offset = 0;
}
wq->array = new_array;
wq->alloc = new_alloc;
}
/* 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. */
return INVALID_HANDLE_VALUE;
index = wq->offset + wq->count;
if (index >= wq->alloc)
index -= wq->alloc;
wq->array[index] = event;
wq->count++;
return event;
}
/* Notifies the first thread from a wait queue and dequeues it. */
static void
gl_waitqueue_notify_first (gl_waitqueue_t *wq)
{
SetEvent (wq->array[wq->offset + 0]);
wq->offset++;
wq->count--;
if (wq->count == 0 || wq->offset == wq->alloc)
wq->offset = 0;
}
/* Notifies all threads from a wait queue and dequeues them all. */
static void
gl_waitqueue_notify_all (gl_waitqueue_t *wq)
{
unsigned int i;
for (i = 0; i < wq->count; i++)
{
unsigned int index = wq->offset + i;
if (index >= wq->alloc)
index -= wq->alloc;
SetEvent (wq->array[index]);
}
wq->count = 0;
wq->offset = 0;
}
void
glthread_rwlock_init_func (gl_rwlock_t *lock)
{
InitializeCriticalSection (&lock->lock);
gl_waitqueue_init (&lock->waiting_readers);
gl_waitqueue_init (&lock->waiting_writers);
lock->runcount = 0;
lock->guard.done = 1;
}
int
glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
{
if (!lock->guard.done)
{
if (InterlockedIncrement (&lock->guard.started) == 0)
/* This thread is the first one to need this lock. Initialize it. */
glthread_rwlock_init (lock);
else
/* Yield the CPU while waiting for another thread to finish
initializing this lock. */
while (!lock->guard.done)
Sleep (0);
}
EnterCriticalSection (&lock->lock);
/* Test whether only readers are currently running, and whether the runcount
field will not overflow. */
if (!(lock->runcount + 1 > 0))
{
/* This thread has to wait for a while. Enqueue it among the
waiting_readers. */
HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
if (event != INVALID_HANDLE_VALUE)
{
DWORD result;
LeaveCriticalSection (&lock->lock);
/* Wait until another thread signals this event. */
result = WaitForSingleObject (event, INFINITE);
if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
abort ();
CloseHandle (event);
/* The thread which signalled the event already did the bookkeeping:
removed us from the waiting_readers, incremented lock->runcount. */
if (!(lock->runcount > 0))
abort ();
return 0;
}
else
{
/* Allocation failure. Weird. */
do
{
LeaveCriticalSection (&lock->lock);
Sleep (1);
EnterCriticalSection (&lock->lock);
}
while (!(lock->runcount + 1 > 0));
}
}
lock->runcount++;
LeaveCriticalSection (&lock->lock);
return 0;
}
int
glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
{
if (!lock->guard.done)
{
if (InterlockedIncrement (&lock->guard.started) == 0)
/* This thread is the first one to need this lock. Initialize it. */
glthread_rwlock_init (lock);
else
/* Yield the CPU while waiting for another thread to finish
initializing this lock. */
while (!lock->guard.done)
Sleep (0);
}
EnterCriticalSection (&lock->lock);
/* Test whether no readers or writers are currently running. */
if (!(lock->runcount == 0))
{
/* This thread has to wait for a while. Enqueue it among the
waiting_writers. */
HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
if (event != INVALID_HANDLE_VALUE)
{
DWORD result;
LeaveCriticalSection (&lock->lock);
/* Wait until another thread signals this event. */
result = WaitForSingleObject (event, INFINITE);
if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
abort ();
CloseHandle (event);
/* The thread which signalled the event already did the bookkeeping:
removed us from the waiting_writers, set lock->runcount = -1. */
if (!(lock->runcount == -1))
abort ();
return 0;
}
else
{
/* Allocation failure. Weird. */
do
{
LeaveCriticalSection (&lock->lock);
Sleep (1);
EnterCriticalSection (&lock->lock);
}
while (!(lock->runcount == 0));
}
}
lock->runcount--; /* runcount becomes -1 */
LeaveCriticalSection (&lock->lock);
return 0;
}
int
glthread_rwlock_unlock_func (gl_rwlock_t *lock)
{
if (!lock->guard.done)
return EINVAL;
EnterCriticalSection (&lock->lock);
if (lock->runcount < 0)
{
/* Drop a writer lock. */
if (!(lock->runcount == -1))
abort ();
lock->runcount = 0;
}
else
{
/* Drop a reader lock. */
if (!(lock->runcount > 0))
{
LeaveCriticalSection (&lock->lock);
return EPERM;
}
lock->runcount--;
}
if (lock->runcount == 0)
{
/* POSIX recommends that "write locks shall take precedence over read
locks", to avoid "writer starvation". */
if (lock->waiting_writers.count > 0)
{
/* Wake up one of the waiting writers. */
lock->runcount--;
gl_waitqueue_notify_first (&lock->waiting_writers);
}
else
{
/* Wake up all waiting readers. */
lock->runcount += lock->waiting_readers.count;
gl_waitqueue_notify_all (&lock->waiting_readers);
}
}
LeaveCriticalSection (&lock->lock);
return 0;
}
int
glthread_rwlock_destroy_func (gl_rwlock_t *lock)
{
if (!lock->guard.done)
return EINVAL;
if (lock->runcount != 0)
return EBUSY;
DeleteCriticalSection (&lock->lock);
if (lock->waiting_readers.array != NULL)
free (lock->waiting_readers.array);
if (lock->waiting_writers.array != NULL)
free (lock->waiting_writers.array);
lock->guard.done = 0;
return 0;
}
/* --------------------- gl_recursive_lock_t datatype --------------------- */
void
glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
{
lock->owner = 0;
lock->depth = 0;
InitializeCriticalSection (&lock->lock);
lock->guard.done = 1;
}
int
glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
{
if (!lock->guard.done)
{
if (InterlockedIncrement (&lock->guard.started) == 0)
/* This thread is the first one to need this lock. Initialize it. */
glthread_recursive_lock_init (lock);
else
/* Yield the CPU while waiting for another thread to finish
initializing this lock. */
while (!lock->guard.done)
Sleep (0);
}
{
DWORD self = GetCurrentThreadId ();
if (lock->owner != self)
{
EnterCriticalSection (&lock->lock);
lock->owner = self;
}
if (++(lock->depth) == 0) /* wraparound? */
{
lock->depth--;
return EAGAIN;
}
}
return 0;
}
int
glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
{
if (lock->owner != GetCurrentThreadId ())
return EPERM;
if (lock->depth == 0)
return EINVAL;
if (--(lock->depth) == 0)
{
lock->owner = 0;
LeaveCriticalSection (&lock->lock);
}
return 0;
}
int
glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
{
if (lock->owner != 0)
return EBUSY;
DeleteCriticalSection (&lock->lock);
lock->guard.done = 0;
return 0;
}
/* -------------------------- gl_once_t datatype -------------------------- */
void
glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
{
if (once_control->inited <= 0)
{
if (InterlockedIncrement (&once_control->started) == 0)
{
/* This thread is the first one to come to this once_control. */
InitializeCriticalSection (&once_control->lock);
EnterCriticalSection (&once_control->lock);
once_control->inited = 0;
initfunction ();
once_control->inited = 1;
LeaveCriticalSection (&once_control->lock);
}
else
{
/* Undo last operation. */
InterlockedDecrement (&once_control->started);
/* Some other thread has already started the initialization.
Yield the CPU while waiting for the other thread to finish
initializing and taking the lock. */
while (once_control->inited < 0)
Sleep (0);
if (once_control->inited <= 0)
{
/* Take the lock. This blocks until the other thread has
finished calling the initfunction. */
EnterCriticalSection (&once_control->lock);
LeaveCriticalSection (&once_control->lock);
if (!(once_control->inited > 0))
abort ();
}
}
}
}
#endif
/* ========================================================================= */