Re: FreeBSD native threads

Date view Thread view Subject view Author view

From: Amancio Hasty (hasty@whistle.com)
Date: Mon Feb 08 1999 - 15:46:21 EST


Godmar Back wrote:

> >
> > Godmar Back writes:
> > > > He's working on getting Apache Jserv working on the InterJet,
> > > > for which we're using Kaffe as the JVM.
> > > >
> > > > The threads stuff (which is Kaffe work, not kernel work, though
> > > > it requires a 4.0-current system) is sortof just for fun.
> > > >
> > >
> > > Is he starting with/looking at oskit-pthreads or linux-threads at all?
> >
> > I think he's looked at oskit, but not using it. I believe he's
> > using linux-threads for this little demo.
> >
>
> I have to tell you that linux-threads, aside from being incomplete,
> do not work. In fact, the version that's in CVS right now doesn't
> even compile.
>
> However, I have outstanding changes that make it compile.
>
> The reason I had checked in this unfinished work is so that I could share
> it with a guy from Australia, who wanted to work on it. However, I haven't
> heard from him for a while now.
>
> While I think I have an idea of how this may work, I'm still getting
> lots and lots of seg faults, mostly due to memory corruption, in various
> places. Plus, the latest version frequently deadlocks.
> It does pass HelloWorld, though, and even compiles stuff.
>
> Since I don't have the time right now, I check in what I have: feel free
> to use it or ignore it or whatever.
>
> - Godmar

Hi Godmar,
Enclosed is my version of pjthread.c to interface linux-threads on FreeBSD 4.0
-current to
kaffe. Richard Seaman as well as other freebsd hackers have ported the linux
kernel threads
to FreeBSD.

Whats the purpose of jthread_spinon, jthread_spinoff, jthread_suspendall and
jthread_resumeall?

The reason that I am asking is because on the beos-native interface the above
routines are stubs and
are preceded by this comment:
/*============================================================================

 *
 * Functions related to interrupt handling
 *
 */

/*
 * Woo-hoo! BeOS has spinlocks and interrupt enable/disable functions.
 */

    Tnks,
    Amancio

/*
 * Copyright (c) 1998 The University of Utah. All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file.
 *
 * Contributed by the Flux Research Group at the University of Utah.
 * Authors: Godmar Back, Leigh Stoller
 */
/*
 * This file implements jthreads on top of Linux-pthreads.
 */

#include "debug.h"
#include "jthread.h"
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>

#include <semaphore.h>
/*
 * This idea stolen from gc/linux_threads.c in Ferguson & Boehm's pthreads
 * port.
 */
static sem_t GC_suspend_ack_sem;
#define SIG_STOP SIGXFSZ
#define SIG_RESUME SIGXCPU
#define SIGUNUSED SIGXCPU
/* thread status */
#define THREAD_NEWBORN 0
#define THREAD_RUNNING 1
#define THREAD_DYING 2
#define THREAD_DEAD 3
#define THREAD_STOPPED 4
#define THREAD_CONTINUE 5

/*
 * Variables.
 * These should be kept static to ensure encapsulation.
 */
static int talive; /* number of threads alive */
static int tdaemon; /* number of daemons alive */
static void (*runOnExit)(void); /* function to run when all non-daemon die */

static struct jthread* liveThreads; /* list of all live threads */

/* static lock to protect liveThreads etc. */
/*static pthread_mutex_t threadLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER ; */
static pthread_mutex_t threadLock = {_SPINLOCK_INITIALIZER, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}} ;

static void remove_thread(jthread_t tid);
static void mark_thread_dead(void);
void dumpLiveThreads(int);

/*
 * the following variables are set by jthread_init, and show how the
 * threading system is parametrized.
 */
static void *(*allocator)(size_t); /* malloc */
static void (*deallocator)(void*); /* free */
static void (*destructor1)(void*); /* call when a thread exits */
static void (*onstop)(void); /* call when a thread is stopped */
static int max_priority; /* maximum supported priority */
static int min_priority; /* minimum supported priority */

pthread_key_t cookie_key; /* key to map pthread -> Hjava_lang_Thread */
pthread_key_t jthread_key; /* key to map pthread -> jthread */

/*
 * Function declarations.
 */

/*============================================================================
 *
 * Functions related to interrupt handling
 *
 */

/* All spin locks are one */
static pthread_mutex_t spin = {_SPINLOCK_INITIALIZER, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}} ;

/*
 * Native linux threads.
 *
 * This "spinlock" implementation will grind the machine to a halt...
 */
