U0 Exit() {//Terminate own task. if (Fs == sys_focus_task && IsDebugMode) { LBts(&Fs->task_flags, TASKf_KILL_AFTER_DEBUG); G; } else { if (!Gs->num && !IsDebugMode) SingleUser(OFF); Fs->rflags = RFlagsGet; Fs->rsp = RSPGet; Fs->rbp = RBPGet; Fs->rip = $; CLI LBts(&Fs->task_flags, TASKf_KILL_TASK); TaskEndNow; } } Bool TaskValidate(CTask *task) {//return TRUE if task looks valid. if (!(0 < task <= I32_MAX) || task->addr != task || task->task_signature != TASK_SIGNATURE_VAL) return FALSE; else return TRUE; } I64 BirthWait(CTask **_task, I64 task_num=-1) {//Wait for task valid and not task_num. while (!TaskValidate(*_task) || (*_task)->task_num == task_num) Yield; return (*_task)->task_num; } U0 DeathWait(CTask **_task, Bool send_exit=FALSE) {//Wait for task death. if (send_exit && TaskValidate(*_task)) { TaskWait(*_task, TRUE); XTalk(*_task, "Exit;\n"); } while (TaskValidate(*_task)) Yield; } Bool Kill(CTask *task, Bool wait=TRUE, Bool just_break=FALSE) {//Terminate other task. I64 i; if (TaskValidate(task)) { if (just_break) { if (task != Fs) Break; else {//TODO wait sys_focus_task = task; LBts(sys_ctrl_alt_flags, CTRL_ALT_C); return TRUE; } } else { if (task != sys_winmgr_task) { for (i = 0; i < mp_count; i++) if (task == cpu_structs[i].executive_task) return FALSE; LBts(&task->task_flags, TASKf_KILL_TASK); if (wait) { do Yield; while (TaskValidate(task) && Bt(&task->task_flags, TASKf_KILL_TASK)); } return TRUE; } } } return FALSE; } Bool Suspend(CTask *task=NULL, Bool state=TRUE) {//Tell scheduler to skip task. Bool res; if (!task) task = Fs; PUSHFD CLI if (TaskValidate(task)) res = LBEqual(&task->task_flags, TASKf_SUSPENDED, state); else res = FALSE; POPFD return res; } Bool IsSuspended(CTask *task=NULL) {//You might use this in a DrawIt() or Animatetask(). if (!task) task = Fs; if (TaskValidate(task)) return Bt(&task->task_flags, TASKf_SUSPENDED); else return FALSE; } CTaskStack *TaskStackNew(I64 stack_size, CTask *task) { CTaskStack *tmps = MAlloc(stack_size + offset(CTaskStack.stack_base), task); tmps->next_stack = NULL; tmps->stack_ptr = NULL; tmps->stack_size = MSize(tmps) - offset(CTaskStack.stack_base); return tmps; } #exe {Option(OPTf_NO_REG_VAR, ON);}; argpop I64 CallStackGrow(I64 stack_size_threshold, I64 stack_size, /*argpop*/I64 (*fp_addr)(...), ...) {//Grow stack in call with any fixed num of args. //See ::/Demo/StackGrow.ZC. CTaskStack *tmps, *tmps2, **_stack; I64 res, *rsp, *rsp2, *old_stack; if (UnusedStack >= stack_size_threshold) { asm { LEAVE POP RAX //return addr ADD RSP, 16 //pop threshold, stack_size POP RBX // *f ADD RSP, 8 //pop ARGC PUSH RAX JMP RBX //CALL fp_addr() }; } else { tmps2 = TaskStackNew(stack_size, Fs); tmps2->next_stack = tmps = Fs->stack; rsp2 = (&tmps2->stack_base)(U8 *) + tmps2->stack_size; old_stack = rsp = &argv[argc]; while (argc-- > 0) *--rsp2 = *--rsp; _stack = &Fs->stack; tmps->stack_ptr = rsp = RSPGet; asm { IMPORT _FREE; //We are in a function, not at global scope. //The compiler treats these in isolation. PUSHFD POP RDX //flags CLI MOV RBX, U64 &tmps2[RBP] MOV RAX, &_stack[RBP] MOV U64 [RAX], RBX //Fs->stack=tmps2 MOV RSP, U64 &rsp2[RBP] PUSH RDX POPFD CALL U64 &fp_addr[RBP] MOV U64 &res[RBP], RAX PUSHFD POP RDX //flags CLI MOV RBX, U64 &tmps[RBP] MOV RAX, &_stack[RBP] MOV U64 [RAX], RBX //Fs->stack=tmps MOV RSP, U64 &rsp[RBP] PUSH RDX POPFD PUSH U64 &tmps2[RBP] CALL _FREE MOV RDX, U64 &old_stack[RBP] MOV RBX, U64 8[RBP] MOV RAX, U64 &res[RBP] MOV RBP, U64 [RBP] MOV RSP, RDX JMP RBX //return }; } return 0; //dummy to get rid of warning } ; #exe {Option(OPTf_NO_REG_VAR, OFF);}; I64 TaskInit(CTask *task, I64 stack_size) {//Returns Fs of task CTaskStack *tmps; QueueInit(&task->code_heap->next_mem_blk); task->code_heap->last_mergable = NULL; if (task->code_heap != task->data_heap) { QueueInit(&task->data_heap->next_mem_blk); task->data_heap->last_mergable = NULL; } task->addr = task->next_task = task->last_task = task->next_input_filter_task = task->last_input_filter_task = task; task->task_num = sys_num_spawned_tasks++; task->rflags = RFLAGG_NORMAL; task->win_inhibit = WIG_TASK_DEFAULT; task->next_child_task = task->last_child_task = (&task->next_child_task)(U8 *) - offset(CTask.next_sibling_task); JobCtrlInit(&task->server_ctrl); QueueInit(&task->next_cc); QueueInit(&task->next_except); QueueInit(&task->next_ctrl); QueueInit(&task->next_ode); task->fpu_mmx = MAllocAligned(sizeof(CFPU), 0x10, task); MemCopy(task->fpu_mmx, SYS_FIXED_AREA + offset(CSysFixedArea.init_fpu_mmx), sizeof(CFPU)); task->hash_table = HashTableNew(TASK_HASH_TABLE_SIZE, task); if (!stack_size) stack_size = MEM_DEFAULT_STACK; task->stack = tmps = TaskStackNew(stack_size, task); task->rsp = (&tmps->stack_base)(U8 *) + tmps->stack_size; task->text_attr = WHITE << 4 + BLUE; task->border_src = BDS_CONST; task->border_attr = DriveTextAttrGet(':'); task->title_src = TTS_CONST; task->win_left = 1; task->win_right = text.cols - 2; task->win_top = text.rows / 8 + 3; task->win_bottom = text.rows - 2; if (blkdev.home_dir) {//Beware System TaskInit. Maybe ok until DiskChange(). task->cur_dv = blkdev.let_to_drive[*blkdev.home_dir - 'A']; task->cur_dir = StrNew(blkdev.home_dir + 2, task); } else task->cur_dir = StrNew("/Home", task); Seed(, task); return task; } CTask *Spawn(U0 (*fp_start_addr)(U8 *data), U8 *data=NULL, U8 *task_name=NULL, I64 target_cpu=-1, // -1 for current CPU. See multi-core. CTask *parent=NULL, // NULL means sys_task I64 stack_size=0, // 0=default I64 flags=1 << JOBf_ADD_TO_QUE) {//Create task on core running at address. //Alloc CTask structure from code heap so address will be short. //Could be alloced off of data heap. CTask *task; if (target_cpu >= 0) return SpawnQueue(fp_start_addr, data, task_name, target_cpu, parent, stack_size, flags); task = CAlloc(sizeof(CTask), sys_task->code_heap); task->task_signature = TASK_SIGNATURE_VAL; if (!task_name) task_name = "Unnamed Task"; if (!parent) parent = Gs->executive_task; task->parent_task = parent; task->gs = parent->gs; if (sys_code_bp) task->code_heap = HeapCtrlInit(, task, sys_code_bp); if (sys_data_bp) task->data_heap = HeapCtrlInit(, task, sys_data_bp); else task->data_heap = task->code_heap; TaskInit(task, stack_size); task->rip = fp_start_addr; task->rsp(U8 *) -= 8; *task->rsp = data; task->rsp(U8 *) -= 8; *task->rsp = &Exit; task->hash_table->next = parent->hash_table; MemCopy(task->task_name, task_name, TASK_NAME_LEN); StrCopy(task->task_title, task->task_name); task->title_src = TTS_TASK_NAME; PUSHFD CLI if (Bt(&flags, JOBf_ADD_TO_QUE)) { TaskQueueInsChild(task); TaskQueueIns(task); } POPFD return task; } U0 TaskDerivedValsUpdate(CTask *task=NULL, Bool update_z_buf=TRUE) {//Those things calculated from other variables. if (!task) task = Fs; PUSHFD CLI while (LBts(&task->task_flags, TASKf_TASK_LOCK)) PAUSE WinDerivedValsUpdate(task); if (fp_update_ctrls) (*fp_update_ctrls)(task); if (update_z_buf && Bt(&task->display_flags, DISPLAYf_SHOW)) LBts(&sys_semas[SEMA_UPDATE_WIN_Z_BUF], 0); LBtr(&task->task_flags, TASKf_TASK_LOCK); POPFD } I64 ExeCmdLine(CCompCtrl *cc) {//Terminal JIT-compile-and-execute loop for CCompCtrl. I64 res = 0, type, old_title_src = Fs->title_src; U8 *ptr, *ptr2, *ptr3, *machine_code, *old_task_title = StrNew(Fs->task_title); F64 t0; CDocEntry *doc_e; CDoc *doc; if (Fs->title_src != TTS_LOCKED_CONST) Fs->title_src = TTS_CUR_LEX; while (cc->token && (cc->token != '}' || !(cc->flags & CCF_EXE_BLK)) ) { if (Fs->title_src == TTS_CUR_LEX) { ptr2 = &Fs->task_title; ptr3 = ptr2 + STR_LEN - 1; if (cc->lex_include_stack->flags & LFSF_DOC) { doc_e = cc->lex_include_stack->cur_entry; doc = cc->lex_include_stack->doc; while (doc_e != doc && ptr2 < ptr3) { switch (doc_e->type_u8) { case DOCT_TEXT: ptr = doc_e->tag; while (*ptr && ptr2 < ptr3) *ptr2++ = *ptr++; break; case DOCT_TAB: case DOCT_NEW_LINE: *ptr2++ = '.'; break; } doc_e = doc_e->next; } if (ptr2 < ptr3) *ptr2 = 0; } else if ((ptr = cc->lex_include_stack->line_start) && *ptr) MemCopy(ptr2, ptr, STR_LEN - 1); } cc->flags &= ~CCF_HAS_MISC_DATA; machine_code = LexStatement2Bin(cc, &type); if (machine_code != INVALID_PTR) { if (!(cc->flags & CCF_JUST_LOAD)) { t0 = tS; res = Call(machine_code); Fs->answer = res; Fs->answer_type = type; Fs->answer_time = tS - t0; Fs->new_answer = TRUE; cc->prompt_line = 0; } if (!(cc->flags & CCF_HAS_MISC_DATA)) Free(machine_code); } } if (Fs->title_src != TTS_LOCKED_CONST) { Fs->title_src = old_title_src; StrCopy(Fs->task_title, old_task_title); } Free(old_task_title); if (cc->flags & CCF_JUST_LOAD) { if (cc->error_count) return FALSE; else return TRUE; } else return res; } U0 ServerTaskCont() {//Act as server task in a loop handling commands. I64 old_flags = RFlagsGet; FlushMessages; while (TRUE) { CLI if (JobsHandler(old_flags) && Fs->title_src == TTS_TASK_NAME) MemCopy(Fs->task_title, Fs->task_name, TASK_NAME_LEN); FlushMessages; LBts(&Fs->task_flags, TASKf_IDLE); LBts(&Fs->task_flags, TASKf_AWAITING_MESSAGE); Yield; RFlagsSet(old_flags); } } U0 UserTaskCont() {//Terminal key-input-execute loop. CCompCtrl *cc; CDoc *doc; Bool cont = TRUE; do { cc = CompCtrlNew(, CCF_CMD_LINE | CCF_PROMPT | CCF_QUESTION_HELP); QueueInsert(cc, Fs->last_cc); try { Lex(cc); ExeCmdLine(cc); cont = Bt(&cc->flags, CCf_PROMPT); QueueRemove(cc); CompCtrlDel(cc); } catch { if ((doc = Fs->put_doc) && doc->doc_signature == DOC_SIGNATURE_VAL) DocUnlock(doc); PutExcept; } } while (cont); } U0 ServerCmdLine(I64 dummy=0) { no_warn dummy; Fs->win_inhibit = WIG_USER_TASK_DEFAULT; CallExtStr("ServerStartUp"); ServerTaskCont; } U0 UserCmdLine(I64 dummy=0) {//A user task ends-up calling this. no_warn dummy; Fs->win_inhibit = WIG_USER_TASK_DEFAULT; CallExtStr("UserStartUp"); if (!LBts(&Fs->display_flags, DISPLAYf_SHOW)) Debug; UserTaskCont; } CTask *User(U8 *format=NULL, ...) {//Create user term task. U8 *st; CTask *task = Spawn(&UserCmdLine,, "Terminal"); TaskWait(task); if (format) { st = StrPrintJoin(NULL, format, argc, argv); XTalk(task, st); Free(st); } return task; } U0 TaskDel(CTask *task) {//We delay freeing in case lingering pointer to reincarnated. HeapCtrlDel(task->code_heap); if (task->data_heap != task->code_heap) HeapCtrlDel(task->data_heap); Free(task); } I64 TaskEnd() {//Called with irqs off. CTask *task = Fs, *tmpt, *tmpt1; if (task == sys_task_being_screen_updated) { LBts(&task->task_flags, TASKf_KILL_TASK); return task->next_task; } if (task->task_end_cb) { task->wake_jiffy = 0; LBtr(&task->task_flags, TASKf_KILL_TASK); TaskResetAwaitingMessage(task); Suspend(task, FALSE); task->rip = task->task_end_cb; task->task_end_cb = NULL; return task; } if (task->parent_task && task->parent_task->popup_task == task) { task->parent_task->popup_task = NULL; Kill(task->parent_task, FALSE); return task->parent_task; } DrivesRelease; BlkDevsRelease; tmpt1 = (&task->next_child_task)(U8 *) - offset(CTask.next_sibling_task); tmpt = tmpt1->next_sibling_task; if (tmpt != tmpt1) { do { LBts(&tmpt->task_flags, TASKf_KILL_TASK); tmpt = tmpt->next_sibling_task; } while (tmpt != tmpt1); return task->next_task; } if (LBtr(&task->display_flags, DISPLAYf_SHOW)) LBts(&sys_semas[SEMA_UPDATE_WIN_Z_BUF], 0); while (LBts(&task->task_flags, TASKf_TASK_LOCK)) PAUSE while (LBts(&task->server_ctrl.flags, JOBCf_LOCKED)) PAUSE JobQueueDel(&task->server_ctrl.next_waiting); JobQueueDel(&task->server_ctrl.next_done); if (IsRaw) LFBFlush; if (sys_focus_task == task) { if (!Gs->num) SingleUser(OFF); sys_focus_task = NULL; if (fp_set_std_palette) (*fp_set_std_palette)(); } //QueueRemove task->task_signature(I64) = 0; tmpt =task->next_input_filter_task; tmpt1 =task->last_input_filter_task; tmpt1->next_input_filter_task = tmpt; tmpt->last_input_filter_task = tmpt1; tmpt = task->next_sibling_task; tmpt1 = task->last_sibling_task; tmpt1->next_sibling_task = tmpt; tmpt->last_sibling_task = tmpt1; tmpt = task->next_task; //save to return TaskQueueRemove(task); LBtr(&task->server_ctrl.flags, JOBCf_LOCKED); LBtr(&task->task_flags, TASKf_TASK_LOCK); task->wake_jiffy = counts.jiffies + DYING_JIFFIES; while (LBts(&Gs->cpu_flags, CPUf_DYING_TASK_QUE)) PAUSE QueueInsert(task, Gs->last_dying); LBtr(&Gs->cpu_flags, CPUf_DYING_TASK_QUE); return tmpt; } U0 TaskKillDying() {//Delay freeing to prevent asking for trouble with quick reincarnations. //What if the user is doing this: DoTreeCheckers. CTaskDying *task, *task1; if (Gs->kill_jiffy < counts.jiffies) {//Avoid doing as many lock operations. while (LBts(&Gs->cpu_flags, CPUf_DYING_TASK_QUE)) PAUSE task = Gs->next_dying; while (task != &Gs->next_dying && task->wake_jiffy < counts.jiffies) { task1 = task->next; QueueRemove(task); TaskDel(task); task = task1; } LBtr(&Gs->cpu_flags, CPUf_DYING_TASK_QUE); Gs->kill_jiffy = counts.jiffies + DYING_JIFFIES; } }