src/thread/win32/win_ce_semaphore.c
author Sam Lantinga
Tue, 26 Aug 2008 07:34:23 +0000
changeset 2727 76c2fc9696ea
parent 1895 c121d94672cb
permissions -rw-r--r--
Fixed crash when tablet isn't detected properly
slouken@36
     1
/* win_ce_semaphore.c
slouken@36
     2
slouken@36
     3
   Copyright (c) 1998, Johnson M. Hart
slouken@36
     4
   (with corrections 2001 by Rainer Loritz)
slouken@36
     5
   Permission is granted for any and all use providing that this
slouken@36
     6
   copyright is properly acknowledged.
slouken@36
     7
   There are no assurances of suitability for any use whatsoever.
slouken@36
     8
slouken@36
     9
   WINDOWS CE: There is a collection of Windows CE functions to simulate
slouken@36
    10
   semaphores using only a mutex and an event. As Windows CE events cannot
slouken@36
    11
   be named, these simulated semaphores cannot be named either.
slouken@36
    12
slouken@36
    13
   Implementation notes:
slouken@36
    14
   1. All required internal data structures are allocated on the process's heap.
slouken@36
    15
   2. Where appropriate, a new error code is returned (see the header
slouken@36
    16
      file), or, if the error is a Win32 error, that code is unchanged.
slouken@36
    17
   3. Notice the new handle type "SYNCHHANDLE" that has handles, counters,
slouken@36
    18
      and other information. This structure will grow as new objects are added
slouken@36
    19
      to this set; some members are specific to only one or two of the objects.
slouken@36
    20
   4. Mutexes are used for critical sections. These could be replaced with
slouken@36
    21
      CRITICAL_SECTION objects but then this would give up the time out
slouken@36
    22
      capability.
slouken@36
    23
   5. The implementation shows several interesting aspects of synchronization, some
slouken@36
    24
      of which are specific to Win32 and some of which are general. These are pointed
slouken@36
    25
      out in the comments as appropriate.
slouken@36
    26
   6. The wait function emulates WaitForSingleObject only. An emulation of
slouken@36
    27
      WaitForMultipleObjects is much harder to implement outside the kernel,
slouken@36
    28
      and it is not clear how to handle a mixture of WCE semaphores and normal
slouken@36
    29
      events and mutexes. */
slouken@36
    30
slouken@1433
    31
#define WIN32_LEAN_AND_MEAN
slouken@1433
    32
#include <windows.h>
slouken@1433
    33
slouken@36
    34
#include "win_ce_semaphore.h"
slouken@36
    35
slouken@1895
    36
static SYNCHHANDLE CleanUp(SYNCHHANDLE hSynch, DWORD Flags);
slouken@36
    37
slouken@1895
    38
SYNCHHANDLE
slouken@1895
    39
CreateSemaphoreCE(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  /* pointer to security attributes */
slouken@1895
    40
                  LONG lInitialCount,   /* initial count */
slouken@1895
    41
                  LONG lMaximumCount,   /* maximum count */
slouken@1895
    42
                  LPCTSTR lpName)
slouken@36
    43
/* Semaphore for use with Windows CE that does not support them directly.
slouken@36
    44
   Requires a counter, a mutex to protect the counter, and an
slouken@36
    45
   autoreset event.
slouken@36
    46
slouken@36
    47
   Here are the rules that must always hold between the autoreset event
slouken@36
    48
   and the mutex (any violation of these rules by the CE semaphore functions
slouken@36
    49
   will, in all likelihood, result in a defect):
slouken@36
    50
    1. No thread can set, pulse, or reset the event,
slouken@36
    51
       nor can it access any part of the SYNCHHANDLE structure,
slouken@36
    52
       without first gaining ownership of the mutex.
slouken@36
    53
       BUT, a thread can wait on the event without owning the mutex
slouken@36
    54
       (this is clearly necessary or else the event could never be set).
slouken@36
    55
    2. The event is in a signaled state if and only if the current semaphore
slouken@36
    56
       count ("CurCount") is greater than zero.
slouken@36
    57
    3. The semaphore count is always >= 0 and <= the maximum count */