void
/* ARGSUSED */
jthread_spinon(void *arg)
{
        pthread_mutex_lock(&spin);
}

void
/* ARGSUSED */
jthread_spinoff(void *arg)
{
        pthread_mutex_unlock(&spin);
}

int
jthread_on_current_stack(void *bp)
{
#if defined(STACKGROWSUP)
        not yet
#else
        /* only need to check to one side !? */
        return (bp < GET_JTHREAD()->end);
#endif
}

int
jthread_stackcheck(int need)
{
        int x;
        struct jthread *jtid = GET_JTHREAD();

        /* Linux grows automatically to 2MB
         * We say 1 MB is enough for this guy.
         */
        return (jtid->end - (void*)&x < 1024*1024);
}

int
jthread_extract_stack(jthread_t jtid, void **from, unsigned *len)
{
        /* This information is not valid unless the thread is stopped */
        if (jtid->status != THREAD_STOPPED) {
                return (0);
        } else {
#if defined(STACKGROWSUP)
        not yet
#else
DBG(JTHREAD,
                dprintf("thread %d has a stack from %p to %p\n",
                        jtid->native_thread, jtid->sp, jtid->end);
    )
                *from = jtid->sp;
                *len = jtid->end - jtid->sp;
#endif
                return (1);
        }
}

void
resumeHere(int s)
{
        struct jthread *jtid = GET_JTHREAD();

DBG(JTHREAD,
        dprintf("%d received resume signal (%d->%d)\n", jtid->native_thread,
                jtid->saved_status, jtid->status);
    )
        jtid->status = jtid->saved_status;
}

/*
 * Note: in linuxthreads, none of the pthread_ functions are async-signal-safe.
 * That is, we can't use any of them in here or we would deadlock.
 *
 * This also means that if onstop simply jumps out of the signal handler
 * to deliver the ThreadDeathException we're screwed too. Great.
 */
void
stopHere(int s)
{
        int x;
        struct jthread *jtid = GET_JTHREAD();

DBG(JTHREADDETAIL,
        dprintf("thread %d (jthread@%p), pid %d caught signal\n",
                jtid->native_thread, jtid, getpid());
    )

        /* DIE */
        if (jtid->status == THREAD_DYING) {
DBG(JTHREAD,
                dprintf("calling onstop\n");
    )
                onstop();
        } else {
                sigset_t mask;

                jtid->sp = &x;
                jtid->saved_status = jtid->status;
DBG(JTHREAD,
                if (jtid->saved_status == THREAD_STOPPED) {
                        dprintf("%d already stopped!?\n", jtid->native_thread);
                }
    )
                jtid->status = THREAD_STOPPED;
                sem_post(&GC_suspend_ack_sem);

        DBG(JTHREAD,
                dprintf("%d stopped\n", jtid->native_thread);
            )
                sigfillset(&mask);
                sigdelset(&mask, SIG_RESUME);
                while (jtid->status == THREAD_STOPPED) {
                        sigsuspend(&mask);
                }

        DBG(JTHREAD,
                dprintf("%d resumed\n", jtid->native_thread);
            )
        }
}

/*
 * Stop all threads. pthread-compatible multiprocessor version
 */
void
jthread_suspendall(void)
{
        int n = 0, rc;
        struct jthread *tid, *current = GET_JTHREAD();

        /* taking and holding this lock prevents other threads from
         * starting while the world stands still.
         * The lock is unlocked in jthread_resumeall().
         */
        pthread_mutex_lock(&threadLock);
DBG(JTHREAD,
        dprintf("STARTING TO STOP THE WORLD\n");
    )
        for (tid = liveThreads; tid != NULL; tid = tid->nextlive) {
                if (tid == current) {
DBG(JTHREADDETAIL,
                        dprintf("skipping myself %d\n", tid->native_thread);
    )
                        continue;
                }
                if (!(tid->status == THREAD_RUNNING ||
                      tid->status == THREAD_DYING))
                {
DBG(JTHREADDETAIL,
                        dprintf("skipping thread %d (s=%d)\n",
                                tid->native_thread, tid->status);
    )
                        continue;
                }
DBG(JTHREAD,
                dprintf("attempting to stop %d\n", tid->native_thread);
    )
                n++;
                rc = pthread_kill(tid->native_thread, SIG_STOP);
                switch (rc) {
                case ESRCH:
DBG(JTHREAD,
                        dprintf("didn't find %d\n", tid->native_thread);
    )
                        n--;
                        break;
                case 0:
                        break;
                default:
                        fprintf(stderr, "pthread_kill failed %d\n", rc);
                        exit(-1);
                }
        }
DBG(JTHREAD,
        dprintf("waiting for %d threads till stop completes...\n", n);
    )
        while (n-- > 0) {
DBG(JTHREADDETAIL,
                dprintf("%d left\n", n+1);
    )
                sem_wait(&GC_suspend_ack_sem);
        }
DBG(JTHREAD,
        dprintf("ALL THREADS STOPPED\n");
    )
}

