On Wed, 23 Oct 2002 15:58:40 +0200 "Ralf S. Engelschall" <[EMAIL PROTECTED]> wrote:
> Good idea. The name for pth_msgport_create() is actually only required > of one wants to search the message port via pth_msgport_find(). So I've > changed this now for GNU Pth 1.5.0 to allow a NULL name, too. Thanks for > the hint. Indeed, and effectively in my case I did not need to locate the port, on AmigaOS tasks really needed the functionality, although it's of course optional with user-space threads where memory is already shared among them. Thanks, I'm happy to see that it's still being maintained actively, it's a great piece of work. > Yes, the same idea is also on my TODO list. Unfortunately it is perhaps > not as easy as it looks, because some of the pth_mctx_*() stuff is just > macro based and hence exporting it means moving it physically to pth.h > -- perhaps with other side-effects. I'll look into this, perhaps we > could wrap the pth_mctx_*() functionality with extra C function for > easier exporting. Yes, that is also why I suggest to perhaps provide an SVR4/SUS like interface, which could consist of C functions... and then exported and documented. The API is already well known so it could be a plus... BTW, yesterday night I wrote a quick small test, attempting to still write a scheduler around current Pth, after I had the idea about using pth_yield() and specifying the thread to switch to :) unfortunately it does not seem to work yet. Of course it's only a small hack, but I beleive it was enough to try it (attached, there may be some extra stuff which was copy-pasted from an older project to code it faster). ----- microxisop.h #ifndef MICROXISOP_H #define MICROXISOP_H #include <sys/types.h> #include <stdlib.h> #include <pth.h> #include <mmtypes.h> #include <mmlist.h> #define STATE_READY 1 #define STATE_WAIT 2 #define STATE_SUSPENDED 3 #define STATE_DEAD 4 #define xisop_malloc(s) malloc((s)) #define xisop_free(b) free((b)) typedef int xisop_sig_t; typedef u_int64_t xisop_sigmask_t; typedef int xisop_lock_t; struct xisop_context { /* A simple hack to run Xisop on unix */ pth_t id; }; struct xisop_task { node nod; node usernod; void *(*start)(void *); struct xisop_context context; char state, priority, credits; xisop_sigmask_t sigalloc, sigwait, sigrecv; list private_msgports; }; struct xisop_root { xisop_lock_t sched_lock; list *task_pool; list queue_new, queue_ready, queue_wait, queue_suspended, queue_dead; list msgports; list libraries; list devices; list handlers; list volumes; struct xisop_task *root_task, *current_task; struct xisop_context context; u_int64_t ticks; }; struct xisop_msgport { node nod; node usernod; xisop_lock_t lock; u_int64_t hash; struct xisop_task *sigtask; xisop_sig_t signum; int maxqueue; list queue; }; struct xisop_message { node nod; struct xisop_msgport *replyport; }; struct xisop_timerhook { node nod; void (*hook)(void); }; extern struct xisop_root *Root; void xisop_init(void); /* Task */ struct xisop_task * xisop_gettask(void); struct xisop_task * xisop_newtask(void *(*)(void *), char); struct xisop_task * xisop_freetask(struct xisop_task *); void xisop_runtask(struct xisop_task *); void xisop_deadtask(void); /* Time */ void xisop_timer_on(void); void xisop_timer_off(void); void xisop_timer_setfrequency(u_int32_t); void xisop_timer_attach_hook(void (*)(void)); void xisop_timer_unlink_hook(void (*)(void)); /* Schedule */ void xisop_forbid(void); void xisop_permit(void); void xisop_disable(void); void xisop_enable(void); void xisop_yield(void); void xisop_lock_init(xisop_lock_t *); void xisop_simplelock(xisop_lock_t *, bool); /* IPC */ xisop_sig_t xisop_sigalloc(void); void xisop_sigfree(xisop_sig_t); void xisop_signal(struct xisop_task *, int); xisop_sigmask_t xisop_wait(xisop_sigmask_t); struct xisop_msgport * xisop_openport(u_int64_t, size_t); struct xisop_msgport * xisop_closeport(struct xisop_msgport *); struct xisop_msgport * xisop_findport(u_int64_t); bool xisop_sendmsg(struct xisop_msgport *, struct xisop_message *); struct xisop_message * xisop_getmsg(struct xisop_msgport *); bool xisop_replymsg(struct xisop_message *); void xisop_waitport(struct xisop_msgport *); struct xisop_message * xisop_openmsg(struct xisop_msgport *, size_t); struct xisop_message * xisop_closemsg(struct xisop_message *); #endif ----- microxisop.c /* This only consists of an attempt to use pth for context switching among * user threads (in this case future unix-xisop tasks). * It seems to have problems, it may be related to pth_yield() being called * in the "interrupt" context (SIGALRM signal handler). */ #include <microxisop.h> #include <sys/time.h> #include <stdio.h> #include <mmstring.h> static void x_timer_init(void); static void x_cpuidle(void); static void x_timer_exechooks(void); static struct xisop_task *x_schedule(void); static void x_swappercode(void); static void x_switchcontext(struct xisop_context *); static xisop_sig_t x_sigalloc(struct xisop_task *); static void x_sigfree(struct xisop_task *, xisop_sig_t); static xisop_sigmask_t x_wait(struct xisop_task *, xisop_sigmask_t); static struct xisop_task *schedule(struct xisop_task *); static void *scheduler(void *); static void *init(void *); static void *thread1(void *); static void *thread2(void *); static void *thread3(void *); struct xisop_root *Root; static struct itimerval itv; static pth_attr_t attr; #define DISABLE() signal(SIGALRM, SIG_IGN) #define ENABLE() signal(SIGALRM, sighandler) #define FORBID() Root->sched_lock++; #define PERMIT() Root->sched_lock--; #define FREQUENCY 10 static void sighandler(int sig) { switch (sig) { case SIGALRM: Root->ticks++; DISABLE(); write(1, "Z", 0); /* XXX Does not occur */ if (!Root->sched_lock) { /* Save current context and restore scheduler one */ pth_yield(Root->context.id); } ENABLE(); break; } } int main(void) { xisop_init(); } struct xisop_task * xisop_newtask(void *(*start)(void *), char pri) { struct xisop_task *task; FORBID(); task = (struct xisop_task *)allocnode(Root->task_pool, FALSE); PERMIT(); if (task) { task->start = start; task->priority = task->credits = pri; return (task); } return (NULL); } void xisop_runtask(struct xisop_task *task) { FORBID(); appendnode(&Root->queue_new, (node *)task); PERMIT(); } void xisop_init(void) { struct xisop_task *task; Root = malloc(sizeof(struct xisop_root)); mm_memclr(Root, sizeof(struct xisop_root)); Root->task_pool = openlist(malloc, free, sizeof(struct xisop_task), 4096, 0); pth_init(); DISABLE(); /* Init scheduler and start our time interrupt */ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = FREQUENCY; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = FREQUENCY; setitimer(ITIMER_REAL, &itv, NULL); /* Setup init task */ task = xisop_newtask(init, 0); xisop_runtask(task); Root->current_task = task; /* XXX hmm I have to start a thread, else I wouldn't have a main * scheduler context to switch to */ attr = pth_attr_new(); pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE); Root->context.id = pth_spawn(attr, scheduler, NULL); ENABLE(); /* Scheduler is activated here */ pth_yield(Root->context.id); for (;;) pth_sleep(100); } /* Returns next task in ready queue which deserves a run */ static struct xisop_task * schedule(struct xisop_task *old) { struct xisop_task *ntsk, *tsk; char credit; for (;;) { /* Find which task has the most credits and deserves next run, but * continue the loop at previous task, otherwise the highest * priority task will get all turns at once until it reaches * equality with other tasks. */ credit = -128; tsk = old; do { /* We assume we were not passed NULL, and that all tasks are * always in the ready queue, for this test. */ if ((tsk = (struct xisop_task *)tsk->nod.next) == NULL) tsk = (struct xisop_task *)Root->queue_ready.top; if (credit < tsk->credits) { credit = tsk->credits; ntsk = tsk; } } while (tsk != old); if (credit == -128) { /* All out of credits, redistribute them */ putchar('\n'); for (tsk = (struct xisop_task *)Root->queue_ready.top; (tsk); tsk = (struct xisop_task *)tsk->nod.next) { tsk->credits = tsk->priority; if (tsk->credits == -128) tsk->credits++; } continue; } ntsk->credits--; return (ntsk); } } static void * scheduler(void *data) { struct xisop_task *task; struct xisop_context *context; for (;;) { context = NULL; FORBID(); /* Launch any new tasks */ if ((task = (struct xisop_task *)Root->queue_new.top)) { task->state = STATE_READY; swapnode(&Root->queue_ready, &Root->queue_new, (node *)task, FALSE); task->context.id = pth_spawn(attr, task->start, NULL); context = &task->context; } else { task = Root->current_task; if (task->state == STATE_WAIT) { struct xisop_task *next = (struct xisop_task *)task->nod.next; swapnode(&Root->queue_wait, &Root->queue_ready, (node *)task, FALSE); task = next; } /* Give next round to task which deserves it */ task = schedule(Root->current_task); if (task) { Root->current_task = task; context = &task->context; } } PERMIT(); if (context) { char buf[1024]; snprintf(buf, 1024, "\nswitching to: %p\n", context); write(1, buf, mm_strlen(buf)); pth_yield(context->id); } } } static void * init(void *data) { struct xisop_task *task; if (task = xisop_newtask(thread1, 0)) xisop_runtask(task); if (task = xisop_newtask(thread2, 0)) xisop_runtask(task); if (task = xisop_newtask(thread3, 0)) xisop_runtask(task); for (;;) pth_sleep(100); } static void * thread1(void *data) { for (;;) write(1, "a", 1); } static void * thread2(void *data) { for (;;) write(1, "b", 1); } static void * thread3(void *data) { for (;;) write(1, "c", 1); } ----- EOF ______________________________________________________________________ GNU Portable Threads (Pth) http://www.gnu.org/software/pth/ User Support Mailing List [EMAIL PROTECTED] Automated List Manager (Majordomo) [EMAIL PROTECTED]