;       SCCSID = @(#)sproc.asm  6.4 92/03/11
SCCSID  equ     "@(#)sproc.asm  6.4 92/03/11"
        page    ,132

TITLE   Sproc -- OS/2 2.0 Sched/Process Manager

;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved


;***    OS/2 Scheduler Process Management Module
;
;
;       This module comprises the process management part of the
;       scheduler.  There are a few other modules included in the
;       scheduler proper:
;
;               sched.inc       Definitions used elsewhere in scheduler
;               proc.inc        Definitions for ProcBlock/ProcRun
;               sched.asm       Scheduler utility routines
;               sutil.asm       Process control and context switching
;               scdata.asm      Common scheduler data
;
;       The functions performed by this module include:
;
;               1)      Process creation and death
;               2)      Management of sleeps and wakeups
;
;
;       Modification History:
;
;               08 Dec 1986 - Bruce Bailey
;                  Code modified to comply with DCR 451 - routines
;                  changed are ProcBlock, ProcRun, DevDone
;
;             THG = change for PTM 917 (put SIS_MaxScrnGrp in LIS_CurScrnGrp
;                                       of detached PID's)
;
;       01-Jul-87       R. Cook (IBM) - Add wakeup based on
;                           FIFO blocking.
;
;       06-Jun-88       JSF     Allow for tracing of entire trees or just
;                               child. (1.1 DCR 114)
;
;       10-Aug-88       JSF     Cure race condition between procrun and
;                               schedclock (1.2 PTM 1687)
;
;       26-Aug-88       JSF     Document that ProcRun leaves carry clear
;                               (1.2 PTM 1858)
;       08-Mar-92       SFB     b734380 Reduce perfview overhead
;       12-16-93        RAC     76699 Make Perfview optionally assembled
;       01-Mar-94       TPL     79048 Allow MTE access under ChkDelayHardErr
;       08-Apr-94       ARJ     77461 Debuggers not Notified when Process Die

.xlist
include dosseg.inc
.xcref
include basemaca.inc
include osmaca.inc
include fnotask.inc
include defdbug.inc
include dossymc.inc
include devsym.inc
include error.inc
include proc.inc
include sched.inc
include exec.inc
include tkexport.inc
include screen.inc
include infoseg.inc
include mi.inc
include ldrxport.inc            ; D93289/D101249

ifdef INCDEBUG
include dbgex.inc
endif

.cref
.list
        CpuMode 386
        ModName , FNO_SPROC_ASM

        EXTRN           FreeSysSem:NEAR
        EXTRN           LastPacket:BYTE
        EXTRN           _LdrSem:QWORD  

ifdef PROFILE
        EXTRN           ProfTerm:NEAR
endif

        EXTRNF32        CWait

ifdef INCDEBUG
        EXTRNF32        _DbgProcTerm
        EXTRNF32        _DbgKillDebuggees
endif ;DEBUG

        EXTRNF32        _SEMCleanProcDying
        EXTRNF32        KMTHREADTERMINATE
        EXTRNF32        _ReleaseNPX

        EXTRN32         _LDRFreeTask
        ; EXTRN32         g__LDRClearSem           ; D93289/D101249/D110795
        EXTRN32         _TKSleep
        EXTRN32         _TKWakeup
        EXTRN32         _TKUnlinkTask
        EXTRN32         _tkUnlinkTCB
        EXTRN32         _TKForceTask
ifdef INCDEBUG
        EXTRN32         _PRODestroyPCB
endif
        EXTRN32         _timProcTerminate
;        EXTRN32         _SCHSetState
        EXTRN32         _TKUpdateState
        EXTRN32         _tkSchedNext

ifdef PERFVIEW
        EXTRN32         _PVWKillInstance_Thd
;b734380        EXTRN32         _PVWKillInstance_Prc
endif

IFDEF PMDEBUG
        EXTRNFAR        DebugThreadOneDying
ENDIF
        EXTRNFAR        ChkDelayHardErr

        ReferGlobal     FirstPacket,BYTE
        ReferGlobal     InterruptLevel,BYTE
        ReferGlobal     _ThreadCount,WORD
        ReferGlobal     TaskNumber,WORD
        ReferGlobal     papTCBPtrs,WORD
        ReferGlobal     _Lis2,BYTE
        ReferGlobal     MaxReqPktsSegs,BYTE
        ReferGlobal     ReqPktSegs,WORD

        ReferTask       Pid,WORD
        ReferTask       Ppid,WORD
        ReferTask       ProcType,BYTE

ifdef INCDEBUG
        ReferTask       ptda_flDbgEnabled,DWORD
        ReferTask       ptda_flDbg,DWORD
endif ;DEBUG

        ReferTask       ptda_pTCB1,DWORD
        ReferTask       ptda_start,BYTE


; 3/19/94 SBE defect 81036
; modified ifdef from INCDEBUG to PROFILE to fix build break, definition needs
; to match using code, which is defined as PROFILE

ifdef INCDEBUG
        ReferTask       ptda_pPCB,DWORD
endif ;DEBUG

        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD

ifdef PERFVIEW
        ReferFlat       _fPerfview,DWORD                               ;b734380
endif

        FARCODE LOW
CODE    SEGMENT


;***LP  ProcessFreeze - Freeze this Process
;
;       ProcessFreeze freezes the current task.  The process goes
;       on the freeze queue where it gets no more CPU service until
;       ProcessThaw restores it to run-state.
;
;       When we want to freeze a process we set FF_ICE; the next time
;       we're about to return control to that process (from an interrupt
;       or a system call) we see FF_ICE and call ProcessFreeze.  It
;       is impossible for a process to execute user instructions after
;       FF_ICE has been set; this potential delay just allows us to
;       complete any half-done system calls, freeing any kernel resources.
;
;       Note that the we must not clear the FF_ICE bit because other
;       parts of the system use that flag to know that the process has
;       been (or will be) frozen.  Internal consistency checks do not
;       permit thawing a process which is not frozen.  The indication
;       that the process has actually been frozen is from its scheduler
;       state.
;
;       ENTRY   None
;       EXIT    None
;       USES    All

    ASSUME      DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure ProcessFreeze,<near,faronly,far16>
        FlatContext <ds,es>,ax
        mov     eax,[_pTCBCur]