void
jthread_unsuspendall(void)
{
        struct jthread *tid;
        for (tid = liveThreads; tid != NULL; tid = tid->nextlive) {
                if (tid->status != THREAD_STOPPED) {
                        continue;
                }
DBG(JTHREAD,
                dprintf("unsuspending %d\n", tid->native_thread);
    )
                pthread_kill(tid->native_thread, SIG_RESUME);
        }
DBG(JTHREAD,
        dprintf("ALL THREADS RESUMED\n");
    )
        pthread_mutex_unlock(&threadLock);
}

/*============================================================================
 *
 * Functions dealing with thread contexts and the garbage collection interface
 *
 */

/*
 * free a thread context
 */
void
jthread_destroy(jthread_t tid)
{
        void *status;

        assert(tid);
DBG(JTHREAD,
        dprintf("destroying tid %d\n", tid->native_thread);
    )
        pthread_join(tid->native_thread, &status);
        deallocator(tid);
}

/*
 * find a native pthread's cookie
 * (Maps pthreads to java.lang.Threads)
 */
void*
jthread_getcookie(void* ntid)
{
        struct jthread* tid;

        for (tid = liveThreads; tid != NULL; tid = tid->nextlive) {
                if ((void*)tid->native_thread == ntid) {
                        return (tid->jlThread);
                }
        }
        return (0);
}

/*
 * iterate over all live threads
 */
void
jthread_walkLiveThreads(void (*func)(void *jlThread))
{
        jthread_t tid;

        pthread_mutex_lock(&threadLock);
        for (tid = liveThreads; tid != NULL; tid = tid->nextlive) {
                func(tid->jlThread);
        }
        pthread_mutex_unlock(&threadLock);
}

/*
 * XXX this is supposed to count the number of stack frames
 */
int
jthread_frames(jthread_t thrd)
{
        return 0;
}

/*============================================================================
 *
 * Functions for initialization and thread creation
 *
 */

/*
 * Initialize the threading system.
 *
 * XXX: pthread_init() has already been called.
 */
jthread_t
jthread_init(int pre,
        int maxpr, int minpr, int mainthreadpr,
        size_t mainThreadStackSize,
        void *(*_allocator)(size_t),
        void (*_deallocator)(void*),
        void (*_destructor1)(void*),
        void (*_onstop)(void),
        void (*_ondeadlock)(void)) /* ignored for now */
{
        pthread_t pmain;
        jthread_t jtid;
        struct sigaction act;

        max_priority = maxpr;
        min_priority = minpr;
        allocator = _allocator;
        deallocator = _deallocator;
        onstop = _onstop;
        destructor1 = _destructor1;

        pmain = pthread_self();

        /*
         * XXX: ignore mapping of min/max priority for now and assume
         * pthread priorities include java priorities.
         *
         * This does not hold true for Linux pthreads, where we must
         * scale priorities and renice.
         */
        /* XXX: use
           pthread_setschedparam here --- Linux doesn't have setprio
           pthread_setprio(pmain, mainthreadpr);
         */

        pthread_key_create(&jthread_key, 0 /* destructor */);
        pthread_key_create(&cookie_key, 0 /* destructor */);

        jtid = allocator(sizeof (*jtid));
        SET_JTHREAD(jtid);

        jtid->native_thread = pmain;

        jtid->nextlive = liveThreads;
        liveThreads = jtid;
        jtid->status = THREAD_RUNNING;
        talive++;
DBG(JTHREAD,
        dprintf("main thread has id %d\n", jtid->native_thread);
    )
#if defined(STACKGROWSUP)
        jtid->sp = jtid->base = &pmain;
#else
        jtid->sp = jtid->end = &pmain;
#endif
        act.sa_handler = resumeHere;
        sigfillset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
        sigaction(SIG_RESUME, &act, 0);

        act.sa_handler = stopHere;
        sigfillset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
        sigaction(SIG_STOP, &act, 0);

        if (0 != sem_init(&GC_suspend_ack_sem, 0, 0)) {
                fprintf(stderr, "couldn't init sem\n");
                abort();
        }
#if defined(DEBUG)
        signal(SIGHUP, dumpLiveThreads);
#endif
        return (jtid);
}

