Files
TempleOS-Rijn/TempleOS/Kernel/Scheduler.CPP
T
2018-09-08 21:45:37 +02:00

411 lines
8.4 KiB
C++

/*
See $LK,"TempleOS MultiTasking","FI:::/TempleOS/Doc/MultiTasking.TXT"$.
Tasks are ordered in a que based
on window front-to-back order with the
window mgr as the head, which
paints the wallpaper. The scheduler
goes in a circle, checking if a task is
ready and swapping-it-in if so.
Swapping just involves storing and
restoring registers -- no disk I/O for
virtual memory and no addr map
changes. Tasks can be switched in half
a microsecond.
The scheduler checks if a task is
waiting for a certain time or waiting
on a message and skips it if it's not
ready. A task runs until it
voluntarily yields ctrl with a call
to $LK,"Yield","MN:Yield"$ or gets preempted by the
timer interrupt. See $LK,"SYS_TIMER_HANDLER","MN:SYS_TIMER_HANDLER"$.
The timer freqency is $LK,"JIFFY_FREQ","MN:JIFFY_FREQ"$. Tasks
can ask not to be preempted with
$LK,"Preempt","MN:Preempt"$(ON/OFF). Tasks waiting on I/O
often loop, checking for a stat and
swapping-out. This does not really
degrade performance, though it pegs the
CPU Load.
The scheduler checks for a few critical
key-strokes such as <CTRL-ALT-X>, which
kills a task; <CTRL-ALT-DEL>, which reboots;
<CTRL-ALT-T>, which changes focus and <CTRL-ALT-C>,
which breaks execution of a program.
Each core has it's own circular task
que. For AP processors, they have a
"Seth" task which stays in a loop
waiting for jobs or requests to spawn
tasks. See $LK,"MPSethServerTask","MN:MPSethServerTask"$().
All cores have an idle task. If the
scheduler determined an IRQ opportunity
hasn't happened, the scheduler swaps in
the idle task(enabling IRQs).
$HL,1$*/
asm {
USE64
// ************************************
SYS_CONTEXT_SAVE::
//OUT: RSI=FS
PUSH RSI
PUSHFD
XOR RSI,RSI
MOV RSI,FS:CTask.addr[RSI]
POP U64 CTask.rflags[RSI]
POP U64 CTask.rsi[RSI]
MOV U64 CTask.rax[RSI],RAX
/*
We divert the stk to the Task memory
and push onto it and divert it back.
It's a little faster.
*/
MOV RAX,RSP
LEA RSP,U64 CTask.r15+8[RSI]
PUSH R15
PUSH R14
PUSH R13
PUSH R12
PUSH R11
PUSH R10
PUSH R9
PUSH R8
PUSH RDI
PUSH RBP
PUSH RBX
PUSH RDX
PUSH RCX
MOV RSP,RAX
RDTSC
SHL RDX,32
ADD RAX,RDX
SUB RAX,U64 CTask.time_slice_start[RSI]
ADD U64 CTask.total_time[RSI],RAX
MOV RAX,U64 CTask.fpu_mmx[RSI]
FXSAVE U64 [RAX]
MOV RDX,U64 CTask.bpt_lst[RSI]
@@05: OR RDX,RDX
JZ @@10
MOV RDI,U64 CBpt.addr[RDX]
MOV AL,U8 CBpt.val[RDX]
MOV U8 [RDI],AL
MOV RDX,U64 CBpt.next[RDX]
JMP @@05
@@10: LOCK
BTR U64 CTask.task_flags[RSI],TASKf_CURRENTLY_RUNNING
RET
// ************************************
SYS_CONTEXT_RESTORE::
XOR RAX,RAX
INC U64 GS:CCPU.swap_cnter[RAX]
MOV RSI,FS:CTask.addr[RAX]
BT U32 CTask.rflags[RSI],RFLAGf_INT
JNC @@05
LOCK //We will be enabling IRQ's
BTS U32 GS:CCPU.cpu_flags[RAX],CPUf_HAD_IRQ_OPPORTUNITY
@@05: LOCK
BTS U64 CTask.task_flags[RSI],TASKf_CURRENTLY_RUNNING
BT U64 CTask.task_flags[RSI],TASKf_DISABLE_BPTS
JC @@15
MOV RDX,U64 CTask.bpt_lst[RSI]
@@10: OR RDX,RDX
JZ @@15
MOV RDI,U64 CBpt.addr[RDX]
MOV U8 [RDI],OC_BPT
MOV RDX,U64 CBpt.next[RDX]
JMP @@10
@@15: INC U64 CTask.swap_cnter[RSI]
MOV RAX,U64 CTask.fpu_mmx[RSI]
FXRSTOR U64 [RAX]
RDTSC
SHL RDX,32
ADD RAX,RDX
MOV U64 CTask.time_slice_start[RSI],RAX
MOV RAX,RSP
LEA RSP,U64 CTask.rcx[RSI]
POP RCX
POP RDX
POP RBX
POP RBP
POP RDI
POP R8
POP R9
POP R10
POP R11
POP R12
POP R13
POP R14
POP R15
MOV RSP,RAX
MOV RAX,U64 CTask.rax[RSI]
PUSH GDT_ZERO_DS_SEL
PUSH U64 CTask.rsp[RSI]
PUSH U64 CTask.rflags[RSI]
PUSH GDT_CS64_SEL
PUSH U64 CTask.rip[RSI]
MOV RSI,U64 CTask.rsi[RSI]
IRET
// ************************************
END_RSI_TASK:
MOV RAX,RSI
CALL SET_FS_BASE
SYS_TASK_END_NOW::
CALL &TaskEnd
MOV RSI,RAX
CALL SET_FS_BASE
JMP I32 RESTORE_RSI_TASK_WITH_KEY_CHECK
RESTORE_RSI_NO_PREEMPT_TASK:
BT U32 [SYS_CTRL_ALT_FLAGS],SYSf_CTRL_ALT_DEL
JC I32 HANDLE_SWAP_KEY_EVENT
XOR RAX,RAX
CMP U64 GS:CCPU.idle_task[RAX],RSI //IDLE TASK HAS PREEMPT-OFF
JNE RESTORE_RSI_TASK_NO_KEY_CHECK
JMP I32 SWAP_IN_SETH_TASK
SYS_RET::
RET
SYS_YIELD::
PUSHFD
PUSH RAX
MOV RAX,SYS_SEMAS
BT U32 SYS_SEMA_SINGLE_USER*SEMA_STRUCT_SIZE[RAX],0
JC @@05
POP RAX
CLI
PUSH RSI
XOR RSI,RSI
MOV RSI,FS:CTask.addr[RSI]
CMP U64 CTask.next_task[RSI],RSI
JNE @@15
PUSH RAX
MOV RAX,U64 [SYS_JIFFIES]
CMP U64 CTask.wake_jiffy[RSI],RAX
JA @@10
POP RAX
POP RSI
POPFD
RET
@@05: POP RAX
POPFD
RET
@@10: POP RAX
@@15: POP RSI
CALL SYS_CONTEXT_SAVE
MOV EBX,U32 SYS_RET
MOV U64 CTask.rip[RSI],RBX
POP U64 CTask.rflags[RSI]
MOV U64 CTask.rsp[RSI],RSP
RESTORE_NEXT_RSI_TASK:
MOV RSI,U64 CTask.next_task[RSI]
RESTORE_RSI_TASK_WITH_KEY_CHECK:
TEST U64 [SYS_CTRL_ALT_FLAGS],1<<SYSf_CTRL_ALT_DEL|
1<<SYSf_CTRL_ALT_TAB|1<<SYSf_CTRL_ALT_X|1<<SYSf_CTRL_ALT_C
JNZ HANDLE_SWAP_KEY_EVENT
RESTORE_RSI_TASK_NO_KEY_CHECK:
@@20: BT U64 CTask.task_flags[RSI],TASKf_KILL_TASK
JC I32 END_RSI_TASK
TEST U64 CTask.task_flags[RSI],1<<TASKf_AWAITING_MSG|1<<TASKf_SUSPENDED
JNZ @@25
MOV RAX,U64 [SYS_JIFFIES]
CMP U64 CTask.wake_jiffy[RSI],RAX
JA @@25
CMP U32 CTask.task_in_que_signature[RSI],TASK_IN_QUE_SIGNATURE_VAL
JNE SWAP_IN_SETH_TASK
MOV RAX,RSI
CALL SET_FS_BASE
JMP I32 SYS_CONTEXT_RESTORE
@@25: MOV RSI,U64 CTask.next_task[RSI]
XOR RAX,RAX
CMP U64 GS:CCPU.seth_task[RAX],RSI
JNE @@20
LOCK
BTR U32 GS:CCPU.cpu_flags[RAX],CPUf_HAD_IRQ_OPPORTUNITY
JC @@20
MOV RAX,U64 GS:CCPU.idle_task[RAX]
MOV RSP,U64 CTask.stk[RAX]
ADD RSP,DFT_STK+CTaskStk.stk_base //Rst to top
CALL SET_FS_BASE
STI
HLT
SYS_IDLE_PT::
CLI
SWAP_IN_SETH_TASK:
XOR RAX,RAX
MOV RSI,GS:CCPU.seth_task[RAX]
JMP I32 RESTORE_RSI_TASK_WITH_KEY_CHECK
HANDLE_SWAP_KEY_EVENT:
MOV RAX,RSI
CALL SET_FS_BASE
XOR RAX,RAX
MOV RAX,GS:CCPU.num[RAX]
OR RAX,RAX
JNZ I32 RESTORE_RSI_TASK_NO_KEY_CHECK
MOV EAX,U32 SYS_CTRL_ALT_FLAGS
LOCK
BTR U32 [RAX],SYSf_CTRL_ALT_DEL
JC I32 &Reboot
LOCK
BTR U32 [RAX],SYSf_CTRL_ALT_TAB
JNC @@05
PUSH RSI
MOV RSI,U64 [SYS_FOCUS_TASK]
MOV U64 [SYS_FOCUS_TASK],0
CALL FOCUS_RSI_NEXT_USER
POP RSI
JMP I32 RESTORE_FS_TASK_WITH_KEY_CHECK
@@05: LOCK
BTR U32 [RAX],SYSf_CTRL_ALT_X
JC END_FOCUS_USER
LOCK
BTR U32 [RAX],SYSf_CTRL_ALT_C
JNC I32 RESTORE_RSI_TASK_WITH_KEY_CHECK
BREAK_FOCUS_USER:
MOV RSI,U64 [SYS_FOCUS_TASK]
OR RSI,RSI
JZ SWAP_IN_SETH_TASK
BT U64 CTask.task_flags[RSI],TASKf_USER
JNC I32 RESTORE_RSI_TASK_NO_KEY_CHECK
LOCK
BTR U64 CTask.task_flags[RSI],TASKf_BREAK_LOCKED
JNC @@10
LOCK
BTS U64 CTask.task_flags[RSI],TASKf_PENDING_BREAK
JMP I32 RESTORE_RSI_TASK_NO_KEY_CHECK
@@10: MOV RAX,&SysThrowBreak
MOV U64 CTask.rip[RSI],RAX
BT U64 CTask.task_flags[RSI],TASKf_BREAK_TO_SHIFT_ESC
JC @@15
//We do these two now, in case interrupt happens
MOV U64 CTask.wake_jiffy[RSI],0
PUSH RSI
CALL &TaskRstAwaitingMsg
@@15: JMP I32 RESTORE_RSI_TASK_NO_KEY_CHECK
END_FOCUS_USER:
MOV RSI,U64 [SYS_FOCUS_TASK]
MOV U64 [SYS_FOCUS_TASK],0
PUSH RSI
CALL FOCUS_RSI_NEXT_USER
POP RSI
OR RSI,RSI
JZ I32 SWAP_IN_SETH_TASK
MOV RAX,RSI
CALL SET_FS_BASE
BT U64 CTask.task_flags[RSI],TASKf_USER
JNC I32 RESTORE_RSI_TASK_WITH_KEY_CHECK
LOCK
BTS U64 CTask.task_flags[RSI],TASKf_KILL_TASK
JMP I32 END_RSI_TASK
RESTORE_FS_TASK_WITH_KEY_CHECK:
XOR RSI,RSI
MOV RSI,FS:CTask.addr[RSI]
JMP I32 RESTORE_RSI_TASK_WITH_KEY_CHECK
FOCUS_RSI_NEXT_USER:
OR RSI,RSI
JNZ @@20
MOV ESI,U32 SYS_ADAM_TASK
@@20: MOV RDI,RSI
MOV RSI,U64 CTask.next_task[RSI]
@@25: BT U64 CTask.task_flags[ESI],TASKf_USER
JNC @@30
CMP U64 [SYS_FOCUS_TASK],RSI
JE @@30
MOV U64 [SYS_FOCUS_TASK],RSI
MOV RAX,U64 [SYS_EXTERN_TABLE]
MOV RAX,U64 EXT_WIN_TO_TOP*8[RAX]
OR RAX,RAX
JZ @@35
PUSH TRUE
PUSH RSI
CALL RAX
JMP @@35
@@30: MOV RSI,U64 CTask.next_task[RSI]
CMP RSI,RDI
JNE @@25
MOV U64 [SYS_FOCUS_TASK],0
@@35: RET
}
_extern SYS_CONTEXT_RESTORE U0 TaskContextRestore();
_extern SYS_YIELD U0 Yield();
_extern SYS_TASK_END_NOW U0 TaskEndNow();
U0 TaskQueIns(CTask *task,CTask *pred=NULL)
{
CTask *last;
PushFD;
Cli;
if (!pred) pred=Fs;
last=pred->last_task;
last->next_task=pred->last_task=task;
task->last_task=last;
task->next_task=pred;
task->task_in_que_signature=TASK_IN_QUE_SIGNATURE_VAL;
PopFD;
}
U0 TaskQueRem(CTask *task)
{
CTask *next,*last;
PushFD;
Cli;
task->task_in_que_signature=0;
next=task->next_task;
last=task->last_task;
last->next_task=next;
next->last_task=last;
PopFD;
}
U0 TaskQueInsChild(CTask *task)
{
CTask *last,*pred;
PushFD;
Cli;
pred=task->parent_task->last_child_task;
last=pred->last_sibling_task;
last->next_sibling_task=pred->last_sibling_task=task;
task->last_sibling_task=last;
task->next_sibling_task=pred;
PopFD;
}