U0 JobDel(CJob *tmpc)
{//Free one cmd node.
        Free(tmpc->aux_str);
        Free(tmpc);
}

U0 JobQueueDel(CJob *head)
{
        CJob *tmpc = head->next, *tmpc1;

        while (tmpc != head)
        {
                tmpc1 = tmpc->next;
                QueueRemove(tmpc);
                JobDel(tmpc);
                tmpc = tmpc1;
        }
}

U0 JobCtrlInit(CJobCtrl *ctrl)
{
        QueueInit(&ctrl->next_waiting);
        QueueInit(&ctrl->next_done);
        ctrl->flags = 0;
}

U0 TaskResetAwaitingMessage(CTask *task=NULL)
{//Pop-ups get parent messages so wake-up our pop-ups if we got a message.
        if (!task)
                task = Fs;

        PUSHFD
        CLI
        do
        {
                if (TaskValidate(task))
                        LBtr(&task->task_flags, TASKf_AWAITING_MESSAGE);
                else
                        break;
        }
        while (task = task->popup_task);
        POPFD
}

CJob *TaskExe(CTask *server, CTask *master, U8 *data, I64 flags)
{//Queueues a request to compile and execute src code text.
        CJob *res;

        if (!data || !TaskValidate(server) || master && !TaskValidate(master) ||
                        server->popup_task && !Bt(&server->task_flags, TASKf_FILTER_INPUT))
                return NULL;

        res = ZCAlloc(sizeof(CJob));
        res->master_task        = master;
        res->job_code           = JOBT_EXE_STR;
        res->flags                      = flags;
        res->aux_str            = ZStrNew(data);
        res->ctrl                       = &server->server_ctrl;

        PUSHFD
        CLI
        while (LBts(&server->server_ctrl.flags, JOBCf_LOCKED))
                PAUSE
        if (!TaskValidate(server))
        {
                LBtr(&server->server_ctrl.flags, JOBCf_LOCKED);
                POPFD
                JobDel(res);
                return NULL;
        }
        else
        {
                LBtr(&server->task_flags, TASKf_IDLE);
                TaskResetAwaitingMessage(server);
                QueueInsert(res, server->server_ctrl.last_waiting);
                LBtr(&server->server_ctrl.flags, JOBCf_LOCKED);
                if (Bt(&flags, JOBf_WAKE_MASTER))
                {
                        Suspend(master);
                        Yield;
                }
        }
        POPFD

        return res;
}

CJob *TaskText(CTask *server, CTask *master, U8 *data, I64 flags)
{//Post StdIn text to servant task. Tell who the master task is.
        CJob  *res;
        CTask *task;

        if (!data || !TaskValidate(server) || master && !TaskValidate(master) ||
                        server->popup_task && !Bt(&server->task_flags, TASKf_FILTER_INPUT))
                return NULL;

        res = ZCAlloc(sizeof(CJob));
        res->master_task        = master; //in case somebody cares
        res->job_code           = JOBT_TEXT_INPUT;
        res->flags                      = flags;
        res->aux_str            = ZStrNew(data);

        PUSHFD
        task = server->last_input_filter_task;
        if (Bt(&flags, JOBf_HIGHEST_PRIORITY) || task == server)
        {
                if (task != server)
                        TaskWait(server);
                task = Spawn(&InputFilterTask, NULL, "Input Filter",, server);
                CLI
                task->next_input_filter_task = server->next_input_filter_task;
                task->last_input_filter_task = server;
                server->next_input_filter_task = task;
                task->next_input_filter_task->last_input_filter_task = task;
        }
        else
        {
                CLI
                task = server->next_input_filter_task;
        }
        res->ctrl = &task->server_ctrl;

        while (LBts(&task->server_ctrl.flags, JOBCf_LOCKED))
                PAUSE

        if (!TaskValidate(task))
        {
                JobDel(res);
                res = NULL;
        }
        else
        {
                LBtr(&task->task_flags, TASKf_IDLE);
                TaskResetAwaitingMessage(task);
                QueueInsert(res, task->server_ctrl.last_waiting);
                LBtr(&task->server_ctrl.flags, JOBCf_LOCKED);
        }
        POPFD

        return res;
}