;        CCALL32 _SCHSetState,<eax,STATE_FROZEN>
        mov     [eax].TCBState,STATE_FROZEN
        CCALL32 _TKUpdateState,<eax>

        CCALL32 _tkSchedNext,,nocall
        RETC
EndProc ProcessFreeze

CODE    ENDS


        FARCODE HIGH3
HIGH3CODE SEGMENT


;***LP  ProcessKill - Expunge Process
;
;       First, the process is check to see if it is being Debugged. If
;       so, DbgProcTerm is called to let the debugging process know we are
;       about to die.  DbgProcTerm completes the notification before returning.
;
;       ProcessKill kills the current task.  The children are adopted
;       by the grandparents.  Any zombies outstanding for this guy
;       are erased.  Children of this process will not become zombies
;       because they detect that parent has died by detecting Pid!=Csid.
;
;       We then free the task's memory.  At this point, all that is
;       left is the PTDA memory.
;
;       TKUnlinkTask (remove process from the tree, perhaps linking a Zombie)
;       SetState (STATE_VOID)
;       _tkSchedNext to switch away from this task.
;       The next task selected will see the Evap bit, and will free the PTDA.
;
;       ENTRY   Current process dying.
;       EXIT    * * DOESN'T * *
;       USES    All
;
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:TASKAREA

Procedure ProcessKill,NEAR

;       PMONLY  TASKTIME
        TaskTime

        SaveReg <ds,es>
        FlatContext <ds,es>,ax

;       Cancel any timers in this process

        CCALL32 _timProcTerminate,[_pPTDACur]
        RestoreReg <es,ds>

ifdef   PROFILE
        call    ProfTerm                ; Free profiler structure if one exist
endif ;PROFILE