/*
 * set a function to be run when all non-daemon threads have exited
 */
void
jthread_atexit(void (*f)(void))
{
        runOnExit = f;
}

/*
 * disallow cancellation
 */
void
jthread_disable_stop(void)
{
        /* XXX */
}

/*
 * reallow cancellation and stop if cancellation pending
 */
void
jthread_enable_stop(void)
{
        /* XXX */
}

/*
 * interrupt a thread
 */
void
jthread_interrupt(jthread_t tid)
{
    fprintf(stderr, "jthread_interrupt is not yet implemented\n");
    /* ignore */
}

/*
 * start function for each thread.
 * This function sets jthread-specific data and calls the actual work function.
 */
void*
start_me_up(void *arg)
{
        jthread_t tid = (jthread_t)arg;

DBG(JTHREAD,
        dprintf("starting thread %p\n", tid);
    )
        pthread_mutex_lock(&threadLock);
        SET_JTHREAD(tid);
        SET_COOKIE(tid->jlThread);
#if defined(STACKGROWSUP)
        tid->sp = tid->base = &tid;
#else
        tid->sp = tid->end = &tid;
#endif
        tid->status = THREAD_RUNNING;
        pthread_mutex_unlock(&threadLock);

DBG(JTHREAD,
        dprintf("calling thread %p, %d\n", tid, tid->native_thread); )
        tid->func(tid->jlThread);
DBG(JTHREAD,
        dprintf("thread %d returned, calling jthread_exit\n",
                tid->native_thread);
    )

        /* drop onstop handler if that thread is exiting by itself */
        assert (tid->status != THREAD_DYING);

        mark_thread_dead();
        /* by returning, we exit this thread */
        return (0);
}

/*
 * create a new jthread
 */
jthread_t
jthread_create(unsigned int pri, void (*func)(void *), int daemon,
        void *jlThread, size_t threadStackSize)
{
        int err;
        pthread_t new;
        jthread_t tid;
        pthread_attr_t attr;

        pthread_attr_init(&attr);

        /* use setschedparam here */
        /* Linux doesn't have setstacksize */
        /*
            pthread_attr_setstacksize(&attr, threadStackSize);
            pthread_attr_setprio(&attr, pri);
        */

        /*
         * Note that we create the thread in a joinable state, which is the
         * default. Our finalizer will join the threads, allowing the
         * pthread system to free its resources.
         */

        tid = allocator(sizeof (*tid));
        assert(tid != 0); /* XXX */

        pthread_mutex_lock(&threadLock);
        tid->jlThread = jlThread;
        tid->func = func;

        tid->nextlive = liveThreads;
        liveThreads = tid;
        tid->status = THREAD_NEWBORN;

        err = pthread_create(&new, &attr, start_me_up, tid);
        tid->native_thread = new;

        talive++;
        if ((tid->daemon = daemon) != 0) {
                tdaemon++;
        }
        pthread_mutex_unlock(&threadLock);
DBG(JTHREAD,
        dprintf("created thread %d, daemon=%d\n", new, daemon); )

        return (tid);
}

/*============================================================================
 *
 * Functions that are part of the user interface
 *
 */

/*
 * sleep for time milliseconds
 */
void
jthread_sleep(jlong time)
{
        /* XXX */
}

/*
 * Check whether a thread is alive.
 *
 * Note that threads executing their cleanup function are not (jthread-) alive.
 * (they're set to THREAD_DEAD)
 */
int
jthread_alive(jthread_t tid)
{
        return tid && (tid->status == THREAD_NEWBORN ||
                       tid->status == THREAD_RUNNING);
}

/*
 * Change thread priority.
 */
void
jthread_setpriority(jthread_t jtid, int prio)
{
        /* XXX use setschedparam */
}

/*
 * Stop a thread in its tracks.
 */
void
jthread_stop(jthread_t jtid)
{
        /* can I cancel myself safely??? */
        /* NB: jthread_stop should never be invoked on the current thread */
        jtid->status = THREAD_DYING;
        pthread_kill(jtid->native_thread, SIG_STOP);
}

