/*ident "@(#)C++env:lib/task/task/task.c 1.6" */ /************************************************************************** Copyright (c) 1984 AT&T All Rights Reserved THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T The copyright notice above does not evidence any actual or intended publication of such source code. *****************************************************************************/ // this file now assumes that the stack grows down; // make another if the stack grows up, or split this // into pieces such that one piece knows about stack growing // and the other doesn't. // DO NOT JUST USE IFDEFS!!!! #include #include #include #ifdef uts enum {SIZE = 2048}; #else enum {SIZE = 750}; #endif // align stack if needed #define ALIGN(stack) ((ulong *)((long)(stack) & ~(sizeof(double) - 1))) enum { UNTOUCHED = 052525 }; /* value considered never used */ typedef unsigned long ulong; // a team is a collection of tasks that share one stack class team { friend task; int no_of_tasks; task *got_stack; ulong *stack; ulong *stackend; // of the stack team(task*, int); // stacksize == 0 ==> using the main stack ~team() { delete stackend; } }; int _hwm; static ulong *stack_base; static int stackdown; // does the stack grow down or up? static task *running; // task on the current stack team::team(task* t, int stacksize) { no_of_tasks = 1; got_stack = t; if (stacksize) { stack = new ulong[stacksize]; stackend = ALIGN(stack + stacksize - 1); while (stack == 0) object::task_error(E_STORE, (object*)0); if (_hwm) for (ulong *x = stack; x <= stackend; x++) *x = UNTOUCHED; if (stackdown) { ulong *t = stack; stack = stackend; stackend = t; } } else { stackend = 0; stack = stack_base; } } /* called from _main to set __main__base */ void __task__init() { // get args for main // main => _main => __task__init Label m; setlabel(&m); stack_base = stackbase(m); stackdown = (ulong)stack_base > m.sp; new task("main"); // fire up a main task } Label task::copy_task(Label top, ulong *base, ulong *stack) { ulong *stop = (ulong *)top.sp; if (base == stop) task_error(E_STACK, this); if (stackdown) while (base > stop) *stack-- = *base--; else while (base < stop) *stack++ = *base++; *stack = *base; return movelabel(top, (ulong)stack); } void task::swapjmp() { if (t_savearea) delete t_savearea; t_savearea = 0; t_size = 0; t_team->got_stack = this; gotolabel(1, &t_env); } void task::doswap(task *on, int ischild) { // make sure we have enough room to work // if we are running on the stack we want to swap if (running == on) { Label m; setlabel(&m); ulong *t = argbase(m); ulong sz = stackdown ? (ulong)t_team->stack - (ulong)t : (ulong)t - (ulong)t_team->stack; // recursive call to increase the size of this stack if (sz <= t_size) { doswap(on, ischild); abort(); } } on->t_size += sizeof(double); on->t_savearea = new char[on->t_size]; on->t_save = stackdown ? on->t_savearea + on->t_size - sizeof(ulong) : on->t_savearea + sizeof(double) - sizeof(ulong); on->t_save = (char *)ALIGN(on->t_save); on->t_env = copy_task(on->t_env, t_team->stack, (ulong *)on->t_save); if (!ischild) t_env = copy_task(t_env, (ulong *)t_save, t_team->stack); swapjmp(); } void task::swap(int ischild) { task *on = on = t_team->got_stack; if (this != on) { if (ischild) t_size = stackdown ? (ulong)t_team->stack - t_env.sp : t_env.sp - (ulong)t_team->stack; on->t_size = stackdown ? (ulong)t_team->stack - on->t_env.sp : (ulong)on->t_env.sp - (ulong)t_team->stack; doswap(on, ischild); } swapjmp(); } inline void task::settrap() { if (t_team->stackend) // Don't set trap for main task t_trap = *t_team->stackend; } inline void task::checktrap() { // Don't test for main task if (t_team->stackend && t_trap != *t_team->stackend) task_error(E_STACK, this); } task::task(char* name, modetype mode, int stacksize) /* executed in the task creating a new task - thistask. 1: put thistask at head of scheduler queue, 2: create new task 3: transfer execution to new task derived::derived can never return - its return link is destroyed if thistask==0 then we are executing on main()'s stack and should turn it into the "main" task */ { running = thxstxsk; t_name = name; t_mode = (mode) ? mode : (modetype) DEFAULT_MODE; t_stacksize = (stacksize) ? stacksize : SIZE; t_savearea = 0; t_size = 0; t_alert = 0; s_state = RUNNING; t_next = txsk_chxin; txsk_chxin = this; if (thxstxsk == 0) { // called from __task__init() // create "main" task t_team = new team(this, 0); /* don't allocate stack */ t_team->no_of_tasks = 2; /* never deallocate */ thxstxsk = this; return; } // schedule this task to run now thxstxsk->insert(0, this); switch (t_mode) { case DEDICATED: t_team = new team(this, t_stacksize); if (setlabel(&thxstxsk->t_env) == 0) { // child; returns first Label child = upframe(thxstxsk->t_env); thxstxsk = this; child = copy_task(child, argbase(child), t_team->stack); settrap(); gotolabel((long)this, &child); } break; case SHARED: thxstxsk->t_mode = SHARED; /* you cannot share on your own */ t_team = thxstxsk->t_team; t_team->no_of_tasks++; if (setlabel(&thxstxsk->t_env) == 0) { // child; return first if (setlabel(&t_env) == 0) swap(1); // ok, now we are really running; thxstxsk = this; Label child = upframe(t_env); settrap(); gotolabel((long)this, &child); } break; default: task_error(E_TASKMODE, this); } // parent if (team_to_delete) { delete team_to_delete; team_to_delete = 0; } /* return this to function that called the derived constructor */ Label me = upframe(upframe(thxstxsk->t_env)); gotolabel((long)this, &me); } void task::resume() { running = thxstxsk; thxstxsk = this; if (running && running->s_state != TERMINATED) running->checktrap(); // NOTE -- need to do shared here if (running == 0 || setlabel(&running->t_env) == 0) { if (t_mode == SHARED) swap(0); else gotolabel(1, &this->t_env); } if (team_to_delete) { delete team_to_delete; team_to_delete = 0; } } void task::cancel(int val) /* TERMINATE and free stack space */ { if (this->s_state != TERMINATED) { sched::cancel(val); if (_hwm) t_size = curr_hwm(); if (t_team && (t_team->no_of_tasks-- == 1)) { if (this != thxstxsk) { delete t_team; } else { // don't delete current stack! // delete will be called from task::restore // immediately after task switch assert(team_to_delete == 0); team_to_delete = t_team; } t_team = 0; // no further access to deleted team } } } task::~task() /* free stack space and remove task from task chain */ { if (s_state != TERMINATED) task_error(E_TASKDEL, this); if (this == txsk_chxin) txsk_chxin = t_next; else { register task* t; register task* tt; for (t=txsk_chxin; tt=t->t_next; t=tt) if (tt == this) { t->t_next = t_next; break; } } if (t_savearea) delete t_savearea; if (this == thxstxsk) { delete (int*) thxstxsk; /* fudge: free(_that) */ thxstxsk = 0; schedule(); } } void task::resultis(int val) { cancel(val); if (this == thxstxsk) schedule(); } void task::sleep(object* t) { if (t) t->remember(this); if (s_state == RUNNING) remove(); if (this == thxstxsk) schedule(); } void task::delay(long d) { insert(d,this); if (thxstxsk == this) schedule(); } long task::preempt() { if (s_state == RUNNING) { remove(); return s_time - get_clock(); } else { task_error(E_TASKPRE, this); return 0; } } char* state_string(sched::statetype s) { switch (s) { case sched::IDLE: return "IDLE"; case sched::TERMINATED: return "TERMINATED"; case sched::RUNNING: return "RUNNING"; default: return 0; } } char* mode_string(task::modetype m) { switch(m) { case task::SHARED: return "SHARED"; case task::DEDICATED: return "DEDICATED"; default: return 0; } } void task::print(int n, int baseClass) /* "n" values: CHAIN, VERBOSE, STACK */ { if (!baseClass) fprintf(stderr, "task\n"); char* ss = state_string(s_state); char* ns = (t_name) ? t_name : ""; fprintf(stderr, "task %s ",ns); if (this == thxstxsk) fprintf(stderr, "(is thistask, %s) ", ss); else if (ss) fprintf(stderr, "(%s) ",ss); else fprintf(stderr, "(state==%d CORRUPTED) ",s_state); fprintf(stderr, "\tthis = %x:\n", this); if (n&VERBOSE) { char* ms = mode_string(t_mode); if (ms == 0) ms = "CORRUPTED"; fprintf(stderr, "\tmode=%s t_alert=%x t_next=%x", ms, t_alert, t_next); fprintf(stderr, (s_state==TERMINATED) ? " result=%d\n" : " s_time=%d\n", s_time); } if (n&STACK) { fprintf(stderr, "\tstack: "); fprintf(stderr, "stack=0x%x, stackend=0x%x, ", t_team->stack, t_team->stackend); if (s_state == TERMINATED) { fprintf(stderr, "deleted. "); if (_hwm) fprintf(stderr, "hwm size=%d", t_size); fprintf(stderr, "\n"); } else { fprintf(stderr, "\tsizes:\t"); ulong *top; if (this==thxstxsk) { // figure out real current size Label m; setlabel(&m); top = (ulong *)m.sp; } else // approximate at last switch top = (ulong *)t_env.sp; ulong sz = stackdown ? (ulong)t_team->stack - (ulong)top : (ulong)top - (ulong)t_team->stack; if (t_mode == SHARED) sz = t_size; fprintf(stderr, "max=%d, current=%d", t_stacksize, sz); int my_hwm; if (_hwm) { my_hwm = curr_hwm(); fprintf(stderr, ", hwm=%d", my_hwm); } fprintf(stderr, "\n\t\taddresses:\t"); fprintf(stderr, "top of stack=0x%x", top); if (_hwm) fprintf(stderr, ", hwm=0x%x", stackdown ? t_team->stack - my_hwm : t_team->stack + my_hwm); fprintf(stderr, "\n"); } } if (n&CHAIN) { sched::print(n, 1); // call sched::print here to keep // output for same object together // Start at beginning of task chain, and print all tasks task *tp = get_task_chain(); if (tp == this) { tp = tp->t_next; // just printed, skip it } else { fprintf(stderr, "\nChain of all tasks:\n"); } for (; tp; tp = tp->t_next) { fprintf(stderr, "Next task on chain of all tasks is:\n"); tp->print(n & ~CHAIN); } } else { sched::print(n, 1); } } int task::curr_hwm() { ulong *b = t_team->stackend; ulong *e = t_team->stack; if (stackdown) while (b < e && *b == UNTOUCHED) b++; else while (b > e && *b == UNTOUCHED) b--; return stackdown ? e - b : b - e; } void task::wait(object* ob) { if (ob == (object*)this) task_error(E_WAIT, this); t_alert = ob; while (ob->pending()) sleep(ob); } int task::waitlist(object* a ...) { return waitvec(&a); } int task::waitvec(object** v) /* first determine if it is necessary to sleep(), return hint: who caused return */ { int i; int j; register object* ob; for(;;) { for (i = 0; ob = v[i]; i++) { if (!ob->pending()) goto ex; ob->remember(this); } if (i==1 && v[0]==(object*)this) task_error(E_WAIT, this); sleep(); } ex: t_alert = ob; for (j = 0; ob = v[j]; j++) ob->forget(this); return i; } // not on plan 9 yet Interrupt_alerter interrupt_alerter;