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]