diff options
Diffstat (limited to '')
-rw-r--r-- | private/os2/client/dllsem16.c | 4340 |
1 files changed, 4340 insertions, 0 deletions
diff --git a/private/os2/client/dllsem16.c b/private/os2/client/dllsem16.c new file mode 100644 index 000000000..cdb82dc75 --- /dev/null +++ b/private/os2/client/dllsem16.c @@ -0,0 +1,4340 @@ +/*++ + +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 ; +} |