| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1999-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| /* |
| * File: w32ipcsem.c |
| * Description: implements named semaphores for NT and WIN95. |
| */ |
| |
| #include "primpl.h" |
| |
| #ifdef WINCE |
| static HANDLE OpenSemaphore(DWORD inDesiredAccess, |
| BOOL inInheritHandle, |
| const char *inName) |
| { |
| HANDLE retval = NULL; |
| HANDLE semaphore = NULL; |
| PRUnichar wideName[MAX_PATH]; /* name size is limited to MAX_PATH */ |
| |
| MultiByteToWideChar(CP_ACP, 0, inName, -1, wideName, MAX_PATH); |
| /* 0x7fffffff is the max count for our semaphore */ |
| semaphore = CreateSemaphoreW(NULL, 0, 0x7fffffff, wideName); |
| if (NULL != semaphore) { |
| DWORD lastErr = GetLastError(); |
| |
| if (ERROR_ALREADY_EXISTS != lastErr) |
| CloseHandle(semaphore); |
| else |
| retval = semaphore; |
| } |
| return retval; |
| } |
| #endif |
| |
| /* |
| * NSPR-to-NT access right mapping table for semaphore objects. |
| * |
| * The SYNCHRONIZE access is required by WaitForSingleObject. |
| * The SEMAPHORE_MODIFY_STATE access is required by ReleaseSemaphore. |
| * The OR of these three access masks must equal SEMAPHORE_ALL_ACCESS. |
| * This is because if a semaphore object with the specified name |
| * exists, CreateSemaphore requests SEMAPHORE_ALL_ACCESS access to |
| * the existing object. |
| */ |
| static DWORD semAccessTable[] = { |
| STANDARD_RIGHTS_REQUIRED|0x1, /* read (0x1 is "query state") */ |
| STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, /* write */ |
| 0 /* execute */ |
| }; |
| |
| #ifndef _PR_GLOBAL_THREADS_ONLY |
| |
| /* |
| * A fiber cannot call WaitForSingleObject because that |
| * will block the other fibers running on the same thread. |
| * If a fiber needs to wait on a (semaphore) handle, we |
| * create a native thread to call WaitForSingleObject and |
| * have the fiber join the native thread. |
| */ |
| |
| /* |
| * Arguments, return value, and error code for WaitForSingleObject |
| */ |
| struct WaitSingleArg { |
| HANDLE handle; |
| DWORD timeout; |
| DWORD rv; |
| DWORD error; |
| }; |
| |
| static void WaitSingleThread(void *arg) |
| { |
| struct WaitSingleArg *warg = (struct WaitSingleArg *) arg; |
| |
| warg->rv = WaitForSingleObject(warg->handle, warg->timeout); |
| if (warg->rv == WAIT_FAILED) { |
| warg->error = GetLastError(); |
| } |
| } |
| |
| static DWORD FiberSafeWaitForSingleObject( |
| HANDLE hHandle, |
| DWORD dwMilliseconds |
| ) |
| { |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| if (_PR_IS_NATIVE_THREAD(me)) { |
| return WaitForSingleObject(hHandle, dwMilliseconds); |
| } else { |
| PRThread *waitThread; |
| struct WaitSingleArg warg; |
| PRStatus rv; |
| |
| warg.handle = hHandle; |
| warg.timeout = dwMilliseconds; |
| waitThread = PR_CreateThread( |
| PR_USER_THREAD, WaitSingleThread, &warg, |
| PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); |
| if (waitThread == NULL) { |
| return WAIT_FAILED; |
| } |
| |
| rv = PR_JoinThread(waitThread); |
| PR_ASSERT(rv == PR_SUCCESS); |
| if (rv == PR_FAILURE) { |
| return WAIT_FAILED; |
| } |
| if (warg.rv == WAIT_FAILED) { |
| SetLastError(warg.error); |
| } |
| return warg.rv; |
| } |
| } |
| |
| #endif /* !_PR_GLOBAL_THREADS_ONLY */ |
| |
| PRSem *_PR_MD_OPEN_SEMAPHORE( |
| const char *osname, PRIntn flags, PRIntn mode, PRUintn value) |
| { |
| PRSem *sem; |
| SECURITY_ATTRIBUTES sa; |
| LPSECURITY_ATTRIBUTES lpSA = NULL; |
| PSECURITY_DESCRIPTOR pSD = NULL; |
| PACL pACL = NULL; |
| |
| sem = PR_NEW(PRSem); |
| if (sem == NULL) { |
| PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
| return NULL; |
| } |
| if (flags & PR_SEM_CREATE) { |
| if (_PR_NT_MakeSecurityDescriptorACL(mode, semAccessTable, |
| &pSD, &pACL) == PR_SUCCESS) { |
| sa.nLength = sizeof(sa); |
| sa.lpSecurityDescriptor = pSD; |
| sa.bInheritHandle = FALSE; |
| lpSA = &sa; |
| } |
| #ifdef WINCE |
| { |
| /* The size of a sem's name is limited to MAX_PATH. */ |
| PRUnichar wosname[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, osname, -1, wosname, MAX_PATH); |
| sem->sem = CreateSemaphoreW(lpSA, value, 0x7fffffff, wosname); |
| } |
| #else |
| sem->sem = CreateSemaphoreA(lpSA, value, 0x7fffffff, osname); |
| #endif |
| if (lpSA != NULL) { |
| _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); |
| } |
| if (sem->sem == NULL) { |
| _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); |
| PR_DELETE(sem); |
| return NULL; |
| } |
| if ((flags & PR_SEM_EXCL) && (GetLastError() == ERROR_ALREADY_EXISTS)) { |
| PR_SetError(PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS); |
| CloseHandle(sem->sem); |
| PR_DELETE(sem); |
| return NULL; |
| } |
| } else { |
| sem->sem = OpenSemaphore( |
| SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, FALSE, osname); |
| if (sem->sem == NULL) { |
| DWORD err = GetLastError(); |
| |
| /* |
| * If we open a nonexistent named semaphore, NT |
| * returns ERROR_FILE_NOT_FOUND, while Win95 |
| * returns ERROR_INVALID_NAME |
| */ |
| if (err == ERROR_INVALID_NAME) { |
| PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); |
| } else { |
| _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); |
| } |
| PR_DELETE(sem); |
| return NULL; |
| } |
| } |
| return sem; |
| } |
| |
| PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem) |
| { |
| DWORD rv; |
| |
| #ifdef _PR_GLOBAL_THREADS_ONLY |
| rv = WaitForSingleObject(sem->sem, INFINITE); |
| #else |
| rv = FiberSafeWaitForSingleObject(sem->sem, INFINITE); |
| #endif |
| PR_ASSERT(rv == WAIT_FAILED || rv == WAIT_OBJECT_0); |
| if (rv == WAIT_FAILED) { |
| _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); |
| return PR_FAILURE; |
| } |
| if (rv != WAIT_OBJECT_0) { |
| /* Should not happen */ |
| PR_SetError(PR_UNKNOWN_ERROR, 0); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } |
| |
| PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem) |
| { |
| if (ReleaseSemaphore(sem->sem, 1, NULL) == FALSE) { |
| _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } |
| |
| PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem) |
| { |
| if (CloseHandle(sem->sem) == FALSE) { |
| _PR_MD_MAP_CLOSE_ERROR(GetLastError()); |
| return PR_FAILURE; |
| } |
| PR_DELETE(sem); |
| return PR_SUCCESS; |
| } |