/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */
/*
* Copyright (c) Likewise Software. All rights reserved.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program 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. You should have received a copy of the GNU General
* Public License along with this program. If not, see
* .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewise.com
*/
/*
* Copyright (C) Likewise Software. All rights reserved.
*
* Module Name:
*
* threads.c
*
* Abstract:
*
* Likewise Base
*
* Thread Utilities
*
* Authors: Danilo Almeida (dalmeida@likewise.com)
*/
#include "lwthreads.h"
#include
#include
#include
#include
#include
#include
#define _PTHREAD_FUNCTION_CANNOT_FAIL(Function, ...) \
do { \
int error = Function(__VA_ARGS__); \
LWIO_ASSERT_FORMAT(!error, #Function "() failed (error = %d)", error); \
} while (0)
#define _PTHREAD_FUNCTION_CAN_FAIL(pIsOk, FailValue, Function, ...) \
do { \
int error = Function(__VA_ARGS__); \
LWIO_ASSERT_FORMAT(!error || ((FailValue) == error), #Function "() failed (error = %d)", error); \
*(pIsOk) = error ? FALSE : TRUE; \
} while (0)
#define PTHREAD_MUTEXATTTR_DESTROY(pMutexAttr) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_mutexattr_destroy, pMutexAttr)
#define PTHREAD_MUTEX_TRYLOCK(pIsOk, pMutex) \
_PTHREAD_FUNCTION_CAN_FAIL(pIsOk, EBUSY, pthread_mutex_trylock, pMutex)
#define PTHREAD_MUTEX_LOCK(pMutex) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_mutex_lock, pMutex)
#define PTHREAD_MUTEX_UNLOCK(pMutex) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_mutex_unlock, pMutex)
#define PTHREAD_MUTEX_DESTROY(pMutex) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_mutex_destroy, pMutex)
#define PTHREAD_COND_TIMEDWAIT(pIsOk, pCondition, pMutex, pAbsoluteTimespec) \
_PTHREAD_FUNCTION_CAN_FAIL(pIsOk, ETIMEDOUT, pthread_cond_timedwait, pCondition, pMutex, pAbsoluteTimespec)
#define PTHREAD_COND_WAIT(pCondition, pMutex) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_cond_wait, pCondition, pMutex)
#define PTHREAD_COND_SIGNAL(pCondition) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_cond_signal, pCondition)
#define PTHREAD_COND_BROADCAST(pCondition) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_cond_broadcast, pCondition)
#define PTHREAD_COND_DESTROY(pCondition) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_cond_destroy, pCondition)
#define PTHREAD_JOIN(pThread, pResult) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_join, pThread, pResult)
#define PTHREAD_DETACH(pThread) \
_PTHREAD_FUNCTION_CANNOT_FAIL(pthread_detach, pThread)
#define _LW_RTL_UNIX_TO_WINDOWS_EPOCH_OFFSET_SECONDS 11644473600LL
static
NTSTATUS
LwRtlpUnixTimeFromWindowsTime(
OUT PLW_RTL_UNIX_TIME_SECONDS UnixTimeSeconds,
OUT OPTIONAL PLONG UnixTimeMicroseconds,
OUT OPTIONAL PLONG UnixTimeNanoseconds,
IN LW_RTL_WINDOWS_TIME WindowsTime
)
{
NTSTATUS status = STATUS_SUCCESS;
LW_RTL_UNIX_TIME_SECONDS unixTimeSeconds = 0;
LONG microseconds = 0;
LONG nanoseconds = 0;
LONG64 windowsTimeSeconds = 0;
if (UnixTimeMicroseconds && UnixTimeNanoseconds)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (WindowsTime < 0)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
windowsTimeSeconds = WindowsTime / LW_RTL_WINDOWS_TIMESPAN_SECOND;
unixTimeSeconds = windowsTimeSeconds - _LW_RTL_UNIX_TO_WINDOWS_EPOCH_OFFSET_SECONDS;
LWIO_ASSERT(unixTimeSeconds <= MAXLONG);
LWIO_ASSERT(unixTimeSeconds >= MINLONG);
if (UnixTimeMicroseconds)
{
microseconds = WindowsTime / LW_RTL_WINDOWS_TIMESPAN_MICROSECOND - (windowsTimeSeconds * 1000 * 1000);
LWIO_ASSERT(microseconds < (1000 * 1000));
}
if (UnixTimeNanoseconds)
{
nanoseconds = (WindowsTime - (windowsTimeSeconds * LW_RTL_WINDOWS_TIMESPAN_SECOND)) * 100;
LWIO_ASSERT(nanoseconds < (1000 * 1000 * 1000));
}
cleanup:
if (status)
{
unixTimeSeconds = 0;
microseconds = 0;
nanoseconds = 0;
}
*UnixTimeSeconds = unixTimeSeconds;
if (UnixTimeMicroseconds)
{
*UnixTimeMicroseconds = microseconds;
}
if (UnixTimeNanoseconds)
{
*UnixTimeNanoseconds = nanoseconds;
}
return status;
}
static
NTSTATUS
LwRtlpWindowsTimeFromUnixTime(
OUT PLW_RTL_WINDOWS_TIME WindowsTime,
IN LW_RTL_UNIX_TIME_SECONDS UnixTimeSeconds,
IN OPTIONAL PLONG UnixTimeMicroseconds,
IN OPTIONAL PLONG UnixTimeNanoseconds
)
{
NTSTATUS status = STATUS_SUCCESS;
LW_RTL_WINDOWS_TIME windowsTime = 0;
if (UnixTimeMicroseconds && UnixTimeNanoseconds)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if ((UnixTimeSeconds < 0) &&
((- _LW_RTL_UNIX_TO_WINDOWS_EPOCH_OFFSET_SECONDS) > UnixTimeSeconds))
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
windowsTime = (UnixTimeSeconds + _LW_RTL_UNIX_TO_WINDOWS_EPOCH_OFFSET_SECONDS) * LW_RTL_WINDOWS_TIMESPAN_SECOND;
if (UnixTimeMicroseconds)
{
LONG microseconds = *UnixTimeMicroseconds;
if (microseconds < 0)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
windowsTime += microseconds * LW_RTL_WINDOWS_TIMESPAN_MICROSECOND;
}
if (UnixTimeNanoseconds)
{
LONG nanoseconds = *UnixTimeNanoseconds;
if (nanoseconds < 0)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
windowsTime += nanoseconds / 100;
}
if (windowsTime < UnixTimeSeconds)
{
status = STATUS_INTEGER_OVERFLOW;
GOTO_CLEANUP();
}
cleanup:
if (status)
{
windowsTime = 0;
}
*WindowsTime = windowsTime;
return status;
}
NTSTATUS
LwRtlpGetCurrentUnixTime(
OUT PLW_RTL_UNIX_TIME_SECONDS UnixTimeSeconds,
OUT OPTIONAL PLONG UnixTimeMicroseconds,
OUT OPTIONAL PLONG UnixTimeNanoseconds
)
{
NTSTATUS status = STATUS_SUCCESS;
LW_RTL_UNIX_TIME_SECONDS seconds = 0;
LONG microseconds = 0;
LONG nanoseconds = 0;
if (UnixTimeMicroseconds && UnixTimeNanoseconds)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!UnixTimeMicroseconds && !UnixTimeNanoseconds)
{
time_t now = time(NULL);
if (-1 == now)
{
// POSIX does not indicate that errno is used.
status = STATUS_UNSUCCESSFUL;
GOTO_CLEANUP();
}
seconds = now;
}
else
{
struct timeval now = { 0 };
if (gettimeofday(&now, NULL) < 0)
{
int error = errno;
status = LwErrnoToNtStatus(error);
LWIO_ASSERT(status);
GOTO_CLEANUP_ON_STATUS(status);
}
seconds = now.tv_sec;
microseconds = now.tv_usec;
nanoseconds = microseconds * 1000;
}
cleanup:
*UnixTimeSeconds = seconds;
if (UnixTimeMicroseconds)
{
*UnixTimeMicroseconds = microseconds;
}
if (UnixTimeNanoseconds)
{
*UnixTimeNanoseconds = nanoseconds;
}
return status;
}
NTSTATUS
LwRtlGetCurrentWindowsTime(
OUT PLW_RTL_WINDOWS_TIME WindowsTime
)
{
NTSTATUS status = STATUS_SUCCESS;
LW_RTL_WINDOWS_TIME windowsTime = 0;
LW_RTL_UNIX_TIME_SECONDS unixSeconds = 0;
LONG unixNanoseconds = 0;
status = LwRtlpGetCurrentUnixTime(&unixSeconds, NULL, &unixNanoseconds);
GOTO_CLEANUP_ON_STATUS(status);
status = LwRtlpWindowsTimeFromUnixTime(&windowsTime, unixSeconds, NULL, &unixNanoseconds);
GOTO_CLEANUP_ON_STATUS(status);
cleanup:
*WindowsTime = windowsTime;
return status;
}
//
// time_t is a signed integral type at least 32-bits wide.
//
// struct timespec has:
//
// - time_t tv_sec (seconds)
// - long tv_nsec (nanoseconds)
//
// struct timeval has:
//
// - time_t tv_sec (seconds)
// - suseconds_t tv_usec (microseconds)
//
// suseconds_t is signed integral ranging at least [-1, 1,000,000]
//
static
NTSTATUS
LwRtlpUnixTimespecFromWindowsTime(
OUT struct timespec* UnixTimespec,
IN LW_RTL_WINDOWS_TIME WindowsTime
)
{
NTSTATUS status = STATUS_SUCCESS;
struct timespec result = { 0 };
LW_RTL_UNIX_TIME_SECONDS unixTimeSeconds = 0;
LONG unixTimeNanoseconds = 0;
LW_RTL_WINDOWS_TIME currentTime = 0;
LW_RTL_WINDOWS_TIME useTime = 0;
if (WindowsTime < 0)
{
status = LwRtlGetCurrentWindowsTime(¤tTime);
GOTO_CLEANUP_ON_STATUS(status);
useTime = currentTime + - WindowsTime;
if (useTime < currentTime)
{
status = STATUS_INTEGER_OVERFLOW;
GOTO_CLEANUP();
}
}
else
{
useTime = WindowsTime;
}
status = LwRtlpUnixTimeFromWindowsTime(
&unixTimeSeconds,
NULL,
&unixTimeNanoseconds,
useTime);
GOTO_CLEANUP_ON_STATUS(status);
result.tv_sec = unixTimeSeconds;
result.tv_nsec = unixTimeNanoseconds;
cleanup:
*UnixTimespec = result;
return status;
}
// Internal flags
#define _LW_RTL_EVENT_FLAG_MUTEX_INITIALIZED 0x1
#define _LW_RTL_EVENT_FLAG_COND_INITIALIZED 0x2
#define _LW_RTL_EVENT_FLAG_SET 0x4
static
BOOLEAN
LwRtlpIsInitializedEvent(
IN PLW_RTL_EVENT pEvent
)
{
return (IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_MUTEX_INITIALIZED) &&
IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_COND_INITIALIZED));
}
// TODO: Add IsManualReset
NTSTATUS
LwRtlInitializeEvent(
OUT PLW_RTL_EVENT pEvent
)
{
NTSTATUS status = STATUS_SUCCESS;
int error = 0;
pEvent->Private.Flags = 0;
error = pthread_mutex_init(&pEvent->Private.Mutex, NULL);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
SetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_MUTEX_INITIALIZED);
error = pthread_cond_init(&pEvent->Private.Condition, NULL);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
SetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_COND_INITIALIZED);
cleanup:
if (status)
{
LwRtlCleanupEvent(pEvent);
}
return status;
}
VOID
LwRtlCleanupEvent(
IN OUT PLW_RTL_EVENT pEvent
)
{
if (pEvent)
{
if (IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_COND_INITIALIZED))
{
PTHREAD_COND_DESTROY(&pEvent->Private.Condition);
}
if (IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_MUTEX_INITIALIZED))
{
PTHREAD_MUTEX_DESTROY(&pEvent->Private.Mutex);
}
pEvent->Private.Flags = 0;
}
}
#if 0
static
LW_BOOLEAN
LwRtlpTimedWaitEvent(
IN PLW_RTL_EVENT pEvent,
IN OPTIONAL PLW_RTL_WINDOWS_TIME Timeout
)
{
BOOLEAN gotLock = FALSE;
struct timespec absoluteTimespec = { 0 };
NTSTATUS status = 0;
LWIO_ASSERT(LwRtlpIsInitializedEvent(pEvent));
status = LwRtlpUnixTimespecFromWindowsTime(&absoluteTimespec, *Timeout);
LWIO_ASSERT(NT_SUCCESS(status));
if (status)
{
GOTO_CLEANUP();
}
PTHREAD_MUTEX_LOCK(&pEvent->Private.Mutex);
// Protect against spurious or stolen wakes. Note thta
// this should never happen for pthread condition variables.
while (!IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET))
{
PTHREAD_COND_TIMEDWAIT(
&gotLock,
&pEvent->Private.Condition,
&pEvent->Private.Mutex,
&absoluteTimespec);
if (!gotLock)
{
break;
}
}
if (gotLock)
{
LWIO_ASSERT(IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET));
PTHREAD_MUTEX_UNLOCK(&pEvent->Private.Mutex);
}
cleanup:
return gotLock;
}
static
LW_VOID
LwRtlpSimpleWaitEvent(
IN PLW_RTL_EVENT pEvent
)
{
LWIO_ASSERT(LwRtlpIsInitializedEvent(pEvent));
PTHREAD_MUTEX_LOCK(&pEvent->Private.Mutex);
// Protect against spurious or stolen wakes. Note thta
// this should never happen for pthread condition variables.
while (!IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET))
{
PTHREAD_COND_WAIT(&pEvent->Private.Condition, &pEvent->Private.Mutex);
// TODO-Perhaps remove this assert
LWIO_ASSERT(IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET));
}
PTHREAD_MUTEX_UNLOCK(&pEvent->Private.Mutex);
}
#endif
LW_BOOLEAN
LwRtlWaitEvent(
IN PLW_RTL_EVENT pEvent,
IN OPTIONAL PLW_RTL_WINDOWS_TIME Timeout
)
{
BOOLEAN isSignalled = FALSE;
struct timespec absoluteTimespec = { 0 };
LWIO_ASSERT(LwRtlpIsInitializedEvent(pEvent));
if (Timeout)
{
NTSTATUS status = 0;
status = LwRtlpUnixTimespecFromWindowsTime(&absoluteTimespec, *Timeout);
LWIO_ASSERT(NT_SUCCESS(status));
if (status)
{
GOTO_CLEANUP();
}
}
PTHREAD_MUTEX_LOCK(&pEvent->Private.Mutex);
if (Timeout)
{
// Protect against spurious or stolen wakes. Note thta
// this should never happen for pthread condition variables.
while (!IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET))
{
PTHREAD_COND_TIMEDWAIT(
&isSignalled,
&pEvent->Private.Condition,
&pEvent->Private.Mutex,
&absoluteTimespec);
if (!isSignalled)
{
break;
}
}
}
else
{
// Protect against spurious or stolen wakes. Note thta
// this should never happen for pthread condition variables.
while (!IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET))
{
PTHREAD_COND_WAIT(&pEvent->Private.Condition, &pEvent->Private.Mutex);
// TODO-Perhaps remove this assert
LWIO_ASSERT(IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET));
}
isSignalled = TRUE;
}
if (isSignalled)
{
LWIO_ASSERT(IsSetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET));
}
PTHREAD_MUTEX_UNLOCK(&pEvent->Private.Mutex);
cleanup:
return isSignalled;
}
VOID
LwRtlSetEvent(
IN PLW_RTL_EVENT pEvent
)
{
LWIO_ASSERT(LwRtlpIsInitializedEvent(pEvent));
PTHREAD_MUTEX_LOCK(&pEvent->Private.Mutex);
SetFlag(pEvent->Private.Flags, _LW_RTL_EVENT_FLAG_SET);
PTHREAD_COND_BROADCAST(&pEvent->Private.Condition);
PTHREAD_MUTEX_UNLOCK(&pEvent->Private.Mutex);
}
// Internal flags
#define _LW_RTL_MUTEX_FLAG_INITIALIZED 0x1
static
BOOLEAN
LwRtlpIsInitializedMutex(
IN PLW_RTL_MUTEX pMutex
)
{
return IsSetFlag(pMutex->Private.Flags, _LW_RTL_MUTEX_FLAG_INITIALIZED);
}
NTSTATUS
LwRtlInitializeMutex(
OUT PLW_RTL_MUTEX pMutex,
IN BOOLEAN IsRecursive
)
{
NTSTATUS status = STATUS_SUCCESS;
int error = 0;
BOOLEAN isMutexAttrInitialized = FALSE;
pthread_mutexattr_t mutexAttr;
pthread_mutexattr_t* useMutexAttr = NULL;
pMutex->Private.Flags = 0;
if (IsRecursive)
{
error = pthread_mutexattr_init(&mutexAttr);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
isMutexAttrInitialized = TRUE;
error = pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
useMutexAttr = &mutexAttr;
}
#if 1
else
{
error = pthread_mutexattr_init(&mutexAttr);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
isMutexAttrInitialized = TRUE;
error = pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_ERRORCHECK);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
useMutexAttr = &mutexAttr;
}
#endif
error = pthread_mutex_init(&pMutex->Private.Mutex, useMutexAttr);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
SetFlag(pMutex->Private.Flags, _LW_RTL_MUTEX_FLAG_INITIALIZED);
cleanup:
if (status)
{
LwRtlCleanupMutex(pMutex);
}
if (isMutexAttrInitialized)
{
PTHREAD_MUTEXATTTR_DESTROY(&mutexAttr);
}
return status;
}
VOID
LwRtlCleanupMutex(
IN OUT PLW_RTL_MUTEX pMutex
)
{
if (pMutex)
{
if (IsSetFlag(pMutex->Private.Flags, _LW_RTL_MUTEX_FLAG_INITIALIZED))
{
PTHREAD_MUTEX_DESTROY(&pMutex->Private.Mutex);
}
pMutex->Private.Flags = 0;
}
}
BOOLEAN
LwRtlTryLockMutex(
IN PLW_RTL_MUTEX pMutex
)
{
BOOLEAN gotLock = FALSE;
LWIO_ASSERT(LwRtlpIsInitializedMutex(pMutex));
PTHREAD_MUTEX_TRYLOCK(&gotLock, &pMutex->Private.Mutex);
return gotLock;
}
VOID
LwRtlLockMutex(
IN PLW_RTL_MUTEX pMutex
)
{
LWIO_ASSERT(LwRtlpIsInitializedMutex(pMutex));
PTHREAD_MUTEX_LOCK(&pMutex->Private.Mutex);
}
VOID
LwRtlUnlockMutex(
IN PLW_RTL_MUTEX pMutex
)
{
LWIO_ASSERT(LwRtlpIsInitializedMutex(pMutex));
PTHREAD_MUTEX_UNLOCK(&pMutex->Private.Mutex);
}
// Internal flags
#define _LW_RTL_CONDITION_VARIABLE_FLAG_INITIALIZED 0x1
static
BOOLEAN
LwRtlpIsInitializedConditionVariable(
IN PLW_RTL_CONDITION_VARIABLE pConditionVariable
)
{
return IsSetFlag(pConditionVariable->Private.Flags, _LW_RTL_CONDITION_VARIABLE_FLAG_INITIALIZED);
}
LW_NTSTATUS
LwRtlInitializeConditionVariable(
OUT PLW_RTL_CONDITION_VARIABLE pConditionVariable
)
{
NTSTATUS status = STATUS_SUCCESS;
int error = 0;
pConditionVariable->Private.Flags = 0;
error = pthread_cond_init(&pConditionVariable->Private.Condition, NULL);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
SetFlag(pConditionVariable->Private.Flags, _LW_RTL_CONDITION_VARIABLE_FLAG_INITIALIZED);
cleanup:
if (status)
{
LwRtlCleanupConditionVariable(pConditionVariable);
}
return status;
}
LW_VOID
LwRtlCleanupConditionVariable(
IN OUT PLW_RTL_CONDITION_VARIABLE pConditionVariable
)
{
if (pConditionVariable)
{
if (IsSetFlag(pConditionVariable->Private.Flags, _LW_RTL_CONDITION_VARIABLE_FLAG_INITIALIZED))
{
PTHREAD_COND_DESTROY(&pConditionVariable->Private.Condition);
}
pConditionVariable->Private.Flags = 0;
}
}
LW_BOOLEAN
LwRtlWaitConditionVariable(
IN PLW_RTL_CONDITION_VARIABLE pConditionVariable,
IN PLW_RTL_MUTEX pMutex,
IN OPTIONAL PLW_RTL_WINDOWS_TIME Timeout
)
{
BOOLEAN isSignalled = FALSE;
LWIO_ASSERT(LwRtlpIsInitializedConditionVariable(pConditionVariable));
LWIO_ASSERT(LwRtlpIsInitializedMutex(pMutex));
if (Timeout)
{
NTSTATUS status = 0;
struct timespec absoluteTimespec = { 0 };
status = LwRtlpUnixTimespecFromWindowsTime(&absoluteTimespec, *Timeout);
LWIO_ASSERT(NT_SUCCESS(status));
if (!status)
{
PTHREAD_COND_TIMEDWAIT(
&isSignalled,
&pConditionVariable->Private.Condition,
&pMutex->Private.Mutex,
&absoluteTimespec);
}
}
else
{
PTHREAD_COND_WAIT(&pConditionVariable->Private.Condition, &pMutex->Private.Mutex);
isSignalled = TRUE;
}
return isSignalled;
}
LW_VOID
LwRtlSignalConditionVariable(
IN PLW_RTL_CONDITION_VARIABLE pConditionVariable
)
{
LWIO_ASSERT(LwRtlpIsInitializedConditionVariable(pConditionVariable));
PTHREAD_COND_SIGNAL(&pConditionVariable->Private.Condition);
}
LW_VOID
LwRtlBroadcastConditionVariable(
IN PLW_RTL_CONDITION_VARIABLE pConditionVariable
)
{
LWIO_ASSERT(LwRtlpIsInitializedConditionVariable(pConditionVariable));
PTHREAD_COND_BROADCAST(&pConditionVariable->Private.Condition);
}
typedef struct _LW_RTL_THREAD {
struct {
pthread_t Thread;
} Private;
} LW_RTL_THREAD;
LW_NTSTATUS
LwRtlCreateThread(
OUT PLW_RTL_THREAD* ppThread,
IN LW_THREAD_PROC ThreadRoutine,
IN LW_PVOID ThreadContext
)
{
NTSTATUS status = 0;
int error = 0;
PLW_RTL_THREAD pThread = NULL;
status = LW_RTL_ALLOCATE(&pThread, LW_RTL_THREAD, sizeof(*pThread));
GOTO_CLEANUP_ON_STATUS(status);
error = pthread_create(&pThread->Private.Thread, NULL, ThreadRoutine, ThreadContext);
status = LwErrnoToNtStatus(error);
// cannot fail after here
cleanup:
if (status)
{
RTL_FREE(&pThread);
}
*ppThread = pThread;
return status;
}
LW_PVOID
LwRtlJoinThread(
IN OUT PLW_RTL_THREAD pThread
)
{
LW_PVOID result = NULL;
PTHREAD_JOIN(pThread->Private.Thread, &result);
RTL_FREE(&pThread);
return result;
}
LW_VOID
LwRtlDetachThread(
IN OUT PLW_RTL_THREAD pThread
)
{
PTHREAD_DETACH(pThread->Private.Thread);
RTL_FREE(&pThread);
}
////////
static
NTSTATUS
LwRtlThreadsCreateThreadEx(
OUT pthread_t** ppThread,
IN BOOLEAN bStartDetached,
IN PVOID (*ThreadRoutine)(PVOID),
IN PVOID ThreadContext
)
{
NTSTATUS status = 0;
int error = 0;
pthread_t* pThread = NULL;
pthread_attr_t* pThreadAttr = NULL;
pthread_attr_t threadAttr;
status = LW_RTL_ALLOCATE(&pThread, pthread_t, sizeof(*pThread));
GOTO_CLEANUP_ON_STATUS(status);
if (bStartDetached)
{
error = pthread_attr_init(&threadAttr);
// ISSUE-2008/10/02-dalmeida -- Map error code?
status = error;
GOTO_CLEANUP_ON_STATUS(status);
error = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
pThreadAttr = &threadAttr;
}
error = pthread_create(pThread, pThreadAttr, ThreadRoutine, ThreadContext);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
if (bStartDetached)
{
RTL_FREE(&pThread);
}
cleanup:
if (status)
{
RTL_FREE(&pThread);
}
*ppThread = pThread;
return status;
}
NTSTATUS
LwRtlThreadsCreateThread(
OUT pthread_t** ppThread,
IN PVOID (*ThreadRoutine)(PVOID),
IN PVOID ThreadContext
)
{
return LwRtlThreadsCreateThreadEx(ppThread, FALSE, ThreadRoutine, ThreadContext);
}
NTSTATUS
LwRtlThreadsCreateDetachedThread(
IN PVOID (*ThreadRoutine)(PVOID),
IN PVOID ThreadContext
)
{
pthread_t* pThread = NULL;
return LwRtlThreadsCreateThreadEx(&pThread, TRUE, ThreadRoutine, ThreadContext);
}
static
NTSTATUS
LwRtlThreadsCreateOrInitializeMutex(
OUT OPTIONAL pthread_mutex_t** NewMutex,
OUT OPTIONAL pthread_mutex_t* ExistingMutex,
IN LW_RTL_THREADS_MUTEX_TYPE MutexType
)
{
NTSTATUS status = 0;
int error = 0;
pthread_mutexattr_t mutexAttrStorage;
pthread_mutexattr_t* mutexAttr = NULL;
pthread_mutex_t* newMutex = NULL;
pthread_mutex_t* useMutex = NULL;
if (IS_BOTH_OR_NEITHER(NewMutex, ExistingMutex))
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (MutexType)
{
error = pthread_mutexattr_init(&mutexAttrStorage);
if (error)
{
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
}
mutexAttr = &mutexAttrStorage;
switch (MutexType)
{
case LW_RTL_THREADS_MUTEX_TYPE_RECURSIVE:
error = pthread_mutexattr_settype(mutexAttr, PTHREAD_MUTEX_RECURSIVE);
if (error)
{
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
}
GOTO_CLEANUP_ON_STATUS(status);
break;
default:
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
}
useMutex = ExistingMutex;
if (!useMutex)
{
status = LW_RTL_ALLOCATE(&newMutex, pthread_mutex_t, sizeof(*newMutex));
GOTO_CLEANUP_ON_STATUS(status);
useMutex = newMutex;
}
error = pthread_mutex_init(useMutex, mutexAttr);
if (error)
{
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
}
newMutex = NULL;
cleanup:
LwRtlThreadsDestroyMutex(&newMutex);
if (mutexAttr)
{
pthread_mutexattr_destroy(mutexAttr);
}
if (NewMutex)
{
*NewMutex = useMutex;
}
return status;
}
NTSTATUS
LwRtlThreadsCreateMutex(
OUT pthread_mutex_t** ppMutex,
IN LW_RTL_THREADS_MUTEX_TYPE MutexType
)
{
return LwRtlThreadsCreateOrInitializeMutex(ppMutex, NULL, MutexType);
}
VOID
LwRtlThreadsDestroyMutex(
IN OUT pthread_mutex_t** ppMutex
)
{
if (*ppMutex)
{
pthread_mutex_destroy(*ppMutex);
LwRtlMemoryFree(*ppMutex);
*ppMutex = NULL;
}
}
NTSTATUS
LwRtlThreadsInitializeMutex(
OUT pthread_mutex_t* pMutex,
IN LW_RTL_THREADS_MUTEX_TYPE MutexType
)
{
return LwRtlThreadsCreateOrInitializeMutex(NULL, pMutex, MutexType);
}
VOID
LwRtlThreadsCleanupMutex(
IN OUT pthread_mutex_t* pMutex
)
{
if (pMutex)
{
pthread_mutex_destroy(pMutex);
}
}
#define _LW_RTL_THREADS_LOG_ERROR(Format, ...)
#define _LW_RTL_THREADS_MUTEX_OP(Function, pMutex) \
do { \
int mutexOpError = (Function)(pMutex); \
if (mutexOpError) \
{ \
_LW_RTL_THREADS_LOG_ERROR(#Function "() failed (error = %d)", mutexOpError); \
} \
} while (0)
VOID
LwThreadsAcquireMutex(
IN pthread_mutex_t* pMutex
)
{
_LW_RTL_THREADS_MUTEX_OP(pthread_mutex_lock, pMutex);
}
VOID
LwThreadsReleaseMutex(
IN pthread_mutex_t* pMutex
)
{
_LW_RTL_THREADS_MUTEX_OP(pthread_mutex_unlock, pMutex);
}
NTSTATUS
LwRtlThreadsCreateCond(
OUT pthread_cond_t** ppCond
)
{
NTSTATUS status = 0;
int error = 0;
pthread_cond_t* pCond = NULL;
status = LW_RTL_ALLOCATE(&pCond, pthread_cond_t, sizeof(*pCond));
GOTO_CLEANUP_ON_STATUS(status);
error = pthread_cond_init(pCond, NULL);
status = LwErrnoToNtStatus(error);
GOTO_CLEANUP_ON_STATUS(status);
cleanup:
if (status)
{
RTL_FREE(&pCond);
}
*ppCond = pCond;
return status;
}
VOID
LwRtlThreadsDestroyCond(
IN OUT pthread_cond_t** ppCond
)
{
if (*ppCond)
{
pthread_cond_destroy(*ppCond);
LwRtlMemoryFree(*ppCond);
*ppCond = NULL;
}
}