;       SCCSID = @(#)sched.asm  6.1 90/11/17
;       SCCSID = @(#)sched.asm  1.30 90/09/17
        page    ,132
        title   CP-DOS Scheduler Routines         **** IBM CONFIDENTIAL ****

;***************************************************************************
;
; Source File Name:   SCHED.ASM
;
; Descriptive Name:   CP-DOS Scheduler kernel routines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
; Function:
;
;       This module contains routines which take account of system
;       events and determine whether or not a reschedule should
;       take place.  If so, the Resched (and possibly TCResched)
;       flags are set.  It also contains the GetNextRunner routine
;       which determines which thread should run next.
;
; Dependencies:
;
;       The routines in this module should be called only in
;       kernel mode or from a first-level interrupt handler.  The
;       ExitKmode and return from interrupt routines must check
;       the Resched flag and, if set, call the dispatcher.  The
;       dispatcher must call GetNextRunner for the number of the
;       thread to dispatch.
;
;       The flags "resched" and "TCresched" are made public for
;       the purpose of providing the scheduler a mechanism to inform
;       other components of the system that some other thread should
;       be running. Only the scheduler is allowed to change the state
;       of these flags; other components MUST treat them as READ ONLY.
;
; Restrictions:
;
;       Thread number zero (which implies the currently executing
;       thread) is not allowed at interrupt time.
;
;       SetPriority is not callable at interrupt time because it
;       checks to see if the affected thread it with-in the currently
;       executing process.
;
;***************************************************************************
;       18:Nov-95       MLM     Defect 72417, moved find.inc after tkexport.inc

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include struc.inc
include mi.inc
include infoseg.inc
include proc.inc
include screen.inc
include ostypes.inc
include sft.inc
include dossymc.inc
include sigdef.inc
include except.inc
include argframe.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include sem.inc
include tkexport.inc
include find.inc
include tkptda.inc
include perfvw.inc
include sched.inc
.list

        CpuMode 386
        ModName sched

        EXTRN32         _schSetThreadKbdLocus
        EXTRN32         _TKiSlotTo_pTCB

        EXTRN32         _TKGetReadyThread
        EXTRN32         _TKDelayedWakeup
        EXTRN32         _TKUpdatePriority
        EXTRN32         _TKUpdateState

        ReferFlat       _FgndSGID,BYTE
        ReferFlat       SISData,BYTE

        ReferFlat       runner,DWORD

        ReferFlat       _papTCBSlots,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _ptcbPriQDelayed,DWORD
        ReferFlat       _TCResched,BYTE
        ReferFlat       _Resched,BYTE
        ReferFlat       _ThreadCount,WORD


        FARCODE LOW
CODE    segment

BREAK   <dh_ModifyPriority>
;***********************************************************************
;
; Subroutine Name:  dh_ModifyPriority
;
; Descriptive Name:  Modify Priority
;
; Function:
;       This routine sets a thread's WINDOW modifier from a device driver.
;
; Notes:
;       This device helper is UNDOCUMENTED.
;
; Entry Point:  dh_ModifyPriority
;       Linkage:  Call (near) (register-based calling sequence)
;
; Entry:
;       BX = absolute thread number
;       DH = keyboard switch (DL is garbage from DevHlp router)
;         0 =>  remove WINDOW modifier
;         NZ => give WINDOW modifier
;
; Exit-Normal:
;       none
;
; Exit-Error:
;       none
;
; Effects:
;       May change thread's priority
;
;       FS and GS must be preserved (DevHelp requirement)
;
;***********************************************************************

Procedure       dh_ModifyPriority
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING
        SaveReg         <ds,es>
        FlatContext     <ds,es>
        cmp     bx,[_ThreadCount]       ; Slot too large?
        ja      short mpx               ; yes - ignore call

        SlotToTCB       bx,eax          ; (EAX) -> TCB, from slot in (BX)
        or      eax,eax                 ; Empty Slot?
        jz      short mpx               ; yes - ignore call

        test    [eax].TCBSchFlags,SCH_WINDOWBOOST_LOCK
        jnz     short mpx

        .if <dh ne 0>
            test    [eax].TCBPriClassMod,CLASSMOD_WINDOW
            jnz     short mpx

            or      [eax].TCBPriClassMod,CLASSMOD_WINDOW
        .else
            test    [eax].TCBPriClassMod,CLASSMOD_WINDOW
            jz      short mpx

            and     [eax].TCBPriClassMod,NOT CLASSMOD_WINDOW
        .endif

;       Account for a priority change.

        CCALL32 _TKUpdatePriority,<eax> ; Recalculate priority
mpx:    RestoreReg <es,ds>
        RETC
EndProc dh_ModifyPriority

CODE    ends


        FARCODE HIGH32
_TEXT   SEGMENT

BREAK   <_schWindowBoost>
;***********************************************************************
;
; Subroutine Name:  _schWindowBoost
;
; Descriptive Name:   Set the window class modifier
;
; Function:
;       This routine sets the window class modifier of the specified
;       thread.
;
; Notes:
;
; Entry Point:  _schWindowBoost
;       Linkage:  C Calling Convention
;
; Entry:
;       iSlot = Thread slot.  0 not allowed
;       Scope = Modifier Scope Switch
;               SCOPE_THREAD  = give thread window boost (CLASSMOD_WINDOW)
;               SCOPE_GIVEMSG = give thread window boost
;               SCOPE_TAKEMSG = remove window boost from thread
;
; Exit-Normal:
;       none
;
; Exit-Error:
;       none
;
; Effects:
;       nothing
;
;***********************************************************************

Procedure       _schWindowBoost,,,,esp
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

ArgVar  iSlot,DWORD
ArgVar  Scope,DWORD

        mov     eax,[iSlot]
        cmp     ax,[_ThreadCount]       ; Slot too large?
        ja      short wbx               ; yes - ignore call

        SlotToTCB       ax,eax          ; (EAX) -> TCB, from slot
        or      eax,eax                 ; Empty Slot?
        jz      short wbx               ; yes - ignore call

        .if <Scope eq SCOPE_THREAD>
            or      [eax].TCBSchFlags,SCH_WINDOWBOOST_LOCK
        .else
            test    [eax].TCBSchFlags,SCH_WINDOWBOOST_LOCK
            jnz     wbx
        .endif

        .if <Scope ne SCOPE_TAKEMSG>
            inc     [eax].TCBcWindowBoost
            test    [eax].TCBPriClassMod,CLASSMOD_WINDOW
            jnz     wbx

            or      [eax].TCBPriClassMod,CLASSMOD_WINDOW
        .else
            dec     [eax].TCBcWindowBoost
            jnz     wbx

            and     [eax].TCBPriClassMod,NOT CLASSMOD_WINDOW
        .endif

        CCALL32 _TKUpdatePriority,<eax> ; Recalculate priority
wbx:    xor     eax,eax
        RETC
EndProc _schWindowBoost


BREAK   <_SCHGetNextRunner>
;***************************************************************************
;
; Subroutine Name:  _SCHGetNextRunner
;
; Descriptive Name:  Get the Next Runner
;
; Function:
;       This routine gets the next thread to run.
;
; Notes:
;       The highest priority thread in the ready state will be selected.
;       The last runner will be chosen only if it is the highest priority
;       runnable thread.
;
; Entry Point:  _SCHGetNextRunner
;       Linkage:  C Calling convention
;
; Entry:
;       Called with interrutps enabled.
;
; Exit-Normal:
;       EAX = pointer to TCB of next thread to run
;         0 => no threads ready
;       Resched = 0
;       TCResched = 0
;
; Exit-Error:
;       none
;
; Effects:
;       Modifies runner, Resched, TCResched.
;
; Internal References:
;       Routines:
;
; External References:
;       Routines:
;
; Pseudo Code:
;       Procedure _SCHGetNextRunner
;
;       while (Delayed actions exist) {
;           Call _TKDelayedWakeup to process delayed events
;       }
;       If (descending thread exists and still wants to run) {
;           Switch states from running to ready.
;       }
;       runner = _TKGetReadyThread ()
;       if (runner != NULL) {
;           Remove TC boost, if any.
;           Switch thread's state from ready to running
;       }
;       Resched = TCResched = 0
;       return (runner);
;
;***************************************************************************


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

;       **** NOT AN ENTRY POINT ****

gnr0:   mov     [runner],0              ; Invalidate runner before window
        sti                             ; Begin interrupt window.
        jmp     _SCHGetNextRunner       ; Start over, checking delayed actions

gnr1:   sti                             ; With interrupts enabled,
        CCALL32 _TKDelayedWakeup        ; Handle delayed scheduler actions.

;       **** NORMAL ENTRY POINT ****

Entry   _SCHGetNextRunner

        cli                             ; disable interrupts for switch.
        cmp     [_ptcbPriQDelayed],0    ; Any delayed actions?
        jne     short gnr1              ; yes - handle delayed actions

;       Handle the descending thread, if any.

        mov     edx,[runner]
        or      edx,edx                 ; Any descending thread?
        jz      short gnr2              ; no - just get the ascending thread.

        cmp     [edx].TCBState,STATE_RUNNING    ; Does it still want to run?
        jne     short gnr2              ; no - just switch

;       Mark the old thread as ready, recalculating priority.

gnr1a:  mov     [edx].TCBState,STATE_READY
        CCALL32 _TKUpdateState,<edx>    ; Update to new thread state

;       Attempt to get the next ascending thread.

gnr2:   CCALL32 _TKGetReadyThread       ; Select a thread to run.
        cmp     eax,-1                  ; Interrupt window required?
        je      short gnr0              ; yes - provide the interrupt window

        mov     [runner],eax            ; save runner's TCB pointer
        or      eax,eax
        jz      short gnr4              ; no next thread to run - idle

;       Handle the ascending thread.
;       Mark the new thread as running.

        mov     [eax].TCBState,STATE_RUNNING
        CCALL32 _TKUpdateState,<eax>    ; Update new thread to running state
        mov     eax,[runner]
gnr4:   mov     [_Resched],0            ; clear Resched and TCResched
Entry   DispLatency_TCreschedFF         ; (for dispatch latency measurements)
        mov     [_TCResched],0
        sti                             ; reenable interrupts again.
        RETC
EndProc GetNextRunner


BREAK   <_schInitTCB>
;***************************************************************************
;
; Subroutine Name:  _schInitTCB
;
; Descriptive Name:   Initialize Scheduler's TCB variables
;
; Function:
;       This routine initializes the specified thread's Scheduler TCB fields.
;
; Notes:
;       none
;
; Entry Point:  _schInitTCB
;       Linkage:  C Calling convention
;
; Entry:
;       ptcb    pointer to thread to initialize
;
; Exit-Normal:
;       nothing
;
; Exit-Error:
;       none
;
; Effects:
;       Initializes the scheduler's TCB fields
;
; Internal References:
;            Routines:
;       _schSetThreadKbdLocus
;
; External References:
;            Routines:
;
; Pseudo Code:
;       Procedure Init_TCB(Thread)
;       Set thread's class to parent's class (CLASS_REGULAR if initial thread)
;       Set thread's level to parent's level (0 if initial thread)
;       Clear Miscelaneous Scheduler fields
;       If dynamic priority and in fgnd screen group and not detached
;         Set the foreground modifier
;       Else
;         Clear the foreground modifier
;       Endif
;       Set thread's keyboard attribute (call _schSetThreadKbdLocus)
;       Return
;
;***************************************************************************

Procedure       _schInitTCB,,,,esp
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

ArgVar  ptcb,DWORD

        EnterProc
        mov     ecx,[ptcb]                      ; (ECX) -> New TCB
        mov     edx,[runner]                    ; (EDX) -> Runner's TCB
        .if <nonzero edx>
            mov     al,[edx].TCBPriClass        ; inherit class
            mov     [ecx].TCBPriClass,al
            mov     al,[edx].TCBPriLevel        ; inherit level
            mov     [ecx].TCBPriLevel,al
        .else                                   ; else initial thread...
            mov     [ecx].TCBPriClass,CLASS_REGULAR     ; regular class
            mov     [ecx].TCBPriLevel,0                 ; lowest level
        .endif

        xor     eax,eax
        mov     [ecx].TCBWakeFlags,al           ; No TCBWakeFlags
        mov     [ecx].TCBcWindowBoost,al        ; No WindowBoosts
        mov     [ecx].TCBSchFlags,al            ; No Sch Flags
        mov     [ecx].TCBPriorityMin,ax         ; No Minimum Priority
        mov     [ecx].TCBcBoostLock,eax         ; No kernel boosts

;       If dynamic variation is enabled, the current SGID is foreground,
;       and the process is not detached then set the foreground boost.

        mov     edx,[ecx].TCBpPTDA              ; (EDX) = pPTDA
        mov     al,[_FgndSGID]

        cmp     al,[edx].Cons_Loc.Scrn_Num      ; If not foreground,
        mov     al,0                            ; (assume not foreground)
        jnz     short sit1                      ; make not foreground

        cmp     BYTE PTR [edx].ptda_ProcType, PT_DETACHED ; If detached,
        je      short sit1                      ; make not foreground

        cmp     [SISData].SIS_Dynamic,0         ; If priority = absolute
        je      short sit1                      ; ignore boost

        mov     al,CLASSMOD_FOREGROUND
sit1:   mov     [ecx].TCBPriClassMod,al

;       Set the thread's keyboard locus.

        PushP   0                       ; _schSetThreadKbdLocus parms
        PushP   ecx                     ; pTCB
        PushP   edx                     ; pPTDA
        CCALL32 _schSetThreadKbdLocus,,,PushP
        LeaveProc
        RETC
EndProc _schInitTCB


BREAK   <_SCHSlotSetPriority>
;***********************************************************************
;
; Subroutine Name:  _SCHSlotSetPriority
;
; Descriptive Name:   Set Priority, given a slot number and priority
;
; Function:
;       This routine sets the priority of the specified thread to the
;       specified class and level.
;
; Notes:
;       If the thread's protected priority flag is clear, the priority
;       will be set.  However, if it is set, the priority will be set
;       ONLY if the protected priority flag (argument) is also set.
;
; Entry Point:  _SCHSlotSetPriority
;       Linkage:  C Calling convention
;
; Entry:
;       iSlot   - Thread slot to affect.  0 = current slot.
;       usPri   - New thread priority.   (new Class and Delta)
;
; Exit-Normal:
;       Resched = NZ (see notes)
;
; Exit-Error:
;       none
;
; Effects:
;       nothing
;
; Internal References:
;            Routines:
;       _SCHSetPriority
;
; External References:
;            Routines:
;       none
;
; Pseudo Code
;       Procedure SCHSlotSetPriority(Thread,NewClass,Delta)
;       Map Slot Zero.
;       Address the thread's TCB
;       If thread is in current process
;         Set the protected priority flag
;       Endif
;       If thread is in current process OR
;       If SCH_PROTECTED_PRI flag is clear
;         Call _SCHSetPriority
;       Return
;
;***********************************************************************

Procedure       _SCHSlotSetPriority,,,,esp
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

ArgVar  iSlot,DWORD
ArgVar  usPri,DWORD

        mov     eax,[iSlot]
        MapZeroSlot     ax,eax          ; (AX) = valid slot, from (AX)
        SlotToTCB       ax,eax          ; (EAX) -> TCB, from slot in (AX)

        mov     ecx,[eax].TCBpPTDA      ; (EAX) -> TCB, (ECX) -> PTDA
        cmp     ecx,[_pPTDACur]         ; Current process?
        jne     short sp1               ; no - see if protected

        cmp     BYTE PTR [ecx].ptda_ProcType,PT_REALMODE
        jne     short sp1               ; real mode target - don't protect

        or      [eax].TCBSchFlags,SCH_PROTECTED_PRI     ; protect priority
        jmp     short sp2               ; set priority

sp1:    test    [eax].TCBSchFlags,SCH_PROTECTED_PRI     ; protected priority ?
        jnz     short sp3               ; yes - Skip SetPri

sp2:    mov     edx,usPri
        CCALL32 _SCHSetPriority,<eax,edx>       ; Go ahead with SetPri
sp3:    xor     eax,eax
        RETC
EndProc _SCHSlotSetPriority


BREAK   <_SCHSetPriority>
;***********************************************************************
;
; Subroutine Name:  SCHSetPriority
;
; Descriptive Name:  C-callable Set Priority procedure
;
; Function:
;       This routine sets the priority of the specified thread to the
;       specified class and level even if we are calling from a different
;       process.
;
; Entry Point:  SCHSetPriority
;       Linkage:  C Calling convention
;
; Entry:
;       ptcb    - Pointer to TCB to set priority
;       ulpri   - New base scheduling priority
;
; Exit-Normal:
;       Resched = NZ (see notes)
;
; Exit-Error:
;       none
;
; Effects:
;       nothing
;
; Internal References:
;            Routines:
;
; External References:
;            Routines:
;       _TKUpdatePriority
;
; Pseudo Code
;       Procedure SCHSetPriority
;       If thread's Class is not no change
;         Set thread's Class to NewClass
;         Set thread's Level to zero
;       Endif
;       Set thread's Level to Level + Delta
;       If thread's Level is less than minimum
;         Set thread's Level to minimum
;       Endif
;       If thread's Level is greater than maximum
;         Set thread's Level to maximum
;       Endif
;       Calculate thread's Priority (call Calculate_Priority)
;       Check if thread should be running (call Resched?)
;       Reposition thread in ready list (call InsertList)
;       Return
;
;***********************************************************************

Procedure       _SCHSetPriority,,,,esp
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

ArgVar  ptcb,DWORD                      ; Thread's TCB pointer
ArgVar  ulpri,DWORD                     ; Thread'd new priority

        mov     ecx,[ptcb]              ; (EBX) -> TCB
        mov     edx,[ulpri]             ; (DX) = priority

        .if     <dh ne CLASS_NOCHANGE>
          mov     [ecx].TCBPriClass,dh ;set thread's class
          mov     [ecx].TCBPriLevel,0  ;set thread's level
        .endif

        add     dl,[ecx].TCBPriLevel   ;add delta to level
        .if     <dl lt LEVEL_MIN>       ;check for underflow
          mov     dl,LEVEL_MIN
        .endif

        .if     <dl gt LEVEL_MAX>       ;check for overflow
          mov     dl,LEVEL_MAX
        .endif
        mov     [ecx].TCBPriLevel,dl   ;set thread's level

        mov           eax,[ecx].TCBpPTDA
        .if           <<byte ptr [eax].ptda_ProcType> ne LIS_PT_REALMODE>
          or          [ecx].TCBForcedActions,TK_FF_TIBPRI   ;59463
        .endif

        CCALL32 _TKUpdatePriority,<ecx>
        RETC
EndProc _SCHSetPriority


BREAK   <_SCHSetState>
;***************************************************************************
;
; Subroutine Name:  _SCHSetState
;
; Descriptive Name:   Set the State of a thread
;
; Function:
;       This routine sets the state of the specified thread to the
;       specified state.
;
; Notes:
;       See _TKUpdateState.
;
; Entry Point:  SetState
;       Linkage:  C Calling Convention
;
; Entry:
;       ptcb    -> TCB that needs a state change
;       ulState = New state for the thread
;
; Exit-Normal:
;       nothing
;
; Exit-Error:
;       none
;
; Effects:
;       nothing
;
; Internal References:
;            Routines:
;       none
;
; External References:
;            Routines:
;       none
;
; Pseudo Code:
;       Set TCBState to desired state
;       call TKUpdateState to update queues
;
;***************************************************************************

;Procedure       _SCHSetState,,,,esp
;        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

;ArgVar  ptcb,DWORD                      ; TCB to modify
;ArgVar  ulState,DWORD                   ; New state

;        mov     ecx,[ptcb]
;        mov     eax,[ulState]
;        mov     [ecx].TCBState,al       ; Set desired state
;        CCALL32 _TKUpdateState,<ecx>    ; Update queues
;        RETC
;EndProc _SCHSetState

_TEXT   ENDS

                end
