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]

Reply via email to