|
Packit |
5e354d |
/* npth.c - a lightweight implementation of pth over native threads
|
|
Packit |
5e354d |
* Copyright (C) 2011, 2014 g10 Code GmbH
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* This file is part of nPth.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* nPth is free software; you can redistribute it and/or modify
|
|
Packit |
5e354d |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
5e354d |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
5e354d |
* the License, or (at your option) any later version.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* nPth is distributed in the hope that it will be useful, but
|
|
Packit |
5e354d |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e354d |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
Packit |
5e354d |
* the GNU Lesser General Public License for more details.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e354d |
* License along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
Packit |
5e354d |
*/
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We implement the join mechanism ourself. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_CONFIG_H
|
|
Packit |
5e354d |
#include <config.h>
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#include <assert.h>
|
|
Packit |
5e354d |
#include <errno.h>
|
|
Packit |
5e354d |
#include <io.h>
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#include "npth.h"
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#include <stdio.h>
|
|
Packit |
5e354d |
#define DEBUG_CALLS 1
|
|
Packit |
5e354d |
#define _npth_debug(x, ...) fprintf(stderr, __VA_ARGS__)
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifndef TEST
|
|
Packit |
5e354d |
#undef DEBUG_CALLS
|
|
Packit |
5e354d |
#define DEBUG_CALLS 0
|
|
Packit |
5e354d |
#undef _npth_debug
|
|
Packit |
5e354d |
#define _npth_debug(x, ...)
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* This seems to be a common standard. */
|
|
Packit |
5e354d |
#define THREAD_NAME_MAX 15
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The global lock that excludes all threads but one. Note that this
|
|
Packit |
5e354d |
implements the single-user-thread policy, but also protects all our
|
|
Packit |
5e354d |
global data such as the thread_table. GOT_SCEPTRE is a flag used
|
|
Packit |
5e354d |
for debugging to tell wether we hold SCEPTRE. */
|
|
Packit |
5e354d |
static CRITICAL_SECTION sceptre;
|
|
Packit |
5e354d |
static int got_sceptre;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* This flag is set as soon as npth_init has been called or if any
|
|
Packit |
5e354d |
* thread has been created. It will never be cleared again. The only
|
|
Packit |
5e354d |
* purpose is to make npth_protect and npth_unprotect more robust in
|
|
Packit |
5e354d |
* that they can be shortcut when npth_init has not yet been called.
|
|
Packit |
5e354d |
* This is important for libraries which want to support nPth by using
|
|
Packit |
5e354d |
* those two functions but may have been initialized before nPth. */
|
|
Packit |
5e354d |
static int initialized_or_any_threads;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
typedef struct npth_impl_s *npth_impl_t;
|
|
Packit |
5e354d |
#define MAX_THREADS 1024
|
|
Packit |
5e354d |
#define INVALID_THREAD_ID 0
|
|
Packit |
5e354d |
/* Thread ID to thread context table. We never allocate ID 0. */
|
|
Packit |
5e354d |
static npth_impl_t thread_table[MAX_THREADS];
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The TLS index to store thread ID of the current thread. Used to
|
|
Packit |
5e354d |
make faster lookups of the thread data. */
|
|
Packit |
5e354d |
DWORD tls_index;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Map a windows error value (GetLastError) to a POSIX error value. */
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
map_error (int winerr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* FIXME */
|
|
Packit |
5e354d |
return EIO;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
wait_for_single_object (HANDLE obj, DWORD msecs)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
DWORD res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = WaitForSingleObject(obj, msecs);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (res == WAIT_ABANDONED)
|
|
Packit |
5e354d |
return EDEADLK;
|
|
Packit |
5e354d |
else if (res == WAIT_TIMEOUT)
|
|
Packit |
5e354d |
return ETIMEDOUT;
|
|
Packit |
5e354d |
else if (res == WAIT_FAILED)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
else if (res != WAIT_OBJECT_0)
|
|
Packit |
5e354d |
return EINTR;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_clock_gettime(struct timespec *tp)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
FILETIME ftime;
|
|
Packit |
5e354d |
ULARGE_INTEGER systime;
|
|
Packit |
5e354d |
unsigned long long usecs;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
GetSystemTimeAsFileTime (&ftime);
|
|
Packit |
5e354d |
systime.LowPart = ftime.dwLowDateTime;
|
|
Packit |
5e354d |
systime.HighPart = ftime.dwHighDateTime;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* systime.QuadPart has the 100-nanosecond intervals since Jan 1, 1601. */
|
|
Packit |
5e354d |
tp->tv_sec = systime.QuadPart / 10000000ULL;
|
|
Packit |
5e354d |
tp->tv_nsec = (systime.QuadPart * 100ULL) % 1000000000ULL;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
calculate_timeout (const struct timespec *abstime, DWORD *msecs_r)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
struct timespec tp;
|
|
Packit |
5e354d |
struct timespec tp_delta;
|
|
Packit |
5e354d |
DWORD msecs;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
npth_clock_gettime (&tp);
|
|
Packit |
5e354d |
/* Make sure there is a positive time delta. */
|
|
Packit |
5e354d |
if (!(npth_timercmp (&tp, abstime, <)))
|
|
Packit |
5e354d |
return ETIMEDOUT;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
npth_timersub (abstime, &tp, &tp_delta);
|
|
Packit |
5e354d |
/* Make sure to round up to at least one millisecond. Note that
|
|
Packit |
5e354d |
within reasonable timeouts and the above macros, we should always
|
|
Packit |
5e354d |
end up with a positive wait time here. */
|
|
Packit |
5e354d |
msecs = (tp_delta.tv_sec * 1000) + (tp_delta.tv_nsec + 999999) / 1000000;
|
|
Packit |
5e354d |
if (msecs < 1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Log a critical error here. */
|
|
Packit |
5e354d |
return ETIMEDOUT;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*msecs_r = msecs;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
enter_npth (const char *function)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (DEBUG_CALLS)
|
|
Packit |
5e354d |
_npth_debug (DEBUG_CALLS, "tid %lu: enter_npth (%s)\n",
|
|
Packit |
5e354d |
npth_self (), function ? function : "unknown");
|
|
Packit |
5e354d |
got_sceptre = 0;
|
|
Packit |
5e354d |
LeaveCriticalSection (&sceptre);
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
leave_npth (const char *function)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
EnterCriticalSection (&sceptre);
|
|
Packit |
5e354d |
got_sceptre = 1;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (DEBUG_CALLS)
|
|
Packit |
5e354d |
_npth_debug (DEBUG_CALLS, "tid %lu: leave_npth (%s)\n",
|
|
Packit |
5e354d |
npth_self (), function ? function : "");
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#define ENTER() enter_npth(__FUNCTION__)
|
|
Packit |
5e354d |
#define LEAVE() leave_npth(__FUNCTION__)
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_impl_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Usually there is one ref owned by the thread as long as it is
|
|
Packit |
5e354d |
running, and one ref for everybody else as long as the thread is
|
|
Packit |
5e354d |
joinable. */
|
|
Packit |
5e354d |
int refs;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
HANDLE handle;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* True if thread is detached. */
|
|
Packit |
5e354d |
int detached;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The start routine and arg. */
|
|
Packit |
5e354d |
void *(*start_routine) (void *);
|
|
Packit |
5e354d |
void *start_arg;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
char name[THREAD_NAME_MAX + 1];
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Doubly-linked list for the waiter queue in condition
|
|
Packit |
5e354d |
variables. */
|
|
Packit |
5e354d |
npth_impl_t next;
|
|
Packit |
5e354d |
npth_impl_t *prev_ptr;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The event on which this thread waits when it is queued. */
|
|
Packit |
5e354d |
HANDLE event;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void *result;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
dequeue_thread (npth_impl_t thread)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Unlink the thread from any condition waiter queue. */
|
|
Packit |
5e354d |
if (thread->next)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
thread->next->prev_ptr = thread->prev_ptr;
|
|
Packit |
5e354d |
thread->next = NULL;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
if (thread->prev_ptr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
*(thread->prev_ptr) = thread->next;
|
|
Packit |
5e354d |
thread->prev_ptr = NULL;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Enqueue THREAD to come after the thread whose next pointer is
|
|
Packit |
5e354d |
prev_ptr. */
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
enqueue_thread (npth_impl_t thread, npth_impl_t *prev_ptr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (*prev_ptr)
|
|
Packit |
5e354d |
(*prev_ptr)->prev_ptr = &thread->next;
|
|
Packit |
5e354d |
thread->prev_ptr = prev_ptr;
|
|
Packit |
5e354d |
thread->next = *prev_ptr;
|
|
Packit |
5e354d |
*prev_ptr = thread;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
find_thread (npth_t thread_id, npth_impl_t *thread)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (thread_id < 1 || thread_id >= MAX_THREADS)
|
|
Packit |
5e354d |
return ESRCH;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (! thread_table[thread_id])
|
|
Packit |
5e354d |
return ESRCH;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*thread = thread_table[thread_id];
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
new_thread (npth_t *thread_id)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
int id;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* ID 0 is never allocated. */
|
|
Packit |
5e354d |
for (id = 1; id < MAX_THREADS; id++)
|
|
Packit |
5e354d |
if (! thread_table[id])
|
|
Packit |
5e354d |
break;
|
|
Packit |
5e354d |
if (id == MAX_THREADS)
|
|
Packit |
5e354d |
return EAGAIN;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread = malloc (sizeof (*thread));
|
|
Packit |
5e354d |
if (! thread)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->refs = 1;
|
|
Packit |
5e354d |
thread->handle = INVALID_HANDLE_VALUE;
|
|
Packit |
5e354d |
thread->detached = 0;
|
|
Packit |
5e354d |
thread->start_routine = NULL;
|
|
Packit |
5e354d |
thread->start_arg = NULL;
|
|
Packit |
5e354d |
thread->next = NULL;
|
|
Packit |
5e354d |
thread->prev_ptr = NULL;
|
|
Packit |
5e354d |
/* We create the event when it is first needed (not all threads wait
|
|
Packit |
5e354d |
on conditions). */
|
|
Packit |
5e354d |
thread->event = INVALID_HANDLE_VALUE;
|
|
Packit |
5e354d |
memset (thread->name, '\0', sizeof (thread->name));
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread_table[id] = thread;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*thread_id = id;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
free_thread (npth_t thread_id)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_impl_t thread = thread_table[thread_id];
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread->handle)
|
|
Packit |
5e354d |
CloseHandle (thread->handle);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Unlink the thread from any condition waiter queue. */
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
free (thread);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread_table[thread_id] = NULL;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
deref_thread (npth_t thread_id)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_impl_t thread = thread_table[thread_id];
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->refs -= 1;
|
|
Packit |
5e354d |
if (thread->refs == 0)
|
|
Packit |
5e354d |
free_thread (thread_id);
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_init (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_t thread_id;
|
|
Packit |
5e354d |
BOOL res;
|
|
Packit |
5e354d |
HANDLE handle;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
InitializeCriticalSection (&sceptre);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Track that we have been initialized. */
|
|
Packit |
5e354d |
initialized_or_any_threads = 1;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Fake a thread table item for the main thread. */
|
|
Packit |
5e354d |
tls_index = TlsAlloc();
|
|
Packit |
5e354d |
if (tls_index == TLS_OUT_OF_INDEXES)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = new_thread(&thread_id);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* GetCurrentThread() is not usable by other threads, so it needs to
|
|
Packit |
5e354d |
be duplicated. */
|
|
Packit |
5e354d |
res = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
|
|
Packit |
5e354d |
GetCurrentProcess(), &handle,
|
|
Packit |
5e354d |
0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
Packit |
5e354d |
if (!res)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free_thread (thread_id);
|
|
Packit |
5e354d |
return map_error(GetLastError());
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread = thread_table[thread_id];
|
|
Packit |
5e354d |
thread->handle = handle;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (! TlsSetValue(tls_index, (LPVOID) thread_id))
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_attr_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int detachstate;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_attr_init (npth_attr_t *attr_r)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_attr_t attr;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr = malloc (sizeof *attr);
|
|
Packit |
5e354d |
if (!attr)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr->detachstate = NPTH_CREATE_JOINABLE;
|
|
Packit |
5e354d |
*attr_r = attr;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_attr_destroy (npth_attr_t *attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free (*attr);
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_attr_getdetachstate (npth_attr_t *attr,
|
|
Packit |
5e354d |
int *detachstate)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
*detachstate = (*attr)->detachstate;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_attr_setdetachstate (npth_attr_t *attr, int detachstate)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (detachstate != NPTH_CREATE_JOINABLE
|
|
Packit |
5e354d |
&& detachstate != NPTH_CREATE_DETACHED)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*attr)->detachstate = detachstate;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_getname_np (npth_t target_thread, char *buf, size_t buflen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (buflen < THREAD_NAME_MAX + 1)
|
|
Packit |
5e354d |
return ERANGE;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (target_thread, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
strcpy (buf, thread->name);
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_setname_np (npth_t target_thread, const char *name)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (strlen(name) > THREAD_NAME_MAX)
|
|
Packit |
5e354d |
return ERANGE;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (target_thread, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
strcpy (thread->name, name);
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static DWORD
|
|
Packit |
5e354d |
thread_start (void *arg)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_t thread_id = (npth_t) arg;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
void *result;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (! TlsSetValue(tls_index, (LPVOID) thread_id))
|
|
Packit |
5e354d |
/* FIXME: There is not much we can do here. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
/* We must be protected here, because we access the global
|
|
Packit |
5e354d |
thread_table. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread = thread_table[thread_id];
|
|
Packit |
5e354d |
result = thread->start_routine (thread->start_arg);
|
|
Packit |
5e354d |
/* We might not return here if the thread calls npth_exit(). */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->result = result;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Any joiner will be signaled once we terminate. */
|
|
Packit |
5e354d |
deref_thread (thread_id);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can not return result, as that is a void*, not a DWORD. */
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_create (npth_t *newthread, const npth_attr_t *user_attr,
|
|
Packit |
5e354d |
void *(*start_routine) (void *), void *start_arg)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err = 0;
|
|
Packit |
5e354d |
npth_t thread_id = INVALID_THREAD_ID;
|
|
Packit |
5e354d |
npth_attr_t attr;
|
|
Packit |
5e354d |
int attr_allocated;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
HANDLE handle;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We must stay protected here, because we access the global
|
|
Packit |
5e354d |
thread_table. Also, creating a new thread is not a blocking
|
|
Packit |
5e354d |
operation. */
|
|
Packit |
5e354d |
if (user_attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
attr = *user_attr;
|
|
Packit |
5e354d |
attr_allocated = 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = npth_attr_init (&attr);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
attr_allocated = 1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = new_thread (&thread_id);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread = thread_table[thread_id];
|
|
Packit |
5e354d |
if (attr->detachstate == NPTH_CREATE_DETACHED)
|
|
Packit |
5e354d |
thread->detached = 1;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
thread->refs += 1;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->start_routine = start_routine;
|
|
Packit |
5e354d |
thread->start_arg = start_arg;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
handle = CreateThread (NULL, 0,
|
|
Packit |
5e354d |
(LPTHREAD_START_ROUTINE)thread_start,
|
|
Packit |
5e354d |
(void *) thread_id, CREATE_SUSPENDED,
|
|
Packit |
5e354d |
NULL);
|
|
Packit |
5e354d |
if (handle == NULL)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = map_error (GetLastError());
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->handle = handle;
|
|
Packit |
5e354d |
*newthread = thread_id;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ResumeThread (thread->handle);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err_out:
|
|
Packit |
5e354d |
if (thread_id)
|
|
Packit |
5e354d |
free_thread (thread_id);
|
|
Packit |
5e354d |
if (attr_allocated)
|
|
Packit |
5e354d |
npth_attr_destroy (&attr);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
npth_t
|
|
Packit |
5e354d |
npth_self (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
LPVOID thread_id;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread_id = TlsGetValue (tls_index);
|
|
Packit |
5e354d |
if (thread_id == 0 && GetLastError() != ERROR_SUCCESS)
|
|
Packit |
5e354d |
/* FIXME: Log the error. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
return (npth_t) thread_id;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Not part of the public interface at the moment, thus static. */
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
npth_tryjoin_np (npth_t thread_id, void **thread_return)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (thread_id, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread->detached)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = wait_for_single_object (thread->handle, 0);
|
|
Packit |
5e354d |
if (err == ETIMEDOUT)
|
|
Packit |
5e354d |
err = EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread_return)
|
|
Packit |
5e354d |
*thread_return = thread->result;
|
|
Packit |
5e354d |
deref_thread (thread_id);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_join (npth_t thread_id, void **thread_return)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = npth_tryjoin_np (thread_id, thread_return);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (thread_id, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread->detached)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = wait_for_single_object (thread->handle, INFINITE);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread_return)
|
|
Packit |
5e354d |
*thread_return = thread->result;
|
|
Packit |
5e354d |
deref_thread (thread_id);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_detach (npth_t thread_id)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (thread_id, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (thread->detached)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The detached flag indicates to other thread that the outside
|
|
Packit |
5e354d |
reference in the global thread table has been consumed. */
|
|
Packit |
5e354d |
thread->detached = 1;
|
|
Packit |
5e354d |
deref_thread (thread_id);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_exit (void *retval)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_t thread_id;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread_id = npth_self();
|
|
Packit |
5e354d |
err = find_thread (thread_id, &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
/* FIXME: log this? */
|
|
Packit |
5e354d |
return;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
thread->result = retval;
|
|
Packit |
5e354d |
/* Any joiner will be signaled once we terminate. */
|
|
Packit |
5e354d |
deref_thread (thread_id);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can not use retval here, as that is a void*, not a DWORD. */
|
|
Packit |
5e354d |
ExitThread(0);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Never reached. But just in case ExitThread does return... */
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_key_create (npth_key_t *key,
|
|
Packit |
5e354d |
void (*destr_function) (void *))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
DWORD idx;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (destr_function)
|
|
Packit |
5e354d |
return EOPNOTSUPP;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
idx = TlsAlloc ();
|
|
Packit |
5e354d |
if (idx == TLS_OUT_OF_INDEXES)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*key = idx;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_key_delete (npth_key_t key)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
BOOL res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = TlsFree (key);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void *
|
|
Packit |
5e354d |
npth_getspecific (npth_key_t key)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Pthread doesn't support error reporting beyond returning NULL for
|
|
Packit |
5e354d |
an invalid key, which is also what TlsGetValue returns in that
|
|
Packit |
5e354d |
case. */
|
|
Packit |
5e354d |
return TlsGetValue (key);
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_setspecific (npth_key_t key, const void *pointer)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
BOOL res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = TlsSetValue (key, (void *) pointer);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_mutexattr_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int kind;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutexattr_init (npth_mutexattr_t *attr_r)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_mutexattr_t attr;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr = malloc (sizeof *attr);
|
|
Packit |
5e354d |
if (!attr)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr->kind = NPTH_MUTEX_DEFAULT;
|
|
Packit |
5e354d |
*attr_r = attr;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutexattr_destroy (npth_mutexattr_t *attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free (*attr);
|
|
Packit |
5e354d |
*attr = NULL;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutexattr_gettype (const npth_mutexattr_t *attr,
|
|
Packit |
5e354d |
int *kind)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
*kind = (*attr)->kind;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutexattr_settype (npth_mutexattr_t *attr, int kind)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (kind != NPTH_MUTEX_NORMAL && kind != NPTH_MUTEX_RECURSIVE
|
|
Packit |
5e354d |
&& kind != NPTH_MUTEX_ERRORCHECK)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*attr)->kind = kind;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_mutex_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* We have to use a mutex, not a CRITICAL_SECTION, because the
|
|
Packit |
5e354d |
latter can not be used with timed waits. */
|
|
Packit |
5e354d |
HANDLE mutex;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_init (npth_mutex_t *mutex_r, const npth_mutexattr_t *mutex_attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_mutex_t mutex;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can not check *mutex_r here, as it may contain random data. */
|
|
Packit |
5e354d |
mutex = malloc (sizeof (*mutex));
|
|
Packit |
5e354d |
if (!mutex)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We ignore MUTEX_ATTR. */
|
|
Packit |
5e354d |
mutex->mutex = CreateMutex (NULL, FALSE, NULL);
|
|
Packit |
5e354d |
if (!mutex->mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err = map_error (GetLastError());
|
|
Packit |
5e354d |
free (mutex);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*mutex_r = mutex;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_destroy (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
BOOL res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*mutex == 0 || *mutex == NPTH_MUTEX_INITIALIZER
|
|
Packit |
5e354d |
|| *mutex == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = CloseHandle ((*mutex)->mutex);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
free (*mutex);
|
|
Packit |
5e354d |
*mutex = NULL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Must be called with global lock held. */
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
mutex_init_check (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_mutexattr_t attr;
|
|
Packit |
5e354d |
int kind;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*mutex == 0)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*mutex) == NPTH_MUTEX_INITIALIZER)
|
|
Packit |
5e354d |
kind = NPTH_MUTEX_NORMAL;
|
|
Packit |
5e354d |
else if ((*mutex) == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
|
|
Packit |
5e354d |
kind = NPTH_MUTEX_RECURSIVE;
|
|
Packit |
5e354d |
else if ((*mutex) == NPTH_ERRORCHECK_MUTEX_INITIALIZER_NP)
|
|
Packit |
5e354d |
kind = NPTH_MUTEX_ERRORCHECK;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
/* Already initialized. */
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure we don't try again in case of error. */
|
|
Packit |
5e354d |
*mutex = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_mutexattr_init (&attr);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_mutexattr_settype (&attr, kind);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_mutexattr_destroy (&attr);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_mutex_init (mutex, &attr);
|
|
Packit |
5e354d |
npth_mutexattr_destroy (&attr);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_lock (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = mutex_init_check (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = npth_mutex_trylock (mutex);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = wait_for_single_object ((*mutex)->mutex, INFINITE);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_trylock (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
DWORD res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = mutex_init_check (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We do not leave the global lock for a quick try. */
|
|
Packit |
5e354d |
err = wait_for_single_object ((*mutex)->mutex, 0);
|
|
Packit |
5e354d |
if (err == ETIMEDOUT)
|
|
Packit |
5e354d |
err = EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_timedlock (npth_mutex_t *mutex, const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
DWORD msecs;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = mutex_init_check (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = npth_mutex_trylock (mutex);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = calculate_timeout (abstime, &msecs);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = wait_for_single_object ((*mutex)->mutex, msecs);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_unlock (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
BOOL res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*mutex == 0 || *mutex == NPTH_MUTEX_INITIALIZER
|
|
Packit |
5e354d |
|| *mutex == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = ReleaseMutex ((*mutex)->mutex);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_cond_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* All conditions are protected by the global lock, so this is
|
|
Packit |
5e354d |
simple. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The waiter queue. */
|
|
Packit |
5e354d |
npth_impl_t waiter;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_init (npth_cond_t *cond_r,
|
|
Packit |
5e354d |
const npth_condattr_t *cond_attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_cond_t cond;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (cond_attr != NULL)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can not check *cond_r here, as it may contain random data. */
|
|
Packit |
5e354d |
cond = malloc (sizeof (*cond));
|
|
Packit |
5e354d |
if (!cond)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
cond->waiter = NULL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*cond_r = cond;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_destroy (npth_cond_t *cond)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (*cond == 0)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*cond)->waiter)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
free (*cond);
|
|
Packit |
5e354d |
*cond = NULL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Must be called with global lock held. */
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
cond_init_check (npth_cond_t *cond)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*cond == 0 || *cond == NPTH_COND_INITIALIZER)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*cond != NPTH_COND_INITIALIZER)
|
|
Packit |
5e354d |
/* Already initialized. */
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure we don't try again in case of error. */
|
|
Packit |
5e354d |
*cond = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_cond_init (cond, NULL);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_signal (npth_cond_t *cond)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
DWORD res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = cond_init_check (cond);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*cond)->waiter == INVALID_THREAD_ID)
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Dequeue the first thread and wake it up. */
|
|
Packit |
5e354d |
thread = (*cond)->waiter;
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = SetEvent (thread->event);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
/* FIXME: An error here implies a mistake in the npth code. Log it. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Force the woken up thread into the mutex lock function (for the
|
|
Packit |
5e354d |
mutex associated with the condition, which is why we have to
|
|
Packit |
5e354d |
release the global lock here). This helps to ensure fairness,
|
|
Packit |
5e354d |
because otherwise our own thread might release and reacquire the
|
|
Packit |
5e354d |
lock first (followed by removing the condition that lead to the
|
|
Packit |
5e354d |
wakeup) and starve the woken up thread. */
|
|
Packit |
5e354d |
ENTER ();
|
|
Packit |
5e354d |
Sleep (0);
|
|
Packit |
5e354d |
LEAVE ();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_broadcast (npth_cond_t *cond)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
DWORD res;
|
|
Packit |
5e354d |
int any;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = cond_init_check (cond);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*cond)->waiter == INVALID_THREAD_ID)
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
while ((*cond)->waiter)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Dequeue the first thread and wake it up. */
|
|
Packit |
5e354d |
thread = (*cond)->waiter;
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = SetEvent (thread->event);
|
|
Packit |
5e354d |
if (res == 0)
|
|
Packit |
5e354d |
/* FIXME: An error here implies a mistake in the npth code. Log it. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Force the woken up threads into the mutex lock function (for the
|
|
Packit |
5e354d |
mutex associated with the condition, which is why we have to
|
|
Packit |
5e354d |
release the global lock here). This helps to ensure fairness,
|
|
Packit |
5e354d |
because otherwise our own thread might release and reacquire the
|
|
Packit |
5e354d |
lock first (followed by removing the condition that lead to the
|
|
Packit |
5e354d |
wakeup) and starve the woken up threads. */
|
|
Packit |
5e354d |
ENTER ();
|
|
Packit |
5e354d |
Sleep (0);
|
|
Packit |
5e354d |
LEAVE ();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* As a special exception in W32 NPTH, mutex can be NULL, in which
|
|
Packit |
5e354d |
case the global lock doubles as the mutex protecting the condition.
|
|
Packit |
5e354d |
This is used internally in the RW implementation as an
|
|
Packit |
5e354d |
optimization. Note that this is safe as long as the caller does
|
|
Packit |
5e354d |
not yield to other threads (directly or indirectly) between
|
|
Packit |
5e354d |
checking the condition and waiting on it. */
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
int err2;
|
|
Packit |
5e354d |
BOOL bres;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
npth_impl_t *prev_ptr;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = cond_init_check (cond);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (npth_self(), &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Ensure there is an event. */
|
|
Packit |
5e354d |
if (thread->event == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
thread->event = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
Packit |
5e354d |
if (thread->event == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Find end of queue and enqueue the thread. */
|
|
Packit |
5e354d |
prev_ptr = &(*cond)->waiter;
|
|
Packit |
5e354d |
while (*prev_ptr)
|
|
Packit |
5e354d |
prev_ptr = &(*prev_ptr)->next;
|
|
Packit |
5e354d |
enqueue_thread (thread, prev_ptr);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure the event is not signaled before releasing the mutex. */
|
|
Packit |
5e354d |
bres = ResetEvent (thread->event);
|
|
Packit |
5e354d |
if (bres == 0)
|
|
Packit |
5e354d |
/* Log an error. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = npth_mutex_unlock (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = wait_for_single_object (thread->event, INFINITE);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure the thread is dequeued (in case of error). */
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err2 = npth_mutex_lock (mutex);
|
|
Packit |
5e354d |
if (err2)
|
|
Packit |
5e354d |
/* FIXME: Log this at least. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex,
|
|
Packit |
5e354d |
const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
int err2;
|
|
Packit |
5e354d |
BOOL bres;
|
|
Packit |
5e354d |
npth_impl_t thread;
|
|
Packit |
5e354d |
npth_impl_t *prev_ptr;
|
|
Packit |
5e354d |
DWORD msecs;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = calculate_timeout (abstime, &msecs);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (err != ETIMEDOUT)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We have to give up the lock anyway to give others a chance to
|
|
Packit |
5e354d |
signal or broadcast. */
|
|
Packit |
5e354d |
err = npth_mutex_unlock (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
Sleep (0);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
err = npth_mutex_lock (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return (err);
|
|
Packit |
5e354d |
return ETIMEDOUT;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* While we are protected, let's check for a static initializer. */
|
|
Packit |
5e354d |
err = cond_init_check (cond);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = find_thread (npth_self(), &thread);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Ensure there is an event. */
|
|
Packit |
5e354d |
if (thread->event == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
thread->event = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
Packit |
5e354d |
if (thread->event == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
return map_error (GetLastError());
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure the event is not signaled. */
|
|
Packit |
5e354d |
bres = ResetEvent (thread->event);
|
|
Packit |
5e354d |
if (bres == 0)
|
|
Packit |
5e354d |
/* Log an error. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Find end of queue and enqueue the thread. */
|
|
Packit |
5e354d |
prev_ptr = &(*cond)->waiter;
|
|
Packit |
5e354d |
while (*prev_ptr)
|
|
Packit |
5e354d |
prev_ptr = &(*prev_ptr)->next;
|
|
Packit |
5e354d |
enqueue_thread (thread, prev_ptr);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_mutex_unlock (mutex);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
dequeue_thread (thread);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = wait_for_single_object (thread->event, msecs);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err2 = npth_mutex_lock (mutex);
|
|
Packit |
5e354d |
if (err2)
|
|
Packit |
5e354d |
/* FIXME: Log this at least. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_rwlockattr_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int kind;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlockattr_init (npth_rwlockattr_t *attr_r)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_rwlockattr_t attr;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr = malloc (sizeof *attr);
|
|
Packit |
5e354d |
if (!attr)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
attr->kind = NPTH_RWLOCK_DEFAULT_NP;
|
|
Packit |
5e354d |
*attr_r = attr;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlockattr_destroy (npth_rwlockattr_t *attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free (*attr);
|
|
Packit |
5e354d |
*attr = NULL;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlockattr_gettype_np (const npth_rwlockattr_t *attr,
|
|
Packit |
5e354d |
int *kind)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
*kind = (*attr)->kind;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlockattr_settype_np (npth_rwlockattr_t *attr, int kind)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (kind != NPTH_RWLOCK_PREFER_READER_NP
|
|
Packit |
5e354d |
&& kind != NPTH_RWLOCK_PREFER_WRITER_NP
|
|
Packit |
5e354d |
&& kind != NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*attr)->kind = kind;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct npth_rwlock_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Objects are protected by the global lock, so no lock here
|
|
Packit |
5e354d |
necessary. This is even true for the condition (by specifying
|
|
Packit |
5e354d |
NULL as the mutex in npth_cond_wait and npth_cond_timedwait). */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* True if we prefer writers over readers. */
|
|
Packit |
5e354d |
int prefer_writer;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Readers who want the lock wait on this condition, which is
|
|
Packit |
5e354d |
broadcast when the last writer goes away. */
|
|
Packit |
5e354d |
npth_cond_t reader_wait;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The number of readers waiting on the condition. */
|
|
Packit |
5e354d |
int nr_readers_queued;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The number of current readers. */
|
|
Packit |
5e354d |
int nr_readers;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Writers who want the lock wait on this condition, which is
|
|
Packit |
5e354d |
signaled when the current writer or last reader goes away. */
|
|
Packit |
5e354d |
npth_cond_t writer_wait;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The number of queued writers. */
|
|
Packit |
5e354d |
int nr_writers_queued;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The number of current writers. This is either 1 (then nr_readers
|
|
Packit |
5e354d |
is 0) or it is 0. At unlock time this value tells us if the
|
|
Packit |
5e354d |
current lock holder is a writer or a reader. */
|
|
Packit |
5e354d |
int nr_writers;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_init (npth_rwlock_t *rwlock_r,
|
|
Packit |
5e354d |
const npth_rwlockattr_t *user_attr)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_rwlock_t rwlock;
|
|
Packit |
5e354d |
npth_rwlockattr_t attr;
|
|
Packit |
5e354d |
int attr_allocated;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (user_attr != NULL)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
attr = *user_attr;
|
|
Packit |
5e354d |
attr_allocated = 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = npth_rwlockattr_init (&attr);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can not check *rwlock_r here, as it may contain random data. */
|
|
Packit |
5e354d |
rwlock = malloc (sizeof (*rwlock));
|
|
Packit |
5e354d |
if (!rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = errno;
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
rwlock->prefer_writer = (attr->kind == NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_cond_init (&rwlock->reader_wait, NULL);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free (rwlock);
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_cond_init (&rwlock->writer_wait, NULL);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_cond_destroy (&rwlock->reader_wait);
|
|
Packit |
5e354d |
free (rwlock);
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
rwlock->nr_readers = 0;
|
|
Packit |
5e354d |
rwlock->nr_readers_queued = 0;
|
|
Packit |
5e354d |
rwlock->nr_writers = 0;
|
|
Packit |
5e354d |
rwlock->nr_writers_queued = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*rwlock_r = rwlock;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err_out:
|
|
Packit |
5e354d |
if (attr_allocated)
|
|
Packit |
5e354d |
npth_rwlockattr_destroy (&attr);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Must be called with global lock held. */
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
rwlock_init_check (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
npth_rwlockattr_t attr;
|
|
Packit |
5e354d |
int kind;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*rwlock == 0)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock) == NPTH_RWLOCK_INITIALIZER)
|
|
Packit |
5e354d |
kind = NPTH_RWLOCK_PREFER_READER_NP;
|
|
Packit |
5e354d |
if ((*rwlock) == NPTH_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP)
|
|
Packit |
5e354d |
kind = NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
/* Already initialized. */
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Make sure we don't try again in case of error. */
|
|
Packit |
5e354d |
*rwlock = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_rwlockattr_init (&attr);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_rwlockattr_settype_np (&attr, kind);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
npth_rwlockattr_destroy (&attr);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_rwlock_init (rwlock, &attr);
|
|
Packit |
5e354d |
npth_rwlockattr_destroy (&attr);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_destroy (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (*rwlock == 0 || *rwlock == NPTH_RWLOCK_INITIALIZER)
|
|
Packit |
5e354d |
return EINVAL;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers || (*rwlock)->nr_readers || (*rwlock)->nr_writers_queued
|
|
Packit |
5e354d |
|| (*rwlock)->nr_readers_queued)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_cond_destroy (&(*rwlock)->reader_wait);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
/* FIXME: Log this. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = npth_cond_destroy (&(*rwlock)->writer_wait);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
/* FIXME: Log this. */
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
free (rwlock);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*rwlock = NULL;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_tryrdlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers_queued && (*rwlock)->prefer_writer)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_readers++;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_rdlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
while (1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Quick check. */
|
|
Packit |
5e354d |
err = npth_rwlock_tryrdlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_readers_queued++;
|
|
Packit |
5e354d |
err = npth_cond_wait (&(*rwlock)->reader_wait, NULL);
|
|
Packit |
5e354d |
(*rwlock)->nr_readers_queued--;
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_timedrdlock (npth_rwlock_t *rwlock,
|
|
Packit |
5e354d |
const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
while (1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Quick check. */
|
|
Packit |
5e354d |
err = npth_rwlock_tryrdlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_readers_queued++;
|
|
Packit |
5e354d |
err = npth_cond_timedwait (&(*rwlock)->reader_wait, NULL, abstime);
|
|
Packit |
5e354d |
(*rwlock)->nr_readers_queued--;
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_trywrlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock)->nr_readers)
|
|
Packit |
5e354d |
return EBUSY;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_writers = 1;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_wrlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
while (1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Quick check. */
|
|
Packit |
5e354d |
err = npth_rwlock_trywrlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_writers_queued++;
|
|
Packit |
5e354d |
err = npth_cond_wait (&(*rwlock)->writer_wait, NULL);
|
|
Packit |
5e354d |
(*rwlock)->nr_writers_queued--;
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_timedwrlock (npth_rwlock_t *rwlock,
|
|
Packit |
5e354d |
const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
while (1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Quick check. */
|
|
Packit |
5e354d |
err = npth_rwlock_trywrlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(*rwlock)->nr_writers_queued++;
|
|
Packit |
5e354d |
err = npth_cond_timedwait (&(*rwlock)->writer_wait, NULL, abstime);
|
|
Packit |
5e354d |
(*rwlock)->nr_writers_queued--;
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_unlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers)
|
|
Packit |
5e354d |
/* We are the writer. */
|
|
Packit |
5e354d |
(*rwlock)->nr_writers = 0;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
/* We are the reader. */
|
|
Packit |
5e354d |
(*rwlock)->nr_readers--;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if ((*rwlock)->nr_readers == 0)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if ((*rwlock)->nr_writers_queued)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = npth_cond_signal (&(*rwlock)->writer_wait);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else if ((*rwlock)->nr_readers_queued)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = npth_cond_broadcast (&(*rwlock)->reader_wait);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Standard POSIX Replacement API */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_usleep(unsigned int usec)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
Sleep((usec + 999) / 1000);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
unsigned int
|
|
Packit |
5e354d |
npth_sleep(unsigned int sec)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
Sleep (sec * 1000);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_system(const char *cmd)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = system(cmd);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
pid_t
|
|
Packit |
5e354d |
npth_waitpid(pid_t pid, int *status, int options)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
return EOPNOTSUPP;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_connect(int s, const struct sockaddr *addr, socklen_t addrlen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = connect(s, addr, addrlen);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = accept(s, addr, addrlen);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_select(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
|
Packit |
5e354d |
struct timeval *timeout)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = select(nfd, rfds, wfds, efds, timeout);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ssize_t
|
|
Packit |
5e354d |
npth_read(int fd, void *buf, size_t nbytes)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ssize_t res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = read(fd, buf, nbytes);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ssize_t
|
|
Packit |
5e354d |
npth_write(int fd, const void *buf, size_t nbytes)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ssize_t res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = write(fd, buf, nbytes);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_recvmsg (int fd, struct msghdr *msg, int flags)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
return EOPNOTSUPP;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_sendmsg (int fd, const struct msghdr *msg, int flags)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
return EOPNOTSUPP;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_unprotect (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* If we are not initialized we may not access the semaphore and
|
|
Packit |
5e354d |
* thus we shortcut it. Note that in this case the unprotect/protect
|
|
Packit |
5e354d |
* is not needed. For failsafe reasons if an nPth thread has ever
|
|
Packit |
5e354d |
* been created but nPth has accidentally not initialized we do not
|
|
Packit |
5e354d |
* shortcut so that a stack backtrace (due to the access of the
|
|
Packit |
5e354d |
* uninitialized semaphore) is more expressive. */
|
|
Packit |
5e354d |
if (initialized_or_any_threads)
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_protect (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* See npth_unprotect for commentary. */
|
|
Packit |
5e354d |
if (initialized_or_any_threads)
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_is_protected (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
return got_sceptre;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Maximum number of extra handles. We can only support 31 as that is
|
|
Packit |
5e354d |
the number of bits we can return. This is smaller than the maximum
|
|
Packit |
5e354d |
number of allowed wait objects for WFMO (which is 64). */
|
|
Packit |
5e354d |
#define MAX_EVENTS 31
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Although the WSAEventSelect machinery seems to have no limit on the
|
|
Packit |
5e354d |
number of selectable fds, we impose the same limit as used by
|
|
Packit |
5e354d |
traditional select. This allows us to work with a static data
|
|
Packit |
5e354d |
structure instead of an dynamically allocated array. */
|
|
Packit |
5e354d |
#define MAX_FDOBJS FD_SETSIZE
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Using WFMO even for sockets makes Windows objects more composable,
|
|
Packit |
5e354d |
which helps faking signals and other constructs, so we support
|
|
Packit |
5e354d |
that. You can still use npth_select for the plain select
|
|
Packit |
5e354d |
function. */
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_eselect(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
|
Packit |
5e354d |
const struct timespec *timeout,
|
|
Packit |
5e354d |
HANDLE *events, unsigned int *events_set)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err = 0;
|
|
Packit |
5e354d |
DWORD msecs;
|
|
Packit |
5e354d |
int i;
|
|
Packit |
5e354d |
u_int idx;
|
|
Packit |
5e354d |
/* One more for the handle associated with socket events. */
|
|
Packit |
5e354d |
HANDLE obj[MAX_EVENTS + 1];
|
|
Packit |
5e354d |
int nr_obj = 0;
|
|
Packit |
5e354d |
/* Number of extra events. */
|
|
Packit |
5e354d |
int nr_events = 0;
|
|
Packit |
5e354d |
HANDLE sock_event = INVALID_HANDLE_VALUE;
|
|
Packit |
5e354d |
/* This will be (nr_obj - 1) == nr_events. */
|
|
Packit |
5e354d |
int sock_event_idx = -1;
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
DWORD ret;
|
|
Packit |
5e354d |
SOCKET fd;
|
|
Packit |
5e354d |
long flags;
|
|
Packit |
5e354d |
int cnt;
|
|
Packit |
5e354d |
struct {
|
|
Packit |
5e354d |
SOCKET fd;
|
|
Packit |
5e354d |
long flags;
|
|
Packit |
5e354d |
} fdobj[MAX_FDOBJS];
|
|
Packit |
5e354d |
int nr_fdobj = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
(void)nfd; /* No need for it under Windows. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (events)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (!events_set)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
errno = EINVAL;
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We always ensure that the events_set is valid, even after an
|
|
Packit |
5e354d |
error. */
|
|
Packit |
5e354d |
*events_set = 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (timeout && (timeout->tv_sec < 0 || timeout->tv_nsec < 0))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
errno = EINVAL;
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (timeout == NULL)
|
|
Packit |
5e354d |
msecs = INFINITE;
|
|
Packit |
5e354d |
else if (timeout->tv_sec == 0 && timeout->tv_nsec == 0)
|
|
Packit |
5e354d |
msecs = 0;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
msecs = (timeout->tv_sec * 1000) + (timeout->tv_nsec + 999999) / 1000000;
|
|
Packit |
5e354d |
if (msecs < 1)
|
|
Packit |
5e354d |
msecs = 1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (events)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Copy the extra handles. */
|
|
Packit |
5e354d |
for (i = 0; i < MAX_EVENTS; i++)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
if (events[i] == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
break;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
obj[nr_obj] = events[i];
|
|
Packit |
5e354d |
nr_obj++;
|
|
Packit |
5e354d |
nr_events++;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We can only return the status of up to MAX_EVENTS handles in
|
|
Packit |
5e354d |
EVENTS_SET. */
|
|
Packit |
5e354d |
if (events[i] != INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
errno = EINVAL;
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* From here on, we clean up at err_out, and you can set ERR to
|
|
Packit |
5e354d |
return an error. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
sock_event = WSACreateEvent ();
|
|
Packit |
5e354d |
if (sock_event == INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = EINVAL;
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
sock_event_idx = nr_obj;
|
|
Packit |
5e354d |
obj[nr_obj] = sock_event;
|
|
Packit |
5e354d |
nr_obj++;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Combine FDs from all lists. */
|
|
Packit |
5e354d |
#define SET_FDOBJ(x,v) do { \
|
|
Packit |
5e354d |
for (idx=0; idx < (x)->fd_count; idx++) \
|
|
Packit |
5e354d |
{ \
|
|
Packit |
5e354d |
for (i=0; i < nr_fdobj; i++) \
|
|
Packit |
5e354d |
if (fdobj[i].fd == (x)->fd_array[idx]) \
|
|
Packit |
5e354d |
break; \
|
|
Packit |
5e354d |
if (i < nr_fdobj) \
|
|
Packit |
5e354d |
; \
|
|
Packit |
5e354d |
else if (nr_fdobj < MAX_FDOBJS) \
|
|
Packit |
5e354d |
{ \
|
|
Packit |
5e354d |
i = nr_fdobj++; \
|
|
Packit |
5e354d |
fdobj[i].fd = (x)->fd_array[idx]; \
|
|
Packit |
5e354d |
fdobj[i].flags = 0; \
|
|
Packit |
5e354d |
} \
|
|
Packit |
5e354d |
else \
|
|
Packit |
5e354d |
{ \
|
|
Packit |
5e354d |
err = EINVAL; \
|
|
Packit |
5e354d |
goto err_out; \
|
|
Packit |
5e354d |
} \
|
|
Packit |
5e354d |
fdobj[i].flags |= (v); \
|
|
Packit |
5e354d |
} \
|
|
Packit |
5e354d |
} while (0)
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (rfds)
|
|
Packit |
5e354d |
SET_FDOBJ (rfds, FD_READ | FD_ACCEPT);
|
|
Packit |
5e354d |
if (wfds)
|
|
Packit |
5e354d |
SET_FDOBJ (wfds, FD_WRITE);
|
|
Packit |
5e354d |
if (efds)
|
|
Packit |
5e354d |
SET_FDOBJ (efds, FD_OOB | FD_CLOSE);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#undef SET_FDOBJ
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Set the select flags. */
|
|
Packit |
5e354d |
for (i = 0; i < nr_fdobj; i++)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
res = WSAEventSelect (fdobj[i].fd, sock_event, fdobj[i].flags);
|
|
Packit |
5e354d |
if (res == SOCKET_ERROR)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = map_error (WSAGetLastError());
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Let's wait. */
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
ret = WaitForMultipleObjects (nr_obj, obj, FALSE, msecs);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
if (ret == WAIT_TIMEOUT)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = ETIMEDOUT;
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else if (ret == WAIT_FAILED)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = map_error (GetLastError());
|
|
Packit |
5e354d |
goto err_out;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* All other return values: We look at the objects. We must not
|
|
Packit |
5e354d |
fail from here, because then we could lose events. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Keep track of result count. */
|
|
Packit |
5e354d |
cnt = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
for (i = 0; i < nr_events; i++)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ret = WaitForSingleObject (obj[i], 0);
|
|
Packit |
5e354d |
if (ret != WAIT_OBJECT_0)
|
|
Packit |
5e354d |
/* We ignore errors here. */
|
|
Packit |
5e354d |
continue;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*events_set = (*events_set) | (1 << i);
|
|
Packit |
5e354d |
/* We consume the event here. This may be undesirable, but
|
|
Packit |
5e354d |
unless we make it configurable we need a common policy,
|
|
Packit |
5e354d |
and this saves the user one step. */
|
|
Packit |
5e354d |
ResetEvent (obj[i]);
|
|
Packit |
5e354d |
/* Increase result count. */
|
|
Packit |
5e354d |
cnt++;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Now update the file descriptors sets. */
|
|
Packit |
5e354d |
if (rfds)
|
|
Packit |
5e354d |
FD_ZERO (rfds);
|
|
Packit |
5e354d |
if (wfds)
|
|
Packit |
5e354d |
FD_ZERO (wfds);
|
|
Packit |
5e354d |
if (efds)
|
|
Packit |
5e354d |
FD_ZERO (efds);
|
|
Packit |
5e354d |
for (i = 0; i < nr_fdobj; i++)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
WSANETWORKEVENTS ne;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
fd = fdobj[i].fd;
|
|
Packit |
5e354d |
flags = fdobj[i].flags;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
res = WSAEnumNetworkEvents (fd, NULL, &ne);
|
|
Packit |
5e354d |
if (res == SOCKET_ERROR)
|
|
Packit |
5e354d |
continue; /* FIXME: We ignore this error here. */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* NB that the test on FLAGS guarantees that ?fds is not NULL. */
|
|
Packit |
5e354d |
if ((flags & FD_READ) && (ne.lNetworkEvents & (FD_READ | FD_ACCEPT)))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
FD_SET (fd, rfds);
|
|
Packit |
5e354d |
cnt++;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
if ((flags & FD_WRITE) && (ne.lNetworkEvents & FD_WRITE))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
FD_SET (fd, wfds);
|
|
Packit |
5e354d |
cnt++;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
if ((flags & FD_CLOSE) && (ne.lNetworkEvents & (FD_OOB | FD_CLOSE)))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
FD_SET (fd, efds);
|
|
Packit |
5e354d |
cnt++;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
WSAEventSelect (fd, NULL, 0); /* We ignore errors. */
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* We ignore errors. */
|
|
Packit |
5e354d |
WSACloseEvent (sock_event);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return cnt;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Cleanup. */
|
|
Packit |
5e354d |
err_out:
|
|
Packit |
5e354d |
if (sock_event != INVALID_HANDLE_VALUE)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
for (i = 0; i < nr_fdobj; i++)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
WSAEventSelect (fdobj[i].fd, NULL, 0); /* We ignore errors. */
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
WSACloseEvent (sock_event); /* We ignore errors. */
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (err == ETIMEDOUT)
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
errno = err;
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
}
|