Task Group Creation Howto ------------------------- This is a small draft that explains how to implement task group creation under the S.Ha.R.K. Kernel (snapshot 4 Oct 2000). If you find any bugs, English errors, and if you have some comments, please mail them to me at pj@hartik.sssup.it ----------- The problem ----------- When a new task is created, what happens? Usually, in the program you write these two primitives: task_create(...) task_activate(...) The implementation of the task_create calls these functions in the given order: - level_accept_task_model (Level call) - task_create (Task call) - level_accept_resource_model and res_register (if you use resources) - level_guarantee (Level call) ------------------------------------------------------------------------- And if we want to create 3 tasks at the same time, what happens? Usually, you will write something like that: task_create(...) ... task_create(...) group_activate(...) ------------------------------------------------------------------------- This situation is not the best if only a subset of the created task group can be accepted into the system, because the task creation simply guarantee one task after another, working with only a new task at a time and without considering the "group"... ------------ The Solution ------------ A few primitives are added into the Snapshot of 4 October 2000 to allow task group creation. The primitives are the following: ------------------------------------------------------------------------------ PID group_create(char *name, TASK (*body)(), TASK_MODEL *m, ...); This function allow to create a set of tasks without guarantee. It must be called with INTERRUPTS DISABLED and it must be used with group_create_accept and group_create_reject. This function allocates a task descriptor and fills it. After that, the guarantee() function should be called to check for task(s) admission. Next, each task created with group_create must be accepted with a call to group_create_accept() or rejected with a call to group_create_reject. The function returns the PID of the allocated descriptor, or NIL(-1) if the descriptor cannot be allocated or some problems arises the creation. If -1 is returned, errno is set to a value that represent the error: ENO_AVAIL_TASK -> no free descriptors available ENO_AVAIL_SCHEDLEVEL -> there were no scheduling modules that can handle the TASK_MODEL *m passed as parameter ETASK_CREATE -> there was an error during the creation of the task into the scheduling module ENO_AVAIL_RESLEVEL -> there were no resource modules that can handle one of the RES_MODEL * passed as parameter ------------------------------------------------------------------------------ int group_create_accept(PID i, TASK_MODEL *m); This function should be called when a task created with group_create is successfully guaranteed and accepted in the system. This function finish the creation process allocating the last resources for the task (i.e., the stack and the context). it returns: 0 in case of success (all the resources can be allocated) -1 if something goes wrong. In this case, THE TASK IS AUTOMATICALLY REJECTED AND THE GROUP_CREATE_REJECT MUST NOT BE CALLED. errno is set to a value that explains the problem occurred: ENO_AVAIL_STACK_MEM -> No stack memory available for the task ENO_AVAIL_TSS -> No context available for the task (This is a CRITICAL error, and usually never happens...) ------------------------------------------------------------------------------ void group_create_reject(PID i); This function should be called when a task created with group_create can not be successfully guaranteed. This function reject the task from the system. You cannot use the PID of a rejected task after this call. ------------------------------------------------------------------------------ Here is some little pieces of code that explains how to write a Module that implements group creation and guarantee, and how an application should use them: ------------------------------------------------------------------------------ The Module // These defines are the states of the task set handled by the Module #define MYMODULE_NOTYET_GUARANTEED 1 #define MYMODULE_GUARANTEED 2 #define MYMODULE_REJECTED 3 struct { ... // this private data structure handles the guarantee state of each task // handled by the Module int guarantee_status[MAX_PROC]; } MYMODULE_level_des; // The guarantee function static int MYMODULE_level_guarantee(LEVEL l, bandwidth_t *freebandwidth) { MYMODULE_level_des *lev = (MYMODULE_level_des *)(level_table[l]); ... check the guarantee of the task set if () lev->guarantee_status[Ji] = MYMODULE_GUARANTEED; else lev->guarantee_status[Ji] = MYMODULE_REJECTED; } // This function is called by group_create to register the task into the system static int MYMODULE_task_create(LEVEL l, PID p, TASK_MODEL *m) { MYMODULE_level_des *lev = (MYMODULE_level_des *)(level_table[l]); ... // the guarantee function has not been called yet lev->guarantee_status[p] = MYMODULE_NOTYET_GUARANTEED; } // this function is called if something goes wrong in: group_create (before calling the guarantee function) group_create accept (after calling the guarantee function!!!) static void MYMODULE_task_detach(LEVEL l, PID p) { MYMODULE_level_des *lev = (MYMODULE_level_des *)(level_table[l]); ... // the task is no longer handled by the Module lev->guarantee_status[p] = MYMODULE_NOTYET_GUARANTEED; } void MYMODULE_register_level() { ... // Module startup for (i=0; iguarantee_status = MYMODULE_NOTYET_GUARANTEED; } // this function is added to the system, it is not part of the Module interface int MYMODULE_taskaccepted(LEVEL l, PID p) { // return 1 if the task is guaranteed MYMODULE_level_des *lev = (MYMODULE_level_des *)(level_table[l]); if (lev->l.level_code == MYMODULE_LEVEL_CODE && lev->l.level_version == MYMODULE_LEVEL_VERSION) return lev->guarantee_status[p] == MYMODULE_GUARANTEED; else return 0; } ------------------------------------------------------------------------------ The Application void myfunction() { SYSFLAGS f; // disable the interrupts ... also kern_cli(); can be used f = kern_fsave(); // J1 is the task name, bodyJ1 the task body, m1 the TASK_MODEL, // r1...rn the resource models (optional) p1 = group_create("J1", bodyJ1, &m1, &r11, ..., &r1x, NULL); ... pn = group_create("Jn", bodyJn, &mn, &rn1, ..., &rny, NULL); // returns 0 if the task set is guaranteed, -1 if not guarantee(); if (MYMODULE_taskaccepted(MYMODULE_level,p1)) group_create_accept(p1,&m1); else group_create_reject(p1); // enable again the interrupts ... use kern_sti(); if before you used // kern_cli(); kern_frestore(f); } ------------------------------------------------------------------------- Comments: - the group_create function only calls the task_create task call and returns a suitable PID - the level_guarantee function is called only into the guarantee() function, so only 1 time for all the newly created tasks - MYMODULE_taskaccepted returns 1 only if the task can be guaranteed by the system - If the task Pi can be guaranteed by the system, you MUST call group_create_accept to finish the task creation process. Then, you can use the PID Pi (returned by the correspondent function group_create_init) as a valid PID - If the task can not be guaranteed by the system, you MUST call group_create_reject. That function will free the task descriptor, and all the resources allocated for the task. The function also calls the task_detach task_call. After that call you cannot use again the PID passed to the function, until it will be returned again by a task_create or group_create primitive ---------------------------------------------------------------------------