;       SCCSID = @(#)tksubr.asm 6.5 92/03/11
SCCSID  equ     "@(#)tksubr.asm 6.5 92/03/11"

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;  Modification History
;
;  09/16/93     TPL     73453  Duplicate PIDs is being returned
;  11/15/94     Change Team  85346
;       Add ldrvars.inc to includes
;       Add _LDRTstnClrSem to extern32 declarations and
;          add call to _LDRTstnClrSem to tkStartProcess
;  08/11/95     Change Team D131886 When PID wraps, PID's are
;               given out that are already in use.
;  04/25/96  150163 Back out most of change to 131886 (except for setting
;            additional flags on TKScanTasks since there are some apps
;            which depend on pid increment logic. Pid assignments, once
;            the pid wraps will also be faster, using the old PidLowestAbove logic.
;  05/03/96  153763 Remote debug support for thread notification. Built in debug
;                   versions only.


INCL_ERROR_H    EQU     1               ; for error.inc  EQUates
INCL_ERROR2_H   EQU     1               ; for error2.inc EQUates

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include fnotask.inc
include osconst.inc
include mi.inc
include tib.inc
include proc.inc
include protmode.inc
include seldesc.inc
include bseerr.inc
include tkexport.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include except.inc
include sigdef.inc
include dossymc.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include sem.inc
include infoseg.inc
include kmode.inc
include tkptda.inc
include sched.inc
include exec.inc
include dbgex.inc
include ldrvars.inc                                                     ;85346;
.list
        CpuMode 386
        ModName ,FNO_TKSUBR_ASM

        ReferFlat       _pPTDAFirstZombie,DWORD
        ReferFlat       _pPTDACur,DWORD

IFDEF PMDEBUG
        EXTRN16         DebugThreadOneStarting
ENDIF

ifdef INCDEBUG
        EXTRN32         _DbgNewProc
endif ;DEBUG
;        EXTRN32         _SCHSetState
        EXTRN32         _TKUpdateState
        EXTRN32         _TKScanTasks
        EXTRN32         _TKLinkTask
        EXTRN32         _tkEvapPTDA
        EXTRN32         _TKSleep
        EXTRN32         _LDRTstnClrSem                                  ;85346;

CONSTANTS       SEGMENT
PidLast         dw      PID_MIN
PidLowestAbove  dw      PID_MAX
CONSTANTS       ENDS


        FARCODE HIGH32
SWAP32_13 segment
; structure used by tkNewPid and tkNewPidWorker

NewPid_s        STRUC
prop_pid        dw      ?                       ; proposed pid
ret_pid         dw      ?                       ; pid returned
NewPid_s        ENDS


;***LP  _tkNewPid - Allocate process id for new process
;
;       This routine allocates a process id (Pid) for a newly created process.
;       process id is a positive integer in the range PID_MIN thru PID_MAX
;       that uniquely identifies a process.  A check is made to ensure that
;       the newly allocated process id is not already in use as either the id
;       of an existing process, or as the id of command subtree (Csid).
;
;       tkNewPid also checks zombie processes.
;
;       ENTRY   None
;
;       EXIT    (EAX) = New Pid
;
        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING

Procedure  _tkNewPid,near
LocalVar wrkarg,<SIZE NewPid_s>
LocalVar TestID,WORD                                                          ;AA04812
        EnterProc

;       Try to give out the next id above the last one given out.  We can do
;       this if we have not reached the lowest id that is in use and that is
;       numerically greater than the last one given out.

        movzx   eax,PidLast                     ; (AX) = last Pid given out
;        inc     eax                             ; (AX) = next id above
        inc     ax                              ; (AX) = next id above        150163
        jz      short npd1                      ; pid wrap?                   150163
        cmp     ax,PidLowestAbove               ; Do we know it's not in use
        je      short npd1                      ; Jump if we don't know
        mov     PidLast,ax                      ; Update last Pid given out
        jmp     npd4                                                          ;AA04812

;       The next Pid may be in use because it matches the lowest Pid above
;       the last one given out as calculated previously.  Skip what we think
;       is probably a bunch of old processes.  Recalculate the lowest id above
;       the id being checked that is in use as we go; discard this calculation
;       if we find that the id is already in use as a Pid or Csid.

npd1:   mov     TestID,ax                       ; TestID = last pid           ;AA04812
npd2:   add     TestID,16                       ; Skip bunch of old processes ;AA04812
;73453  cmp     TestID,0                        ; discard if test id = 0      ;AA04812
;73453  jz      short npd2                      ;                             ;AA04812
        jc      npd2                            ; Same as JAE since it wrapped;73453
;;      cmp     TestID,PID_MAX                  ; Is test id >= PID_MAX ?     ;AA04812
;;      jae     short npd2                      ; discard if too big.         ;AA04812

        mov     wrkarg.ret_pid,PID_MAX          ; lowest above in use
        mov     ax,TestID                                                     ;AA04812
        mov     wrkarg.prop_pid,ax                                            ;AA04812
        SSToDS  eax,wrkarg

;       Search them for the test id

        ; D131886 - Added TK_SF_DYING and TK_SF_EXEC to call
        CCALL32 _TKScanTasks,<<TK_TG_ALL + TK_SF_WORKER + TK_SF_DYING + TK_SF_EXEC>, \
                              0, \
                              <offset FLAT:tkNewPidWorker>, \
                              eax>

        movzx   edx,wrkarg.ret_pid
        or      edx,edx                         ; Is this one in use
        jz      short npd2                      ; Jump if yes, try another

        mov     eax,_pPTDAFirstZombie           ; search zombies for PID
npd2a:  or      eax,eax
        jz      short npd3                      ; no zombies - pid OK

        push    eax                             ; EAX = pointer to zombie ptda
        SSToDS  ecx,wrkarg
        CCALL32 tkNewPidWorker,<eax,ecx>        ; Search them for the test id
        or      eax,eax
        pop     eax                             ; EAX = pointer to zombie ptda
        mov     eax,[eax].pPTDANextSibling      ; reference next zombie
        jz      short npd2a                     ; Pid ok - try next zombie.
        jmp     short npd2                      ; discard if in use.

;       We've found a new pid to give out.  Return it to the caller for
;       allocation to a new process, and return the next id above it that
;       is in use as a Pid or Csid.

npd3:   movzx   eax,wrkarg.ret_pid
        mov     PidLowestAbove,ax               ; Record lowest in use above
        movzx   eax,wrkarg.prop_pid
        mov     PidLast,ax                      ; Record as last Pid given out
npd4:   LeaveProc
        RETC                                    ; Return new pid in EAX.
EndProc _tkNewPid
SWAP32_13  ENDS

TKSWAP_TEXT     SEGMENT


;***LP  tkNewPidWorker - Worker routine for ScanTasks call made in tkNewPid
;
;       Whenever the next id above the last one given out is suspected to be
;       in use as a Pid or Csid, the tkNewPid routine checks a proposed new id
;       by searching all tasks to see if that id is in use.  This is done
;       using TKScanTasks, and this worker routine.  For each task, this worker
;       compares the test id with the Pid and csid fields of the task.
;
;       ENTRY   pPid    pointer to proposed new process id to be checked
;               pPTDA   pointer to PTDA
;
;       EXIT    EAX     1
;                        The id in CX is in use as the Pid or Csid of the
;                        task being checked.  The scan of tasks will stop
;                        and a new scan will be started by ScanTasks to
;                        check out a new id.
;
;                        pPid set to 0
;
;               EAX     0
;                         The id in pPid is not currently in use as a Pid
;                         or Csid.
;
;                        pPid set to Lowest id in use above the id being
;                        checked. tkNewPid can give out all id's starting
;                        from the id just checked up to, but not including,
;                        the ID in pPid.
;
        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING

Procedure tkNewPidWorker,near,local
ArgVar  pPTDA,DWORD
ArgVar  pwrkarg,DWORD
        EnterProc
        SaveReg <ebx,esi>

        mov     esi,pwrkarg                     ; EAX = pointer to arg
        movzx   ecx,[esi].prop_pid
        movzx   edx,[esi].ret_pid

        mov     eax,pPTDA
        movzx   ebx,[eax].Csid
        movzx   eax,[eax].ptda_Pid              ; EAX = target PTDA's Pid

;       Compare the test id with the Pid and the Csid of the target task.
;       If either matches, abandon the scan.

        cmp     ecx,eax                         ; Test id = target Pid ?
        je      short npw3                      ; Jump if in use as target Pid
        cmp     ecx,ebx                         ; Test id = target Csid ?
        je      short npw3                      ; Jump if in use as target Csid
        ja      short npw1                      ; Jump if test id > Csid

;       The test id is not in use as either the target PTDA's Pid or Csid.  If
;       the target PTDA's Pid or Csid lies between the test id and the lowest
;       id in use found so far that is above the test id, update this value.
;       Continue scanning PTDA's.

        cmp     ebx,edx                         ; Csid < lowest id above in use?
        jae     short npw1                      ; Jump if not
        mov     edx,ebx                         ; Update lowest id above in use

npw1:   cmp     ecx,eax                         ; Test id < Pid ?
        jae     short npw2                      ; Jump if not
        cmp     eax,edx                         ; Pid < lowest id above in use ?
        jae     short npw2                      ; Jump if not
;
; The pid in this ptda is lower than the lowest id above in use. So, save this
; pid in edx. The following line incorrectly saved the csid into ebx, instead
; of the pid.                                                                 150163
;;;        mov     edx,ebx                         ; Update lowest id above in use
        mov     edx,eax                         ; Update lowest id above in use 150163
npw2:   mov     ebx,pwrkarg
        mov     [ebx].ret_pid,dx
        xor     eax,eax
        jmp     short npwx                      ; Continue scanning PTDA's

;       The test id is in use as either the target Pid.  Stop scanning PTDA's
;       with this test id.  NewPid will start a new scan using a new test id.

npw3:   mov     ebx,pwrkarg                                          ;AA04689
        mov     [ebx].ret_pid,0                 ; Indicate Pid in use
        mov     eax,1                           ; Stop scanning with this id
npwx:   RestoreReg <esi,ebx>
        LeaveProc
        RETC

EndProc  tkNewPidWorker
TKSWAP_TEXT     ENDS
SWAP32_13 segment
;***EP  tkStartProcess - Create new Process
;
;       tkStartProcess is called from the EXEC system call to change an image
;       that has been loaded into memory into a task that the scheduler will
;       run. The process involved is fairly simple:
;
;               1)      Use the slot assigned by TKCreateThread
;               2)      Set the task address and initialize scheduling data
;                       for task.
;               3)      Either put the new task in the run queue or into the
;                       freeze queue depending on the function code passed.
;
;       If the code is "exec and wait" then PS_WAITPARENT is set in the
;       child's ProcStatus to indicate this.  The parent then uses CWait
;       to pick up the return codes for this (synchronous) process tree.
;
;       ENTRY   pptda   Pointer to child task's PTDA
;
;               state   Function code from EXEC call:
;                       0       Exec and Wait
;                       4       Exec and Go
;                       5       Exec and Freeze
;                       6       Exec and Trace
;                       7       Exec and Trace entire subtree
;
;               type    Exec mode:
;                       0       Normal  <save exit code for parent>
;                       1       Orphan  <don't save exit code for parent>
;                       3       Detached<don't save exit code for parent and
;                                        detach child from parent.>
;
;               prc     Pointer to return code
;
;       EXIT    Async exec :
;                       *prc = PID
;               Sync exec :
;                       prc->lo_word = ulExitType
;                       prc->hi_word = ulExitCode
;
;       EFFECTS Switches Context (iff "Exec&Wait")

        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING
