/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_task.c: Simple task management library */ /** * SPL Task Management Module * * This module provides basic functions for handling SPL tasks. SPL tasks * may be used like threads, co-routines, or anything simmilar. */ #include #include #include #include "spl.h" #include "compat.h" static struct spl_code bytecode = #include "spl_modules/mod_task.splh" ; extern void SPL_ABI(spl_mod_task_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_task_done)(struct spl_vm *vm, struct spl_module *mod); /** * This function creates a new task. The 1st parameter is the name of the * new task and the 2nd is the SPL program code for the task. The 2nd * parameter will be compiled when executing the function, so it is a good * idea to keep it small and move the big portion of the task logic to * seperate functions. The third option is the context in which the task * should be executed. * * If the task name is undefined, the task won't have a name attached to * it and it won't be possible to access it later. * * If the context is ommitted, the task will run in the same context as * the function which called this function. */ // builtin task_create(name, code, ctx) static struct spl_node *handler_task_create(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); char *program = spl_clib_get_string(t); struct spl_node *ctx = spl_clib_get_node(t); struct spl_asm *as = spl_asm_create(); as->vm = t->vm; if ( spl_compiler(as, program, "task_main", 0, 0) ) SPL_NEW_INT(1); if ( spl_compiler(as, "task_kill();", "task_epilogue", 0, 0) ) SPL_NEW_INT(2); struct spl_task *task = spl_task_create(t->vm, name); if ( ctx ) { spl_put(task->vm, task->ctx); task->ctx = ctx; } spl_task_setcode(task, spl_asm_dump(as)); spl_asm_destroy(as); task->flags |= SPL_TASK_FLAG_PAUSED; return SPL_NEW_INT(0); } static struct spl_node *handler_task_kill(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: t->flags |= SPL_TASK_FLAG_ZOMBIE; return SPL_NEW_INT(0); } t= t->next; } return SPL_NEW_INT(1); } /** * This functions pauses the specified task until it is woken up by * [[task_continue()]] (or by an external event). * * If the task name is ommitted, the current task will be affected by * this function. */ // builtin task_pause(name) static struct spl_node *handler_task_pause(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: t->flags |= SPL_TASK_FLAG_PAUSED; t->flags &= ~SPL_TASK_FLAG_SYSTEM; return SPL_NEW_INT(0); } t= t->next; } return SPL_NEW_INT(1); } /** * This function wakes up the specified task. It also can be used to * remove the SYSTEM flag from the current task (see [[task_system()]]). * * If the task name is ommitted, the current task will be affected by * this function. */ // builtin task_continue(name) static struct spl_node *handler_task_continue(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: t->flags &= ~(SPL_TASK_FLAG_PAUSED|SPL_TASK_FLAG_SYSTEM); return SPL_NEW_INT(0); } t= t->next; } return SPL_NEW_INT(1); } /** * This function sets the SYSTEM flag on the specified task. That means * that the scheduler will not schedule any other task until the system * flag is removed again using [[task_continue()]] or the task is paused * ([[task_pause()]]) or killed ([[task_kill()]]). * * If the task name is ommitted, the current task will be affected by * this function. Usually this function is only used to manipulate the * currently running task. */ // builtin task_system(name) static struct spl_node *handler_task_system(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: t->flags |= SPL_TASK_FLAG_SYSTEM; return SPL_NEW_INT(0); } t= t->next; } return SPL_NEW_INT(1); } /** * This function is used to set the PUBLIC flag on a task. This is e.g. * needed when the task should be woken up by WebSPL when the taskname * is encoded into the session id. * * If the task name is ommitted, the current task will be affected by * this function. */ // builtin task_public(name) static struct spl_node *handler_task_public(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: t->flags |= SPL_TASK_FLAG_PUBLIC; return SPL_NEW_INT(0); } t= t->next; } return SPL_NEW_INT(1); } /** * Returns the name of the current task. */ // builtin task_getname() static struct spl_node *handler_task_getname(struct spl_task *t, void UNUSED(*d)) { return t->id ? SPL_NEW_STRING_DUP(t->id) : spl_get(0); } /** * Returns 1 if the specified task exists and 0 otherwise. */ // builtin task_check(name) static struct spl_node *handler_task_check(struct spl_task *t, void UNUSED(*d)) { char *name = spl_clib_get_string(t); if ( !name[0] ) goto this_task; t = t->vm->task_list; while (t) { if ( t->id && !strcmp(t->id, name) ) { this_task: return SPL_NEW_INT(1); } t= t->next; } return SPL_NEW_INT(0); } /** * Pauses current task for given amount of time, * without consuming processing time. * After the time has elapsed (or an external signal is received), * the task is automatically resumed (in contrast to task_pause). * * The time can be given as a float value, representing seconds * and fraction of seconds. * * TODO: sleep affects the process, not the task, find a task equivalent */ // builtin task_sleep(seconds) static struct spl_node *handler_task_sleep(struct spl_task *t, void UNUSED(*d)) { float f = spl_clib_get_float(t); if (f < 0) return SPL_NEW_INT(0); unsigned int seconds = (unsigned int)f; #ifndef USEWIN32API useconds_t useconds = f-seconds; #endif sleep(seconds); #ifndef USEWIN32API usleep(useconds); #endif return SPL_NEW_INT(0); } void SPL_ABI(spl_mod_task_init)(struct spl_vm *vm, struct spl_module UNUSED(*mod), int UNUSED(restore)) { spl_clib_reg(vm, "task_create", handler_task_create, 0); spl_clib_reg(vm, "__task_kill", handler_task_kill, 0); spl_clib_reg(vm, "task_pause", handler_task_pause, 0); spl_clib_reg(vm, "task_continue", handler_task_continue, 0); spl_clib_reg(vm, "task_system", handler_task_system, 0); spl_clib_reg(vm, "task_public", handler_task_public, 0); spl_clib_reg(vm, "task_getname", handler_task_getname, 0); spl_clib_reg(vm, "task_check", handler_task_check, 0); spl_clib_reg(vm, "task_sleep", handler_task_sleep, 0); if ( !restore ) spl_eval_bytecode(vm, 0, strdup(mod->name), &bytecode); } void SPL_ABI(spl_mod_task_done)(struct spl_vm UNUSED(*vm), struct spl_module UNUSED(*mod)) { return; }