;       SCCSID = @(#)tkexit1.asm        6.7 92/03/25
SCCSID  equ     "@(#)tkexit1.asm        6.7 92/03/25"
;       Tasking - Thread termination Routines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved

;       Created   Jan 89        Jameel Hyder.
;       3/8/92    sfb reduce perfview overhead
;      12/15/93   RAC 76699  Make perfview optional assembled with flag PERFVIEW
;
;***********************************************************************;
; 02/09/96  Change Team Defect 148071                                   ;
; Call to _LDRDLLExitList in _TKTermThread was altering FS and causing  ;
; a trap.                                                               ;
;***********************************************************************;

.xlist
include dosseg.inc
include basemaca.inc
include fnotask.inc
include osmaca.inc
include oldmaca.inc
include osconst.inc
include mi.inc
include dossymc.inc
include except.inc
include proc.inc
include infoseg.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include tkexport.inc
include ldrxport.inc
include bsetib.inc
include tklocal.inc
include seldesc.inc
include sched.inc
include kmode.inc
include filemode.inc
include sft.inc
include error.inc
include curdir.inc
include dirent.inc
include screen.inc
include sem.inc
include find.inc
include sigdef.inc
include tkptda.inc
.list

        ModName , FNO_TKEXIT1_ASM
        CpuMode 386

        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pTSDCur,DWORD
        ReferFlat       _Lis2,BYTE

ifdef PERFVIEW
        ReferFlat       _fPerfview,DWORD                               ;sfb
endif
        ReferGlobal     _SwapFullPTDA,DWORD                            ; 69005
        ReferGlobal     _HardErrTCB,DWORD                              ; 69005
        ReferGlobal     _HardErrSem,BYTE                               ; 69005

        EXTRN16         SFFromHandle
        EXTRN16         w_Close
        EXTRN16         ResetSignals
        EXTRN16         SysSemExitList
        EXTRN16         SysSemOwnerDied
        EXTRN16         ProcessTerminate

IFDEF PMDEBUG
        EXTRN16         DebugThreadNDying
ENDIF
        EXTRN32         KMTHREADTERMINATE
        EXTRN32         _tkExitList

ifdef INCDEBUG
        EXTRN32         _DbgThreadTerm
endif ;DEBUG

        EXTRN32         _ReleaseNPX
        EXTRN32         _SEMMarkOwnerDied
        EXTRN32         _SysServTerm
        EXTRN32         _TKTstnClrKernelSem
        EXTRN32         _TKDieSoonPTDA
        EXTRN32         _TKExit
        EXTRN32         _tkFreeUserStacks
        EXTRN32         _tkSchedNext
        EXTRN32         _tkLibiCleanup
        EXTRN32         _TKSleep
        EXTRN32         _TKTCYield
        EXTRN32         _tkUnlinkTCB
        EXTRN32         _TKWakeup
        EXTRN32         _schForceExitCritSec
        EXTRN32         _KSEMRequestMutex
        EXTRN32         _KSEMQueryMutex
        EXTRN32         _KSEMReleaseMutex

ifdef PERFVIEW
        EXTRN32         _PVWKillInstance_Thd
endif

        EXTRN32         _LDRDLLExitList

        FARCODE HIGH32
HIGH32CODE      SEGMENT

;***EP  TKTermTask      Terminate current task
;
;       TEMPORARY: We call ProcessTerminate to do the job
;
;       ENTRY:  None
;
;       EXIT:   Does not return

Procedure _TKTermTask,near
        CALL16  ProcessTerminate                ; does not return
ifdef   MISCSTRICT
        Panic <"TKTermTask:ProcessTerminate returned !!">
endif
EndProc _TKTermTask


;***EP  _TKTermThread - Terminate this thread. (after exception handling)
;
;       _TKTermThread is called from _TKExit, and from the Exception Manager
;       as the default action of fatal exceptions.  _TKTermThread terminates
;       the current thread.  It may only return when thread 1 attempts to
;       run exitlist.
;
;       void _TKTermThread ();
;
;       ENTRY   NONE
;
;       EXIT    Void.  Sometimes does not return.
;               _TKTermThread MAY RETURN for thread 1 of a process that
;               has an exitlist routine, or as we run exitlist.  No
;               other threads return.
;       Change history:
;       ---------------
;       02/16/94 Skip Nizinski 69005. Check if terminating thread is
;                involved with any current hard error pop up message and
;                set indicator in _SwapFullPTDA and _HardErrTCB globals.

Procedure _TKTermThread,near
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

LocalVar  cnest,WORD  ; 52915

        EnterProc
        SaveReg <ebx,edi,esi>

;       Load up 'register variables' in (EBX) and (EDI).  These are used
;       all over the place in this routine...

        mov     ebx,[_pPTDACur]         ; (EBX) = _pPTDACur
        mov     edi,[_pTCBCur]          ; (EDI) = _pTCBCur

; If current TCB matches _HardErrTCB, then this thread is displaying    69005
; a hard error pop up. Set to -1 since TCB is going away.               69005
; If thread 1 is being terminated, then its process is going away       69005
; soon...if this process currently is displaying a hard error popup     69005
; for a swapper full condition, set _SwapFullPTDA to -1 so that hard    69005
; error processing will know not to try to terminate this process.      69005
        cmp     _HardErrSem,0           ; Is there a hard error popup?  69005
        jz      short nothard           ; No. Skip around.              69005
        cmp     _HardErrTCB,edi         ;                               69005
        jnz     short nothardtcb        ; doesn't match.                69005
        mov     _HardErrTCB,-1          ; let harderr processing know.  69005
nothardtcb:                             ;                               69005
        cmp     [edi].TCBOrdinal,1      ; Is this thread 1?             69005
        jnz     short nothard           ; No. Bypass SwapFullPTDA check.69005
        cmp     _SwapFullPTDA,ebx       ; Swap full for this ptda?      69005
        jnz     short nothard           ; No.                           69005
        mov     _SwapFullPTDA,-1        ; let harderr processing know.  69005
nothard:                                ;                               69005

; 52915 - add code to free DLM sem

dlmredo:
        SSToDS  ecx,cnest
        lea     eax,[ebx].ptda_DLMsem   ; ebx points to ptda
        push    eax
        CCALL32 _KSEMQueryMutex,<eax,ecx>
        or      eax,eax
        pop     eax
        jz      short dlmdone
        movzx   ecx,cnest
        or      ecx,ecx
        jz      short dlmdone
;       int     3
        CCALL32 _KSEMReleaseMutex,<eax>
        jmp     short dlmredo             ; Inefficient, use cnest to count.
                                          ; But, this is a very rare code case.
                                          ; It can only happen if a thread dies
                                          ; whilst running DLL initialisation.
dlmdone:

; 52915 - end add code to free DLM sem


;       If we are in Exit List processing, skip cleanup duties and continue.

        test    [ebx].ptda_infoseg.LIS_ProcStatus,PS_XITLST
        jnz     term7                   ; In exitlist - contine.

;       If thread 1, perform cleanup duties.

        cmp     [edi].TCBOrdinal,1      ; Thread 1?
        jne     short term3             ; no - skip cleanup work

        SaveReg <ebx,edi>               ; save 'register variables'

;       Close any open monitor handles in the process.  This kills the
;       internal threads used by monitors.
;       For each file handle,
;               If (it's a vaild open monitor handle)
;                       call w_Close

        movzx   ecx,[ebx].JFN_Length    ; (ECX) = number of file handles
term1:  mov     ebx,ecx
        dec     ebx                     ; (EBX) = file handle (zero based)
        CALL16  SFFromHandle            ; (ES:DI) -> SFT (bx=handle)
        jc      short term2             ; invalid or closed - get next handle

        test    es:[di].sf_fsi.sfi_mode,open_monitor
                                        ; open monitor handle ?
        jz      short term2             ; no - leave it alone.

        push    dx
        push    ecx
        mov     cx, es:[di].sf_LockID.LowWord  ; filehandlelockID lowword
        mov     dx, es:[di].sf_LockID.HighWord ; filehandlelockID highword
        CALL16  w_Close                 ; close the monitor handle.
        pop     ecx
        pop     dx

term2:  loop    short term1             ; Move on to next JFN, if any.

;       Clean up signal handling for this process.

        DosContext      ds
        CALL16  ResetSignals            ; Reset signals to defaults
        FlatContext <ds,es>,ax
        RestoreReg <edi,ebx>            ; restore 'register variables'

term3:  CCALL32 _TKTCYield              ; yield if necessary

;       Note that every thread passes through this point exactly once...

ifdef INCDEBUG
        cmp     [ebx].ptda_flDbgEnabled,0 ; Debuggee or DPMI watchpoints?
        jz      short term4             ; No. continue.

        mov     eax,[_pTSDCur]          ; (EAX) = _pTSDCur
        PushP   [eax].TSDulExitCode     ; 'proposed' exit code from thread.
        CCALL32 _DbgThreadTerm,,,PushP  ; notify Debug of thread termination
endif ;DEBUG

;       If this is thread 1, and there are more threads in the process,
;       we need to wait for them to die.

term4:  cmp     [edi].TCBOrdinal,1      ; Thread 1?
        jne     term10                  ; no - erase this thread

;***    THREAD 1 TERMINATION

        or      [ebx].ptda_infoseg.LIS_ProcStatus,PS_XITTH1 ; thread 1 is dying
        or      [ebx].ptda_ForcedActions,TK_FF_PIB ; force PIB update
        mov     fs,[_Lis2.is_sel]
    ASSUME      FS:NOTHING
        or      fs:[LIS_ProcStatus],PS_XITTH1   ; Set bit in shadow infoseg

;       Thread 1 must now wait for all the other threads to die before
;       performing its ExitList duties.

term5:  cmp     [ebx].ptda_cTCB,1       ; Thread 1 is the last thread ?
        je      short term6             ; yes - we're all done.

;       Thread 1 Block ID = Flat Address of ptda_pTCBHead
;       (EBX) = _pPTDACur
;       (EDI) = _pTCBCur

        push    fs
        lea     eax,[ebx].ptda_pTCBHead
        CCALL32 _TKSleep <eax,-1,1,0>
        pop     fs                      ; (FS) = [_Lis2.is_sel]
        jmp     short term5

;       All the other threads have died. But before thread 1 runs ExitList,
;       we need to register any DLL termination routines after the first
;       ExitList.  This causes these termination routines to be called after
;       all of the routines already registered in the ExitList.  We used to
;       run DLL Termination before ExitLists, but it this broke the C/Set2
;       compiler people. Note that the implementation does not match the
;       external docs. Doc APAR, here we come. C'est la vie. Changed to
;       not match the books per Ross Cook.

term6:
        push    fs                      ;148071
        CCALL32 _LDRDLLExitList
        pop     fs                      ;148071

;       Thread 1 now runs ExitList.

        or      [ebx].ptda_infoseg.LIS_ProcStatus,PS_XITLST
        or      [ebx].ptda_ForcedActions,TK_FF_PIB ; force PIB update
        or      fs:LIS_ProcStatus,PS_XITLST ; Update shadow infoseg
    ASSUME      FS:NOTHING

        CCALL32 _SysServTerm            ; check if daemon is dying
        CALL16  SysSemExitList          ; change sem owners to exitlist thread
        FlatContext <ds,es>,ax          ; more annoying 16 bit code...
        mov     edi,[_pTCBCur]          ; (EDI) = _pTCBCur

;       Clear out force flags so that we can run ExitList.
;       (EBX) is trashed here...

?TK_FF_CLEAR = (TK_FF_TRC + TK_FF_CRITSEC + TK_FF_ICE + TK_FF_EXIT)

term7:  and     [edi].TCBForce_Flg,NOT (?TK_FF_CLEAR)
        CCALL32 _tkExitList <0,TK_TYPE16,3>; Transfer to next Exitlist routine
        RestoreReg <esi,edi,ebx>
        LeaveProc
        RETC                            ; and (maybe) return.

;***    SUBTHREAD TERMINATION
;
;       A subthread (not thread 1) is dying.  Do subthread cleanup here.
;       Note that we don't bother cleaning up the stack after calls anymore.
;
;       We will never get to Exit_Kmode, so we must _TKClearKernelSem here.
;       ForceExitCritSec will resume other threads if we are in a CritSec.

term10: SaveReg <ebx,edi>               ; save 'register variables'
IFDEF PMDEBUG

;       If this thread had any BPs set through it's LDT, we want to change
;       them to point through thread 1's LDT (which is the same, of course)

        CALL16  DebugThreadNDying
ENDIF
        CALL16  SysSemOwnerDied         ; Do system sem owner died processing
        FlatContext <ds,es>,ax
        RestoreReg <edi,ebx>            ; restore 'register variables'
        CCALL32 _schForceExitCritSec    ; resume other CritSec threads
        CCALL32 _SEMMarkOwnerDied,<FALSE> ; mark owned mutex with owner died
;       Clear PTDA semaphore if owned.
        CCALL32 _TKTstnClrKernelSem
        CCALL32 _ReleaseNPX             ; Release the NPX if exiting task
        CCALL32 KMTHREADTERMINATE       ; Clean up hook block allocations
        CCALL32 _tkFreeUserStacks,<edi> ; Free the user stacks (if any left)
        CCALL32 _tkLibiCleanup,<edi>    ; Free any libinit blocks

ifdef PERFVIEW
        cmp  _fPerfview,0               ; check for perfview up ?      ;sfb
        je   short term12                                              ;sfb
        CCALL32 _PVWKillInstance_Thd,<edi>      ; Destroy Perfview data block
term12:                                                                ;sfb
endif

term15: lea     eax, [ebx].ptda_ptdasem
        CCALL32 _KSEMRequestMutex, <eax, -1>
        .errnz (NO_ERROR)
        or      eax, eax
        jnz     short term15

;       WARNING : The remainder of _TKTermThread MUST NOT BLOCK

        BeginNoBlock                    ; Beginning of final Timeslice.

        cmp     [ebx].ptda_cTCB,2       ; last sub-thread to die?
        jne     short term11            ; no - skip _TKWakeup

        test    [ebx].ptda_infoseg.LIS_ProcStatus,PS_XITTH1 ; thread 1 exiting?
        jz      short term11            ; no - skip _TKWakeup

;       The last SubThread is dying, so we must wake up thread 1.
;       TKWakeup(blockid, wakeup type, &count)

        lea     eax,[ebx].ptda_pTCBHead ; Sleep Event ID (&ptda_pTCBHead)
        CCALL32 _TKWakeup,<eax,0,0>     ; multiple wakeup, ignore count

;       Move this thread's TCB from the active list to the free list.

        PUBLIC     TCBUnlink2_StartHook
TCBUnlink2_StartHook:
term11: CCALL32 _tkUnlinkTCB <edi>      ; Unlink the TCB  dcr 1555
        lea     eax, [ebx].ptda_ptdasem
        PUBLIC     TCBUnlink2_EndHook
TCBUnlink2_EndHook:
        EndNoBlock                      ; End of final Timeslice.
        CCALL32 _KSEMReleaseMutex, <eax>
        CCALL32 _tkSchedNext
        Panic   <"_TKTermThread: _tkSchedNext returned">
EndProc _TKTermThread

HIGH32CODE      ENDS

        END