Procedure   _tkStartProcess,near
ArgVar  pptda,DWORD                             ; ptda pointer
ArgVar  state,DWORD                             ; process state
ArgVar  ptype,DWORD                             ; process type
ArgVar  prc,  DWORD                             ; return code buffer
IFDEF PMDEBUG
LocalVar pidtid, DWORD ; Savearea for thread handle for KDP
ENDIF

        EnterProc
        SaveReg <ebx,esi,edi>
        mov     esi,pptda
        mov     edi,_pPTDACur
        mov     eax,[esi].ptda_pTCBHead

;       (EAX) now addresses the child  TCB
;       (ESI) now addresses the child  PTDA
;       (EDI) now addresses the parent PTDA

IFDEF PMDEBUG
        movzx   ecx,[esi].ptda_Pid
        shl     ecx,16
        mov     cx,[eax].TCBOrdinal
        mov     [pidtid],ecx
ENDIF

        movzx   ecx,[edi].ptda_handle           ; (CX) = Assume not detached
        mov     edx,state                       ; DL = state
        mov     dh,byte ptr ptype               ; DH = type

;       (CX)  parent ptda handle
;       (DL)  state
;       (DH)  ptype

        or      dl,dl                           ; Synchronous Exec ?
        jz      short tksp0                     ; yes.

        or      dh,dh                           ; Save exit code ?
        jz      short tksp1                     ; yes - Async with exit code

        test    dh,2                            ; Nondetached orphan child ?
        jz      short tksp2                     ; yes - Async without exit code