CJob *TaskMessage(CTask *_server, CTask *master, I64 message_code, I64 arg1, I64 arg2, I64 flags)
{//Post message to servant task.        Tell who the master task is.
//See flags and message_code.
        CJob  *tmpc1, *tmpc;
        CTask *server = _server;

        if (!TaskValidate(server) || master && !TaskValidate(master)||
                        server->popup_task && !Bt(&server->task_flags, TASKf_FILTER_INPUT))
                return NULL;

        tmpc = ZCAlloc(sizeof(CJob));
        tmpc->master_task       = master;
        tmpc->job_code          = JOBT_MESSAGE;
        tmpc->message_code      = AbsI64(message_code); //negative means do a down and up
        tmpc->aux1                      = arg1;
        tmpc->aux2                      = arg2;
        tmpc->flags                     = flags;

        PUSHFD
        if (Bt(&sys_semas[SEMA_RECORD_MACRO], 0) && server != sys_macro_task && message_code == MESSAGE_KEY_DOWN)
        {
                tmpc1 = ZMAllocIdent(tmpc);
                CLI
                QueueInsert(tmpc1, sys_macro_head.last);
        }
        CLI

        while (Bt(&server->task_flags, TASKf_FILTER_INPUT) && !Bt(&flags, JOBf_DONT_FILTER))
                server = server->next_input_filter_task;

        tmpc->ctrl = &server->server_ctrl;

        while (LBts(&server->server_ctrl.flags, JOBCf_LOCKED))
                PAUSE

        if (!TaskValidate(server))
        {
                JobDel(tmpc);
                tmpc = NULL;
        }
        else
        {
                LBtr(&server->task_flags, TASKf_IDLE);
                TaskResetAwaitingMessage(server);
                QueueInsert(tmpc, server->server_ctrl.last_waiting);
                LBtr(&server->server_ctrl.flags, JOBCf_LOCKED);
        }
        POPFD

        if (message_code < 0) //Down-Up
                TaskMessage(_server, master, -message_code + 1, arg1, arg2, flags);

        return tmpc;
}

Bool JobResScan(CJob *request=NULL, I64 *_res=NULL)
{//Check request complete, return with or without.
        CJobCtrl        *ctrl;
        CJob            *tmpc, *tmpc1;

        if (!request || Bt(&request->flags, JOBf_DONE))
        {
                if (!request || request->master_task)
                        ctrl = &Fs->server_ctrl;
                else
                        ctrl = request->ctrl;

                PUSHFD
                CLI

                while (LBts(&ctrl->flags, JOBCf_LOCKED))
                        PAUSE

                tmpc1 = &ctrl->next_done;
                tmpc = tmpc1->next;

                while (tmpc != tmpc1)
                {
                        if (!request || request == tmpc)
                        {
                                QueueRemove(tmpc);
                                LBtr(&ctrl->flags, JOBCf_LOCKED);
                                POPFD
                                if (_res)
                                        *_res = tmpc->res;
                                JobDel(tmpc);
                                return TRUE;
                        }
                        tmpc = tmpc->next;
                }
                LBtr(&ctrl->flags, JOBCf_LOCKED);
                POPFD
        }
        if (_res)
                *_res = 0;

        return FALSE;
}

I64 JobResGet(CJob *request=NULL)
{//See ::/Demo/MultiCore/Lock.CC
        I64   res;
        CJob *tmpc1;

        if (!request)
        {
                tmpc1 = &Fs->server_ctrl.next_done;
                while (tmpc1 == tmpc1->next)
                {
                        LBts(&Fs->task_flags, TASKf_IDLE);
                        Yield;
                }
        }
        else
        {
                while (!Bt(&request->flags, JOBf_DONE)) {
                        LBts(&Fs->task_flags, TASKf_IDLE);
                        Yield;
                }
        }
        LBtr(&Fs->task_flags, TASKf_IDLE);
//Could get taken by someone else.
        JobResScan(request, &res);

        return res;
}

U0 TaskWait(CTask *task=NULL, Bool cmd_line_prompt=FALSE)
{//Wait for idle.
        CTask *task1;
        CJob  *tmpc1;

        if (!task)
                task = Fs;
        if (TaskValidate(task))
        {
                PUSHFD
                CLI
                while (TRUE)
                {
                        task1 = task->last_input_filter_task;
                        tmpc1 = &task1->server_ctrl.next_waiting;
                        if (task1 == Fs || !TaskValidate(task1) ||
                                        tmpc1 == tmpc1->next && Bt(&task1->task_flags, TASKf_IDLE) &&
                                        (!cmd_line_prompt || Bt(&task1->task_flags, TASKf_CMD_LINE_PROMPT)))
                                break;
                        Yield;
                }
                POPFD
        }
}

U0 MessagePost(CTask *task, I64 message_code, I64 arg1, I64 arg2, I64 flags=0)
{//Post message to a task and return immediately.  See message_code.
        if (TaskValidate(task))
        {
                if (Bt(&task->task_flags, TASKf_INPUT_FILTER_TASK))
                        TaskMessage(task->last_input_filter_task, NULL, message_code, arg1, arg2, flags | 1 << JOBf_DONT_FILTER);
                else
                        TaskMessage(task, NULL, message_code, arg1, arg2, flags);
        }
}