ifdef INCDEBUG
        cmp     ss:[ptda_pPCB],0        ; Current proc being profiled?

        jz      short prok0

        mov     ebx,offset TASKAREA:ptda_pPCB
        sub     ebx,offset TASKAREA:ptda_start
        SaveReg <ds,es>
        FlatContext <ds,es>,ax
        add     ebx,_pPTDACur
        CCALL32 _PRODestroyPCB <EBX>    ; Notify profiler that task is dying.
        RestoreReg <es,ds>

        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:TASKAREA

;       Check to see if we need to notify a debugger.
;       Note that for a VDM process with DPMI watchpoints,
;       we must have cleaned up before we get here.

prok0:  cmp     ptda_flDbgEnabled,0     ; Debugged process?
        je      prok0a                  ; no
        FlatContext <ds,es>,ax
        CALLF32 _DbgProcTerm            ; notify debugger of death
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:TASKAREA
endif ;DEBUG

prok0a:
IFDEF PMDEBUG
;       Before freeing memory, we need to have the debugger disable all
;       BPs that were set through this LDT (as each non-one thread died,
;       it's BP's task numbers were set to that of this; thread one)

        CALLFAR DebugThreadOneDying
ENDIF
        DosContext ds                   ; use DS to access DOSGROUP
        call    FreeSysSem              ; close (and maybe free) open sys sems
        FlatContext <ds,es>,ax
        CALLF32 _ReleaseNPX             ; ReleaseNPX if dieing task is owner
        CALLF32 _SEMCleanProcDying      ; clean up 32-bit semaphores
        CALLF32 KMTHREADTERMINATE       ; clean up hook blocks

ifdef INCDEBUG
        test    ss:[ptda_flDbg],DBGP_ISADBGER ; current proc a debugger ?
        jz      short prok2             ; no.
        CALLF32 _DbgKillDebuggees       ; clean out all debuggee processes
endif ;DEBUG

;79048  Make sure routines below ChkDelayHardErr still have access to
;       things in the MTE
prok2:  CALLFAR ChkDelayHardErr         ; Check for delayed hard errors  ;79048
        LDRGetSem                       ; Set loader semaphore           D93289/D101249
        or      eax,eax
        jnz     prok2                   ; interrupted by a signal    ;AA04564
        CCALL32 _LDRFreeTask,<[_pPTDACur]> ; release memory, mte, etc... ;79048

        ; CCALL32 _LDRClearSem           ; Release loader semaphore       D93289/D101249
        mov	eax, OFFSET FLAT:_LdrSem ; D110795
        CCALL32 _KSEMReleaseMutex,<eax>  ; D110795

        FlatContext <ds,es>,ax

ifdef PERFVIEW
        cmp _fPerfview,0                ; check if perfview is up      ;b734380
        je  short prok2a                                               ;b734380
        CCALL32 _PVWKillInstance_Thd,<[_pTCBCur]> ; Destroy Perfview thread data
;b734380 CCALL32 _PVWKillInstance_Prc,<[_pPTDACur]> ; Destroy Perfview process data
prok2a:                                                                ;b734380
endif

;       This task is dying, so any of its children which have been put
;       into the zombie list should be picked up now.

prok3:  mov     ax,PS_SYNCPARENT or PS_WAITPARENT       ; type = don't care
        mov     bx,0010H                ; Ignore Tree + NoWait
        xor     cx,cx                   ; any dead direct child.
        CALLF32 CWait
        jnc     short prok3             ; no error - get another one.

;       CWait either returned because there are no children, or
;       there were living children, but NoWait was specified.

;       To wake up any CWaiting proceses, we issue a single ProcRun
;       on the  parent, unless there is no parent.

        ASSUME  SS:TASKAREA

        mov     bx,ss:Ppid              ; (BX) = parent's Pid or zero if none
        CCALL32 _TKUnlinkTask,<[_pPTDACur]> ; Unlink the task from process tree
        PUBLIC     TCBUnlink1_StartHook
TCBUnlink1_StartHook:
        CCALL32 _tkUnlinkTCB,<[_pTCBCur]> ; Unlink the TCB from the PTDA 1555
        or      bx,bx                   ; Does parent exist?
        PUBLIC     TCBUnlink1_EndHook
TCBUnlink1_EndHook:
        jz      short prok5             ;  no, just evaporate

        mov     ax,OFFSET TASKAREA:Pid  ; (AX:BX) = CWait Block ID
        CALLFAR ProcRun                 ; wakeup parent, if sleeping

