/*++ Copyright (c) 1991 Microsoft Corporation Module Name: dllsem16.c Abstract: This module implements 32 equivalents of OS/2 V1.21 Semaphore API Calls. These are called from 16->32 thunks (i386\doscalls.asm). NOTE: there is a semantic problem with os2 1.x Semaphores since they are used in both a mutex manner (Request/Clear) and an event manner (Wait/Clear). The manuals don't tell apps writers what will happen if they use the same semaphore both ways. The current implementation is: A semaphore is associated with an Nt Semaphore object AND a Cruiser event. If Wait is called, it blocks on the event. If a request is called, it blocks on the Semaphore. A clear can be both a Release and a Post, depends on the Wait/Request called. Author: Yaron Shamir (YaronS) 12-Apr-1991 (stubs) Yaron Shamir (YaronS) 28-May-1991 (implementation as described above) Revision History: --*/ #define INCL_OS2V20_SEMAPHORES #define INCL_OS2V20_MEMORY #define INCL_OS2V20_TASKING #define INCL_OS2V20_ERRORS #include "os2dll.h" #include "os2dll16.h" #include "os2win.h" #include "stdlib.h" #if DBG // Set the values as appropriate (with NTSD or at compile-time) to see all // semaphore operations relating to any of these 4 semaphores. // To disable this feature, leave the variables at 0. // WARNING: remember to use the flat address, not the 16-bit seg:off PVOID Os2DebugSem = (HSEM)0x0; PVOID Os2DebugSem1 = (HSEM)0x0; PVOID Os2DebugSem2 = (HSEM)0x0; PVOID Os2DebugSem3 = (HSEM)0x0; // Undef the 2 lines below to see print-out of failures to open this NT event //#define DBG_SEM_EVENT_STR "\\SEM\\HACKNT3C" //#define DBG_SEM_INDEX 0x3c #endif // DBG // // Owner Thread values: // // SEM_AT_INIT - semaphore initialized, used both for mutex and for event semanitcs // SEM_MUTEX_NO_OWNER - mutex semaphore, not owned // SEM_EVENT - event semaphore (sem/wait/clear) // Other - mutex semaphore, owned // #define SEM_DUAL -3 #define SEM_AT_INIT -2 #define SEM_MUTEX_NOT_OWNED -1 #define SEM_EVENT 0 #define OD2SEMTHRESHOLD 800 ULONG Od2NumSemCreated = 0; // // The following support the hacky pm print driver usage of FS ram semaphore // with cb == 12 instead of 14 as documented // #define NUMOFPRINTDRIVERSEM 500 PULONG pHackPrinterDriverSem; // // The following is used to sync RAM shared semaphores // HANDLE Od2SyncSem; // // The following is used to sync when garbage collector is at work // HANDLE Od2GarbageCollectSem; // // The following flag is used to optimized allocation // of semaphores until no more space/reources, then we lock // it all, clean and unlock // BOOLEAN LockFlag = FALSE; // // The hint for the next index for shared RAM semaphore // USHORT Od2SemIndexHint; // // Special Heap for tiled structrures - need to be in the 512M tiled area // extern PVOID Od2TiledHeap; APIRET DosGetShrSeg( IN PSZ pszSegName, OUT PSEL pSel ); APIRET DosAllocShrSeg( IN USHORT cbSize, IN PSZ pszSegName, OUT PSEL pSel ); APIRET DosHoldSignal( ULONG fDisable, ULONG pstack ); APIRET DosCloseSemNoRemove( IN POS21X_SEM pRealSem, IN BOOL FreeMem ); APIRET DosSemWait( IN HSEM hsem, IN LONG lTimeOut ); APIRET Od2CloseSem( IN BOOL SyncFlag, IN HSEM hsem ); // Alertable wait for single object. By enabling alert allow context change to be // done by server. But in the case that it wasn't done, continue to wait for the // object. NTSTATUS Od2AlertableWaitForSingleObject( IN HANDLE handle ) { NTSTATUS Status; while (((Status = NtWaitForSingleObject( handle, TRUE, // alertable NULL)) == STATUS_ALERTED) || (Status == STATUS_USER_APC)) { #if DBG if (Status == STATUS_USER_APC) { DbgPrint("WARNING !!! Od2AlertableWaitForSingleObject was broken by APC\n"); } #endif } return Status; } NTSTATUS Od2AcquireMutant( IN HANDLE handle ) { if (Od2CurrentThreadId() == 1) { DosHoldSignal(HLDSIG_DISABLE, 0); } return Od2AlertableWaitForSingleObject(handle); } NTSTATUS Od2ReleaseMutant( IN HANDLE handle ) { NTSTATUS Status; Status = NtReleaseMutant(handle, NULL); if (Od2CurrentThreadId() == 1) { DosHoldSignal(HLDSIG_ENABLE, 0); } return Status; } // Acquire Sync Sem. Write the thread Id of the thread that started critical // semaphore processing. This will be used by server to allow to the thread // owner Sync to finish semaphores processing before termination. NTSTATUS Od2AcquireSync(VOID) { NTSTATUS Status; Status = Od2AcquireMutant(Od2SyncSem); if (NT_SUCCESS(Status)) { Od2Process->Pib.SyncOwner = Od2CurrentThreadId(); } else { #if DBG DbgPrint("Od2AcquireSync: Wait for Sync, Status=%x\n", Status); ASSERT(FALSE); #endif // DBG } return Status; } // Release Sync Sem. Before actual release, sign that the semaphore processing is // over. NTSTATUS Od2ReleaseSync(VOID) { NTSTATUS Status; Od2Process->Pib.SyncOwner = 0; Status = Od2ReleaseMutant(Od2SyncSem); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("Od2AcquireSync: Relsease Sync, Status=%x\n", Status); ASSERT(FALSE); } #endif // DBG return Status; } NTSTATUS Od2InitSem() { NTSTATUS Status; UNICODE_STRING SemString_U; OBJECT_ATTRIBUTES Obja; RtlInitUnicodeString( &SemString_U, OS2_SS_SYNCHRONIZATION_SEM); InitializeObjectAttributes( &Obja, &SemString_U, OBJ_CASE_INSENSITIVE, NULL, NULL); // // Open the global subsystem synchronization Nt mutant // Status = NtOpenMutant(&Od2SyncSem, MUTANT_ALL_ACCESS, &Obja); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2InitSem: error at NtopenSemaphore, Status %x\n", Status); } #endif } // // Create an Nt mutant that has one free unit, for garbage collect // syncronization // Status = NtCreateMutant( &Od2GarbageCollectSem, MUTANT_ALL_ACCESS, NULL, FALSE); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2InitSem: error at NtCreateSemaphore, Status %x\n", Status); } #endif } // Initialize index hint for shared RAM semaphores. We assume that there will // be no more than 256 processes permanently. We assume also that there are // no more than 256 shared RAM semaphores will be initialized by each // process. If this assumptions are TRUE, the hint will be always the index of // the empty slot for each process. // Just in the case that there is the process that has been created more then // 256 shared RAM semaphores, the hint will point to the semaphores of the next // process. But there is a good chance that the next process doesn't use // shared RAM semaphores at all or uses semaphores of the processes that // were started before, or uses a little number of semaphores. Od2SemIndexHint = (((USHORT)Od2Process->Pib.ProcessId) << 8); return(Status); } POS21X_SEM Od2LookupSem ( HSEM hsem ) { POS21X_SEM pSem; BOOLEAN TookTaskLock = FALSE; NTSTATUS Status; pSem = (POS21X_SEM)(*(PULONG)hsem & 0xFFFFFFFC); if (((ULONG)pSem < (ULONG)Od2TiledHeap + OD2TILEDHEAP_SIZE) && ((ULONG)pSem >= (ULONG)Od2TiledHeap)) { // // No need for special support (RAM sem in shared memory hook) // - the 4 bytes in user area contains // bit: 31 1 // | pointer to prealsem in od2heap | 2bits for set/clear // if (pSem->pMyself == (PVOID)hsem) { return pSem; } #if DBG else { DbgPrint("[%d,%d]Od2LookupSem: Private RAM sem signature corrupted hsem=%x, *hsem=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, pSem ); } #endif // DBG } if (LockFlag) { TookTaskLock = TRUE; Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status) && Status != STATUS_SEMAPHORE_LIMIT_EXCEEDED) { KdPrint(("Od2LookupSem: failed to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2LookupSem: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } // // Special support for RAM semaphores in shared memory - we look // them up in the linked list // lookagain: pSem = (POS21X_SEM)Od2Sem16ListHead; try { while (pSem != NULL) { // // Check if pMyself is equal to hsem // if (pSem->pMyself == (PVOID)hsem) { // // Special support for lousy apps like SQL1.11, // that look for RAM sem being zero after clear // if (((*(PULONG)hsem & 0xFFFFFFFC) == 0) && (pSem->u.SharedRamSignature == 0)){ *(PULONG)hsem = (ULONG)pSem | (*(PULONG)hsem & 0x3); } if (TookTaskLock) { Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2LookupSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif } return pSem; } pSem = (POS21X_SEM)(pSem->Next); } if (TookTaskLock) { Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2LookupSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif } } except( EXCEPTION_EXECUTE_HANDLER ) { #if DBG DbgPrint("Od2LookupSem: A Pointer became invalid while searching\n"); #endif Sleep(100); goto lookagain; } return NULL; } APIRET Od2GetSemNtEvent( IN HSEM hsem, OUT PHANDLE pNtEventHandle ) { APIRET rc = NO_ERROR; POS21X_SEM pRealSem; POR2_HANDLE_TABLE SemaphoreTable; POD2_SEMAPHORE Semaphore; BOOLEAN SharedSem; ULONG HandleIndex; pRealSem = Od2LookupSem (hsem); if (pRealSem == NULL) return ERROR_INVALID_HANDLE; // // Validate the passed OS/2 2.0 semaphore handle and extract the // shared/private flag and the index field. Return an error if // not a valid handle. // rc = Od2ValidateSemaphoreHandle( pRealSem->Event, &SharedSem, &HandleIndex ); if (rc != NO_ERROR) { return( rc ); } // // Get the pointer to either the shared or private semaphore table. // Table must exist. Return an error if it does not. // SemaphoreTable = Od2GetSemaphoreTable( SharedSem, FALSE ); if (!SemaphoreTable) { return( ERROR_INVALID_HANDLE ); } // // Map the semaphore handle into a pointer to the semaphore structure // contained in the table. Return an error if the handle is outside // the current limits of the table. If the mapping is successful then // the semaphore table is left locked while we use the pointer. // Semaphore = (POD2_SEMAPHORE)Or2MapHandle( SemaphoreTable, HandleIndex, FALSE ); if (Semaphore == NULL) { return( ERROR_INVALID_HANDLE ); } // // Entry in semaphore table is for an Event semaphore, so extract the // NT Event handle from the record and release the lock, so we are // not holding the lock while we are doing the query. // *pNtEventHandle = Semaphore->u.EventHandle; ReleaseHandleTableLock( SemaphoreTable ); } POS21X_SEM SemWasCreatedByThisProcess( IN PCHAR name) { POS21X_SEM pSem; for (pSem = (POS21X_SEM)Od2Sem16ListHead; pSem != NULL; pSem = (POS21X_SEM)(pSem->Next)) { // search list for a if (pSem->pMyself == (PVOID)(&(pSem->pMyself)) && // System Semaphore !strcmp(pSem->u.SysSemName,name)) { // with same name return(pSem); } } return(NULL); } APIRET Od2OpenSem( IN HSEM UserSem, OUT POS21X_SEM *ppRealSem, IN PSZ pszSemName, IN BOOL Insert ) { APIRET rc = NO_ERROR; NTSTATUS Status; ULONG CreateAttributes = 0; POS21X_SEM pRealSem; PSZ eventname = NULL; PSZ pszSrc, pszDst; STRING CanonicalSemString, CanonicalEventString; UNICODE_STRING CanonicalSemString_U; OBJECT_ATTRIBUTES Obja; BOOL KeepName=FALSE; if (pszSemName == NULL) return ERROR_INVALID_NAME; // // We support \sem\XXX by creating a named event \sem32\XXX // and a NtSemaphore named \sem32\XXXNt16Sem // eventname = RtlAllocateHeap (Od2Heap, 0, CCHMAXPATH); if (eventname == NULL) return ERROR_NOT_ENOUGH_MEMORY; // // first chars must be \sem\. We leave the rest for Od2Canonicalize // try { if ( ((pszSemName[0] != '\\') && (pszSemName[0] != '/')) || ((pszSemName[1] != 's') && (pszSemName[1] != 'S')) || ((pszSemName[2] != 'e') && (pszSemName[2] != 'E')) || ((pszSemName[3] != 'm') && (pszSemName[3] != 'M')) || ((pszSemName[4] != '\\') && (pszSemName[4] != '/'))) { return ERROR_INVALID_NAME; } else { eventname[0] = '\\'; eventname[1] = 's'; eventname[2] = 'e'; eventname[3] = 'm'; eventname[4] = '3'; eventname[5] = '2'; eventname[6] = '\\'; // // Copy rest of string // pszSrc = pszSemName + 5; pszDst = eventname + 7; while (*pszSrc) { *pszDst++ = *pszSrc++; if ((pszSrc - pszSemName) >= CCHMAXPATH) { RtlFreeHeap (Od2Heap, 0, eventname); return ERROR_INVALID_NAME; } } *pszDst = '\0'; } } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // // Prepare the Nt Semaphore name, by // Canonicalizing and appendint nt16sem // rc = Od2Canonicalize( eventname, CANONICALIZE_SEMAPHORE, &CanonicalEventString, NULL, NULL, NULL ); if (rc) { RtlFreeHeap (Od2Heap, 0, eventname); return(rc); } if (UserSem == NULL && Insert) { if (pRealSem = SemWasCreatedByThisProcess(CanonicalEventString.Buffer)) { // // We tried to open a system semaphore which has already // been created or opened by this process // we just have to inc. the counter and return the same handle // RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap(Od2Heap, 0,CanonicalEventString.Buffer); if (pRealSem->SysSemCount == 0xff) { KdPrint(("Od2OpenSem: SysSemCount overflow\n")); } else { pRealSem->SysSemCount++; } *ppRealSem = pRealSem; return (NO_ERROR); } KeepName = TRUE; } CanonicalSemString.Buffer = RtlAllocateHeap ( Od2Heap, 0, CanonicalEventString.Length+8 // 8 for Ntsem16 ); if (CanonicalSemString.Buffer == NULL) { RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap(Od2Heap, 0,CanonicalEventString.Buffer); return(ERROR_NOT_ENOUGH_MEMORY); } strncpy( CanonicalSemString.Buffer, CanonicalEventString.Buffer, CanonicalEventString.Length ); CanonicalSemString.Buffer[CanonicalEventString.Length] = '\0'; strcpy(&(CanonicalSemString.Buffer[CanonicalEventString.Length]), "Nt16sem"); CanonicalSemString.Buffer[CanonicalEventString.Length + 7] = '\0'; if (! KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } // // Convert Nt Semaphore string to Unicode // Od2InitMBString(&CanonicalSemString, CanonicalSemString.Buffer); // // UNICODE conversion - // rc = Od2MBStringToUnicodeString( &CanonicalSemString_U, &CanonicalSemString, TRUE); RtlFreeHeap(Od2Heap, 0,CanonicalSemString.Buffer); if (rc) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2OpenSem: no memory for Unicode Conversion\n"); } #endif RtlFreeHeap (Od2Heap, 0, eventname); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return rc; } InitializeObjectAttributes( &Obja, &CanonicalSemString_U, OBJ_CASE_INSENSITIVE, NULL, NULL); pRealSem = (HSYSSEM)RtlAllocateHeap (Od2TiledHeap, 0, sizeof (OS21X_SEM)); if (pRealSem == NULL) { #if DBG DbgPrint("Od2OpenSem: out of space on Od2TiledHeap\n"); ASSERT(FALSE); #endif RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeUnicodeString (&CanonicalSemString_U); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_NOT_ENOUGH_MEMORY; } if (((ULONG)(pRealSem) > (ULONG)Od2TiledHeap + OD2TILEDHEAP_SIZE) && ((ULONG)(pRealSem) < (ULONG)Od2TiledHeap)) { // // Got out of the initial Heap, return error // #if DBG DbgPrint("Od2OpenSem: out of space on Od2TiledHeap\n"); ASSERT(FALSE); #endif RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap (Od2TiledHeap, 0, pRealSem); RtlFreeUnicodeString (&CanonicalSemString_U); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_NOT_ENOUGH_MEMORY; } RtlZeroMemory(pRealSem, sizeof (OS21X_SEM)); // // Open an Nt semaphore // Status = NtOpenSemaphore(&(pRealSem->Mutex), SEMAPHORE_ALL_ACCESS, &Obja); RtlFreeUnicodeString (&CanonicalSemString_U); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { if (Status != STATUS_OBJECT_NAME_NOT_FOUND) DbgPrint("Od2OpenSem: error at NtopenSemaphore, Status %x\n", Status); } #endif #if DBG if (((Os2DebugSem != 0) && (UserSem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (UserSem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (UserSem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (UserSem == Os2DebugSem1))) { KdPrint(("[%d,%d] Od2OpenSem(%x,*=%x): failed to NtOpenSemaphore, eventname=%s, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), UserSem, *(PULONG)UserSem, eventname, Status)); } #endif // DBG RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap (Od2TiledHeap, 0, pRealSem); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_SEM_NOT_FOUND; } rc = DosOpenEventSem(eventname, &(pRealSem->Event)); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2OpenSem: error at DosOpenEventSem, Status %d\n", rc); } #endif #if DBG if (((Os2DebugSem != 0) && (UserSem == Os2DebugSem)) || ((Os2DebugSem2 != 0) && (UserSem == Os2DebugSem2)) || ((Os2DebugSem3 != 0) && (UserSem == Os2DebugSem3)) || ((Os2DebugSem1 != 0) && (UserSem == Os2DebugSem1))) { KdPrint(("[%d,%d] Od2OpenSem(%x,*=%x): failed to DosOpenEventSem, rc=%d\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), UserSem, *(PULONG)UserSem, rc)); } #endif // DBG NtClose(pRealSem->Mutex); RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap (Od2TiledHeap, 0, pRealSem); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return rc; } pRealSem->OwnerThread = (TID)SEM_AT_INIT; // // UserSem: // if NULL - indicates sys semaphore // if non NULL - this is the pointer to a RAM sem // that resides in a shared mem // the reason we open it is to get real handles between processes // if (UserSem == NULL) { pRealSem->pMyself = (PVOID)(&(pRealSem->pMyself)); if (Insert) { pRealSem->u.SysSemName = CanonicalEventString.Buffer; pRealSem->SysSemCount = 1; } } else { pRealSem->pMyself = (PVOID)UserSem; } RtlFreeHeap (Od2Heap, 0, eventname); if (Insert) { // // Link the new semaphore on the per-process semaphore // list // Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2OpenSem: failed to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2OpenSem: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif pRealSem->Next = Od2Sem16ListHead; Od2Sem16ListHead = (struct OS21X_SEM *)pRealSem; Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2OpenSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif } // // Now set the output pointer to the sem structure // *ppRealSem = pRealSem; return (NO_ERROR); } APIRET Od2CreateSem( IN HSEM UserSem, IN USHORT fExclusive, OUT POS21X_SEM *ppRealSem, IN PSZ pszSemName ) { APIRET rc = NO_ERROR; NTSTATUS Status; ULONG CreateAttributes = 0; POS21X_SEM pRealSem; PSZ eventname = NULL; PSZ pszSrc, pszDst; STRING CanonicalSemString, CanonicalEventString; UNICODE_STRING CanonicalSemString_U; OBJECT_ATTRIBUTES Obja, *pObja; CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH]; // If the least significant byte of the RAM semaphore isn't 0, it must // be created as busy semaphore. That means that after setting the least // significant byte of the RAM semaphore to non-zero and calling for // DosSemRequest or DosSemWait, thread will wait until any other thread // will clear this semaphore. BOOLEAN MakeBusy = (UserSem != NULL) && (((*(PULONG)UserSem) & 0xff) != 0); BOOLEAN MakeSys = FALSE, KeepName=FALSE; ULONG RegionSize = 0x10000; ULONG SharedFlag; HSEM tmpsem; char *pchtmp; pObja = NULL; // // UserSem: // if NULL - indicates we create a sys semaphore // by calling DosCreateSem // if non NULL - this is the pointer to a RAM sem // given by the app. // if (UserSem == NULL) { MakeSys = TRUE; } else { // // Due to (somewhat awkward) 1.21 semantics, a RAM semaphore // can be magically used by two processes, if happend to reside // in a memory shared between them // rc = DosQueryMem( (PVOID) UserSem, &RegionSize, &SharedFlag); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("Can't query shared mem %d\n", rc); } #endif } if (SharedFlag & PAG_SHARED) { MakeSys = TRUE; // // makeup a name from the air, to be used later. // pszSemName = "\\SEM\\HACKNT00000"; // // try in a loop to open it, increment number until // we succeed. // Start from the hint, that possibly will be the index of // the empty slot. // do { Od2SemIndexHint++; // We don't use index 0 if (Od2SemIndexHint == 0) { Od2SemIndexHint++; } // // construct last 4 characters as the digits of i // pchtmp = _itoa (Od2SemIndexHint, &pszSemName[11], 16); rc = Od2OpenSem(NULL, &(POS21X_SEM)tmpsem, pszSemName,TRUE); if (rc != NO_ERROR) { // // does not exist - go for it // // HSEM is: // // bit: 31 19 17 1 // |magic number|iswait| 16b index | 2bits for set/clear *(PULONG)UserSem = ((ULONG)Od2SemIndexHint << 2) | 0xCCC00000; #if DBG #ifdef DBG_SEM_INDEX if (Od2SemIndexHint == DBG_SEM_INDEX) { DbgPrint("[%d,%d] Od2LookUpOrCreateSem: looping for open, got rc=%x for index %x for hsem=%x, *=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), rc, DBG_SEM_INDEX UserSem, *(PULONG)UserSem ); } #endif // DBG_SEM_INDEX #endif // DBG break; } Od2CloseSem(FALSE,tmpsem); } while (TRUE); } } if ((MakeSys) && (pszSemName != NULL)) { pObja = &Obja; // // We support \sem\XXX by creating a named event \sem32\XXX // and a NtSemaphore named \sem32\XXXNt16Sem // eventname = RtlAllocateHeap (Od2Heap, 0, CCHMAXPATH); if (eventname == NULL) return ERROR_NOT_ENOUGH_MEMORY; // // first chars must be \sem\. We leave the rest for Od2Canonicalize // try { if ( ((pszSemName[0] != '\\') && (pszSemName[0] != '/')) || ((pszSemName[1] != 's') && (pszSemName[1] != 'S')) || ((pszSemName[2] != 'e') && (pszSemName[2] != 'E')) || ((pszSemName[3] != 'm') && (pszSemName[3] != 'M')) || ((pszSemName[4] != '\\') && (pszSemName[4] != '/'))) { return ERROR_INVALID_NAME; } else { eventname[0] = '\\'; eventname[1] = 's'; eventname[2] = 'e'; eventname[3] = 'm'; eventname[4] = '3'; eventname[5] = '2'; eventname[6] = '\\'; // // Copy rest of string // pszSrc = pszSemName + 5; pszDst = eventname + 7; while (*pszSrc) { *pszDst++ = *pszSrc++; if ((pszSrc - pszSemName) >= CCHMAXPATH) { RtlFreeHeap (Od2Heap, 0, eventname); return ERROR_INVALID_NAME; } } // Null terminate string *pszDst = '\0'; } } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // // Prepare the Nt Semaphore name, by // Canonicalizing and appendint nt16sem // rc = Od2Canonicalize( eventname, CANONICALIZE_SEMAPHORE, &CanonicalEventString, NULL, NULL, NULL ); if (rc) { RtlFreeHeap (Od2Heap, 0, eventname); if (rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND || rc == ERROR_FILENAME_EXCED_RANGE) { return(ERROR_INVALID_NAME); } return(rc); } KeepName = (UserSem == NULL); CanonicalSemString.Buffer = RtlAllocateHeap (Od2Heap, 0, CanonicalEventString.Length+8 // 8 for Ntsem16 ); if (CanonicalSemString.Buffer == NULL) { RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap(Od2Heap, 0,CanonicalEventString.Buffer); return(ERROR_NOT_ENOUGH_MEMORY); } strncpy( CanonicalSemString.Buffer, CanonicalEventString.Buffer, CanonicalEventString.Length ); CanonicalSemString.Buffer[CanonicalEventString.Length] = '\0'; strcpy (&(CanonicalSemString.Buffer[strlen(CanonicalSemString.Buffer)]), "Nt16sem"); CanonicalSemString.Buffer[CanonicalEventString.Length+7] = '\0'; if (!KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } // // Convert Nt Semaphore string to Unicode // Od2InitMBString(&CanonicalSemString, CanonicalSemString.Buffer); // // UNICODE conversion - // rc = Od2MBStringToUnicodeString( &CanonicalSemString_U, &CanonicalSemString, TRUE); RtlFreeHeap(Od2Heap, 0,CanonicalSemString.Buffer); if (rc) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2CreateSem: no memory for Unicode Conversion\n"); } #endif RtlFreeHeap (Od2Heap, 0, eventname); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return rc; } // PatrickQ: create and attach a security descriptor. This was missing // till August 10, 1994 and caused a bug when one instance of OS2.EXE // creates an event, then in another user context (e.g. when using AT) // OS2.EXE attempts to open the existing event => gets access denied. // Prevented PMSHELL from running as a service. Status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if (!NT_SUCCESS( Status )) { #if DBG DbgPrint("OS2: Od2CreateSem, failed at RtlCreateSecurityDescriptor %x\n", Status); #endif RtlFreeHeap (Od2Heap, 0, eventname); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_ACCESS_DENIED; } Status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor, (BOOLEAN)TRUE, (PACL) NULL, (BOOLEAN)FALSE ); if (!NT_SUCCESS( Status )) { #if DBG DbgPrint("OS2: Od2CreateSem, failed at RtlSetDaclSecurityDescriptor %x\n", Status); #endif RtlFreeHeap (Od2Heap, 0, eventname); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_ACCESS_DENIED; } InitializeObjectAttributes( &Obja, &CanonicalSemString_U, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR) &localSecurityDescriptor); } pRealSem = (HSYSSEM)RtlAllocateHeap (Od2TiledHeap, 0, sizeof (OS21X_SEM)); if (pRealSem == NULL) { RtlFreeHeap (Od2Heap, 0, eventname); if (eventname != NULL) { RtlFreeUnicodeString (&CanonicalSemString_U); } if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_NOT_ENOUGH_MEMORY; } if (((ULONG)(pRealSem) > (ULONG)Od2TiledHeap + OD2TILEDHEAP_SIZE) && ((ULONG)(pRealSem) < (ULONG)Od2TiledHeap)) { // // Got out of the initial Heap, return error // #if DBG DbgPrint("Od2CreateSem: out of space on Od2TiledHeap\n"); ASSERT(FALSE); #endif RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap (Od2TiledHeap, 0, pRealSem); RtlFreeUnicodeString (&CanonicalSemString_U); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return ERROR_NOT_ENOUGH_MEMORY; } RtlZeroMemory(pRealSem, sizeof (OS21X_SEM)); if (UserSem == NULL) { if (fExclusive == CSEM_PUBLIC) { CreateAttributes |= DC_SEM_SHARED; pRealSem->FlagsByte |= SYSSEM_PUBLIC; } else { pRealSem->FlagsByte |= SYSSEM_PRIVATE; } } // // Create an Nt semaphore that has one free unit // Status = NtCreateSemaphore(&(pRealSem->Mutex), SEMAPHORE_ALL_ACCESS, pObja, 1, 1); if (eventname != NULL) { // // free the unicode allocated string // RtlFreeUnicodeString (&CanonicalSemString_U); } if (!NT_SUCCESS(Status)) { #if DBG DbgPrint("Od2CreateSem: error at NtCreateSemaphore, Status %x\n", Status); #endif if (eventname != NULL) { RtlFreeHeap (Od2Heap, 0, eventname); } RtlFreeHeap (Od2TiledHeap, 0, pRealSem); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } switch (Status) { case STATUS_OBJECT_NAME_COLLISION: return ERROR_ALREADY_EXISTS; default: return (Or2MapNtStatusToOs2Error(Status, ERROR_TOO_MANY_SEMAPHORES)); } } pRealSem->OwnerThread = (TID)SEM_AT_INIT; if (MakeBusy) { // Busy semaphore will be created. It is created as a result of using // DosSemRequest for the 1st time for the RAM semaphore that it's // least significant byte wasn't zero. NTSTATUS Status; TIME Time = {0L, 0L}; // NT semaphore is free after creation. Take the semaphore // (with timeout 0). Status = NtWaitForSingleObject( pRealSem->Mutex, TRUE, &Time); // We must always get SUCCESS. The semaphore was just created and // no one can get it, because this code is protected by Od2Sync. // Just in the case ... print appropriate message. if (Status != STATUS_SUCCESS) { #if DBG DbgPrint("[%d,%d] ERROR !!! Od2CreateSem %x : *=%x NtWaitForSingleObject fail with %x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), UserSem, *(PULONG)UserSem, Status); #endif // DBG } else { // Print this message always. We will be prompted if apps // use busy semaphores. Lotus Notes 3.0 is the only known app that // do it. #if DBG DbgPrint("[%d,%d] Od2CreateSem %x : *=%x make busy semaphore\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), UserSem, *(PULONG)UserSem); #endif // DBG } } // Make the event initially not signaled if the semaphore must be maked // busy. rc = DosCreateEventSem(eventname, &(pRealSem->Event), CreateAttributes, !MakeBusy); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("Od2CreateSem: error at DosCreateEventSem, Status %d\n", rc); } #endif NtClose(pRealSem->Mutex); if (eventname != NULL) RtlFreeHeap (Od2Heap, 0, eventname); RtlFreeHeap (Od2TiledHeap, 0, pRealSem); if (KeepName) { RtlFreeHeap (Od2Heap, 0, CanonicalEventString.Buffer); } return rc; } // // if a system semaphore: // Make pRealSem->pMyself to point at itself // Keep its name // // if a RAM semaphore: // Make pRealSem->pMyself the initial pointer // provided by the program // if (UserSem == NULL) { pRealSem->pMyself = (PVOID)(&(pRealSem->pMyself)); if (pszSemName != NULL) { pRealSem->u.SysSemName = CanonicalEventString.Buffer; pRealSem->SysSemCount = 1; } } else { pRealSem->pMyself = (PVOID)UserSem; } // // Link the new semaphore on the per-process semaphore // list // Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2CreateSem: failed to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2CreateSem: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif pRealSem->Next = Od2Sem16ListHead; Od2Sem16ListHead = (struct OS21X_SEM *)pRealSem; Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status) && Status != STATUS_SEMAPHORE_LIMIT_EXCEEDED) { KdPrint(("Od2CreateSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif // // Now set the output pointer to the sem structure // *ppRealSem = pRealSem; if (eventname != NULL) RtlFreeHeap (Od2Heap, 0, eventname); return NO_ERROR; } BOOLEAN GarbageCollect(VOID) { // // This routine is called when Od2CreateSem is unable to create a new // RAM semaphore. This can be caused by the fact that in OS/2 a RAM semaphore // requires almost 0 resources, while in NT it is a OS21X_SEM strcuture, plus // two objects - event and semaphore // // The algorithm is: // raise the lockflag so subsequent calls to Od2LookupSem lock, // lock the task lock // go thru the list of RAM semaphores, find private RAM semaphores // which are cleared, close them and free heap. // release the task lock. Subsequent calls to the deleted semaphores will // recreate them // clear the lock flag // POS21X_SEM pPrevSem, pRealSem; HSEM hsem; NTSTATUS Status; APIRET rc; BOOLEAN DeletedSomeStuff = FALSE; ULONG hsem_value; #if DBG ULONG NumSem = 0, NumFreed = 0; #endif //DBG LockFlag = TRUE; Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Od2GarbageCollect: failed to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] GarbageCollect: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif // // Allow all threads in this process, which may got thru Od2LookupSem, // and did not finish an API, to do so, before we gargage collect // 1. Those that were waiting on a cleared semaphore, will get it // 2. Those that were clearing a semaphore will clear it and free // the threads in category 1. // 3. Those waiting on a non-cleared semaphore, will keep waiting, however // the garbage collector does not free these semaphores. // Sleep(2000); for (pRealSem = pPrevSem = (POS21X_SEM)Od2Sem16ListHead; pPrevSem->Next != NULL;) { #if DBG NumSem++; #endif //DBG // // check if a private RAM sem // hsem = (HSEM)(pRealSem->pMyself); // Validate validity of each semaphore in the list try { hsem_value = *(PULONG)hsem; } except( EXCEPTION_EXECUTE_HANDLER ) { #if DBG DbgPrint("[%d,%d] GarbageCollect: WARNING !!! can't access RAM semaphore %x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem); #endif // // Can't access this semaphore - maybe memory was released ! // Remove this semaphore from the list and go to next by // setting value to 0 hsem_value = 0; } if ((hsem_value != (ULONG)hsem) && ((hsem_value & 0xFFF00000) != 0xCCC00000)) { // // Private RAM Semaphore: // if (hsem_value == 0) { LONG PreviousCount; // defend against:DosSemClear already zeroed hsem but haven't // released it yet NtReleaseSemaphore ( pRealSem->Mutex, 1, &PreviousCount); // // It is cleared - delete it // Status = NtClose (pRealSem->Mutex); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSem: error at NtClose, Status %x\n", Status); } #endif } else { DeletedSomeStuff = TRUE; } // defend against:DosSemClear already zeroed hsem but haven't // released it yet DosPostEventSem (pRealSem->Event); // // delete it // rc = DosCloseEventSem (pRealSem->Event); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSem: error at DosCloseEventSem, Status %d\n", rc); } #endif } else { DeletedSomeStuff = TRUE; } #if DBG NumFreed++; #endif // // unlink it // if (pRealSem == (POS21X_SEM)Od2Sem16ListHead) { // // chop the head of the list, start from scratch // Od2Sem16ListHead = pRealSem->Next; // // Free sem structure from heap; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); pRealSem = pPrevSem = (POS21X_SEM)Od2Sem16ListHead; } else { pPrevSem->Next = pRealSem->Next; // // Free sem structure from heap; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); pRealSem = (POS21X_SEM)(pPrevSem->Next); } Od2NumSemCreated = 0; } else { // // Semaphore is not clear - skip // pPrevSem = pRealSem; pRealSem = (POS21X_SEM)(pPrevSem->Next); } } else { // // Not A private RAM semaphore - skip pPrevSem = pRealSem; pRealSem = (POS21X_SEM)(pPrevSem->Next); } } Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Os2 - GarbageCollect: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } KdPrint(("Os2 - GarbageCollect: found %d semaphores, deleted %d\n", NumSem,NumFreed)); #endif LockFlag = FALSE; return(DeletedSomeStuff); } // // The parameter CheckInitializationOfSem must be TRUE only if Od2LookupOrCreateSem was // called from DosSemRequest or DosSemWait. In this case busy semaphore might be created (only // if there is RAM semaphore that used for the 1st time). Busy semaphore, while // created cause until other thread will clear the semaphore by DosSemClear. // POS21X_SEM Od2LookupOrCreateSem ( HSEM hsem, PBOOLEAN firsttime, ULONG source ) { APIRET rc = NO_ERROR; POS21X_SEM pRealSem, pPrevSem; ULONG tmp; NTSTATUS Status; BOOLEAN FlushSharedRamSem = FALSE; PSZ pszSemName; char *pchtmp; pRealSem = Od2LookupSem(hsem); if (pRealSem != NULL) { if (pRealSem->u.SharedRamSignature && pRealSem->pMyself != (PVOID)(&(pRealSem->pMyself))) { // // if a RAM semaphore in shared memory, check that the // actual signature in the pRealSem structure (per process) // is identical to the signature in shared memory. If it // is not, it means that this is a re-use of old sem, in // which case we cleanup and use this structure for the new // semaphore // if ((*(PULONG)hsem & 0x3FFFC) != (pRealSem->u.SharedRamSignature & 0x3FFFC)) { #if PMNT #if DBG DbgPrint("[%d,%d] Od2LookupOrCreateSem: signature mismatch, hsem=%x, *=%x, signature=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, pRealSem->u.SharedRamSignature); #endif // DBG #endif // PMNT FlushSharedRamSem = TRUE; goto SyncLabel; } } *firsttime = FALSE; return(pRealSem); } SyncLabel: Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2LookupOrCreateSem: FAILED to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif pRealSem = Od2LookupSem(hsem); if (FlushSharedRamSem) { // // see if another thread in this process did not fix it yet // if ((*(PULONG)hsem & 0x3FFFC) != (pRealSem->u.SharedRamSignature & 0x3FFFC)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("FlushSharedRamSem: Flushing reference of *hsem %x, pRealSem at %x\n", *(PULONG)hsem, pRealSem); } #endif if ((source & SEM_FROM_CLEAR) && ((*(PULONG)hsem & 0xFFF00000) != 0xCCC00000)) { Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2LookupOrCreateSem (flashing skipped): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif *firsttime = FALSE; // // *(PULONG)hsem &= 0xffffff00; // __asm { mov eax, hsem lock and dword ptr [eax], 0xffffff00 } return(pRealSem); } #if PMNT #if DBG DbgPrint("[%d,%d] FlushSharedRamSem: Flushing reference of hsem=%x, *=%x, signature=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, pRealSem->u.SharedRamSignature); #endif // DBG #endif // PMNT // // Need to flush and recreate the process copy of the // shared RAM sem // // 1. close handles // 2. unlink structure and free memory // 3. let the algorithm below take care of it as a new // shared RAM semaphore // Status = NtClose (pRealSem->Mutex); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("FlushShareRamSem: error at NtClose, Status %x\n", Status); } #endif } rc = DosCloseEventSem (pRealSem->Event); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("FlushShareRamSem: error at DosCloseEventSem, Status %d\n", rc); } #endif } // // Now unlink it from the per-process semaphore list // and free heap space allocated // Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("FlushSharedRamSem: FAILED to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2LookupOrCreateSem: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if (pRealSem == (POS21X_SEM)Od2Sem16ListHead) { // // Get rid of first on the list // Od2Sem16ListHead = pRealSem->Next; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); } else { for (pPrevSem = (POS21X_SEM)Od2Sem16ListHead; pPrevSem->Next != NULL; pPrevSem = (POS21X_SEM)(pPrevSem->Next)) { if ((POS21X_SEM)(pPrevSem->Next) == pRealSem) { // // Found our semaphore on the list // pPrevSem->Next = pRealSem->Next; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); break; } } } // // Shared RAM semaphore was closed. // Od2NumSemCreated--; Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("FlushSharedRamSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif pRealSem = NULL; } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("FlushShareRamSem: Another thread in this process Flushed reference of *hsem %x, pRealSem at %x\n", *(PULONG)hsem, pRealSem); } #endif #if PMNT #if DBG DbgPrint("[%d,%d] FlushShareRamSem: Another thread in this process flushed reference of hsem=%x, *=%x, signature=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, pRealSem->u.SharedRamSignature); #endif // DBG #endif // PMNT } } if (pRealSem != NULL) { *firsttime = FALSE; Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2LookupOrCreateSem (semaphore was found): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif return(pRealSem); } // // A RAM Semaphore first call in this process. // Initialize an OS21X_SEM by a call to // Od2CreateSem or Od2OpenSem // // // check for a magic number that mark RAM semaphores // that reside in shared memory // tmp = *(PULONG)hsem; if ((tmp & 0xFFF00000) == 0xCCC00000) { *firsttime = FALSE; // // makeup a name from the air, to be used later. // pszSemName = "\\SEM\\HACKNT00000"; // // HSEM is: // // bit: 31 17 1 // |magic number | 16b index | 2bits for set/clear| // // extract the sem index out of sem handle // tmp = (tmp >> 2) & 0xFFFF; // // construct last 4 characters as the digits of i // pchtmp = _itoa (tmp, &pszSemName[11], 16); // // open the semaphore (RAM sem created previously in shared mem // rc = Od2OpenSem( hsem, &pRealSem, pszSemName, TRUE); if (rc != NO_ERROR) { #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] Od2LookupOrCreateSem(%x,*=%x): failed to Od2OpenSem on %s, rc=%d\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, pszSemName, rc)); } #endif // DBG // Create semaphore. // if (!(source & SEM_FROM_REQUESTWAIT)) { // Erase the least significat byte of the semaphore, // so semaphore will not be created in busy state. // // *(PULONG)hsem &= 0xffffff00; // __asm { mov eax, hsem lock and dword ptr [eax], 0xffffff00 } } *firsttime = TRUE; // // We failed to open - there was garbage in the user memory // Create the semaphore // rc = Od2CreateSem( hsem, CSEM_PRIVATE, &pRealSem, NULL); #if DBG #ifdef DBG_SEM_INDEX if ((*(PULONG)hsem & 0x3FFFC) == (DBG_SEM_INDEX << 2)) { KdPrint(("[%d,%d] Od2LookupOrCreateSem: created (due to garbage) %x for hsem=%x, *=%x, rc=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), DBG_SEM_INDEX, hsem, *(PULONG)hsem, rc )); } #endif // DBG_SEM_INDEX #endif // DBG } #if DBG #ifdef DBG_SEM_INDEX else { // // success opening // if (tmp == (DBG_SEM_INDEX << 2)) { KdPrint(("[%d,%d] Od2LookupOrCreateSem: opened %x for hsem=%x, *=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), DBG_SEM_INDEX, hsem, *(PULONG)hsem )); } } #endif // DBG_SEM_INDEX #endif // DBG // // record the signature of the ram sem for later checks // if (rc == NO_ERROR) { pRealSem->u.SharedRamSignature = *(PULONG)hsem; } } else { if (!(source & SEM_FROM_REQUESTWAIT)) { // Erase the least significat byte of the semaphore, // so semaphore will not be created in busy state. // // *(PULONG)hsem &= 0xffffff00; // __asm { mov eax, hsem lock and dword ptr [eax], 0xffffff00 } } *firsttime = TRUE; // // Create the semaphore (usual case) // rc = Od2CreateSem( hsem, CSEM_PRIVATE, &pRealSem, NULL); if (rc == NO_ERROR) { if ((*(PULONG)hsem & 0xFFF00000) == 0xCCC00000) { // // record the signature of the ram sem for later checks // pRealSem->u.SharedRamSignature = *(PULONG)hsem; #if DBG #if DBG_SEM_INDEX if ((*(PULONG)hsem & 0x3FFFC) == (DBG_SEM_INDEX << 2)) { KdPrint(("[%d,%d] Od2LookupOrCreateSem: created (new) %x for hsem=%x, *=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), DBG_SEM_INDEX, hsem, *(PULONG)hsem )); } #endif //DBG_SEM_INDEX #endif //DBG } } #ifdef PMNT #if DBG if (FlushSharedRamSem) { DbgPrint("[%d,%d] FlushShareRamSem: created new entry for hsem=%x, *=%x, signature=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, pRealSem->u.SharedRamSignature); } #endif // DBG #endif // PMNT } Od2NumSemCreated++; Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2LookupOrCreateSem (semaphore was created): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if ((rc != NO_ERROR && rc != ERROR_ALREADY_EXISTS && rc != ERROR_DUPLICATE_NAME && rc != ERROR_INVALID_NAME) || (Od2NumSemCreated > OD2SEMTHRESHOLD)) { #if DBG if (rc != NO_ERROR) { DbgPrint ("Od2LookupOrCreate: error initializing a RAM sem %d, call GarbageCollect()\n", rc); } else { DbgPrint ("Od2LookupOrCreate: low on RAM semaphores - call GarbageCollect\n"); } #endif if (GarbageCollect()) { // // Succeeded to gain more resources, try again // #if DBG DbgPrint ("Od2LookupOrCreate: GarbageCollect freed some resource, try create again\n"); #endif // Call Od2LookupOrCreateSem with the same value for parameter "source" return (Od2LookupOrCreateSem (hsem, firsttime, source)); } // // no resources - quit // if (rc != NO_ERROR) { #if DBG DbgPrint ("OS2: GarbageCollect could not free resources, Exit Application\n"); #endif DosExit(EXIT_PROCESS, 0); } #if DBG DbgPrint("OS2: GarbageCollect could not free resources, return NULL\n"); #endif return NULL; } // // expected errors - return NULL (the rest were checked above) // if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("Od2LookupOrCreate: error initializing a RAM sem, %d\n", rc); } #endif return NULL; } return(pRealSem); } /* This routine is called upon process exit clean-up. The idea here is to go through the private list of semaphores and, for each shared RAM semaphore, do: - close the associated NT events - if the signature matches the contents of the RAM semaphore: - try to open the NT events - if we get an error, this means we were the last to keep a handle to those events, so we should clear the RAM semaphore so that the OS/2 ss won't think later on (when/if this same RAM location is used as a shared RAM semaphore) it is a RAM semaphore with a valid index. NOTICE: we do not remove the entries from the Od2Sem16ListHead list. We just close the handles stored there. This is based on the assumption that nobody will need this list past this point and that the memory used for the list will be freed soon when the process exits. */ VOID Od2CloseAllRAMSharedSemaphores( VOID) { POS21X_SEM pSem; HSEM hsem, tmpsem; PSZ pszSemName, pchtmp; ULONG index; APIRET rc; NTSTATUS Status; pSem = (POS21X_SEM)Od2Sem16ListHead; while (pSem != NULL) { if (pSem->u.SharedRamSignature && // Shared pSem->pMyself != (PVOID)(&(pSem->pMyself))) { // Ram Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores: FAILED to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if (DosCloseSemNoRemove(pSem,FALSE)) { Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores (close fail): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif continue; } hsem = pSem->pMyself; try { index = (*(PULONG)hsem & 0x3FFFF) >> 2; } except( EXCEPTION_EXECUTE_HANDLER ) { pSem = (POS21X_SEM)(pSem->Next); Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores (exception handler): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif continue; } if ((pSem->u.SharedRamSignature & 0x3FFFC) == (*(PULONG)hsem & 0x3FFFC)) { // // compose the name for the semaphore // pszSemName = "\\SEM\\HACKNT00000"; // // construct last 4 characters as the digits of i // pchtmp = _itoa (index, &pszSemName[11], 16); rc = Od2OpenSem(NULL, &(POS21X_SEM)tmpsem, pszSemName,FALSE); if (rc == ERROR_SEM_NOT_FOUND) { // // This was the last open handle. The semaphore must flushed by other // processes. // if ((*(PULONG)hsem & 0xFFF00000) == 0xCCC00000) { // // *(PULONG)hsem = 0xCCC00000; // // If the signature is used by app (it isn't 0xCCC?????) like PMSHELL // that use the most significant word of the semaphore for managing // list of free semaphores for reusing, don't write 0xCCC00000 signature. // In this case the semaphore will be flushed as well. // Unfortunately there is a small time window that the value that might // be used by app was destroyed. // __asm { mov eax, hsem mov cx, 0xCCC0 xchg word ptr [eax+2], cx mov dx, cx and dx, 0xFFF0 cmp dx, 0xCCC0 je Od2CloseRamSemOk xchg cx, word ptr [eax+2] Od2CloseRamSemOk: mov word ptr [eax], 0 } } } else if (rc == NO_ERROR) { DosCloseSemNoRemove(tmpsem,TRUE); } #if DBG else { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores: Fail to open. rc=%d\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), rc); } #endif // DBG } pSem = (POS21X_SEM)(pSem->Next); Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } else { pSem = (POS21X_SEM)(pSem->Next); } } // The process is over and we used Od2Sync for the last time. This is the // good opportunity to close it's handle. Status = NtClose(Od2SyncSem); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] Od2CloseAllRAMSharedSemaphores: failed to NtClose sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif // DBG } APIRET DosCreateSem( IN USHORT fExclusive, OUT PHSYSSEM phSem, IN PSZ pszSemName ) { ULONG Sem; APIRET rc = NO_ERROR; POS21X_SEM pRealSem; NTSTATUS Status; try { Od2ProbeForWrite( phSem, sizeof( HSYSSEM ), 1 ); Od2ProbeForRead( pszSemName, 2, 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } if (fExclusive != CSEM_PRIVATE && fExclusive != CSEM_PUBLIC){ return(ERROR_INVALID_PARAMETER); } Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosCreateSem: failed to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif rc = Od2CreateSem( NULL, fExclusive, &pRealSem, pszSemName); Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosCreateSem: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosCreateSem: error calling Od2CreateSem, %d\n", rc); } #endif return rc; } Sem = (ULONG) *phSem = (ULONG) pRealSem; *phSem = (POS21X_SEM)(FLATTOFARPTR(Sem)); return(NO_ERROR); } APIRET Od2CloseSem( IN BOOL SyncFlag, IN HSEM hsem ) { APIRET rc = NO_ERROR; NTSTATUS Status; POS21X_SEM pRealSem, pPrevSem; try { Od2ProbeForWrite( (PVOID)hsem, sizeof( HSYSSEM ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } pRealSem = Od2LookupSem(hsem); if (pRealSem == NULL) { // not found or ram sem handle return ERROR_INVALID_HANDLE; } // // SyncFlag is true when called from DosCloseSem // it is false when called from Od2OpenSem, // where we have already issued Od2AcquireSync() // if (SyncFlag) { Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosCloseSem: failed to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } if (pRealSem->SysSemCount > 1 ) { // // System semaphore was created/opened more than once // we shouldn't close it yet // pRealSem->SysSemCount --; if (SyncFlag) { Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosCloseSem: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } return(NO_ERROR); } if (SyncFlag) { Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosCloseSem: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } // // On OS/2, a Set System Semaphore can not be closed // perform a wait with no blocking, and if no_error // close // if (pRealSem->FlagsByte != 0) { rc = DosSemWait( hsem, 0); if (rc != NO_ERROR){ #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSem: error at DosSemWait, rc %d, return ERROR_SEM_IS_SET\n", rc); } DbgPrint("[%d,%d] DosCloseSem(%x): DosSemWait returned rc=%d\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, rc); #endif return (ERROR_SEM_IS_SET); } } Status = NtClose (pRealSem->Mutex); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSem: error at NtClose, Status %x\n", Status); } #endif return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE)); } rc = DosCloseEventSem (pRealSem->Event); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSem: error at DosCloseEventSem, Status %d\n", rc); } #endif return rc; } // // Now unlink it from the per-process semaphore list // and free heap space allocated // Status = Od2AcquireMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("DosCloseSem: failed to NtWaitForSingleObject on garbage sem, Status=%x\n", Status)); } else if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosCloseSem: WARNING !!! Wait on GarbageCollectSem, status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if (pRealSem->pMyself == (PVOID)(&(pRealSem->pMyself)) && // System Semaphore pRealSem->u.SysSemName ){ RtlFreeHeap(Od2Heap, 0, pRealSem->u.SysSemName); } if (pRealSem == (POS21X_SEM)Od2Sem16ListHead) { // // Get rid of first on the list // Od2Sem16ListHead = pRealSem->Next; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); } else { for (pPrevSem = (POS21X_SEM)Od2Sem16ListHead; pPrevSem->Next != NULL; pPrevSem = (POS21X_SEM)(pPrevSem->Next)) { if ((POS21X_SEM)(pPrevSem->Next) == pRealSem) { // // Found our semaphore on the list // pPrevSem->Next = pRealSem->Next; RtlFreeHeap(Od2TiledHeap, 0, pRealSem); break; } } } Status = Od2ReleaseMutant(Od2GarbageCollectSem); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("DosCloseSem: failed to NtReleaseSemaphore on sync sem, Status=%x\n", Status)); } #endif return (NO_ERROR); } APIRET DosCloseSem( IN HSEM hsem ) { return (Od2CloseSem(TRUE, hsem)); } APIRET DosCloseSemNoRemove( IN POS21X_SEM pRealSem, IN BOOL FreeMem ) { APIRET rc; NTSTATUS Status; HSEM tmpMutex, tmpEvent; tmpMutex = pRealSem->Mutex; tmpEvent = pRealSem->Event; if (FreeMem) RtlFreeHeap(Od2TiledHeap, 0, pRealSem); Status = NtClose (tmpMutex); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSemNoRemove: error at NtClose, Status %x\n", Status); } #endif return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE)); } rc = DosCloseEventSem (tmpEvent); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosCloseSemNoRemove: error at DosCloseEventSem, Status %d\n", rc); } #endif return rc; } return (NO_ERROR); } APIRET DosSemClear( IN HSEM hsem ) { APIRET rc = NO_ERROR; NTSTATUS Status; POS21X_SEM pRealSem; SEMAPHORE_BASIC_INFORMATION SemInfo; BOOLEAN firsttime; ULONG hsemValue; #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemClear(%x, *=%x): entering\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG try { Od2ProbeForWrite( (PVOID)hsem, sizeof( ULONG ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // If the semaphore will be created in will be initially in not busy state. pRealSem = Od2LookupOrCreateSem(hsem, &firsttime, SEM_FROM_CLEAR); if (pRealSem == NULL) return(ERROR_NOT_ENOUGH_MEMORY); if (firsttime) { // if ((*(PULONG)hsem & 0xFFF00000) != 0xCCC00000) { // // private mem RAM sem: set value and return // // *(PULONG)hsem = (ULONG)pRealSem; // } #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemClear 1st time succeeded: Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(), hsem, pRealSem->Mutex,pRealSem->Event ); } if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemClear(%x, *=%x): first time, exiting OK\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG return NO_ERROR; } if (((hsemValue=*(PULONG)hsem) != (ULONG)hsem) && ((hsemValue & 0xFFF00000) != 0xCCC00000)) { // // Private RAM Semaphore: // OS/2 1.X Apps (SQLQ 1.11 ) expects the semaphore location // to be actually ZERO (undocumented) // // BUGBUG Maybe OS/2 clears only low word *(PULONG)hsem = 0; } // // since hsem=0 garbage colection might free pRealSem // we enclose with try-except // // Note that after we will release the sem, // by NtReleseSemaphore or DosPostEventSem // we will not be sure that the user // memory at *hsem will still be valid - would be freed by a thread // waiting for this semaphore. // instead of *hsem we will use local variable hsemValue. // try { if ((pRealSem->OwnerThread != SEM_EVENT) && // This is not a private WAIT/CLEAR RAM semaphore (((hsemValue & 0xFFFC0000) != 0xCCC40000)) ) { // This is not a shared WAIT/CLEAR RAM semaphore // // free a REQUEST - What we need to do is // To NtReleaseSemaphore on the Semaphore // if (hsemValue != (ULONG)hsem || (pRealSem->FlagsByte & SYSSEM_PUBLIC)) { // // RAM sem and public sys sem - simply release // LONG PreviousCount; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemClear about to Release Mutex: Sem %x, *Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(), hsem, hsemValue, pRealSem->Mutex,pRealSem->Event); } #endif Status = NtReleaseSemaphore ( pRealSem->Mutex, 1, &PreviousCount); if ( !NT_SUCCESS(Status) && Status != STATUS_SEMAPHORE_LIMIT_EXCEEDED ) { #if DBG DbgPrint ("DosSemClear: error NtReleaseSempahore , %lx\n", Status); #endif rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } if (pRealSem->OwnerThread != (TID)SEM_AT_INIT && pRealSem->OwnerThread != (TID)SEM_DUAL && !(pRealSem->FlagsByte & SYSSEM_PUBLIC)) { pRealSem->OwnerThread = (TID)SEM_MUTEX_NOT_OWNED; } } else { // // EXCLUSIVE system semaphores - query before release, for ownership // // BUGBUG - EXCLUSIVE system semaphores need to be rewritten, // because ownerthreadid is not shared. Also, tight sync is // needed to get the right errors when the owner dies etc. // for now - we sync every clear with the rest of the processes // Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosSemClear: FAILED to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif Status = NtQuerySemaphore( pRealSem->Mutex, SemaphoreBasicInformation, (PVOID)(&SemInfo), sizeof(SemInfo), NULL); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemClear: error in NtQuerySemaphore, %lx\n", Status); } #endif rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } else { if (SemInfo.CurrentCount == 0) { // // Sempahore is not signaled i.e. it is not clear // so let's release it // if (pRealSem->RequestCount) { // // must be a system semaphore that was prevoiusly // requested by the same thread more than once. // don't really free the thread, just count // pRealSem->RequestCount--; } else if ((ULONG)pRealSem->OwnerThread > 0 && (ULONG)pRealSem->OwnerThread < _64K ){ // // A thread has this semaphore, release it // Status = NtReleaseSemaphore ( pRealSem->Mutex, 1, NULL); if ( !NT_SUCCESS(Status) && Status != STATUS_SEMAPHORE_LIMIT_EXCEEDED ) { #if DBG DbgPrint ("DosSemClear: error NtReleaseSempahore , %lx\n", Status); #endif rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } if (pRealSem->OwnerThread != (TID)SEM_AT_INIT && pRealSem->OwnerThread != (TID)SEM_DUAL) { pRealSem->OwnerThread = (TID)SEM_MUTEX_NOT_OWNED; } } } } Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosSemClear: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } } // // Post the Event if it is not a RAM semaphore // of type REQUEST/CLEAR // if ( (pRealSem->OwnerThread == (TID)SEM_EVENT) || // WAIT/CLEAR private RAM SEM (pRealSem->OwnerThread == (TID)SEM_AT_INIT) || // Initialized RAM SEM (pRealSem->OwnerThread == (TID)SEM_DUAL) || // RAM SEM use for both mutext and event (hsemValue == (ULONG)hsem) || // System Semaphore ( ((hsemValue & 0xFFF00000) == 0xCCC00000) && ((hsemValue & 0xFFFC0000) != 0xCCC80000) ) ) { // This is not a shared REQUEST/CLEAR RAM semaphore // // This is a WAIT/CLEAR type semaphore - post the event // #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemClear about to PostEvent: Sem %x, *Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(), hsem, hsemValue, pRealSem->Mutex,pRealSem->Event ); } #endif rc = DosPostEventSem (pRealSem->Event); if (rc != NO_ERROR) { if (rc == ERROR_ALREADY_POSTED || rc == ERROR_TOO_MANY_POSTS) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemClear: Posting the Event, %d\n", rc); } #endif rc = NO_ERROR; } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemClear: error Posting the Event, %d\n", rc); } #endif } } } if (rc == NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemClear succeeded Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } #endif } } except( EXCEPTION_EXECUTE_HANDLER) { #if DBG DbgPrint ("DosSemClear - user freed Sem %x\n", hsem); #endif } #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemClear(%x, *=%x): exiting, rc=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, rc)); } #endif // DBG return rc; } APIRET DosSemSet( IN HSEM hsem ) { APIRET rc = NO_ERROR; APIRET rc1 = NO_ERROR; NTSTATUS Status = STATUS_SUCCESS; POS21X_SEM pRealSem; ULONG Dummy; LARGE_INTEGER CapturedTimeout; PLARGE_INTEGER NtTimeout; BOOLEAN firsttime; #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemSet(%x, *=%x): entering\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG try { Od2ProbeForWrite( (PVOID)hsem, sizeof( ULONG ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // If the semaphore will be created it will be initially in not busy state. pRealSem = Od2LookupOrCreateSem(hsem, &firsttime, SEM_FROM_SET); if (pRealSem == NULL) return(ERROR_NOT_ENOUGH_MEMORY); if (*(PULONG)hsem != (ULONG)hsem) { // // RAM semaphore: // change state if needed // if (pRealSem->OwnerThread == (TID)SEM_AT_INIT) { pRealSem->OwnerThread = (TID)SEM_EVENT; // mark it as a WAIT/CLEAR semaphore, no owner } if ((*(PULONG)hsem & 0xFFF00000) != 0xCCC00000) { // // private mem RAM sem: set value and return // // *(PULONG)hsem |= 1; __asm { mov eax, hsem lock or dword ptr [eax], 1 } } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemSet on shared mem RAM: Sem %x, *Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, *(PULONG)hsem, pRealSem->Mutex,pRealSem->Event ); } #endif // // *(PULONG)hsem |= 0x00040000; // __asm { mov eax, hsem lock or dword ptr [eax], 0x00040000 } } } // // We Implement a Set by a WaitForSingleObject on the Nt // Semaphore (effectively decrement count only if > 0 ) // and a Reset on the event // // RAM semaphore used for requests as well if ( (pRealSem->OwnerThread != (TID)SEM_EVENT && (*(PULONG)hsem != (ULONG)hsem)) || // public system semaphore ((*(PULONG)hsem == (ULONG)hsem) && (pRealSem->FlagsByte & SYSSEM_PUBLIC)) ){ // // Capture the timeout value of zero and convert it into an // NT timeout value. // NtTimeout = Od2CaptureTimeout( 0, (PLARGE_INTEGER)&CapturedTimeout ); Status = NtWaitForSingleObject( pRealSem->Mutex, TRUE, // Alertable NtTimeout // Immediate return ); if (!NT_SUCCESS(Status)) { #if DBG DbgPrint ("[%d,%d]DosSemSet: ERROR at NtWaitForSingleObject, %x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); #endif rc1 = Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED); } } // // If it not a mutex, we Reset the event // if ((*(PULONG)hsem == (ULONG)hsem) || pRealSem->OwnerThread == (TID)SEM_EVENT || pRealSem->OwnerThread == (TID)SEM_DUAL) { rc = DosResetEventSem (pRealSem->Event, &Dummy); if (rc == ERROR_ALREADY_RESET) rc = NO_ERROR; } if (rc == NO_ERROR && rc1 == NO_ERROR) { if (*(PULONG)hsem != (ULONG)hsem) { // // RAM semaphore: // OS/2 1.X apps expect sem location last two bits to be 1 // // *(PULONG)hsem |= 1; } else { pRealSem->OwnerThread = (TID)SEM_MUTEX_NOT_OWNED; pRealSem->RequestCount = 0; } #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { if (firsttime) { DbgPrint ("[TID %x]: DosSemSet 1st time succeeded: Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(), hsem, pRealSem->Mutex,pRealSem->Event ); } else { DbgPrint ("[TID %x]: DosSemSet succeeded Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(), hsem, pRealSem->Mutex,pRealSem->Event ); } } #endif #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemSet(%x, *=%x): exiting OK\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG return NO_ERROR; } if (rc != NO_ERROR){ #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemSet: error Resetting the Event, %d\n", rc); } #endif } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemSet: error Resetting the Mutex, %d\n", rc1); } #endif rc = rc1; } #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemSet(%x, *=%x): exiting, rc=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, rc)); } #endif // DBG return rc; } APIRET DosSemWait( IN HSEM hsem, IN LONG lTimeOut ) { APIRET rc = NO_ERROR; POS21X_SEM pRealSem; BOOLEAN firsttime; BOOLEAN CheckCurrentOwner = FALSE; LARGE_INTEGER CapturedTimeout; PLARGE_INTEGER NtTimeout; LARGE_INTEGER StartTimeStamp; #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemWait(%x, *=%x): entering\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG // // If the least significant byte of RAM semaphore is 0, it is free (OS/2 native // conmpartability). // This fiture of OS/2 is used by Saros Mezzanine, that reuse memory that contain // semaphores that were set. It is enough to zero this memory in OS/2 to free the // semaphores. // if (((*(PULONG)hsem & 0xff) == 0) && (*(PULONG)hsem != (ULONG)hsem)) { return NO_ERROR; } try { Od2ProbeForWrite( (PVOID)hsem, sizeof( ULONG ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // If the semaphore will be ckeated, will be checked if it must be created // in initially busy state. pRealSem = Od2LookupOrCreateSem(hsem, &firsttime, SEM_FROM_REQUESTWAIT); if (pRealSem == NULL) return(ERROR_NOT_ENOUGH_MEMORY); if (*(PULONG)hsem != (ULONG)hsem) { // // RAM semaphore: // if (pRealSem->OwnerThread != (TID)SEM_DUAL && pRealSem->OwnerThread != (TID)SEM_EVENT) { if (pRealSem->OwnerThread == (TID)SEM_AT_INIT) { pRealSem->OwnerThread = (TID)SEM_EVENT; // mark it as a WAIT/CLEAR semaphore, no owner } else { // // This semaphore used to be a Request/Clear only, need to verify // that we block if currently owned. // CheckCurrentOwner = TRUE; pRealSem->OwnerThread = (TID)SEM_DUAL; } } if (firsttime) { if ((*(PULONG)hsem & 0xFFF00000) != 0xCCC00000) { // // // private mem RAM sem: set value and return // *(PULONG)hsem = (ULONG)pRealSem; } else { // // *(PULONG)hsem |= 0x00040000; // __asm { mov eax, hsem lock or dword ptr [eax], 0x00040000 } } #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemWait 1st time succeeded: Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } #endif // Os2 semaphore can be initialized as busy. In this case we // must wait on it. // return(NO_ERROR); } // // We Implement a Wait by a Waiting on the Event part of // the OS21X_SEM // // // RAM semaphore: // OS/2 1.X apps expect sem location last two bits to be 0 // // // *(PULONG) hsem &= 0xfffffffc; // __asm { mov eax, hsem lock and dword ptr [eax], 0XFFFFFFFC } if ((*(PULONG)hsem & 0xFFF00000) == 0xCCC00000) { // // RAM sem in shared memory. // make sure that subsequent sem clear will wake us // #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemWait on shared mem RAM: Sem %x, *Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, *(PULONG)hsem, pRealSem->Mutex,pRealSem->Event ); } #endif if ((*(PULONG)hsem & 0xFFFC0000) == 0xCCC80000) { CheckCurrentOwner = TRUE; } // WAIT/CLEAR shared RAM sem // // *(PULONG) hsem |= 0x00040000; // __asm { mov eax, hsem lock or dword ptr [eax], 0x00040000 } } if (CheckCurrentOwner) { NTSTATUS Status; // // This semaphore used to be for Request/Clear only, // we have to make sure that if someone owns it we block // 1. Block on the mutex // 2. Free the mutex (a wait does not block others) // // // Capture the timeout value and convert it into an NT timeout value. // NtTimeout = Od2CaptureTimeout( lTimeOut, (PLARGE_INTEGER)&CapturedTimeout ); do { if (NtTimeout) { Od2StartTimeout(&StartTimeStamp); } Status = NtWaitForSingleObject( pRealSem->Mutex, TRUE, // Alertable NtTimeout ); #if DBG if (Status == STATUS_USER_APC) { DbgPrint("[%d,%d] WARNING !!! DosSemRequest was broken by APC\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId() ); } #endif } while (Status == STATUS_USER_APC && (Status = Od2ContinueTimeout(&StartTimeStamp, NtTimeout)) == STATUS_SUCCESS ); // // BUGBUG: This code is a little strange. The status can be positive value, // for example, STATUS_TIMEOUT. In this case rc will remain NO_ERROR too. // if (!NT_SUCCESS(Status)) { rc = Or2MapNtStatusToOs2Error(Status, ERROR_SEM_TIMEOUT); #if DBG DbgPrint ("[%d,%d] DosSemWait after Request: error Waiting for Mutex, %x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); #endif } Status = NtReleaseSemaphore ( pRealSem->Mutex, 1, NULL); if ( !NT_SUCCESS(Status) && Status != STATUS_SEMAPHORE_LIMIT_EXCEEDED ) { #if DBG DbgPrint ("DosSemWait: error NtReleaseSempahore , %lx\n", Status); #endif } if (rc != NO_ERROR) { goto NoWait; } } } rc = DosWaitEventSem (pRealSem->Event, lTimeOut); NoWait: if (rc != NO_ERROR) { if (rc == ERROR_TIMEOUT || rc == ERROR_SEM_TIMEOUT) { rc = ERROR_SEM_TIMEOUT; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemWait, timeout on Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } #endif } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemWait, Error %d on Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),rc, hsem, pRealSem->Mutex,pRealSem->Event ); } #endif } return rc; } #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemWait succeeded Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemWait(%x, *=%x): exiting OK\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG return NO_ERROR; } APIRET DosSemSetWait( IN HSEM hsem, IN LONG lTimeOut ) { APIRET rc; // // we simply set and then Wait. Should be atomic // rc = DosSemSet (hsem); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemSetWait: error at DosSemSet, Status %d\n", rc); } #endif return rc; } rc = DosSemWait(hsem, lTimeOut); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemSetWait: error at DosSemWait, Status %d\n", rc); } #endif return rc; } return (NO_ERROR); } APIRET DosSemRequest( IN HSEM hsem, IN LONG lTimeOut ) { APIRET rc = NO_ERROR; NTSTATUS Status = STATUS_SUCCESS; POS21X_SEM pRealSem; ULONG Dummy; LARGE_INTEGER CapturedTimeout; PLARGE_INTEGER NtTimeout; LARGE_INTEGER StartTimeStamp; BOOLEAN firsttime; BOOLEAN CheckIfClear = FALSE; // // BUGBUG: In OS/2 native the RAM semaphore that has zero least significant byte is // treated free (see comment for DosSemWait). // #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemRequest(%x, *=%x): entering\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG try { Od2ProbeForWrite( (PVOID)hsem, sizeof( ULONG ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // If the semaphore will be created, will be checked if it will be created // initially busy semaphore. pRealSem = Od2LookupOrCreateSem(hsem, &firsttime, SEM_FROM_REQUESTWAIT); if (pRealSem == NULL) return(ERROR_NOT_ENOUGH_MEMORY); if (pRealSem->FlagsByte & SYSSEM_QUEUE) { // // If this is a system semaphore that was used by DosPeekQueue call to DosSemWait. // This is special hack to work around bogus usage of semaphores with queues by // ICL applications (see mail conversations with Thierry Tabard). // [YosefD Jul-19-1995] // return DosSemWait(hsem, lTimeOut); } if (firsttime) { pRealSem->OwnerThread = (TID)SEM_MUTEX_NOT_OWNED; // mark it as a REQUEST/CLEAR semaphore, no owner if ((*(PULONG)hsem & 0xFFF00000) != 0xCCC00000) { // // private mem RAM sem: set value and return // *(PULONG)hsem = (ULONG)pRealSem | 1; } } // // Capture the timeout value and convert it into an NT timeout value. // NtTimeout = Od2CaptureTimeout( lTimeOut, (PLARGE_INTEGER)&CapturedTimeout ); if ((*(PULONG)hsem == (ULONG)hsem) && !(pRealSem->FlagsByte & SYSSEM_PUBLIC)) { // // take semaphore to examine state // Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosSemRequest: failed to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif if (pRealSem->OwnerThread == (TID)Od2CurrentThreadId()){ // // must be a system semaphore that was prevoiusly // requested by this thread. // pRealSem->RequestCount++; Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosSemRequest (nested): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif Status = STATUS_SUCCESS; goto no_wait; } Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosSemRequest (not nested): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } // // Implement a Request by a WaitForSingleObject on the Nt // Semaphore // // // first watch for the ill case where someone used DosSemWait // on this semaphore before. Set it to mutex so we get freed // if (pRealSem->OwnerThread == SEM_EVENT) { // // This semaphore used to be a Wait/Clear only, need to verify // that we block if currently not clear. // CheckIfClear = TRUE; pRealSem->OwnerThread = (TID)SEM_DUAL; } // // shared RAM sem - make sure a subsequent sem clear will wake us // if ((*(PULONG)hsem & 0xFFF00000) == 0xCCC00000) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemRequest on shared mem RAM: Sem %x, *Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, *(PULONG)hsem, pRealSem->Mutex,pRealSem->Event ); } #endif if ((*(PULONG)hsem & 0xFFFC0000) == 0xCCC40000) { CheckIfClear = TRUE; } // REQUST/CLEAR shared RAM sem // // *(PULONG) hsem |= 0x00040000; // __asm { mov eax, hsem lock or dword ptr [eax], 0x00080000 } } if (CheckIfClear) { // // This semaphore used to be for Wait/Clear only, // we have to make sure that we block if not cleared // // // Wait for the event, so next clear wakes us, or we have it // rc = DosWaitEventSem (pRealSem->Event, lTimeOut); if (rc != NO_ERROR) { if (rc == ERROR_TIMEOUT) { rc = ERROR_SEM_TIMEOUT; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemRequest on SEM_EVENT, timeout on Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } #endif } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosSemRequest on SEM_EVENT, Error %d on Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),rc, hsem, pRealSem->Mutex,pRealSem->Event ); } #endif } return rc; } } DosSemRequest_retry: if (NtTimeout) { Od2StartTimeout(&StartTimeStamp); } Status = NtWaitForSingleObject( pRealSem->Mutex, TRUE, // Alertable NtTimeout ); if (Status == STATUS_SUCCESS || Status == STATUS_ABANDONED) { // The semaphore is owned only in the case that the status is // STATUS_SUCCESS or STATUS_ABONDONED (if it was, for example, // STATUS_TIMEOUT, that means that the semaphore wasn't actually // owned. if ((*(PULONG)hsem == (ULONG)hsem) && !(pRealSem->FlagsByte & SYSSEM_PUBLIC)) { // // Exclusive System Semaphore, // Mark this semaphore 'owned' // // // take semaphore to set state // Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosSemRequest (take it): failed to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif pRealSem->OwnerThread = (TID)Od2CurrentThreadId(); pRealSem->RequestCount = 0; Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosSemRequest (take it): failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif } else { if (pRealSem->OwnerThread != (TID)SEM_DUAL) { pRealSem->OwnerThread = (TID)SEM_MUTEX_NOT_OWNED; } } } no_wait: if (Status == STATUS_SUCCESS) { rc = NO_ERROR; } else { if (NT_SUCCESS(Status) ) { if (Status == STATUS_TIMEOUT) { rc = ERROR_SEM_TIMEOUT; } else if (Status == STATUS_ABANDONED) { #if DBG DbgPrint("[%d,%d] DosSemRequest NT semaphore status ABONDONED\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId()); #endif // DBG rc = ERROR_SEM_OWNER_DIED; } else if (Status == STATUS_USER_APC) { #if DBG DbgPrint("[%d,%d] WARNING !!! DosSemRequest was broken by APC\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId() ); #endif if (Od2ContinueTimeout(&StartTimeStamp, NtTimeout) == STATUS_SUCCESS) { goto DosSemRequest_retry; } else { rc = ERROR_TIMEOUT; } } else if (Status == STATUS_ALERTED) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("[%d,%d] DosSemRequest ALERTED\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId()); } #endif rc = ERROR_INTERRUPT; } else { // This is some success status that we don't know about. // To be more secure print the message. #if DBG DbgPrint("[%d,%d] DosSemRequest BUGBUG Unknown success status = %x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); #endif // DBG rc = ERROR_INTERRUPT; } } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosSemRequest: error at NtWaitForSingleObject, %x\n", Status); } #endif rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } } // // In the ill case where it used both as a mutex and as an event, // we Reset the event too // if (pRealSem->OwnerThread == (TID)SEM_DUAL) { APIRET rc1; rc1 = DosResetEventSem (pRealSem->Event, &Dummy); if (rc1 == ERROR_ALREADY_RESET) rc1 = NO_ERROR; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { if (rc1 != NO_ERROR) { DbgPrint ("DosSemRequest: error at DosResetEvent %d\n", rc1); } } #endif if (rc == NO_ERROR && rc1 != NO_ERROR) { rc = rc1; } } if (rc == NO_ERROR) { if (*(PULONG)hsem != (ULONG)hsem) { // // RAM Semaphore: // OS/2 1.X Apps expects the semaphore location // last two bits to be actually 1 (undocumented) // // // *(PULONG) hsem |= 1; // __asm { mov eax,hsem lock or dword ptr [eax], 1 } } #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { if (firsttime) { DbgPrint ("DosSemRequest 1st time succeeded: Sem %x, NtSem %lx, Event %lx\n", hsem, pRealSem->Mutex,pRealSem->Event ); } else { DbgPrint ("[TID %x]: DosSemRequest succeeded Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),hsem, pRealSem->Mutex,pRealSem->Event ); } } if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemRequest(%x, *=%x): exiting OK\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem)); } #endif // DBG return(NO_ERROR); } #if DBG if (((Os2DebugSem != 0) && (hsem == Os2DebugSem)) || ((Os2DebugSem3 != 0) && (hsem == Os2DebugSem3)) || ((Os2DebugSem2 != 0) && (hsem == Os2DebugSem2)) || ((Os2DebugSem1 != 0) && (hsem == Os2DebugSem1))) { KdPrint(("[%d,%d] DosSemRequest(%x, *=%x): exiting, rc=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), hsem, *(PULONG)hsem, rc)); } #endif // DBG return rc; } APIRET DosOpenSem( OUT PHSYSSEM phSem, IN PSZ pszSemName ) { ULONG Sem; APIRET rc = NO_ERROR; POS21X_SEM pRealSem; NTSTATUS Status; try { Od2ProbeForWrite( phSem, sizeof( HSYSSEM ), 1 ); Od2ProbeForRead( pszSemName, 2, 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } Status = Od2AcquireSync(); #if DBG if (Status != STATUS_SUCCESS) { DbgPrint("[%d,%d] DosOpenSem: failed to NtWaitForSingleObject on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif rc = Od2OpenSem( NULL, &pRealSem, pszSemName, TRUE); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosOpenSem: error calling Od2OpenSem, %d\n", rc); } #endif Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosOpenSem: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif return rc; } Sem = (ULONG) *phSem = (ULONG) pRealSem; *phSem = (POS21X_SEM)(FLATTOFARPTR(Sem)); if (pRealSem->FlagsByte == 0) { // // This sys sem was not created by this process, and // is opened the first time by this process, assume public syssem // BUGBUG we need to reimplement exclusive sys sem to get the // full OS/2 semantics across processes // pRealSem->FlagsByte |= SYSSEM_PUBLIC; } Status = Od2ReleaseSync(); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("[%d,%d] DosOpenSem: failed to NtReleaseMutant on sync sem, Status=%x\n", Od2Process->Pib.ProcessId, Od2CurrentThreadId(), Status); } #endif return(NO_ERROR); } APIRET DosMuxSemWait( OUT PUSHORT pisemCleared, IN PMUXSEMLIST pmsxl, IN LONG lTimeOut ) { APIRET rc = NO_ERROR; NTSTATUS Status; USHORT i; HANDLE NtHandles[ 16 ]; // Handles to actually block on LARGE_INTEGER CapturedTimeout; PLARGE_INTEGER NtTimeout; ULONG Sem; HSEM FlatSem; POS21X_SEM pRealSem; BOOLEAN firsttime; BOOLEAN CheckCurrentOwner = FALSE; // // Capture the timeout value and convert it into an NT timeout value. // NtTimeout = Od2CaptureTimeout( lTimeOut, (PLARGE_INTEGER)&CapturedTimeout ); // // probe index pointer and pmsxl buffer // try { Od2ProbeForWrite( pisemCleared, sizeof( USHORT ), 1 ); // // probe count first, then actual array // Od2ProbeForRead(pmsxl, sizeof(USHORT),1); Od2ProbeForRead(&pmsxl->amxs, sizeof(MUXSEM)*(pmsxl->cmxs),1); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP(); } // // Go over the array of semaphores. // for each semaphore check if it's event is posted. // If posted - sempahore is clear, return index. // If not posted, add to NtHandles array // for (i = 0; i < pmsxl->cmxs; i++,CheckCurrentOwner = FALSE) { // // Check validity of MUXSEM entry // if (pmsxl->amxs[i].zero != 0) return ERROR_INVALID_PARAMETER; Sem = (ULONG) (pmsxl->amxs[i].hsem); FlatSem = FARPTRTOFLAT(Sem); try { Od2ProbeForWrite( (PVOID)FlatSem, sizeof( ULONG ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // If the semaphore will be created, will be checked if it must be // created in initially busy state. pRealSem = Od2LookupOrCreateSem (FlatSem, &firsttime, SEM_FROM_REQUESTWAIT); if (pRealSem == NULL) return(ERROR_NOT_ENOUGH_MEMORY); if (*(PULONG)FlatSem != (ULONG)FlatSem) { if (pRealSem->OwnerThread != (TID)SEM_DUAL && pRealSem->OwnerThread != (TID)SEM_EVENT) { if (pRealSem->OwnerThread == (TID)SEM_AT_INIT) { pRealSem->OwnerThread = (TID)SEM_EVENT; // mark it as a WAIT/CLEAR semaphore, no owner } else { // // This semaphore used to be a Request/Clear only, need to verify // that we block if currently owned. // CheckCurrentOwner = TRUE; pRealSem->OwnerThread = (TID)SEM_DUAL; } } if (firsttime) { if ((*(PULONG)FlatSem & 0xFFF00000) != 0xCCC00000) { // // private mem RAM sem: set value and return // *(PULONG)FlatSem = (ULONG)pRealSem; } else { *(PULONG)FlatSem |= 0x00040000; // WAIT/CLEAR shared RAM sem } *pisemCleared = i; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosMuxSemWait: 1st time success. sem was cleared. Index %d, Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),*pisemCleared, FlatSem, pRealSem->Mutex, pRealSem->Event); } #endif return(NO_ERROR); } // // make sure that any subsequent semclear will post // this semaphore's event // if ((*(PULONG)FlatSem & 0xFFF00000) == 0xCCC00000) { // // RAM sem in shared memory. // make sure that subsequent sem clear will wake us // #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosMuxSemWait on shared mem RAM: Index %d, Sem %x, *Sem %x, NtSem %lx, Event %lx\n", *pisemCleared, FlatSem, *(PULONG)FlatSem, pRealSem->Mutex,pRealSem->Event ); } #endif if ((*(PULONG)FlatSem & 0xFFFC0000) == 0xCCC80000) { CheckCurrentOwner = TRUE; } *(PULONG)FlatSem |= 0x00040000; // WAIT/CLEAR shared RAM sem } } if (CheckCurrentOwner) { SEMAPHORE_BASIC_INFORMATION SemInfo; ULONG Dummy; NTSTATUS Status; // // This semaphore used to be for Request/Clear only, // we have to make sure that if someone owns it we block // // // query the mutex, to see if owned // Status = NtQuerySemaphore( pRealSem->Mutex, SemaphoreBasicInformation, (PVOID)(&SemInfo), sizeof(SemInfo), NULL); if (!NT_SUCCESS(Status)) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosMuxSemWait: error in NtQuerySemaphore, %lx\n", Status); } #endif } else { if (SemInfo.CurrentCount == 0) { // // Sempahore is not signaled i.e. it is not clear // block ourselves by reset of event before wait // rc = DosResetEventSem (pRealSem->Event, &Dummy); if (rc == ERROR_ALREADY_RESET) rc = NO_ERROR; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { if (rc != NO_ERROR) { DbgPrint ("DosMuxSemWait: error at DosResetEvenSem, error %d\n", rc); } } #endif } } } /* * rc = DosQueryEventSem(pRealSem->Event, &PostCount); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("DosMuxSemWait: error in DosQueryEventSem %d \n", rc); } #endif return (rc); } if (PostCount != 0) { // // Found a posted semaphore: // Perform DosWaitEventSem to lower post count // and return // rc = DosWaitEventSem (pRealSem->Event, 0L); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosMuxSemWait: Unexpected error %d. Index %d\n", Od2CurrentThreadId(),rc, *pisemCleared); } #endif return rc; } *pisemCleared = i; #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosMuxSemWait: success. sem was cleared. Index %d, Sem %x, NtSem %lx, Event %lx\n", Od2CurrentThreadId(),*pisemCleared, FlatSem, pRealSem->Mutex, pRealSem->Event); } #endif return NO_ERROR; } * */ rc = Od2GetSemNtEvent( FlatSem, &NtHandles[i]); } // // Now, that we scanned the whole pmsxl structure and // have a list of Nt Handles to block on, let's block // on it // retry: Status = NtWaitForMultipleObjects( (CHAR)i, NtHandles, WaitAny, TRUE, // Alertable NtTimeout ); if (NT_SUCCESS( Status )) { if (Status <= STATUS_WAIT_63) { *pisemCleared = (USHORT)(Status & 0x3F); #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint ("[TID %x]: DosMuxSemWait: success at NtWait. Index %d\n", Od2CurrentThreadId(),*pisemCleared); } #endif Sem = (ULONG) (pmsxl->amxs[*pisemCleared].hsem); FlatSem = FARPTRTOFLAT(Sem); if ((*(PULONG)FlatSem != (ULONG)FlatSem) && ((*(PULONG)FlatSem & 0xFFF00000) != 0xCCC00000)) { // // Private RAM semaphores: // OS/2 1.X apps (SQL 1.11) expect sem location to be 0 // *(PULONG)FlatSem = 0; } rc = NO_ERROR; } else if (Status == STATUS_ABANDONED) { rc = ERROR_SEM_OWNER_DIED; } else if (Status == STATUS_TIMEOUT) { rc = ERROR_SEM_TIMEOUT; } else if (Status == STATUS_USER_APC) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("MuxSemWait Status STATUS_USER_APC\n"); } #endif rc = ERROR_SS_RETRY; } else if (Status == STATUS_ALERTED) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("MuxSemWait Status STATUS_ALERTED\n"); } #endif rc = ERROR_SS_RETRY; } else { rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } } else { rc = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE); } if (rc == ERROR_SS_RETRY) { goto retry; } return rc; } APIRET DosFSRamSemRequest( IN PDOSFSRSEM16 pdosfsrs, IN LONG lTimeOut ) { APIRET rc = NO_ERROR; BOOLEAN HackPmPrintDrive = FALSE; PULONG pSem; PUSHORT puSemIndex; // // cb must be 14 (1.21 PRM) // try { Od2ProbeForWrite( pdosfsrs, sizeof( DOSFRSEM16 ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } if (pdosfsrs->cb != 14) { if (pdosfsrs->cb == 12) { // // special hack for some print drivers on OS/2, // we have a shared segment with semaphores pointers, // indexed by the USHORT left off pdosfsrs->sem // HackPmPrintDrive = TRUE; #if DBG DbgPrint("DosFSRamSemRequest called with cb=12\n", rc); #endif if (pHackPrinterDriverSem == 0) { // // No thread in this process used this hack yet - // See if a previous process created it // USHORT sel; APIRET rc = DosGetShrSeg("\\SHAREMEM\\OS2SSPRD", &sel); if (rc != NO_ERROR) { if (rc == ERROR_FILE_NOT_FOUND) { // // Create the segment with the semaphores pointers. // The first ULONG is the index to the next available // index // rc = DosAllocShrSeg((sizeof(ULONG)) * NUMOFPRINTDRIVERSEM, "\\SHAREMEM\\OS2SSPRD", &sel); } else { #if DBG DbgPrint("DosFSRamSemRequest - internal error\n"); #endif return ERROR_INVALID_PARAMETER; } } pHackPrinterDriverSem = (PULONG)(SELTOFLAT(sel)); *pHackPrinterDriverSem = 1; } } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemRequest - invalid FSRSEM.cb %d\n", pdosfsrs->cb); } #endif return ERROR_INVALID_PARAMETER; } } if (HackPmPrintDrive) { // // Take the next available index // puSemIndex = (PUSHORT)(&pdosfsrs->sem); if (*puSemIndex == 0){ // // first time this FSR is used // if (*pHackPrinterDriverSem == NUMOFPRINTDRIVERSEM) { // // No More Slots // #if DBG DbgPrint("DosFSRamSemRequest - Not Enough Slots for PM Driver hack\n"); #endif return(ERROR_NOT_ENOUGH_MEMORY); } pSem = pHackPrinterDriverSem + sizeof(ULONG) * (*pHackPrinterDriverSem); *puSemIndex = (USHORT)(*pHackPrinterDriverSem); *pHackPrinterDriverSem = *pHackPrinterDriverSem + 1; } else { // // use the index stored in the sem value // pSem = pHackPrinterDriverSem + sizeof(ULONG) * (*puSemIndex); } } else { pSem = (PULONG)(&pdosfsrs->sem); } // // if not owned - set to owned, use count 1 // and return // if (pdosfsrs->pid == 0) { // // Perform a Request on the semaphore, to ensure that // other threads will now block // rc = DosSemRequest((HSEM)pSem, lTimeOut); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemRequest: Illegal error from DosSemRequest. %d\n", rc); } #endif } else { pdosfsrs->pid = (USHORT)Od2Process->Pib.ProcessId; pdosfsrs->tid = (USHORT)(Od2CurrentThreadId()); pdosfsrs->cUsage = 1; pdosfsrs->client = 0; } return(rc); } // // if we are inside Exit List and the owner process is // the caller's process, then force cUsage to 1 and // thread to caller, else block. // if (Od2ExitListInProgress && (pdosfsrs->pid == (USHORT)Od2Process->Pib.ProcessId) ) { rc = ERROR_SEM_OWNER_DIED; pdosfsrs->tid = (USHORT)(Od2CurrentThreadId()); pdosfsrs->cUsage = 1; return(rc); } // // if owned by calling thread - increment cUsage // if (pdosfsrs->pid == (USHORT)Od2Process->Pib.ProcessId && pdosfsrs->tid == (USHORT)(Od2CurrentThreadId()) ) { pdosfsrs->cUsage++; return(NO_ERROR); } // // now we have to block on the semaphore // rc = DosSemRequest((HSEM)pSem, lTimeOut); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemRequest: error from DosSemRequest. %d\n", rc); } #endif } else { pdosfsrs->pid = (USHORT)Od2Process->Pib.ProcessId; pdosfsrs->tid = (USHORT)(Od2CurrentThreadId()); pdosfsrs->cUsage = 1; } return (rc); } APIRET DosFSRamSemClear( IN PDOSFSRSEM16 pdosfsrs ) { APIRET rc = NO_ERROR; BOOLEAN HackPmPrintDrive = FALSE; PULONG pSem; PUSHORT puSemIndex; try { Od2ProbeForWrite( pdosfsrs, sizeof( DOSFRSEM16 ), 1 ); } except( EXCEPTION_EXECUTE_HANDLER ) { Od2ExitGP (); } // // cb must be 14 (1.21 PRM) // if (pdosfsrs->cb != 14) { if (pdosfsrs->cb == 12) { // // special hack for some print drivers on OS/2, // we have a shared segment with semaphores pointers, // indexed by the USHORT left off pdosfsrs->sem // HackPmPrintDrive = TRUE; #if DBG DbgPrint("DosFSRamSemClear - was called with cb=12\n", rc); #endif if (pHackPrinterDriverSem == 0) { // // No thread in this process used this hack yet - // See if a previous process created it // USHORT sel; APIRET rc = DosGetShrSeg("\\SHAREMEM\\OS2SSPRD", &sel); if (rc != NO_ERROR) { if (rc == ERROR_FILE_NOT_FOUND) { // // Create the segment with the semaphores pointers. // The first ULONG is the index to the next available // index // rc = DosAllocShrSeg((sizeof(ULONG)) * NUMOFPRINTDRIVERSEM, "\\SHAREMEM\\OS2SSPRD", &sel); } else { #if DBG DbgPrint("DosFSRamSemClear - internal error\n"); #endif return ERROR_INVALID_PARAMETER; } } pHackPrinterDriverSem = (PULONG)(SELTOFLAT(sel)); *pHackPrinterDriverSem = 1; } } else { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemClear - invalid FSRSEM. cb=%d\n", pdosfsrs->cb); } #endif return ERROR_INVALID_PARAMETER; } } // // if not owned - return // if (pdosfsrs->pid == 0) { return(NO_ERROR); } if (HackPmPrintDrive) { // // Take the next available index // puSemIndex = (PUSHORT)(&pdosfsrs->sem); if (*puSemIndex == 0){ // // first time this FSR is used // if (*pHackPrinterDriverSem == NUMOFPRINTDRIVERSEM) { // // No More Slots // #if DBG DbgPrint("DosFSRamSemClear - Not Enough Slots for PM Driver hack\n"); #endif return(ERROR_NOT_ENOUGH_MEMORY); } pSem = pHackPrinterDriverSem + sizeof(ULONG) * (*pHackPrinterDriverSem); *puSemIndex = (USHORT)*pHackPrinterDriverSem; *pHackPrinterDriverSem = *pHackPrinterDriverSem + 1; } else { // // use the index stored in the sem value // pSem = pHackPrinterDriverSem + sizeof(ULONG) * (*puSemIndex); } } else { pSem = (PULONG)(&pdosfsrs->sem); } // // if owned by calling thread - decrement cUsage. // if reached 0 - clear semaphore // if (pdosfsrs->pid == (USHORT)Od2Process->Pib.ProcessId && pdosfsrs->tid == (USHORT)(Od2CurrentThreadId()) ) { if (--(pdosfsrs->cUsage) == 0) { // // need to really clear - remove ownership before we release // blocking threads // pdosfsrs->pid = 0; pdosfsrs->tid = 0; rc = DosSemClear((HSEM)pSem); if (rc != NO_ERROR) { #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemClear: Illegal error from DosSemClear. %d\n", rc); } #endif return(rc); } return(NO_ERROR); } else { return(NO_ERROR); } } // // FSRAM sem owned by another thread - return error // #if DBG IF_OD2_DEBUG ( SEMAPHORES ) { DbgPrint("DosFSRamSemClear called by a thread. Sem owned by another thread\n"); } #endif return ERROR_EXCL_SEM_ALREADY_OWNED ; }