andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public
andre@0:  * License, v. 2.0. If a copy of the MPL was not distributed with this
andre@0:  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
andre@0: 
andre@0: /*
andre@0:  * File: w32ipcsem.c
andre@0:  * Description: implements named semaphores for NT and WIN95.
andre@0:  */
andre@0: 
andre@0: #include "primpl.h"
andre@0: 
andre@0: #ifdef WINCE
andre@0: static HANDLE OpenSemaphore(DWORD inDesiredAccess,
andre@0:                             BOOL inInheritHandle,
andre@0:                             const char *inName)
andre@0: {
andre@0:     HANDLE retval = NULL;
andre@0:     HANDLE semaphore = NULL;
andre@0:     PRUnichar wideName[MAX_PATH];  /* name size is limited to MAX_PATH */
andre@0:     
andre@0:     MultiByteToWideChar(CP_ACP, 0, inName, -1, wideName, MAX_PATH);
andre@0:     /* 0x7fffffff is the max count for our semaphore */
andre@0:     semaphore = CreateSemaphoreW(NULL, 0, 0x7fffffff, wideName);
andre@0:     if (NULL != semaphore) {
andre@0:         DWORD lastErr = GetLastError();
andre@0:       
andre@0:         if (ERROR_ALREADY_EXISTS != lastErr)
andre@0:             CloseHandle(semaphore);
andre@0:         else
andre@0:             retval = semaphore;
andre@0:     }
andre@0:     return retval;
andre@0: }
andre@0: #endif
andre@0: 
andre@0: /*
andre@0:  * NSPR-to-NT access right mapping table for semaphore objects.
andre@0:  *
andre@0:  * The SYNCHRONIZE access is required by WaitForSingleObject.
andre@0:  * The SEMAPHORE_MODIFY_STATE access is required by ReleaseSemaphore.
andre@0:  * The OR of these three access masks must equal SEMAPHORE_ALL_ACCESS.
andre@0:  * This is because if a semaphore object with the specified name
andre@0:  * exists, CreateSemaphore requests SEMAPHORE_ALL_ACCESS access to
andre@0:  * the existing object.
andre@0:  */
andre@0: static DWORD semAccessTable[] = {
andre@0:     STANDARD_RIGHTS_REQUIRED|0x1, /* read (0x1 is "query state") */
andre@0:     STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, /* write */
andre@0:     0 /* execute */
andre@0: };
andre@0: 
andre@0: #ifndef _PR_GLOBAL_THREADS_ONLY
andre@0: 
andre@0: /*
andre@0:  * A fiber cannot call WaitForSingleObject because that
andre@0:  * will block the other fibers running on the same thread.
andre@0:  * If a fiber needs to wait on a (semaphore) handle, we
andre@0:  * create a native thread to call WaitForSingleObject and
andre@0:  * have the fiber join the native thread.
andre@0:  */
andre@0: 
andre@0: /*
andre@0:  * Arguments, return value, and error code for WaitForSingleObject
andre@0:  */
andre@0: struct WaitSingleArg {
andre@0:     HANDLE handle;
andre@0:     DWORD timeout;
andre@0:     DWORD rv;
andre@0:     DWORD error;
andre@0: };
andre@0: 
andre@0: static void WaitSingleThread(void *arg)
andre@0: {
andre@0:     struct WaitSingleArg *warg = (struct WaitSingleArg *) arg;
andre@0: 
andre@0:     warg->rv = WaitForSingleObject(warg->handle, warg->timeout);
andre@0:     if (warg->rv == WAIT_FAILED) {
andre@0:         warg->error = GetLastError();
andre@0:     }
andre@0: }
andre@0: 
andre@0: static DWORD FiberSafeWaitForSingleObject(
andre@0:     HANDLE hHandle,
andre@0:     DWORD dwMilliseconds
andre@0: )
andre@0: {
andre@0:     PRThread *me = _PR_MD_CURRENT_THREAD();
andre@0: 
andre@0:     if (_PR_IS_NATIVE_THREAD(me)) {
andre@0:         return WaitForSingleObject(hHandle, dwMilliseconds);
andre@0:     } else {
andre@0:         PRThread *waitThread;
andre@0:         struct WaitSingleArg warg;
andre@0:         PRStatus rv;
andre@0: 
andre@0:         warg.handle = hHandle;
andre@0:         warg.timeout = dwMilliseconds;
andre@0:         waitThread = PR_CreateThread(
andre@0:             PR_USER_THREAD, WaitSingleThread, &warg,
andre@0:             PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
andre@0:         if (waitThread == NULL) {
andre@0:             return WAIT_FAILED;
andre@0:         }
andre@0: 
andre@0:         rv = PR_JoinThread(waitThread);
andre@0:         PR_ASSERT(rv == PR_SUCCESS);
andre@0:         if (rv == PR_FAILURE) {
andre@0:             return WAIT_FAILED;
andre@0:         }
andre@0:         if (warg.rv == WAIT_FAILED) {
andre@0:             SetLastError(warg.error);
andre@0:         }
andre@0:         return warg.rv;
andre@0:     }
andre@0: }
andre@0: 
andre@0: #endif /* !_PR_GLOBAL_THREADS_ONLY */
andre@0: 
andre@0: PRSem *_PR_MD_OPEN_SEMAPHORE(
andre@0:     const char *osname, PRIntn flags, PRIntn mode, PRUintn value)
andre@0: {
andre@0:     PRSem *sem;
andre@0:     SECURITY_ATTRIBUTES sa;
andre@0:     LPSECURITY_ATTRIBUTES lpSA = NULL;
andre@0:     PSECURITY_DESCRIPTOR pSD = NULL;
andre@0:     PACL pACL = NULL;
andre@0: 
andre@0:     sem = PR_NEW(PRSem);
andre@0:     if (sem == NULL) {
andre@0:         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
andre@0:         return NULL;
andre@0:     }
andre@0:     if (flags & PR_SEM_CREATE) {
andre@0:         if (_PR_NT_MakeSecurityDescriptorACL(mode, semAccessTable,
andre@0:                 &pSD, &pACL) == PR_SUCCESS) {
andre@0:             sa.nLength = sizeof(sa);
andre@0:             sa.lpSecurityDescriptor = pSD;
andre@0:             sa.bInheritHandle = FALSE;
andre@0:             lpSA = &sa;
andre@0:         }
andre@0: #ifdef WINCE
andre@0:         {
andre@0:             /* The size of a sem's name is limited to MAX_PATH. */
andre@0:             PRUnichar wosname[MAX_PATH]; 
andre@0:             MultiByteToWideChar(CP_ACP, 0, osname, -1, wosname, MAX_PATH);
andre@0:             sem->sem = CreateSemaphoreW(lpSA, value, 0x7fffffff, wosname);
andre@0:         }
andre@0: #else
andre@0:         sem->sem = CreateSemaphoreA(lpSA, value, 0x7fffffff, osname);
andre@0: #endif
andre@0:         if (lpSA != NULL) {
andre@0:             _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
andre@0:         }
andre@0:         if (sem->sem == NULL) {
andre@0:             _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
andre@0:             PR_DELETE(sem);
andre@0:             return NULL;
andre@0:         }
andre@0:         if ((flags & PR_SEM_EXCL) && (GetLastError() == ERROR_ALREADY_EXISTS)) {
andre@0:             PR_SetError(PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS);
andre@0:             CloseHandle(sem->sem);
andre@0:             PR_DELETE(sem);
andre@0:             return NULL;
andre@0:         }
andre@0:     } else {
andre@0:         sem->sem = OpenSemaphore(
andre@0:                 SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, FALSE, osname);
andre@0:         if (sem->sem == NULL) {
andre@0:             DWORD err = GetLastError();
andre@0: 
andre@0:             /*
andre@0:              * If we open a nonexistent named semaphore, NT
andre@0:              * returns ERROR_FILE_NOT_FOUND, while Win95
andre@0:              * returns ERROR_INVALID_NAME
andre@0:              */
andre@0:             if (err == ERROR_INVALID_NAME) {
andre@0:                 PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
andre@0:             } else {
andre@0:                 _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
andre@0:             }
andre@0:             PR_DELETE(sem);
andre@0:             return NULL;
andre@0:         }
andre@0:     }
andre@0:     return sem;
andre@0: }
andre@0: 
andre@0: PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem)
andre@0: {
andre@0:     DWORD rv;
andre@0: 
andre@0: #ifdef _PR_GLOBAL_THREADS_ONLY
andre@0:     rv = WaitForSingleObject(sem->sem, INFINITE);
andre@0: #else
andre@0:     rv = FiberSafeWaitForSingleObject(sem->sem, INFINITE);
andre@0: #endif
andre@0:     PR_ASSERT(rv == WAIT_FAILED || rv == WAIT_OBJECT_0);
andre@0:     if (rv == WAIT_FAILED) {
andre@0:         _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
andre@0:         return PR_FAILURE;
andre@0:     }
andre@0:     if (rv != WAIT_OBJECT_0) {
andre@0:         /* Should not happen */
andre@0:         PR_SetError(PR_UNKNOWN_ERROR, 0);
andre@0:         return PR_FAILURE;
andre@0:     }
andre@0:     return PR_SUCCESS;
andre@0: }
andre@0: 
andre@0: PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem)
andre@0: {
andre@0:     if (ReleaseSemaphore(sem->sem, 1, NULL) == FALSE) {
andre@0:         _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
andre@0:         return PR_FAILURE;
andre@0:     }
andre@0:     return PR_SUCCESS;
andre@0: }
andre@0: 
andre@0: PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem)
andre@0: {
andre@0:     if (CloseHandle(sem->sem) == FALSE) {
andre@0:         _PR_MD_MAP_CLOSE_ERROR(GetLastError());
andre@0:         return PR_FAILURE;
andre@0:     }
andre@0:     PR_DELETE(sem);
andre@0:     return PR_SUCCESS;
andre@0: }