prok5:  CCALL32 _tkSchedNext,,nocall    ; Run somebody else... They cremate us.
        InternalError <ProcessKill: _tkSchedNext returned>

EndProc ProcessKill


HIGH3CODE ENDS


        FARCODE LOW
CODE    SEGMENT


;***EP  ProcBlock - Block this process from running
;
;       ProcBlock "sleeps" the current process until some other process
;       (or an interrupt service routine) issues an appropriate ProcRun
;       call.  Both the ProcBlock and ProcRun routines are supplied
;       a 32-bit "event identifier".  When ProcRun is called with a
;       particular event identifier it reactivates ALL ProcBlock'ed
;       processes which also presented that identifier, be there 0, 1, or
;       many such processes.
;
;       By convention, the 32-bit "event identifer" is the address of
;       some structure or memory cell.  For example, processes waiting
;       for I/O to complete call ProcBlock with the address of the
;       I/O block.  When the device driver completes the I/O and marks
;       the I/O block done it then would call ProcRun with that address
;       so that any waiting processes can resume operation.  Most
;       device drivers are interrupt driven, so ProcRun is often called
;       at interrupt-time by the device interrupt handler.
;
;       When calling ProcBlock it is important to use the sequence:
;
;              Disable Interrupts
;              while (need to wait)
;                      ProcBlock(value)
;
;       Interrupts are turned off BEFORE we check our condition
;       (I/O done, resource freed, whatever) so that we don't
;       deadlock by getting an interrupt-time ProcRun call before we
;       complete the call to ProcBlock.  ProcBlock re-enables the
;       interrupts.  Also note the "while" clause: it is good practice
;       to re-check the awaited condition and, if necessary, re-disable
;       interrupts and re-call ProcBlock.  The convention of using
;       an address as an "event identifer" should prevent double use
;       of an identifier.
;
;       ProcBlock does its work by entering the particulars in the
;       wait queues and then calling the scheduler to run someone else.
;       Naturally, ProcBlock CANNOT be called at interrupt time.
;
;       When the wakeup is issued, or (optionally) the timeout
;       expires, or (optionally) when the task is signaled
;       ProcBlock exits to the caller.
;
;       ENTRY   (AX:BX) = event identifier
;               (DI:CX) = timeout interval (in MS).  -1 if to never timeout
;               (DH) = 0 if sleep is interruptible
;                    = 1 if sleep is not interruptible
;               Interrupts DISABLED to prevent ProcBlock/ProcRun races
;       EXIT    'C' clear if event wakeup
;               'C' set if unusual wakeup:
;                'Z' set if timeout wakeup
;                'Z' clear if process was interrupted
;               (AL) = awake code, non-zero if unusual wakeup
;               Interrupts enabled
;       USES    AX, Flags
;
;       EFFECTS SWITCHES CONTEXT

Procedure ProcBlock,<near,faronly>
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA
        EnterProc ,,alignesp

        SaveReg <ecx,edx,edi>           ; Save registers
        SaveReg <eax>
        movzx   edx,dh                  ; (edx) = sleep type
        shl     edi,16
        mov     di,cx                   ; (edi) = timeout
        shl     eax,16
        mov     ax,bx                   ; (eax) = event identifier

        push    ds
        push    es
        FlatContext <ds,es>,cx
        CCALL32 _TKSleep,<eax,edi,edx,0>
        pop     es
        pop     ds
    assume ds:nothing, es:nothing

        mov     dl,PBR_NA               ; Assume normal awakening
        or      eax,eax                 ; Abnormal awakening?
        .errnz  NO_ERROR
        je      short pblk2             ;  no, skip this part

; Add check here to see if PM Event was posted ;80729
        mov     dl,PBR_PM                      ;80729
        cmp     eax,PM_EVENT_POSTED            ;80729
        je      pblk2                   ;  Return error if PM Posted

        cmp     eax,ERROR_INTERRUPT     ;  Sleep interrupted?
        jnz     short pblk1             ;  no, must be timeout expired