U0 MessagePostWait(CTask *task, I64 message_code, I64 arg1, I64 arg2, I64 flags=0)
{//Post message to a task and wait until task is idle.See message_code.
        MessagePost(task, message_code, arg1, arg2, flags);
        TaskWait(task);
}

U0 Message(I64 message_code, I64 arg1, I64 arg2, I64 flags=0)
{//Post message to current task and return immediately. See message_code.
        MessagePost(Fs, message_code, arg1, arg2, flags);
}

#define JOB_DONE                                0
#define JOB_CONT                                1
#define JOB_EXIT                                2

I64 JobRunOne(I64 run_flags, CJobCtrl *ctrl)
{//Called with ctrl->flags,JOBCf_LOCKED.
        CJob  *tmpc = ctrl->next_waiting;
        CTask *master;
        I64    res, flags = tmpc->flags, old_flags = RFlagsGet;

        if (Bt(&flags, JOBf_EXIT_ON_COMPLETE))
                res = JOB_EXIT;
        else
                res = JOB_CONT;
        switch (tmpc->job_code)
        {
                case JOBT_SPAWN_TASK:
                        QueueRemove(tmpc);
                        LBts(&tmpc->flags, JOBf_DISPATCHED);
                        LBtr(&ctrl->flags, JOBCf_LOCKED);
                        if (tmpc->aux_str)
                                tmpc->spawned_task = Spawn(tmpc->addr, tmpc->fun_arg, tmpc->aux_str,, tmpc->aux1, tmpc->aux2, tmpc->flags);
                        else
                                tmpc->spawned_task = Spawn(tmpc->addr, tmpc->fun_arg, "Unnamed",, tmpc->aux1, tmpc->aux2, tmpc->flags);
                        break;

                case JOBT_CALL:
                        QueueRemove(tmpc);
                        LBts(&tmpc->flags, JOBf_DISPATCHED);
                        LBtr(&ctrl->flags, JOBCf_LOCKED);
                        RFlagsSet(run_flags);
                        LBtr(&Fs->task_flags, TASKf_IDLE);
                        try
                                tmpc->res = (*tmpc->addr)(tmpc->fun_arg);
                        catch
                                Fs->catch_except = TRUE;
                        RFlagsSet(old_flags);
                        break;

                case JOBT_EXE_STR:
                        QueueRemove(tmpc);
                        LBts(&tmpc->flags, JOBf_DISPATCHED);
                        LBtr(&ctrl->flags, JOBCf_LOCKED);
                        RFlagsSet(run_flags);
                        LBtr(&Fs->task_flags, TASKf_IDLE);
                        try
                                tmpc->res = ExePrint("%s", tmpc->aux_str);
                        catch
                                Fs->catch_except = TRUE;
                        RFlagsSet(old_flags);
                        break;

                default:
                        res = JOB_DONE;
        }
        if (res)
        {
                if (master = tmpc->master_task)
                {
                        if (!Bt(&flags, JOBf_FREE_ON_COMPLETE))
                        {
                                CLI

                                while (LBts(&master->server_ctrl.flags, JOBCf_LOCKED))
                                        PAUSE

                                QueueInsert(tmpc, master->server_ctrl.last_done);
                                LBts(&tmpc->flags, JOBf_DONE);
                                LBtr(&master->server_ctrl.flags, JOBCf_LOCKED);
                                RFlagsSet(old_flags);
                        }
                        if (Bt(&flags, JOBf_FOCUS_MASTER) && !Bt(&master->win_inhibit, WIf_SELF_FOCUS))
                                sys_focus_task = master;
                        if (Bt(&flags, JOBf_WAKE_MASTER))
                                Suspend(master, FALSE);
                }
                if (Bt(&flags, JOBf_FREE_ON_COMPLETE))
                        JobDel(tmpc);
                else if (!master)
                {
                        CLI

                        while (LBts(&ctrl->flags, JOBCf_LOCKED))
                                Yield;

                        QueueInsert(tmpc, ctrl->last_done);
                        LBts(&tmpc->flags, JOBf_DONE);
                        LBtr(&ctrl->flags, JOBCf_LOCKED);
                        RFlagsSet(old_flags);
                }
        }

        return res;
}

I64 JobsHandler(I64 run_flags, CTask *task=NULL)
{//Handle all waiting cmds and return.
        I64 count = 0, old_flags = RFlagsGet;

        if (!task)
                task = Fs;
        while (TRUE)
        {
                CLI

                while (LBts(&task->server_ctrl.flags, JOBCf_LOCKED))
                        PAUSE

                if (task->server_ctrl.next_waiting != &task->server_ctrl)
                        switch (JobRunOne(run_flags, &task->server_ctrl))
                        {
                                case JOB_CONT:
                                        count++;
                                        break;

                                case JOB_EXIT:
                                        Exit;

                                case JOB_DONE:
                                        goto jh_done;
                        }
                else
                        goto jh_done;
        }
jh_done:
        LBtr(&task->server_ctrl.flags, JOBCf_LOCKED);
        RFlagsSet(old_flags);
        return count;
}