slouken@36
    58
{
slouken@1895
    59
    SYNCHHANDLE hSynch = NULL, result = NULL;
slouken@36
    60
slouken@1895
    61
    __try {
slouken@1895
    62
        if (lInitialCount > lMaximumCount || lMaximumCount < 0
slouken@1895
    63
            || lInitialCount < 0) {
slouken@1895
    64
            /* Bad parameters */
slouken@1895
    65
            SetLastError(SYNCH_ERROR);
slouken@1895
    66
            __leave;
slouken@1895
    67
        }
slouken@36
    68
slouken@1895
    69
        hSynch =
slouken@1895
    70
            HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SYNCH_HANDLE_SIZE);
slouken@1895
    71
        if (hSynch == NULL)
slouken@1895
    72
            __leave;
slouken@36
    73
slouken@1895
    74
        hSynch->MaxCount = lMaximumCount;
slouken@1895
    75
        hSynch->CurCount = lInitialCount;
slouken@1895
    76
        hSynch->lpName = lpName;
slouken@36
    77
slouken@1895
    78
        hSynch->hMutex = CreateMutex(lpSemaphoreAttributes, FALSE, NULL);
slouken@36
    79
slouken@1895
    80
        WaitForSingleObject(hSynch->hMutex, INFINITE);
slouken@1895
    81
        /*  Create the event. It is initially signaled if and only if the
slouken@1895
    82
           initial count is > 0 */
slouken@1895
    83
        hSynch->hEvent = CreateEvent(lpSemaphoreAttributes, FALSE,
slouken@1895
    84
                                     lInitialCount > 0, NULL);
slouken@1895
    85
        ReleaseMutex(hSynch->hMutex);
slouken@1895
    86
        hSynch->hSemph = NULL;
slouken@1895
    87
    }
slouken@1895
    88
    __finally {
slouken@1895
    89
        /* Return with the handle, or, if there was any error, return
slouken@1895
    90
           a null after closing any open handles and freeing any allocated memory. */
slouken@1895
    91
        result =
slouken@1895
    92
            CleanUp(hSynch, 6 /* An event and a mutex, but no semaphore. */ );
slouken@1895
    93
    }
slouken@36
    94
slouken@1895
    95
    return result;
slouken@36
    96
}
slouken@36
    97
slouken@1895
    98
BOOL
slouken@1895
    99
ReleaseSemaphoreCE(SYNCHHANDLE hSemCE, LONG cReleaseCount,
slouken@1895
   100
                   LPLONG lpPreviousCount)
slouken@36
   101
/* Windows CE equivalent to ReleaseSemaphore. */
slouken@36
   102
{
slouken@1895
   103
    BOOL Result = TRUE;
slouken@36
   104
slouken@1895
   105
    /* Gain access to the object to assure that the release count
slouken@1895
   106
       would not cause the total count to exceed the maximum. */
slouken@36
   107
slouken@1895
   108
    __try {
slouken@1895
   109
        WaitForSingleObject(hSemCE->hMutex, INFINITE);
slouken@1895
   110
        /* reply only if asked to */
slouken@1895
   111
        if (lpPreviousCount != NULL)
slouken@1895
   112
            *lpPreviousCount = hSemCE->CurCount;
slouken@1895
   113
        if (hSemCE->CurCount + cReleaseCount > hSemCE->MaxCount
slouken@1895
   114
            || cReleaseCount <= 0) {
slouken@1895
   115
            SetLastError(SYNCH_ERROR);
slouken@1895
   116
            Result = FALSE;
slouken@1895
   117
            __leave;
slouken@1895
   118
        }
slouken@1895
   119
        hSemCE->CurCount += cReleaseCount;
slouken@36
   120
slouken@1895
   121
        /*  Set the autoreset event, releasing exactly one waiting thread, now or
slouken@1895
   122
           in the future.  */
slouken@36
   123
slouken@1895
   124
        SetEvent(hSemCE->hEvent);
slouken@1895
   125
    }
slouken@1895
   126
    __finally {
slouken@1895
   127
        ReleaseMutex(hSemCE->hMutex);
slouken@1895
   128
    }
slouken@36
   129
slouken@1895
   130
    return Result;
slouken@36
   131
}
slouken@36
   132
slouken@1895
   133
DWORD
slouken@1895
   134
WaitForSemaphoreCE(SYNCHHANDLE hSemCE, DWORD dwMilliseconds)
slouken@36
   135
   /* Windows CE semaphore equivalent of WaitForSingleObject. */
slouken@36
   136
{
slouken@1895
   137
    DWORD WaitResult;
slouken@36
   138
slouken@1895
   139
    WaitResult = WaitForSingleObject(hSemCE->hMutex, dwMilliseconds);
slouken@1895
   140
    if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0)
slouken@1895
   141
        return WaitResult;