; Add check here to see if PM Event was posted
        mov     dl,PBR_PM
        cmp     eax,PM_EVENT_POSTED
        jz      pblk2                   ;  Return error if PM Posted

        mov     dl,PBR_IN               ;  Sleep interrupted
        jmp     short pblk2

pblk1:  mov     dl,PBR_TE               ;  Timeout expired
pblk2:  RestoreReg <eax>

        mov     al,dl                   ; (al) = wakeup code
        sar     dl,1                    ; 'C' clear if procrun awake
                                        ; 'C' 'Z' set if timeout
                                        ; 'C' set, 'Z' clear if interrupt
        RestoreReg <edi,edx,ecx>
        LeaveProc
        RETP
EndProc ProcBlock


;***EP  ProcRun - Release Blocked Threads
;***EP  ProcRun2 - Release Blocked Threads and IOBoost them
;
;       ProcRun is called to "wakeup" blocked threads.  All
;       processes sleeping on the particular event identifier
;       are woken, be it 0 or many.
;
;       ProcRun is often called at interrupt time.
;
;       ENTRY   (AX:BX) = event identifier
;       EXIT    (AX) = Count of those woken
;              'Z' set according to (ax)
;               CARRY CLEAR
;       USES    NONE

    ASSUME      DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure ProcRun,<near,faronly>
LocalVar Count,DWORD

        EnterProc ,,alignesp
        SaveReg <ecx>
        xor     ecx,ecx                 ; ECX = WakeFlag
pr0:
        SaveReg <eax,edx,ds,es>
        FlatContext <ds,es>,dx
        shl     eax,16
        mov     ax,bx
        SSToDS  edx,Count
        CCALL32 _TKWakeup,<eax,ecx,edx>
        RestoreReg <es,ds,edx,eax>
        RestoreReg <ecx>
        ASSUME DS:NOTHING,ES:NOTHING
        mov     ax,Countl
        or      ax,ax
        LeaveProc
        RETP
EndProc   ProcRun

Procedure ProcRun2,<near,faronly>
LocalVar Count,DWORD
        EnterProc
        SaveReg <ecx>
        mov     ecx,WAKEUP_BOOST        ; ECX = WakeFlag
        jmp     short pr0
EndProc   ProcRun2,NoCheck


;***EP  DevWait - Wait I/O Completion
;
;       DevWait is called from DEV:DevIOcall to put the current process into
;       an I/O wait until the I/O is signaled possible to do by the device
;       driver interrupt routine (see DevDone below).
;
;       ENTRY   ES:BX   Block Value
;               INTERRUPTS OFF
;       EXIT    When awoken
;               Interrupts ON
;       USES    Flags
;
;       EFFECTS Switches Context (via ProcBlock)

    ASSUME      DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure DevWait
        push    ax
        push    bx
        push    cx
        push    dx
        push    di
        DBPRT   2,16,<DevWait calls >,<>
        mov     ax,es           ; (ax:bx) = wait value
        mov     di,-1
        mov     cx,di
        mov     dh,1
        call    ProcBlock
        DBPRT   2,16,<DevWait Alive >,<>
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        RET

EndProc DevWait


;***EP  DevDone - Flag I/O Complete
;
;       DevDone is called to restart a thread for which an I/O request
;       has completed.  It will set the STDON bit in the request packet
;       and call ProcRun2 using the request packet address as the wakeup
;       key.  (DevWait waits on the request packet address, see above.)
;
;       DevDone is called via a DevHlp entry point.
;
;       ENTRY   ES:BX   Pointer to I/O request block
;
;       USES    flags
;
;               FS and GS must be preserved (DevHelp requirement)

    ASSUME      DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure DevDone
        SaveReg <ds,es,fs,gs>   ; Save registers
        pusha                   ; Save registers
        DBPRT   2,16,< DevDone wake $x:$x\n>,<es,bx>
        mov     ax,es           ; (ax:bx) = block/run key
        or      es:[bx].ReqStat,STDON   ;set request done bit
        call    ProcRun2
        popa                    ; Restore registers
        RestoreReg <gs,fs,es,ds> ; Restore registers
        RET
EndProc DevDone

CODE    ends
        END