static void
remove_thread(jthread_t tid)
{
        jthread_t* ntid;
        int found = 0;
        pthread_mutex_lock(&threadLock);

        talive--;
        if (tid->daemon) {
                tdaemon--;
        }

        /* Remove thread from live list so it can be garbage collected */
        for (ntid = &liveThreads; *ntid != 0; ntid = &(*ntid)->nextlive)
        {
                if (tid == (*ntid)) {
                        found = 1;
                        (*ntid) = tid->nextlive;
                        break;
                }
        }

        assert(found);
        pthread_mutex_unlock(&threadLock);

        /* If we only have daemons left, then we should exit. */
        if (talive == tdaemon) {
DBG(JTHREAD,
                dprintf("all done, closing shop\n");
    )
                if (runOnExit != 0) {
                    runOnExit();
                }

                /* does that really make sense??? */
                for (tid = liveThreads; tid != 0; tid = tid->nextlive) {
                        if (destructor1) {
                                (*destructor1)(tid->jlThread);
                        }
                }

                /* Am I suppose to close things down nicely ?? */
                EXIT(0);
        } else {
                if (destructor1) {
                        (*destructor1)(tid->jlThread);
                }
        }
}

/*
 * mark the current thread as dead and remove it from the lists.
 */
static void
mark_thread_dead(void)
{
        jthread_t currentJThread = GET_JTHREAD();
        assert (currentJThread->status != THREAD_DEAD);
        currentJThread->status = THREAD_DEAD;

        remove_thread(currentJThread);
}

/*
 * Have a thread exit.
 * Each thread exits only once.
 */
void
jthread_exit(void)
{
        jthread_t currentJThread = GET_JTHREAD();

DBG(JTHREAD,
        dprintf("jthread_exit called by %d\n", currentJThread->native_thread);
    )

        mark_thread_dead();
        pthread_exit(0);
        while (1)
                assert(!"This better not return.");
}

/*
 * Print info about a given jthread to stderr
 */
void
jthread_dumpthreadinfo(jthread_t tid)
{
        fprintf(stderr, "jthread %p native %ld status %s\n",
                tid, tid->native_thread,
                tid->status == THREAD_NEWBORN ? "NEWBORN" :
                tid->status == THREAD_RUNNING ? "RUNNING" :
                tid->status == THREAD_DYING ? "DYING" :
                tid->status == THREAD_DEAD ? "DEAD" :
                tid->status == THREAD_CONTINUE? "CONTINUE":
                tid->status == THREAD_STOPPED ? "STOPPED" : "???");
}

/*
 * dump info on all live threads
 */
void
/* ARGSUSED */
dumpLiveThreads(int s)
{
        jthread_t tid;
        for (tid = liveThreads; tid != NULL; tid = tid->nextlive) {
                jthread_dumpthreadinfo(tid);
        }
}

/*
 * have main thread wait for all threads to finish
 */
void jthread_exit_when_done(void)
{
        while (talive > 1)
                jthread_yield();
        jthread_exit();
}

/*============================================================================
 *
 * locking subsystem
 *
 */

void
jmutex_initialise(jmutex *lock)
{
        if (0 != pthread_mutex_init(lock, (const pthread_mutexattr_t *)0)) {
                assert(!!!"Could not initialise mutex"); /* XXX */
        }
}

void
jmutex_lock(jmutex *lock)
{
        pthread_mutex_lock(lock);
}

void
jmutex_unlock(jmutex *lock)
{
        pthread_mutex_unlock(lock);
}

void
jcondvar_initialise(jcondvar *cv)
{
        if (0 != pthread_cond_init(cv, (const pthread_condattr_t *)0)) {
                assert(!!!"Could not initialise condvar"); /* XXX */
        }
}

void
jcondvar_wait(jcondvar *cv, jmutex *lock, jlong timeout)
{
        struct timespec abstime;
        struct timeval now;

        if (timeout == (jlong)0) {
                pthread_cond_wait(cv, lock);
                return;
        }

        /* Need to convert timeout to an abstime. Very dumb! */
        gettimeofday(&now, 0);
        TIMEVAL_TO_TIMESPEC(&now, &abstime);

        abstime.tv_sec += timeout / 1000;
        abstime.tv_nsec += (timeout % 1000) * 1000000;
        if (abstime.tv_nsec > 1000000000) {
                abstime.tv_sec += 1;
                abstime.tv_nsec -= 1000000000;
        }
        pthread_cond_timedwait(cv, lock, &abstime);
}

void
jcondvar_signal(jcondvar *cv, jmutex *lock)
{
        pthread_cond_signal(cv);
}

void
jcondvar_broadcast(jcondvar *cv, jmutex *lock)
{
        pthread_cond_broadcast(cv);
}


Date view Thread view Subject view Author view

This archive was generated by hypermail 2b29 : Sat Sep 23 2000 - 19:58:02 EDT