/* *@@sourcefile sem.c: * implements fast mutex semaphores. * * This is an OS/2 implementation of what Win32 calls * "critical sections". This is highly optimized for * the case where there is only one thread trying to * access the critical section, i.e. it is available most * of the time. Therefore we can use these critical * sections for all our serialization and not lose any * performance when concurrent access is unlikely. * * In case there is multiple access, we use the OS/2 kernel * event semaphores. * * Function prefix: * * -- sem*: semaphore helpers. * *@@added V0.9.20 (2002-08-04) [umoeller] *@@header "helpers\semaphores.h" */ /* * Copyright (C) 2002 Sander van Leeuwen. * Copyright (C) 2002 Ulrich M”ller. * This file is part of the "XWorkplace helpers" source package. * This 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, in version 2 as it comes in the * "COPYING" file of the XWorkplace main distribution. * 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. */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSPROCESS #define INCL_DOSSEMAPHORES #define INCL_DOSERRORS #define INCL_WINMESSAGEMGR #include #include #include "setup.h" // code generation and debugging options #include "helpers\sem.h" #pragma hdrstop /* *@@category: Helpers\Control program helpers\Semaphores * see sem.c. */ // encode PID and TID into one 32bit value #define MAKE_THREADID(processid, threadid) ((processid << 16) | threadid) /* *@@ GetTID: * */ ULONG GetTID(VOID) { PTIB ptib; PPIB ppib; APIRET rc; if (!(rc = DosGetInfoBlocks(&ptib, &ppib))) return MAKE_THREADID(ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid); return 0; } /* *@@ GetPID: * */ ULONG GetPID(VOID) { PTIB ptib; PPIB ppib; APIRET rc; if (!(rc = DosGetInfoBlocks(&ptib, &ppib))) return ppib->pib_ulpid; return 0; } /* *@@ semCreate: * the equivalent to DosCreateMutexSem. */ APIRET semCreate(PFASTMTX pmtx, PCSZ pszSemName) { APIRET rc; if (!pmtx) return ERROR_INVALID_PARAMETER; // initialize lock count with special value -1, meaning noone posesses it pmtx->LockCount = -1; pmtx->RecursionCount = 0; pmtx->OwningThread = 0; if (rc = DosCreateEventSem((PSZ)pszSemName, &pmtx->hmtxLock, (pszSemName) ? DC_SEM_SHARED : 0, 0)) pmtx->hmtxLock = 0; pmtx->Reserved = GetPID(); return rc; } /* *@@ semOpen: * the equivalent to DosOpenMutexSem. */ APIRET semOpen(PFASTMTX pmtx, PCSZ pszSemName) { HMTX hmtxLock = 0; APIRET rc; if (!pszSemName) return ERROR_INVALID_PARAMETER; return DosOpenEventSem((PSZ)pszSemName, &hmtxLock); } /* *@@ semClose: * the equivalent to DosCloseMutexSem. */ APIRET semClose(PFASTMTX pmtx) { APIRET rc; if (!pmtx) return ERROR_INVALID_PARAMETER; if (!pmtx->hmtxLock) return ERROR_INVALID_HANDLE; if (pmtx->RecursionCount) /* Should not happen */ return ERROR_SEM_BUSY; pmtx->LockCount = -1; pmtx->RecursionCount = 0; pmtx->OwningThread = 0; rc = DosCloseEventSem(pmtx->hmtxLock); pmtx->hmtxLock = 0; pmtx->Reserved = (ULONG)-1; return rc; } /* *@@ semRequest: * the equivalent to DosRequestMutexSem. */ APIRET semRequest(PFASTMTX pmtx) { ULONG threadid = GetTID(); if (!pmtx) return ERROR_INVALID_PARAMETER; // create pmtx sect just in time... if (!pmtx->hmtxLock) semCreate(pmtx, NULL); // do an atomic increase of the lockcounter and see if it is > 0 // (i.e. it is already posessed) if (lockIncrement(&pmtx->LockCount)) { // semaphore was already requested: testenter: // if the same thread is requesting it again, memorize it if (pmtx->OwningThread == threadid) { pmtx->RecursionCount++; return NO_ERROR; } // current owner is different thread: // do an atomic operation where we compare the owning thread id with 0 // and if this is true, exchange it with the id of the current thread if (lockCompareExchange((PLONG)&pmtx->OwningThread, threadid, 0)) { // the compare did not return equal, i.e. the pmtx sect is in use ULONG cPosts; APIRET rc; /* Now wait for it */ if (rc = DosWaitEventSem(pmtx->hmtxLock, SEM_INDEFINITE_WAIT)) return rc; DosResetEventSem(pmtx->hmtxLock, &cPosts); // multiple waiters could be running now. Repeat the logic so that // only one actually can get the critical section goto testenter; } } pmtx->OwningThread = GetTID(); pmtx->RecursionCount = 1; return NO_ERROR; } /* *@@ semAssert: * returns TRUE if the current thread currently owns * the mutex. * */ BOOL semAssert(PFASTMTX pmtx) { return ( (pmtx) && (pmtx->OwningThread) && (pmtx->OwningThread == GetTID()) ); } /* *@@ semTry: * */ BOOL semTry(PFASTMTX pmtx) { if (lockIncrement(&pmtx->LockCount)) { if (pmtx->OwningThread == GetTID()) { pmtx->RecursionCount++; return TRUE; } lockDecrement(&pmtx->LockCount); return FALSE; } pmtx->OwningThread = GetTID(); pmtx->RecursionCount = 1; return TRUE; } /* *@@ semRelease: * the equivalent of DosReleaseMutexSem. */ APIRET semRelease(PFASTMTX pmtx) { if (!pmtx) return ERROR_INVALID_PARAMETER; if (pmtx->OwningThread != GetTID()) return ERROR_NOT_OWNER; if (--pmtx->RecursionCount) { lockDecrement(&pmtx->LockCount ); return NO_ERROR; } pmtx->OwningThread = 0; if (lockDecrement(&pmtx->LockCount) >= 0) // someone is waiting DosPostEventSem(pmtx->hmtxLock); return NO_ERROR; }