123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- /*++
- Copyright (c) 2015 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- sema.c
- Abstract:
- This module implements support for POSIX semaphores.
- Author:
- Evan Green 4-May-2015
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "pthreadp.h"
- #include <limits.h>
- #include <semaphore.h>
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // These macros manipulate the count inside the semaphore state.
- //
- #define PTHREAD_SEMAPHORE_GET_COUNT(_State) \
- ((INT)(_State) >> PTHREAD_SEMAPHORE_VALUE_SHIFT)
- //
- // This macro returns the semaphore state for a given count.
- //
- #define PTHREAD_SEMAPHORE_SET_COUNT(_Count) \
- (((ULONG)(_Count) << PTHREAD_SEMAPHORE_VALUE_SHIFT) & \
- PTHREAD_SEMAPHORE_VALUE_MASK)
- //
- // These macros increment and decrement the count in the given state.
- //
- #define PTHREAD_SEMAPHORE_DECREMENT_STATE(_State) \
- (((_State) - (1U << PTHREAD_SEMAPHORE_VALUE_SHIFT)) & \
- PTHREAD_SEMAPHORE_VALUE_MASK)
- #define PTHREAD_SEMAPHORE_INCREMENT_STATE(_State) \
- (((_State) + (1U << PTHREAD_SEMAPHORE_VALUE_SHIFT)) & \
- PTHREAD_SEMAPHORE_VALUE_MASK)
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define PTHREAD_SEMAPHORE_SHARED 0x00000001
- #define PTHREAD_SEMAPHORE_VALUE_SHIFT 1
- #define PTHREAD_SEMAPHORE_VALUE_MASK 0xFFFFFFFE
- #define PTHREAD_SEMAPHORE_WAITED_ON PTHREAD_SEMAPHORE_SET_COUNT(-1)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- ClpSemaphoreDecrement (
- PPTHREAD_SEMAPHORE Semaphore
- );
- INT
- ClpSemaphoreTryToDecrement (
- PPTHREAD_SEMAPHORE Semaphore
- );
- INT
- ClpSemaphoreIncrement (
- PPTHREAD_SEMAPHORE Semaphore
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- PTHREAD_API
- int
- sem_init (
- sem_t *Semaphore,
- int Shared,
- unsigned int Value
- )
- /*++
- Routine Description:
- This routine initializes a semaphore object.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to initialize.
- Shared - Supplies a boolean indicating whether the semaphore should be
- shared across processes (non-zero) or private to a particular process
- (zero).
- Value - Supplies the initial value to set in the semaphore.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- PPTHREAD_SEMAPHORE SemaphoreInternal;
- ULONG State;
- if (Value > SEM_VALUE_MAX) {
- errno = EINVAL;
- return -1;
- }
- State = PTHREAD_SEMAPHORE_SET_COUNT(Value);
- if (Shared != 0) {
- State |= PTHREAD_SEMAPHORE_SHARED;
- }
- SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
- SemaphoreInternal->State = State;
- return 0;
- }
- PTHREAD_API
- int
- sem_destroy (
- sem_t *Semaphore
- )
- /*++
- Routine Description:
- This routine releases all resources associated with a POSIX semaphore.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to destroy.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- return 0;
- }
- PTHREAD_API
- int
- sem_wait (
- sem_t *Semaphore
- )
- /*++
- Routine Description:
- This routine blocks until the given semaphore can be decremented to zero or
- above. On success, the semaphore value will be decremented.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to wait on.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- ULONG Operation;
- PPTHREAD_SEMAPHORE SemaphoreInternal;
- ULONG Shared;
- KSTATUS Status;
- ULONG Value;
- SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
- Shared = SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED;
- while (TRUE) {
- if (ClpSemaphoreDecrement(SemaphoreInternal) > 0) {
- return 0;
- }
- Operation = UserLockWait;
- if (Shared == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- Value = PTHREAD_SEMAPHORE_WAITED_ON | Shared;
- Status = OsUserLock(&(SemaphoreInternal->State),
- Operation,
- &Value,
- SYS_WAIT_TIME_INDEFINITE);
- if (Status == STATUS_INTERRUPTED) {
- errno = EINTR;
- return -1;
- }
- }
- return 0;
- }
- PTHREAD_API
- int
- sem_timedwait (
- sem_t *Semaphore,
- const struct timespec *AbsoluteTimeout
- )
- /*++
- Routine Description:
- This routine blocks until the given semaphore can be decremented to zero or
- above. This routine may time out after the specified deadline.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to wait on.
- AbsoluteTimeout - Supplies the deadline as an absolute time after which
- the operation should fail and return ETIMEDOUT.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- ULONG Operation;
- PPTHREAD_SEMAPHORE SemaphoreInternal;
- ULONG Shared;
- KSTATUS Status;
- ULONG TimeoutInMilliseconds;
- ULONG Value;
- SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
- //
- // Try to decrement the semaphore before checking the timeout.
- //
- if (ClpSemaphoreTryToDecrement(SemaphoreInternal) > 0) {
- return 0;
- }
- if ((AbsoluteTimeout == NULL) ||
- (AbsoluteTimeout->tv_sec < 0) ||
- (AbsoluteTimeout->tv_nsec < 0) ||
- (AbsoluteTimeout->tv_nsec > NANOSECONDS_PER_SECOND)) {
- errno = EINVAL;
- return -1;
- }
- Shared = SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED;
- Operation = UserLockWait;
- if (Shared == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- while (TRUE) {
- TimeoutInMilliseconds =
- ClpConvertAbsoluteTimespecToRelativeMilliseconds(AbsoluteTimeout,
- CLOCK_REALTIME);
- //
- // Try to grab the semaphore.
- //
- if (ClpSemaphoreDecrement(SemaphoreInternal) > 0) {
- break;
- }
- Value = PTHREAD_SEMAPHORE_WAITED_ON | Shared;
- Status = OsUserLock(&(SemaphoreInternal->State),
- Operation,
- &Value,
- TimeoutInMilliseconds);
- if (Status == STATUS_TIMEOUT) {
- errno = ETIMEDOUT;
- return -1;
- } else if (Status == STATUS_INTERRUPTED) {
- errno = EINTR;
- return -1;
- }
- }
- return 0;
- }
- PTHREAD_API
- int
- sem_trywait (
- sem_t *Semaphore
- )
- /*++
- Routine Description:
- This routine blocks until the given semaphore can be decremented to zero or
- above. This routine may time out after the specified deadline.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to wait on.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- if (ClpSemaphoreTryToDecrement((PPTHREAD_SEMAPHORE)Semaphore) > 0) {
- return 0;
- }
- errno = EAGAIN;
- return -1;
- }
- PTHREAD_API
- int
- sem_getvalue (
- sem_t *Semaphore,
- int *SemaphoreValue
- )
- /*++
- Routine Description:
- This routine returns the current count of the semaphore.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore.
- SemaphoreValue - Supplies a pointer where the semaphore value will be
- returned.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- PPTHREAD_SEMAPHORE SemaphoreInternal;
- INT Value;
- SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
- Value = PTHREAD_SEMAPHORE_GET_COUNT(SemaphoreInternal->State);
- if (Value < 0) {
- Value = 0;
- }
- *SemaphoreValue = Value;
- return 0;
- }
- PTHREAD_API
- int
- sem_post (
- sem_t *Semaphore
- )
- /*++
- Routine Description:
- This routine increments the semaphore value. If the value is incremented
- above zero, then threads waiting on the semaphore will be released to
- try and acquire it.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to contain more information.
- --*/
- {
- ULONG Count;
- ULONG Operation;
- PPTHREAD_SEMAPHORE SemaphoreInternal;
- INT Value;
- SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
- Value = ClpSemaphoreIncrement(SemaphoreInternal);
- //
- // If there were waiters, wake everyone up.
- //
- if (Value < 0) {
- Operation = UserLockWake;
- if ((SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED) == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- Count = MAX_ULONG;
- OsUserLock(&(SemaphoreInternal->State), Operation, &Count, 0);
- } else if (Value == SEM_VALUE_MAX) {
- errno = EOVERFLOW;
- return -1;
- }
- return 0;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- ClpSemaphoreDecrement (
- PPTHREAD_SEMAPHORE Semaphore
- )
- /*++
- Routine Description:
- This routine decrements the semaphore value and returns the old value,
- honoring the value of negative one, which means there are waiters.
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to decrement.
- Return Value:
- Returns the previous count.
- --*/
- {
- ULONG NewValue;
- ULONG OldValue;
- ULONG SetValue;
- ULONG Shared;
- OldValue = Semaphore->State;
- Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
- //
- // Loop trying to perform a sane decrement.
- //
- while (TRUE) {
- if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) < 0) {
- break;
- }
- SetValue = PTHREAD_SEMAPHORE_DECREMENT_STATE(OldValue) | Shared;
- NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
- SetValue,
- OldValue);
- if (NewValue == OldValue) {
- break;
- }
- OldValue = NewValue;
- }
- return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
- }
- INT
- ClpSemaphoreTryToDecrement (
- PPTHREAD_SEMAPHORE Semaphore
- )
- /*++
- Routine Description:
- This routine attempts to decrement the semaphore value and returns the old
- value. It will not change the value if it was zero or "waited on".
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to attempt to decrement.
- Return Value:
- Returns the previous count.
- --*/
- {
- ULONG NewValue;
- ULONG OldValue;
- ULONG SetValue;
- ULONG Shared;
- OldValue = Semaphore->State;
- Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
- //
- // Loop trying to perform a sane decrement.
- //
- while (TRUE) {
- if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) <= 0) {
- break;
- }
- SetValue = PTHREAD_SEMAPHORE_DECREMENT_STATE(OldValue) | Shared;
- NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
- SetValue,
- OldValue);
- if (NewValue == OldValue) {
- break;
- }
- OldValue = NewValue;
- }
- return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
- }
- INT
- ClpSemaphoreIncrement (
- PPTHREAD_SEMAPHORE Semaphore
- )
- /*++
- Routine Description:
- This routine increments the count in the semaphore and returns the old
- value. The count of -1 is treated equal to the count of 0 (as in, -1 goes
- directly to 1).
- Arguments:
- Semaphore - Supplies a pointer to the semaphore to increment.
- Return Value:
- Returns the previous count.
- --*/
- {
- ULONG NewValue;
- ULONG OldValue;
- ULONG SetValue;
- ULONG Shared;
- OldValue = Semaphore->State;
- Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
- //
- // Loop trying to perform a sane decrement.
- //
- while (TRUE) {
- if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) == SEM_VALUE_MAX) {
- break;
- }
- //
- // Negative values to straight to one.
- //
- if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) < 0) {
- SetValue = PTHREAD_SEMAPHORE_SET_COUNT(1) | Shared;
- } else {
- SetValue = PTHREAD_SEMAPHORE_INCREMENT_STATE(OldValue) | Shared;
- }
- NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
- SetValue,
- OldValue);
- if (NewValue == OldValue) {
- break;
- }
- OldValue = NewValue;
- }
- return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
- }
|