I64 PopUp(U8 *buf, CTask *parent=NULL, CTask **_pu_task=NULL)
{//Execute code in PopUp task.
        I64    res;
        CJob  *tmpc;
        CTask *task = Spawn(&ServerCmdLine, NULL, "Server",, parent);

        if (!parent)
        {
                TaskExe(task, parent, buf, 1 << JOBf_EXIT_ON_COMPLETE | 1 << JOBf_FREE_ON_COMPLETE);
                if (_pu_task)
                        *_pu_task = task;
                return 0;
        }
        else
        {
                Fs->popup_task = task;
                tmpc = TaskExe(task, parent, buf, 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
                if (_pu_task)
                        *_pu_task = task;
                JobResScan(tmpc, &res);
                Fs->popup_task = NULL;
                Kill(task);
                if (_pu_task)
                        *_pu_task = NULL;
                return res;
        }
}

I64 PopUpPrint(U8 *format, ...)
{//Execute code in PopUp task.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);
        I64 res;

        res = PopUp(buf, Fs);
        Free(buf);
        return res;
}

I64 Sys(U8 *format, ...)
{//Make sys_task execute code.
        I64   res;
        U8   *buf = StrPrintJoin(NULL, format, argc, argv);
        CJob *tmpc;

        if (Fs  == sys_task)
        {
                tmpc = TaskExe(sys_task, Fs, buf, 0);
                JobsHandler(RFlagsGet);
        }
        else
        {
                TaskWait(sys_task);
                tmpc = TaskExe(sys_task, Fs, buf, 1 << JOBf_WAKE_MASTER);
        }
        JobResScan(tmpc, &res);
        Free(buf);

        return res;
}

U0 SysLog(U8 *format, ...)
{//Display text in sys_task.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);

        if (Fs == sys_task)
                "%s", buf;
        else if (!IsSingleUser)
                Sys("\"%%s\",%d;", buf);
        Free(buf);
}

U0 SysWarn(U8 *format, ...)
{//Display pink blinking Warn text in sys_task.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv), *st = MStrPrint(ST_WARN_ST "%s", buf);

        if (Fs == sys_task)
                "%s", st;
        else if (!IsSingleUser)
                Sys("\"%%s\",%d;", st);
        Free(st);
        Free(buf);
}

U0 SysErr(U8 *format, ...)
{//Display red blinking Err text in sys_task.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv), *st = MStrPrint(ST_ERR_ST "%s", buf);

        if (Fs == sys_task)
                "%s", st;
        else if (!IsSingleUser)
                Sys("\"%%s\",%d;", st);
        Free(st);
        Free(buf);
}

U0 XTalk(CTask *task, U8 *format, ...)
{//Sends text to other task. See ::/Misc/OSTestSuite.CC.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv), *st = ZStrNew(buf), *st2 = MStrPrint("\"%%s\",%d;Free(%d);", st, st);

        TaskText(task, NULL, st2, 0);
        Free(st2);
        Free(buf);
}

U0 XTalkWait(CTask *task, U8 *format, ...)
{//Send text to other task and wait for it to idle.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv), *st = ZStrNew(buf), *st2 = MStrPrint("\"%%s\",%d;Free(%d);", st, st);

        TaskText(task, NULL, st2, 0);
        Free(st2);
        Free(buf);
        TaskWait(task);
}

U0 InStr(U8 *format, ...)
{//Send InFile code to self.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);

        if (Bt(&Fs->task_flags, TASKf_INPUT_FILTER_TASK))
                ExePrint("%s", buf);
        else
                TaskText(Fs, NULL, buf, 1 << JOBf_HIGHEST_PRIORITY);
        Free(buf);
}

U0 InFile(U8 *filename)
{//Send InFile code file to self.
        U8 *name = ExtDefault(filename, "IN");

        InStr("Cd(\"%C:%s\");;#include \"%s\"", Drive2Letter(Fs->cur_dv), Fs->cur_dir, name);
        Free(name);
}

U0 In(U8 *format, ...)
{//Send text to own input buffer. See ::/Demo/AcctExample/TOS/TOSDistro.CC.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv), *st = ZStrNew(buf);

        InStr("\"%%s\",%d;Free(%d);", st, st);
        Free(buf);
}

U0 XTalkStr(CTask *task, U8 *format, ...)
{//Send InFile code to other task.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);

        TaskText(task, NULL, buf, 0);
        Free(buf);
}

U0 XTalkStrWait(CTask *task, U8 *format, ...)
{//Send InFile code to other task and wait for it to idle.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);

        TaskText(task, NULL, buf, 0);
        Free(buf);
        TaskWait(task);
}