slouken@1895
   142
    while (hSemCE->CurCount <= 0) {
slouken@36
   143
slouken@1895
   144
        /* The count is 0, and the thread must wait on the event (which, by
slouken@1895
   145
           the rules, is currently reset) for semaphore resources to become
slouken@1895
   146
           available. First, of course, the mutex must be released so that another
slouken@1895
   147
           thread will be capable of setting the event. */
slouken@36
   148
slouken@1895
   149
        ReleaseMutex(hSemCE->hMutex);
slouken@36
   150
slouken@1895
   151
        /*  Wait for the event to be signaled, indicating a semaphore state change.
slouken@1895
   152
           The event is autoreset and signaled with a SetEvent (not PulseEvent)
slouken@1895
   153
           so exactly one waiting thread (whether or not there is currently
slouken@1895
   154
           a waiting thread) is released as a result of the SetEvent. */
slouken@36
   155
slouken@1895
   156
        WaitResult = WaitForSingleObject(hSemCE->hEvent, dwMilliseconds);
slouken@1895
   157
        if (WaitResult != WAIT_OBJECT_0)
slouken@1895
   158
            return WaitResult;
slouken@36
   159
slouken@1895
   160
        /*  This is where the properties of setting of an autoreset event is critical
slouken@1895
   161
           to assure that, even if the semaphore state changes between the
slouken@1895
   162
           preceding Wait and the next, and even if NO threads are waiting
slouken@1895
   163
           on the event at the time of the SetEvent, at least one thread
slouken@1895
   164
           will be released. 
slouken@1895
   165
           Pulsing a manual reset event would appear to work, but it would have
slouken@1895
   166
           a defect which could appear if the semaphore state changed between
slouken@1895
   167
           the two waits. */
slouken@36
   168
slouken@1895
   169
        WaitResult = WaitForSingleObject(hSemCE->hMutex, dwMilliseconds);
slouken@1895
   170
        if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0)
slouken@1895
   171
            return WaitResult;
slouken@36
   172
slouken@1895
   173
    }
slouken@1895
   174
    /* The count is not zero and this thread owns the mutex.  */
slouken@36
   175
slouken@1895
   176
    hSemCE->CurCount--;
slouken@1895
   177
    /* The event is now unsignaled, BUT, the semaphore count may not be
slouken@1895
   178
       zero, in which case the event should be signaled again
slouken@1895
   179
       before releasing the mutex. */
slouken@36
   180
slouken@1895
   181
    if (hSemCE->CurCount > 0)
slouken@1895
   182
        SetEvent(hSemCE->hEvent);
slouken@1895
   183
    ReleaseMutex(hSemCE->hMutex);
slouken@1895
   184
    return WaitResult;
slouken@36
   185
}
slouken@36
   186
slouken@1895
   187
BOOL
slouken@1895
   188
CloseSynchHandle(SYNCHHANDLE hSynch)
slouken@36
   189
/* Close a synchronization handle. 
slouken@36
   190
   Improvement: Test for a valid handle before dereferencing the handle. */
slouken@36
   191
{
slouken@1895
   192
    BOOL Result = TRUE;
slouken@1895
   193
    if (hSynch->hEvent != NULL)
slouken@1895
   194
        Result = Result && CloseHandle(hSynch->hEvent);
slouken@1895
   195
    if (hSynch->hMutex != NULL)
slouken@1895
   196
        Result = Result && CloseHandle(hSynch->hMutex);
slouken@1895
   197
    if (hSynch->hSemph != NULL)
slouken@1895
   198
        Result = Result && CloseHandle(hSynch->hSemph);
slouken@1895
   199
    HeapFree(GetProcessHeap(), 0, hSynch);
slouken@1895
   200
    return (Result);
slouken@36
   201
}
slouken@36
   202
slouken@1895
   203
static SYNCHHANDLE
slouken@1895
   204
CleanUp(SYNCHHANDLE hSynch, DWORD Flags)
slouken@1895
   205
{                               /* Prepare to return from a create of a synchronization handle.
slouken@1895
   206
                                   If there was any failure, free any allocated resources.
slouken@1895
   207
                                   "Flags" indicates which Win32 objects are required in the 
slouken@1895
   208
                                   synchronization handle. */
slouken@36
   209
slouken@1895
   210
    BOOL ok = TRUE;
slouken@36
   211
slouken@1895
   212
    if (hSynch == NULL)
slouken@1895
   213
        return NULL;
slouken@1895
   214
    if ((Flags & 4) == 1 && (hSynch->hEvent == NULL))
slouken@1895
   215
        ok = FALSE;
slouken@1895
   216
    if ((Flags & 2) == 1 && (hSynch->hMutex == NULL))
slouken@1895
   217
        ok = FALSE;
slouken@1895
   218
    if ((Flags & 1) == 1 && (hSynch->hEvent == NULL))
slouken@1895
   219
        ok = FALSE;
slouken@1895
   220
    if (!ok) {
slouken@1895
   221
        CloseSynchHandle(hSynch);
slouken@1895
   222
        return NULL;
slouken@1895
   223
    }
slouken@1895
   224
    /* Everything worked */
slouken@1895
   225
    return hSynch;
slouken@36
   226
}
slouken@1895
   227
slouken@1895
   228
/* vi: set ts=4 sw=4 expandtab: */