;       SCCSID = @(#)tkksem.asm	6.5 92/01/06
SCCSID  equ     "@(#)tkksem.asm	6.5 92/01/06"

TITLE   TKKSEM - Fast kernel semaphore routines
NAME    TKKSEM
PAGE    ,132

;***    TKKSEM.ASM
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Title:
;
;       Kernel Event, Mutex, and Share semaphores
;
;       Description:
;
;       These routines implement fast Event, Mutex and Share semaphores.
;       They are fast because they dont do much of error checkings
;       and exit list related processings. They are 'C' callable.
;
;       Modification History:
;
;       89/08/10    Sudeep Bharati  Created
;       90/04/15    Jameel Hyder
;                       Major rehash
;                       Seperated out VDH and the rest of the kernel
;                       Added priority inversion stuff etc.
;       91/06/10    Removed shared ownership, upgrades, and downgrades

INCL_ERROR_H    EQU     1
INCL_ERROR2_H   EQU     1

.xlist
include dosseg.inc
include basemaca.inc
include fnotask.inc
include osmaca.inc
include osconst.inc
include mi.inc
include bseerr.inc
include dossymc.inc
include sigdef.inc
include except.inc
include seldesc.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include argframe.inc
include opncreat.inc
include filemode.inc
include infoseg.inc
include tkexport.inc
include dirent.inc
include curdir.inc
include find.inc
include screen.inc
include sft.inc
include sem.inc
include tkptda.inc
include tkksem.inc
.list

        CpuMode 386
        ModName tkksem, FNO_TKKSEM_ASM

        ReferGlobal     TaskNumber,WORD
        ReferFlat       _pTCBCur,DWORD

        EXTRN32         _TKSleep
        EXTRN32         _TKWakeup
        EXTRN32         _TKExitInversion
        EXTRN32         _TKDeclareInversion

        EXTRN32         ksemAlloc
        EXTRN32         ksemInit
        EXTRN32         ksemCreate
        EXTRN32         ksemDestroy
        EXTRN32         ksemQuery
        EXTRN32         ksemResetEvent
        EXTRN32         ksemPostEvent
        EXTRN32         ksemWaitEvent


_DATA   SEGMENT
ifdef   MISCSTRICT
        PUBLIC  _ksctalc,_ksctalcblk
_ksctalc        dd      0               ; Count of allocs
_ksctalcblk     dd      0               ; Count of times allocation blocked
endif
_DATA   ENDS

;       Mutex Semaphores:
;
;       ks_Signature            KSEM_SIGNATURE
;       ks_bType                KRNL_MUTEXSEM
;       ks_bFlags               KSEM_NOINTERRUPT or KSEM_NOBLOCK
;       ks_Owner                TID of owner. 0 - free
;       ks_cusNest              nesting count
;       ks_cusPendingWriters    # of threads waiting on this semaphore
;
;
;       Shared Semaphores:
;
;       ks_Signature            KSEM_SIGNATURE
;       ks_bType                KRNL_SHAREDSEM
;       ks_bFlags               KSEM_NOINTERRUPT or KSEM_NOBLOCK
;       ks_cusReaders           # of shared owners of this semaphore
;       ks_Owner                TID of excl. owner. 0 - free
;       ks_cusNest              nesting count if excl.
;       ks_cusPendingReaders    # of threads waiting for excl. locks to go away
;       ks_cusPendingWriters    # of threads waiting for excl. locks
;
;       Semaphore ownership issues:
;
;       Mutex:
;
;       If a mutex semaphore is free, it is grabbed by the thread requesting
;       ownership. The thread also calls TKEnterInversion so that other threads
;       blocking on the semaphore can raise the priority of this thread. This
;       prevents deadlocks.
;       If a mutex is not free, then the requesting thread calls TKDeclareInversion
;       to do any necessary priority inversion (i.e.  raise the owner thread's
;       priority to atleast the waiting threads'). It then sleeps.
;       A thread releasing a mutex calls TKExitInversion. It then checks to see
;       if any threads are waiting on this semaphore. The first waiting thread
;       is woken up and the ownership is handed over. (TKWakeup returns the ptcb
;       of the thread that is woken up).
;
;
;       Shared:
;
;       Exclusive access follows most of the logic used for mutex semaphores.

        FARCODE HIGH32
HIGH32CODE      SEGMENT

;***EP  KSEMRequestMutex - Request a Mutex Semaphore
;
;       Request the ownership of a mutex semaphore. If the semaphore is
;       owned and the caller is not the owner, the thread will block. If the
;       caller is the owner a request count will be incremented.
;
;       The request calls may be nested. A maximum of 32k-1 requests is
;       allowed per semaphore at any one time. Crossing this limit will
;       result in panic.
;
;       ENTRY     hksem     - handle of a mutex semaphore
;                 ulTimeOut - timeout in milliseconds
;
;       EXIT      Success - return NO_ERROR
;                 Failure - ERROR_TIMEOUT
;                           Panics if
;                               invalid hksem.
;                               request count crossing 32k-1 limit.
;       CONTEXT
;           Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMRequestMutex,near,,,esp
ArgVar  hksem,DWORD
ArgVar  ulTimeOut,DWORD

        TaskTime                        ; Validate calling context

        mov     ecx,[ulTimeOut]         ; ECX = timeout value
        mov     edx,[hksem]             ; EDX = semaphore handle
        KSemChkSig edx, KRNL_MUTEXSEM   ; Validate signature
        KSemCntAlloc                    ; Keep statistics

rqm10:  mov     ax,[TaskNumber]
        cmp     [edx].ks_Owner,0        ; Is semaphore owned?
        jne     short rqm30             ;  Yes, get in queue

;       Grab the semaphore

        mov     [edx].ks_Owner,ax       ; Set owner
        mov     eax,[_pTCBCur]
        TKEnterInversion eax

rqm20:  inc     [edx].ks_cusNest        ; Up nest count
        KSemChkOvf
        xor     eax,eax
        .errnz  NO_ERROR

rqmx:
        RETC

        align   4
rqm30:  cmp     [edx].ks_Owner,ax       ; Do we own the semaphore?
        je      short rqm20             ;  Yes, just increment nest count

        test    [edx].ks_bFlags,KSEM_NOBLOCK ; Can we block?
        jnz     rqmnb                   ;  No, return error

        SaveReg <ebx,esi>               ; Switch to safe regs
        mov     esi,ecx                 ; ESI = timeout value
        mov     ebx,edx                 ; EBX = semaphore handle
        KSemSetDbgAddr ebx
        movzx   eax,[ebx].ks_Owner
        CCALL32 _TKDeclareInversion,<eax>
        mov     al,[ebx].ks_bFlags
        and     eax,KSEM_NOINTERRUPT
        KSemCntAllocBlk                 ; Keep statistics
        inc     [ebx].ks_cusPendingWriters ; Up count of waiting threads
        KSemChkOvf
        CCALL32 _TKSleep,<ebx, esi, eax, WAKEUP_SINGLE>
        dec     [ebx].ks_cusPendingWriters ; Down count of waiting threads
        KSemChkUnf
        KSemClrDbgAddr
        mov     ecx,esi                 ; ECX = timeout value
        mov     edx,ebx                 ; EDX = semaphore handle
        RestoreReg <esi,ebx>
        or      eax,eax                 ; Did _TKSleep return an error?
        jz      rqm10                   ;  No, try to get semaphore again
        cmp     [edx].ks_Owner,0        ; Is semaphore owned?
        jne     rqmx                    ;  Yes, return
        cmp     [edx].ks_cusPendingWriters,0  ; Any pending writers?
        je      rqmx                    ;  No, return
        SaveReg <eax>                   ;  Yes, wake next pending writer
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE, 0>
        RestoreReg <eax>
        jmp     rqmx

rqmnb:  mov     eax,ERROR_NOBLOCK
        jmp     rqmx

EndProc   _KSEMRequestMutex


;***EP   KSEMReleaseMutex - Release a Mutex Semaphore
;
;       Release the ownership of a mutex semaphore. If the request count
;       comes to zero then highest priority semaphore will be woken up.
;
;       Releasing an unowned mutex semaphore will cause a panic.
;
;       ENTRY     hksem - handle of a mutex semaphore
;
;       EXIT      Success - returns nothing
;                 Failure - Panics if:
;                               hksem is invalid
;                               caller is not owner of the semaphore
;
;       CONTEXT
;           Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMReleaseMutex,near,,,esp
ArgVar  hksem,DWORD

        TaskTime

        mov     edx,[hksem]             ; EDX = semaphore handle
        KSemChkSig edx, KRNL_MUTEXSEM   ; Validate signature

        KSemChkOwn edx                  ; Validate owner
        dec     [edx].ks_cusNest        ; Down nest count
        KSemChkUnf
        jnz     short rlmx              ; Still owner, done
        mov     [edx].ks_Owner,0        ; Semaphore clear for now
        SaveReg <edx>
        CCALL32 _TKExitInversion
        RestoreReg <edx>
        cmp     [edx].ks_cusPendingWriters,0 ; Any pending writers?
        je      short rlmx              ;  No, return
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE, 0>

rlmx:
        RETC

EndProc   _KSEMReleaseMutex


;***EP   KSEMQueryMutex - Query state of Mutex semaphore
;
;       Query state of Mutex semaphore.
;
;       ENTRY     hksem         - handle of a mutex.
;                 puscnest      - pointer to where nest count is returned.
;                                 Can be NULL
;
;       EXIT      EAX = TRUE    semaphore is owned or free.
;                        cnest contains the nest count
;                        if cnest == 0, semaphore is free
;                 EAX = FALSE   semaphore is not owned (busy).
;
;                 Panics if:    hksem is invalid
;
;       CONTEXT
;           Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMQueryMutex,near
ArgVar  hksem,DWORD
ArgVar  pcnest,DWORD

        TaskTime
        EnterProc

        mov     eax,1                   ; default return value
        mov     edx,hksem               ; EDX = semaphore handle
        KSemChkSig edx, KRNL_MUTEXSEM   ; Validate signature
        cmp     [edx].ks_Owner,0        ; Is semaphore free
        jne     short qmt10
        xor     ecx,ecx                 ; Yes, return zero count
        jmp     short qmt20

qmt10:  mov     cx,TaskNumber           ; Semaphore is busy
        cmp     cx,[edx].ks_Owner       ; Do we own it ?
        sete    al
        mov     cx,[edx].ks_cusNest
qmt20:  cmp     pcnest,0
        je      short qmtx
        mov     edx,pcnest
        mov     word ptr [edx],cx

qmtx:   LeaveProc
        RETC

EndProc _KSEMQueryMutex


;***EP   KSEMRequestExclusive - Request exclusive access to a Share Semaphore
;
;       If another thread has access to the semaphore, the caller will block.
;
;       The request calls may be nested. A maximum of 32k-1 requests or 32k-1
;       nested requests (requests by the same thread) are allowed per
;       semaphore at any one time.  Crossing this limit will result in a panic.
;
;       ENTRY     hksem     - handle of a shared semaphore
;                 ulTimeOut - timeout in milliseconds
;
;       EXIT      Success - return NO_ERROR
;                 Failure - ERROR_TIMEOUT
;                           ERROR_NOBLOCK
;                           Panics if
;                               invalid hksem.
;                               request count crossing 32k-1 limit.
;       CONTEXT
;           Task
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMRequestExclusive,near
ArgVar  hksem,DWORD
ArgVar  ulTimeOut,DWORD

        EnterProc
        SaveReg <ebx,edx>
        TaskTime                        ; Validate calling context

        mov     ebx,[hksem]             ; EBX = semaphore handle
        KSemChkSig ebx, KRNL_SHARESEM   ; Validate signature
        KSemCntAlloc                    ; Keep statistics

        mov     ax,TaskNumber
        cmp     [ebx].ks_Owner,ax       ; Do we own the semaphore?
        je      short rex20             ;  Yes, just increment nest count
rex10:  cmp     [ebx].ks_Owner,0        ; Is semaphore owned?
        jne     short rex30             ;  Yes, get in queue
        cmp     [ebx].ks_cusReaders,0   ; Any readers present?
        jne     short rex30             ;  Yes, get in queue
        or      ax,ax                   ; Is this first time thru?
        jz      short rex15             ;  No, skip pending writers check
        cmp     [ebx].ks_cusPendingWriters,0 ; Any pending writers?
        jne     short rex30             ; Yes, get in queue
rex15:  mov     ax,TaskNumber
        mov     [ebx].ks_Owner,ax       ; Set owner
        mov     eax,[_pTCBCur]
        TKEnterInversion eax
rex20:  inc     [ebx].ks_cusNest        ; Up nest count
        KSemChkOvf
        xor     eax,eax
        .errnz  NO_ERROR

rexx:   RestoreReg <edx,ebx>
        LeaveProc
        RETC

rex30:  test    [ebx].ks_bFlags,KSEM_NOBLOCK ; Can we block?
        jnz     rexnb                   ;  No, return error

        inc     [ebx].ks_cusPendingWriters ; Up count of waiting threads
        KSemChkOvf
        KSemSetDbgAddr ebx
        movzx   eax,[ebx].ks_Owner
        or      eax,eax                 ; Any semaphore owner?
        jz      short rex40             ;  No, skip inversion
        CCALL32 _TKDeclareInversion,<eax>
rex40:  mov     al,[ebx].ks_bFlags
        and     eax,KSEM_NOINTERRUPT
        KSemCntAllocBlk                 ; Keep statistics
        lea     edx,[ebx].ks_cusPendingWriters ; EDX = Exclusive ID
        CCALL32 _TKSleep,<edx, [ulTimeOut], eax, WAKEUP_SINGLE>
        dec     [ebx].ks_cusPendingWriters ; Down count of waiting threads
        KSemChkUnf
        KSemClrDbgAddr
        or      eax,eax                 ; Did _TKSleep return an error?
        jz      rex10                   ;  No, try to get semaphore again
        cmp     [ebx].ks_Owner,0        ; Is semaphore owned?
        jne     rexx                    ;  Yes, return
        cmp     [ebx].ks_cusReaders,0   ; Any readers present?
        jne     rexx                    ;  Yes, return
        cmp     [ebx].ks_cusPendingWriters,0  ; Any pending writers?
        je      short rexs              ;  No, check for pending readers
        SaveReg <eax>                   ;  Yes, wake next writer
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE, 0>
        RestoreReg <eax>
        jmp     rexx

rexnb:  mov     eax,ERROR_NOBLOCK
        jmp     rexx

rexs:   cmp     [ebx].ks_cusPendingReaders,0 ; Any pending readers?
        je      rexx                    ;  No, return
        SaveReg <eax>                   ;  Yes, wake all pending readers
        lea     edx,[ebx].ks_cusPendingReaders  ; EDX = Shared ID
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE OR WAKEUP_SINGLE_ALL, 0>
        RestoreReg <eax>
        jmp     rexx

EndProc   _KSEMRequestExclusive


;***EP   KSEMRequestShared - Request shared access to a Share Semaphore
;
;       If another thread has exclusive access to the semaphore or there is a
;       pending exclusive access request, the caller will block.
;
;       The request calls may be nested. A maximum of 32k-1 requests are allowed
;       per semaphore at any one time.  Crossing this limit will result in a
;       panic.
;
;       ENTRY     hksem     - handle of a shared semaphore
;                 ulTimeOut - timeout in milliseconds
;
;       EXIT      Success - return NO_ERROR
;                 Failure - ERROR_TIMEOUT
;                           ERROR_NOBLOCK
;                           Panics if
;                               invalid hksem.
;                               request count crossing 32k-1 limit.
;       CONTEXT
;           Task
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMRequestShared,near
ArgVar  hksem,DWORD
ArgVar  ulTimeOut,DWORD

        TaskTime                        ; Validate calling context
        EnterProc
        SaveReg <ebx,edx>

        mov     ebx,[hksem]             ; EBX = semaphore handle
        KSemChkSig ebx, KRNL_SHARESEM   ; Validate signature
        KSemCntAlloc                    ; Keep statistics

rsh10:  cmp     [ebx].ks_Owner,0        ; Is semaphore owned?
        jnz     short rsh20             ;  Yes, get in queue
        cmp     [ebx].ks_cusPendingWriters,0 ; Any pending writers?
        jne     short rsh20             ;  Yes, get in queue
        inc     [ebx].ks_cusReaders     ; Up count of readers
        KSemChkOvf
        xor     eax,eax
        .errnz  NO_ERROR

rshx:   RestoreReg <edx,ebx>
        LeaveProc
        RETC

rsh20:  test    [ebx].ks_bFlags,KSEM_NOBLOCK ; Can we block?
        jnz     rshnb                   ;  No, return error

        inc     [ebx].ks_cusPendingReaders ; Up count of pending readers
        KSemChkOvf
        KSemSetDbgAddr ebx
        movzx   eax,[ebx].ks_Owner
        or      eax,eax                 ; Any owner?
        jz      short rsh30             ; No, skip Inversion
        CCALL32 _TKDeclareInversion,<eax>
rsh30:  mov     al,[ebx].ks_bFlags
        and     eax,KSEM_NOINTERRUPT
        KSemCntAllocBlk                 ; Keep statistics
        lea     edx,[ebx].ks_cusPendingReaders  ; EDX = Shared ID
        CCALL32 _TKSleep,<edx, [ulTimeOut], eax, WAKEUP_SINGLE>
        dec     [ebx].ks_cusPendingReaders ; Down count of pending readers
        KSemChkUnf
        KSemClrDbgAddr
        or      eax,eax                 ; Did _TKSleep return an error?
        jz      rsh10                   ;  No, try to get semaphore again
        cmp     [ebx].ks_Owner,0        ; Is semaphore owned?
        jne     rshx                    ;  Yes, return
        cmp     [ebx].ks_cusReaders,0   ; Any readers present?
        jne     rshx                    ;  Yes, return
        cmp     [ebx].ks_cusPendingWriters,0  ; Any pending writers?
        je      short rshs              ;  No, check for pending readers
        SaveReg <eax>                   ;  Yes, wake next pending writer
        lea     edx,[ebx].ks_cusPendingWriters ; EDX = Exclusive ID
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE, 0>
        RestoreReg <eax>
        jmp     rshx

rshnb:  mov     eax,ERROR_NOBLOCK
        jmp     rshx

rshs:   cmp     [ebx].ks_cusPendingReaders,0 ; Any pending readers?
        je      rshx                    ;  No, return
        SaveReg <eax>                   ;  Yes, wake all pending readers
        lea     edx,[ebx].ks_cusPendingReaders  ; EDX = Shared ID
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE OR WAKEUP_SINGLE_ALL, 0>
        RestoreReg <eax>
        jmp     rshx

EndProc   _KSEMRequestShared


;;;***EP   KSEMRequestSharedOwned - Request shared access with ownership to
;;;                                 a Share Semaphore
;;;
;;;       If another thread has ownership of the semaphore or there is a
;;;       pending exclusive access request, the caller will block.
;;;
;;;       The request calls may be nested. A maximum of 32k-1 requests or 32k-1
;;;       nested requests (requests by the same thread) are allowed per
;;;       semaphore at any one time.  Crossing this limit will result in a panic.
;;;
;;;       ENTRY     hksem     - handle of a shared semaphore
;;;                 ulTimeOut - timeout in milliseconds
;;;
;;;       EXIT      Success - return NO_ERROR
;;;                 Failure - ERROR_TIMEOUT
;;;                           ERROR_NOBLOCK
;;;                           Panics if
;;;                               invalid hksem.
;;;                               request count crossing 32k-1 limit.
;;;       CONTEXT
;;;           Task
;;;
;;
;;        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
;;Procedure _KSEMRequestSharedOwned,near
;;ArgVar  hksem,DWORD
;;ArgVar  ulTimeOut,DWORD
;;
;;        TaskTime
;;        KSemCntAlloc
;;        EnterProc
;;        SaveReg <ebx,edx>
;;        mov     ebx,[hksem]
;;        mov     ax,TaskNumber
;;        cmp     [ebx].ks_Owner,ax       ; Do we already own this semaphore ?
;;        je      short rso10             ;  Yes, continue
;;
;;        cmp     [ebx].ks_Owner,0        ; semaphore owned by another thread ?
;;        jne     short rso30             ;  yes, block
;;        cmp     [ebx].ks_cusPendingWriters,0 ; pending writers?
;;        jne     short rso30             ;  yes, block
;;        cmp     [ebx].ks_cusPendingReaderWriters,0 ; Any pending reader writers
;;        jne     short rso30             ;  yes, block
;;
;;        mov     eax,[_pTCBCur]
;;        TKEnterInversion eax
;;rso10:  inc     [ebx].ks_cusReaders
;;        KSemChkOvf
;;        inc     [ebx].ks_cusNest
;;        KSemChkOvf
;;rso20:  xor     eax,eax
;;        .errnz  NO_ERROR
;;rsox:   RestoreReg <edx,ebx>
;;        LeaveProc
;;        RETC
;;
;;rso30:  test    [ebx].ks_bFlags,KSEM_NOBLOCK ; can we block?
;;        jnz     rsonb                   ;  no, return error
;;
;;        inc     [ebx].ks_cusPendingReaderWriters
;;                                        ; Up count of waiting rdrwrtrs
;;        KSemChkOvf
;;        KSemSetDbgAddr ebx
;;
;;        movzx   eax,[ebx].ks_Owner
;;        or      eax,eax                 ; Any owner ?
;;        jz      short rso40             ; No, skip Inversion
;;        CCALL32 _TKDeclareInversion, <eax>
;;rso40:  mov     al,[ebx].ks_bFlags
;;        and     eax,KSEM_NOINTERRUPT
;;        lea     edx,[ebx].ks_cusPendingReaderWriters
;;        KSemCntAllocBlk
;;        CCALL32 _TKSleep <edx, [ulTimeOut], eax, WAKEUP_SINGLE>
;;        dec     [ebx].ks_cusPendingReaderWriters
;;                                        ; Not waiting anymore
;;        KSemChkUnf
;;        KSemClrDbgAddr
;;        or      eax,eax                 ; ERROR_INTERRUPT ?
;;        .errnz  NO_ERROR
;;        jnz     rsox                    ;  yes, depart
;;ifdef   MISCSTRICT
;;        mov     cx,TaskNumber
;;        cmp     [ebx].ks_Owner,cx
;;        jne     ksemspwie
;;        test    [ebx].ks_bFlags,KSEM_WRITER
;;        jnz     ksemspwie
;;endif
;;        jmp     rso20
;;
;;rsonb:  mov     eax,ERROR_NOBLOCK
;;        jmp     rsox
;;
;;EndProc   _KSEMRequestSharedOwned


;***EP   KSEMRelease - Release a Semaphore
;
;       Release access to a semaphore. If the access count comes to zero
;       then waiting threads will be woken up in the following order:
;
;       If pending exclusive access request
;           wake highest priority pending exclusive access request
;
;       ENTRY     hksem - handle of a shared semaphore
;
;       EXIT      Success - returns nothing
;                 Failure - Panics if:
;                               hksem is invalid
;
;       CONTEXT
;           Task
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMRelease,near
ArgVar   hksem,DWORD

        EnterProc
        SaveReg <ebx,edx>
        TaskTime

        mov     ebx,[hksem]             ; EBX = semaphore handle
        KSemChkSig ebx, KRNL_SHARESEM   ; Validate signature

        cmp     [ebx].ks_Owner,0        ; Is semaphore owned?
        je      short rls20             ;  No, must be shared semaphore
        KSemChkOwn ebx                  ;  Yes, validate exclusive owner
        dec     [ebx].ks_cusNest        ; Down nest count
        KSemChkUnf
        jnz     rlsx                    ; Still owner, return
        mov     [ebx].ks_Owner,0        ; Clear owner
        CCALL32 _TKExitInversion
rls10:  cmp     [ebx].ks_cusPendingWriters,0 ; Any pending writers?
        je      short rls30             ;  No, check for pending readers
        lea     edx,[ebx].ks_cusPendingWriters  ; EDX = Exclusive ID
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE, 0>
        jmp     short rlsx

rls20:  dec     [ebx].ks_cusReaders     ; Down count of readers
        KSemChkUnf
        jz      short rls10             ; No more readers, check for writers
        jmp     short rlsx

rls30:  cmp     [ebx].ks_cusPendingReaders,0 ; Any pending readers?
        jz      short rlsx              ;  No, return
        lea     edx,[ebx].ks_cusPendingReaders  ; EDX = Shared ID
        CCALL32 _TKWakeup <edx, WAKEUP_SINGLE OR WAKEUP_SINGLE_ALL, 0>
        jmp     short rlsx

rlsx:   RestoreReg <edx,ebx>
        LeaveProc
        RETC

EndProc   _KSEMRelease


;;;***EP   KSEMUpgrade - Upgrade from owned shared access to exclusive access.
;;;
;;;       If other threads has shared access to the semaphore, the caller will
;;;       will block until those threads release the semaphore.
;;;
;;;       ENTRY     hksem     - handle of a shared semaphore
;;;                 ulTimeOut - timeout in milliseconds
;;;
;;;       EXIT      Success - return NO_ERROR
;;;                 Failure - ERROR_TIMEOUT
;;;                           ERROR_NOBLOCK
;;;                           Panics if
;;;                               invalid hksem.
;;;                               request count crossing 32k-1 limit.
;;;       CONTEXT
;;;           Task
;;;
;;
;;        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
;;Procedure _KSEMUpgrade,near
;;ArgVar  hksem,DWORD
;;ArgVar  ulTimeOut,DWORD
;;
;;        TaskTime                        ; Validate calling context
;;        EnterProc
;;        SaveReg <ebx,edx>
;;        mov     ebx,[hksem]
;;        KSemChkOwn ebx                  ; Validate owner
;;        mov     ax,[ebx].ks_cusNest
;;        cmp     [ebx].ks_cusReaders,ax  ; Any other readers present ?
;;        jne     short upg20             ;  Yes, block
;;        mov     eax,[_pTCBCur]
;;        TKEnterInversion eax
;;        or      [ebx].ks_bFlags,KSEM_WRITER ; Set writer
;;upg10:  xor     eax,eax
;;        .errnz  NO_ERROR
;;
;;upgx:   RestoreReg <edx,ebx>
;;        LeaveProc
;;        RETC
;;
;;upg20:  test    [ebx].ks_bFlags,KSEM_NOBLOCK ; can we block?
;;        jnz     short upgnb             ;  no, return error
;;
;;        inc     [ebx].ks_cusPendingUpgrades ; up count of pending upgrades
;;        KSemChkOvf
;;        KSemSetDbgAddr ebx
;;        lea     edx,[ebx].ks_cusPendingUpgrades ; Upgrades ID
;;        movzx   eax,[ebx].ks_bFlags
;;        and     eax,KSEM_NOINTERRUPT
;;        KSemCntAllocBlk
;;        CCALL32 _TKSleep <edx, [ulTimeOut], eax, WAKEUP_SINGLE>
;;        KSemClrDbgAddr
;;        dec     [ebx].ks_cusPendingUpgrades ; down count of pending upgrades
;;        KSemChkUnf
;;        or      eax,eax                 ; Any error ?
;;        jnz     short upgx              ; Yes, depart
;;ifdef   MISCSTRICT
;;        mov     cx,[ebx].ks_cusReaders
;;        cmp     [ebx].ks_cusNest,cx
;;        jne     ksemspwie
;;endif
;;        jmp     upg10
;;
;;upgnb:  mov     eax,ERROR_NOBLOCK
;;        jmp     upgx
;;
;;EndProc   _KSEMUpgrade


;;;***EP   KSEMDowngrade - Downgrade to owned shared access or to shared access.
;;;
;;;       Switch from exclusive access to shared owned or shared access.  Allows
;;;       other threads requesting shared access to run.
;;;
;;;       ENTRY     hksem     - handle of a shared semaphore
;;;                 access    - desired access: KSEM_SHAREDOWNED, KSEM_SHARED
;;;
;;;       EXIT      None
;;;
;;;       CONTEXT
;;;           Task
;;;
;;
;;        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
;;Procedure _KSEMDowngrade,near
;;ArgVar   hksem,DWORD
;;ArgVar   access,DWORD
;;LocalVar ptcb,DWORD                     ; TCB of new owner
;;
;;        TaskTime                        ; Validate calling context
;;        EnterProc
;;        SaveReg <ebx,edx>
;;        mov     ebx,[hksem]
;;        KSemChkOwn ebx                  ; Validate owner
;;        mov     ecx,[access]
;;        and     [ebx].ks_bFlags,NOT KSEM_WRITER ; Clear Writer
;;        .errnz  KSEM_SHARED
;;        .erre   KSEM_SHAREDOWNED
;;        or      ecx,ecx                 ; downgrade to reader ?
;;        jnz     short dgd10             ;  no, leave readerwriters sleeping
;;
;;        mov     [ebx].ks_Owner,cx       ; Clear owner
;;        mov     [ebx].ks_cusNest,cx     ; and nesting level
;;        cmp     [ebx].ks_cusPendingReaderWriters,cx
;;                                        ; pending reader writers?
;;        je      short dgd10             ;  no, skip wakeup
;;
;;        lea     edx,[ebx].ks_cusPendingReaderWriters
;;                                        ; EDX = readerwriters block id
;;        SSToDS  eax,ptcb
;;        CCALL32 _TKWakeup,<edx,WAKEUP_SINGLE,eax>
;;        mov     eax,ptcb
;;        or      eax,eax
;;
;;ifdef   MISCSTRICT
;;        jz      ksemownie               ; Internal error if no new owner
;;else
;;        jz      short dgd10
;;endif
;;
;;        inc     [ebx].ks_cusReaders     ; Take care of this readerwriter
;;        KSemChkOvf
;;        TKEnterInversion eax
;;
;;        mov     ax,[eax].TCBNumber
;;        mov     [ebx].ks_Owner,ax       ; Set him up
;;
;;dgd10:  cmp     [ebx].ks_cusPendingReaders,0 ; pending readers?
;;        jz      short dgdx              ;  no, skip wakeup
;;        lea     edx,[ebx].ks_cusPendingReaders  ; EDX = readers block id
;;        SSToDS  eax,ptcb                ; Get count of readers
;;        CCALL32 _TKWakeup <edx, <WAKEUP_SINGLE OR WAKEUP_SINGLE_ALL>, eax>
;;        mov     eax,ptcb
;;        add     [ebx].ks_cusReaders,ax  ; Update count of readers
;;dgdx:   RestoreReg <edx,ebx>
;;        LeaveProc
;;        RETC
;;
;;EndProc   _KSEMDowngrade


;***EP  KSEMCreate - Create an Event/Mutex Semaphore
;
;       Create an Event or Mutex Semaphore.
;
;       An event semaphore remains in one of the two states i.e.
;       "reset" or "posted". The initial state of an event semaphore
;       is posted. If a KSEMResetEvent call is made on a semaphore
;       when it was posted all the subsequent wait calls on this
;       semaphore will block. Later a call to KSEMPostEvent for
;       this semaphore will wake up all the threads blocked on this
;       semaphore.
;
;       Similarly mutex semaphores have two states i.e. owned and
;       unowned. A mutex semaphore is created as unowned.
;       KSEMRquestMutex is called to own the semaophore if it is not
;       owned. If it is owned request call will block. A subsequent
;       call to KSEMReleaseMutex will unown the semaphore and wokeup
;       a blocked thread. Request and Release calls on mutex semaphores
;       can be nested. In that case a semphore will be unowned when
;       release calls will equal request calls.
;
;
;       ENTRY   phksem      - pointer where semaphore handle will be returned
;               semtype     - KRNL_EVENTSEM      Event Semaphore
;                             KRNL_MUTEXSEM      Mutex Semaphore
;
;       EXIT    SUCCESS     returns NO_ERROR
;               FAILURE     ERROR_NOT_ENOUGH_MEMORY
;                           Invalid semtype will result in a panic.
;
;       CONTEXT
;           Task
;
;       PCODE
;           call ksemCreate
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMCreate,near,,,esp
ArgVar  phksem,DWORD
ArgVar  semtype,DWORD

        EnterProc
        mov     ecx,[phksem]
        mov     edx,[semtype]
        CCALL32 ksemCreate              ; semaphore handle returned in edx
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short crtx
        mov     dword ptr [ecx],edx
crtx:   LeaveProc
        RETC
EndProc _KSEMCreate


;***EP  KSEMInit - Initialize an Event/Mutex/Share Semaphore
;
;       To avoid an extra dynamic memory allocation, semaphores can be
;       statically declared.  Before being used, they must be initialized.
;
;       ENTRY   pksem       - pointer to semaphore structure
;               semtype     - KRNL_EVENTSEM      Event Semaphore
;                             KRNL_MUTEXSEM      Mutex Semaphore
;                             KRNL_SHARESEM      Share Semaphore
;               semflags    - KSEM_NOINTERRUPT | KSEM_NOBLOCK
;                             (default is interruptible blocks,
;                              nestable requests, block if necessary)
;
;       EXIT    returns NO_ERROR
;
;       CONTEXT
;           Task
;
;       PCODE
;           call ksemInit
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMInit,near,,,esp
ArgVar  pksem,DWORD
ArgVar  semtype,DWORD
ArgVar  semflags,DWORD

        EnterProc
        mov     edx,[pksem]
        mov     eax,[semtype]
        mov     ecx,[semflags]
        CCALL32 ksemInit
        LeaveProc
        RETC
EndProc _KSEMInit


;***EP  KSEMDestroy - Destroy a semaphore
;
;       Destroy a semaphore previously created via KSEMCreate. Semaphore
;       must be posted/unowned before making this call.
;
;       ENTRY   hksem   - handle to the semaphore
;
;       EXIT    SUCCESS - returns othing
;               FAILURE - Panics if:
;                           - hksem is invalid.
;                           - sem is reset or owned.
;
;       CONTEXT
;           Task
;
;       PCODE
;           call ksemDestroy
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMDestroy,near,,,esp
ArgVar  hksem,DWORD

        EnterProc
        mov     edx,[hksem]
        CCALL32 ksemDestroy
        LeaveProc
        RETC
EndProc   _KSEMDestroy


;***EP   KSEMQuery - Query a Semaphore'state
;
;       Query the state of an event or mutex semaphore.
;
;       ENTRY     hksem         - handle of event to query
;                 pkSemState    - pointer to area to be filled in with current
;                                 state of semaphore:
;                                 (See semxport.h for KSEMSTATE)
;
;       EXIT      Success - returns nothing; State information in pKSemState
;                 Failure - Panics if:
;                               hksem is invalid
;       CONTEXT
;           Task
;
;       NOTE 1. The validity of pSemState is not checked. Caller should make
;               sure to pass valid space.
;            2. This routine is callable only for event and mutex semaphore
;
;       PSEUDO CODE
;           call ksemQuery
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMQuery,near,,,esp
ArgVar  hksem,DWORD
ArgVar  pkSemState,DWORD

        EnterProc
        mov     edx,[hksem]
        mov     ecx,[pkSemState]
        CCALL32 ksemQuery
        LeaveProc
        RETC
EndProc   _KSEMQuery


;***EP  KSEMAlloc - Allocate an Event/Mutex/Share Semaphore
;
;       Event Semaphores
;
;       An event semaphore remains in one of the two states i.e.
;       "reset" or "posted". The initial state of an event semaphore
;       is posted. If a KSEMResetEvent call is made on a semaphore
;       when it was posted all the subsequent wait calls on this
;       semaphore will block. Later a call to KSEMPostEvent for
;       this semaphore will wake up all the threads blocked on this
;       semaphore.
;
;       Mutex Semaphores
;
;       Similarly mutex semaphores have two states i.e. owned and
;       unowned. A mutex semaphore is created as unowned.
;       KSEMRquestMutex is called to own the semaophore if it is not
;       owned. If it is owned request call will block. A subsequent
;       call to KSEMReleaseMutex will unown the semaphore and wokeup
;       a blocked thread. Request and Release calls on mutex semaphores
;       can be nested. In that case a semphore will be unowned when
;       release calls will equal request calls.
;
;       Shared Semaphores
;
;       Share semaphore allow for shared and exclusive access.  If a thread
;       has shared access to the semaphore, other threads will be allowed to
;       acquired shared access, but threads requesting exclusive access will
;       block until all threads that have shared access release the semaphore.
;
;       If a thread has exclusive access, all other threads that try to access
;       the semaphore will block until that thread releases the semaphore.
;
;       ENTRY   phksem      - pointer where semaphore handle will be returned
;               semtype     - KRNL_EVENTSEM      Event Semaphore
;                             KRNL_MUTEXSEM      Mutex Semaphore
;                             KRNL_SHARESEM      Share Semaphore
;               semflags    - KSEM_NOINTERRUPT | KSEM_NOBLOCK
;                             (default is interruptible blocks,
;                              nestable requests, block if necessary)
;
;       EXIT    SUCCESS     returns NO_ERROR
;               FAILURE     ERROR_NOT_ENOUGH_MEMORY
;                           Invalid semtype will result in a panic.
;
;       CONTEXT
;           Task
;
;       PCODE
;           call ksemAlloc
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMAlloc,near,,,esp
ArgVar  phksem,DWORD
ArgVar  semtype,DWORD
ArgVar  semflags,DWORD

        EnterProc
        mov     ecx,[semflags]
        mov     eax,[semtype]
        CCALL32 ksemAlloc               ; semaphore handle returned in edx
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short alcx
        mov     ecx,[phksem]
        mov     dword ptr [ecx],edx
alcx:   LeaveProc
        RETC
EndProc   _KSEMAlloc


;***EP   KSEMResetEvent - Reset an Event Semaphore
;
;       KSEMResetEvent resets an event semaphore.
;
;       Resetting an already reset semaphore will result in a panic.
;
;
;       ENTRY     hksem - handle of event semaphore
;
;       EXIT      Success - returns nothing
;                 Failure - Panics if:
;                               hksem is invalid
;                               sem is already reset
;       CONTEXT
;           Task
;           Interrupt
;
;       PCODE
;           call ksemResetEvent

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMResetEvent,near,,,esp
ArgVar  hksem,DWORD

        EnterProc
        mov     edx,[hksem]
        CCALL32 ksemResetEvent
        LeaveProc
        RETC
EndProc   _KSEMResetEvent


;***EP   KSEMPostEvent - Post an Event Semaphore
;
;       KSEMPostEvent posts an event semaphore. All the threads
;       blocked on this semaphore will wakeup.
;
;       ENTRY     khsem   - handle of event semaphore
;
;       EXIT      Success - return nothing
;                 Failure - Panics if:
;                               - khsem invalid.
;                               - khsem is already posted.
;       CONTEXT
;           Task
;           Interrupt
;
;       PCODE
;           call ksemPostEvent

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMPostEvent,near,,,esp
ArgVar  hksem,DWORD

        EnterProc
        mov     edx,[hksem]
        CCALL32 ksemPostEvent
        LeaveProc
        RETC
EndProc   _KSEMPostEvent


;***EP   KSEMWaitEvent - Wait on an Event Semaphore
;
;       Wait on an event semaphore. If the semaphore is posted it will
;       return immediately.
;
;       if ulTimeOut is 0 the caller wont block, but ERROR_TIMEOUT will
;       be passed. If ulTimeOut is -1 the caller will be blocked untill
;       semaphore is posted. Otherwise the caller will block for the
;       specified time.
;
;       ENTRY     hksem     - handle of event semaphore
;                 ulTimeOut - timeout in milliseconds
;
;       EXIT      Success - returns NO_ERROR
;                 Failure - ERROR_TIMEOUT
;                           Panics if:
;                               hksem is invalid
;
;       CONTEXT
;           Task
;
;       PCODE
;           return(ksemWaitEvent())

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _KSEMWaitEvent,near,,,esp
ArgVar  hksem,DWORD
ArgVar  ulTimeOut,DWORD

        EnterProc
        mov     edx,[hksem]
        mov     ecx,[ulTimeOut]
        CCALL32 ksemWaitEvent
        LeaveProc
        RETC
EndProc   _KSEMWaitEvent


ifdef   MISCSTRICT
        PUBLIC  ksemovie, ksemsigie,ksemownie
ksemovie:  Panic <"KSEM: Semaphore counter overflow/underflow">
ksemsigie: Panic <"KSEM: Invalid Semaphore Handle">
ksemownie: Panic <"KSEM: Invalid Owner">
;;ksemspwie: Panic <"KSEM: Spurious wakeup">
endif

ifdef   PMDEBUG
        PUBLIC  szKSEM
szKSEM  DB      SIZE KSEMEVT, SIZE KSEMMTX, SIZE KSEMSHR
        .errnz  KRNL_EVENTSEM - 0
        .errnz  KRNL_MUTEXSEM - 1
        .errnz  KRNL_SHARESEM - 2
endif

HIGH32CODE      ENDS

        END