;       Child is to be detached from the parent.  Clear out links to parent
;       and link child to top-level chain of tasks.

        mov     BYTE PTR [esi].ptda_ProcType,PT_DETACHED
;       D110795 (REM'D out the following 2 lines) ; Process is detached (and orphan)
;       xor     ecx,ecx                         ; zero out parent ptda_handle in (CX)
;       mov     [esi].ptda_Ppid,cx              ; zero out parent pid
        jmp     short tksp2                     ; detached, not synchronous

tksp0:  or      BYTE PTR [esi].ptda_ProcStatus,PS_WAITPARENT
                                                ; Synchronous Exec
        jmp     short tksp3                     ; Child will be runnable.

tksp1:  or      BYTE PTR [esi].ptda_ProcStatus,PS_SYNCPARENT
                                                ; Parent wants exit code
;       (EAX) now addresses the child  TCB
;       set (EBX) to child's intitial state for SCHSetState
;       (CX) = parent ptda handle, or zero if detached.
;       (DL) = Function code from EXEC call

tksp2:  mov     ebx,STATE_FROZEN                ; assume frozen thread
        cmp     dl,exec_func_ice                ; Freeze it ?
        je      short tksp4                     ; yes; assumption correct.
tksp3:  mov     ebx,STATE_READY                 ; make it runnable

;       (EBX) = Child's initial state
;       (DL) = Function code from EXEC call

tksp4:  SaveReg <dx>
;       D110795
;       D110795  The call to _TKLinkTask was moved out of this function into
;       D110795  exec.asm and tkexec.c.  This was done to prevent the MTE from
;       D110795  being removed by ldrGarbage collect by one process before
;       D110795  another process had time to finish this link of the ptda.
;       D110795  Anyone who calls tkStartProcess must now call TKLinkTask.
;       D110795  You must also check to see if the process is detached or not.
                                                ; Pass esi: child PTDA pointer
;       CCALL32 _TKLinkTask,esi                 ; D110795 Link child into process tree
;       CCALL32 _SCHSetState,<[esi].ptda_pTCBHead,ebx>
        CCALL32 _LDRTstnClrSem                  ; Rels ldrSem if owned  ;85346;
        mov    ecx,[esi].ptda_pTCBHead
        mov    eax,ebx
        mov    [ecx].TCBState,al
        CCALL32 _TKUpdateState,<ecx>
                                                ; Set state of this thread
        RestoreReg <dx>                    ; (DL) = func. code

        and     [esi].ptda_ProcStatus,NOT PS_EMBRYO

;       See if tracing is to be enabled for child's entire subtree

IFDEF PMDEBUG
        mov     ebx, [pidtid]                   ; Notify Remote debugger of new thread starting
        CALL16  DebugThreadOneStarting
ENDIF

        test    [edi].ptda_flDbg,DBGP_TREE      ; Parent has tree tracing on?
        jnz     short tksp7                     ; Yes, trace child's entire tree
        cmp     dl,exec_func_trace_tree         ; Child subtree to be traced?
        jne     short tksp8                     ; No, don't enable tree tracing
tksp7:  or      [esi].ptda_flDbg,DBGP_TREE      ; Set tree tracing for child

;       Notify debugger of new process if we are tracing this process
;        and all children must be traced.

tksp8:  movzx   ebx,[esi].ptda_Pid

;       (EBX) = PID of new process.


        test    [edi].ptda_flDbg,DBGP_TREE      ; Parent has tree tracing on?
        jz      short tksp9                     ; No, skip notification

ifdef INCDEBUG
        SaveReg <edx>
        CCALL32 _DbgNewProc,ebx                 ; Debug notification of new process
        RestoreReg <edx>
endif ;DEBUG

;       Process might have just run to completion due to a block inside
;       DbgNewProc.  In fact, it's very likely.
;       (DL) = Exec function.
;       (BX) = PID of new process.

tksp9:  mov     ecx,ebx                         ; (CX) = child's PID
        or      dl,dl                           ; Syncronous Exec ?
        mov     edx,0                           ; assume not. (don't touch flags)
        jnz     short tksp11                    ; Asyncronous - do not wait

tksp10: mov     eax,PS_WAITPARENT               ; Syncronous Exec only
        xor     ebx,ebx                         ; Wait until process is done - no tree
        push    ecx                             ; Wait for this specific PID
        CCALL32 CWait
        pop     ecx
        jc      short tksp10                    ; errors simply not allowed.
                                                ; (EDX) = ulExitCode
        mov     ecx,eax                         ; (ECX) = ulExitType
tksp11: mov     eax,prc
        mov     [eax].LowWord,cx
        mov     [eax].HighWord,dx
        RestoreReg <edi,esi,ebx>
        LeaveProc
        RETC
EndProc _tkStartProcess
SWAP32_13  ENDS


        FARCODE HIGH32
_TEXT   SEGMENT


;***LP  CWait - Wait for child process to end
;
;       This procedure uses zombie and PTDA links to search for zombie
;       processes (or trees) and collect their exit code.
;
;       CWait can be used for Synchronous, Asynchronous, or Either kind of
;       root process.
;
;       First, w_CWait searches for an applicable zombie process (or tree)
;       in the zombie list.  If one is found, it is collected.
;
;       CWait then looks for an applicable living child process or tree.
;       If none is found, it returns ERROR_WAIT_NO_CHILDREN.
;
;       If a living process or tree is found, we either wait, or
;       return ERROR_CHILD_NOT_COMPLETE, depending only on the NoWait flag.
;
;       CWait assumes that :
;               Dead or Zombie processes are removed from the process tree.
;               Zombie children of dying processes are evaporated.
;               Orphans evaporate when they die.  Orphans include processes
;                       that are as detached, Async w/o return code, or
;                       processes whose parent has died. (Csid != Pid)
;               Zombies are linked onto the zombie list. (via FirstZombiePTDA)
;               Process tree links for zombies are NOT maintained.
;               PS_WAITPARENT and PS_SYNCPARENT cannot both be set at once
;                       in the ProcStatus byte of any process.
;               Exactly one of PS_WAITPARENT or PS_SYNCPARENT must be set
;                       in the ProcStatus byte of any zombie process.
;
;       ENTRY   (AL) = process type
;                       PS_WAITPARENT : Synchronous zombies only
;                       PS_SYNCPARENT : Asynchronous zombies only
;                       (PS_WAITPARENT or PS_SYNCPARENT) : don't care what type
;                       Other bits in (AL) must be off.
;               (BH) = Action code
;                       0 : Find a finished process - ignore it's subtree
;                       1 : Find a finished process tree
;               (BL) = Wait or return flag
;                       0 : If no child (tree or process) is done, wait.
;                       1 : If no child (tree or process) is done, do not wait.
;               (CX) = PID
;                       0 : Find any finished child (tree or process)
;                       Nonzero : Find this specific child (tree or process)
;
;       EXIT    Carry Clear
;                       (EAX) = ExitType
;                       (EBX) = PID
;                       (EDX) = ExitCode
;               Carry Set
;                       (AX) = ERROR_WAIT_NO_CHILDREN
;                       (AX) = ERROR_CHILD_NOT_COMPLETE
;                       (AX) = ERROR_INTERRUPT
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

Procedure CWait,<near,far32>

;       Look for an eligible zombie child of this process in the zombie list.

        movzx   ecx,cx                  ; ECX = PID

cwt0:   mov     esi,_pPTDACur
        movzx   edx,[esi].ptda_Pid      ; (DX) = Current PID
        mov     esi,_pPTDAFirstZombie   ; scan zombies for this Pid
        jmp     short cwt2              ; start scan.

cwt1:   mov     esi,[esi].pPTDANextSibling; (ESI) = PTDA of next Zombie.
cwt2:   or      esi,esi                 ; more zombies?
        jz      short cwt8              ; no more zombies.

        test    [esi].ptda_ProcStatus,al; Right process type?
        jz      short cwt1              ; no - try next zombie

        cmp     [esi].ptda_Ppid,dx      ; direct child?
        jne     short cwt1              ; no - try next zombie

;       We know that this must be an original direct child, because
;       no children of zombies can be in the zombie list, and we are
;       examining a direct child zombie.

        jecxz   short cwt3              ; wildcard Pid - skip Pid check.
        cmp     [esi].ptda_Pid,cx       ; desired process?
        jne     short cwt1              ; no - try next zombie

cwt3:   or      bh,bh                   ; wait for subtree?
        jz      short cwt5              ; no - collect the zombie now.

;       We are waiting for the process and it's subtree, with a zombie root.

cwt3a:  mov     edi,_pPTDACur
        mov     edi,[edi].pPTDAFirstChild; scan children.
        push    esi                     ; Save Pointer to zombie
        mov     si,[esi].ptda_Pid       ; (SI) = zombie Pid

cwt4:   or      edi,edi                 ; more children ?
        jnz     short cwt6              ; no - subtree is dead - collect it.
        pop     esi                     ; [eax] = ptda of zombie process
cwt5:   mov     eax,esi
        CCALL32 tkCollectZomb           ; collect zombie process via (EAX)
        jmp     cwtx

cwt6:   cmp     [edi].Csid,si           ; member of this subtree ?
        mov     edi,[edi].pPTDANextSibling ; (EDI) = PTDA of next child.
        jnz     short cwt4              ; no - try next child.

;       The subtree is alive.  If we are using the wildcard Pid form, we
;       must continue to check zombies, but if not, we know that the subtree
;       is alive, and that we must either wait or return CHILD_NOT_COMPLETE.

cwt7:   pop     esi                     ; ESI = ptda of zombie process
        jecxz   short cwt1              ; wildcard Pid - try next zombie
        jmp     short cwtC              ; wait or CHILD_NOT_COMPLETE.

;       We can't find this process in the zombie queue, so we see if
;       the process is one of our living children.  If so, Wait.

;       (AL) = Process Type to scan for
;       (BH) = Action Code              (nonzero if Tree)
;       (BL) = Wait or return flag      (nonzero if NoWait)
;       (CX) = PID to search for        (zero if wildcard PID)

cwt8:   mov     edi,_pPTDACur
        mov     edi,[edi].pPTDAFirstChild; scan living children for CSID = (CX)

cwt9:   or      edi,edi                 ; more children?
        jz      short cwtD              ; no - ERROR_WAIT_NO_CHILDREN

        mov     esi,edi
        mov     edi,[esi].pPTDANextSibling ; (EDI) = PTDA of next child.

;       We take this child process if we want all child processes, or if
;       the PS_WAITPARENT bit in ProcStatus and (AL) match.  PS_WAITPARENT
;       is set only for Synchronous Execs.

        cmp     al,(PS_SYNCPARENT + PS_WAITPARENT)      ; want all children ?
        je      short cwtA              ; yes - skip PS_WAITPARENT check.

        mov     ah,[esi].ptda_ProcStatus; (AH) = Target ProcStatus byte
        xor     ah,al                   ; Turn off all matching bits in (AH).
        test    ah,PS_WAITPARENT        ; Did PS_WAITPARENT bits match ?
        jnz     short cwt9              ; no - try next child

;       We have found a current child process of the right process type

cwtA:   mov     dx,[esi].Csid           ; (DX) = child's Csid
        jecxz   short cwtB              ; wildcard - skip Csid check

        cmp     cx,dx                   ; A member of correct tree?
        jne     short cwt9              ; no - try next child

;       We have found a current child process from the right (or wildcard) tree

cwtB:   or      bh,bh                   ; check subtree?
        jnz     short cwtC              ; yes - subtree alive - wait.

;       We are ignoring the subtree

        cmp     dx,[esi].ptda_Pid       ; original direct child? (Pid == Csid)
        je      short cwtC              ; yes - wait

;       The original direct child process with Pid = (DX) died and evaporated

        jecxz   short cwt9              ; wildcard - check other children
        jmp     short cwtD              ; process gone - WAIT_NO_CHILDREN

;       We have found a reason to wait

cwtC:   mov     esi,eax                 ; (ESI) = process types.

        mov     ax,ERROR_CHILD_NOT_COMPLETE
        or      bl,bl                   ; NoWait flag?
        jnz     short cwtxe             ; can't wait - exit with an error.

;       Block ID = (OFFSET TASKAREA:Pid << 16 + [Pid])

        push    ecx                     ; save Pid

        mov     eax,(TKOPTDA + ptda_Pid) SHL 16
        mov     ecx,_pPTDACur
        mov     ax,[ecx].ptda_Pid       ; EAX = block ID
        CCALL32 _TKSleep        <eax, -1, 0, 0>
        pop     ecx                     ; restore (ECX) = Pid
        or      eax,eax
        mov     eax,esi                 ; [EAX] = process types.
        .errnz  NO_ERROR
        jz      cwt0                    ; try again from the top if no error.

        mov     eax,ERROR_INTERRUPT     ; block interrupted.
        jmp     short cwtxe

cwtD:   mov     eax,ERROR_WAIT_NO_CHILDREN

cwtxe:  stc
cwtx:   RETC
EndProc CWait


;***LP  tkCollectZomb - Collect a zombie's information, and evaporate it.
;
;       tkCollectZomb gathers the zombie's ExitCode and ExitType,
;       removes the zombie from the zombie list, and evaporates
;       the PTDA.
;
;       ENTRY   EAX     Pointer to PTDA of zombie process
;
;       EXIT    Carry clear.
;               (EAX) = Termination code of zombie      (ExitType)
;               (EBX) = PID of zombie
;               (EDX) = process return condition code   (ExitCode)
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags


Procedure tkCollectZomb,NEAR
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING

IFDEF   MISCSTRICT
        test    BYTE PTR [eax].ptda_ProcStatus,PS_DYING
                                                ; must be dead
        jnz     short tkczm0
        Panic   <"tkCollectZomb: Not dead">
tkczm0:
ENDIF

        push    [eax].ptda_ulExitType           ; save zombied information
        movzx   ebx,[eax].ptda_Pid
        push    ebx                             ; Pid
        push    [eax].ptda_ulExitCode

;       Remove PTDA from zombie list.

        mov     ebx,[eax].pPTDAPrevSibling      ; (EBX) = Previous Zombie PTDA
        mov     ecx,[eax].pPTDANextSibling      ; (ECX) = Next Zombie PTDA
        or      ebx,ebx                         ; any previous zombie?
        jnz     short tkczm1                    ; yes - link it to next.

        mov     _pPTDAFirstZombie,ecx           ; First = Curr.Next
        jmp     short tkczm2                    ; Try linking Next to top

tkczm1: mov     [ebx].pPTDANextSibling,ecx      ; Prev.Next = Curr.Next

tkczm2: jecxz   short tkczm3                    ; No next zombie.
        mov     [ecx].pPTDAPrevSibling,ebx      ; Next.Prev = Curr.Prev

tkczm3: CCALL32 _tkEvapPTDA,eax                 ; Evaporate PTDA
        clc
        pop     edx                             ; (EDX) = ExitCode
        pop     ebx                             ; (EBX) = Pid
        pop     eax                             ; (EAX) = ExitType
        RETC                                    ; return with his codes

EndProc tkCollectZomb

_TEXT   ENDS

        END
