Milán Major

netscape-revival

1 branch
Code

nspr-/src/swsched.c

#ifdef SW_THREADS
#include <stdio.h>
#include "prlog.h"
#include "prtime.h"
#include "swkern.h"
#include "mdint.h"

#ifdef XP_MAC
#include "prmacos.h"
#endif

#ifdef PURIFY
#include "pure_thr.h"
#endif

PR_LOG_DEFINE(SCHED);
extern PRLogModule CLOCKLog;
#ifdef XP_UNIX
extern PRLogModule IOLog;
#endif

#ifdef PR_NO_PREEMPT
int pr_want_resched;
#endif

#if defined(XP_PC) && !defined(_WIN32)
char *  _pr_top_of_task_stack;
uint32  _pr_PostEventMsgId;
#endif  /* XP_PC && ! _WIN32 */

PRCList _pr_runq[32];
PRCList _pr_sleepq;
PRCList _pr_zombieq;
PRCList _pr_suspendq;
PRCList _pr_lockq;
PRCList _pr_monitorq;

int32 _pr_runq_ready_mask;
int64 _pr_now;
int _pr_user_active;
int _pr_system_active;
PRThread *_pr_current_thread;
PRThread *_pr_idle;

PRMonitor *_pr_heap_lock;
PRMonitor *_pr_rusty_lock;
PRMonitor *_pr_single_lock;
PRMonitor *_pr_dns_lock;
PRMonitor *_pr_child_intr;
PRMonitor *_pr_async_io_intr;
/* This is used by utilities like WH_TempName and WH_FileName: */
PRMonitor *_pr_TempName_lock;

PR_PUBLIC_API(void) PR_SingleThread(void)
{
#ifdef XP_MAC
	// Turn off the time-slicing timer.  When the timer fires, we check to see
	// if there are sleeping threads & rewake them if necessary.  That would be bad. 
    DeactivateTimer();
#endif

    PR_EnterMonitor(_pr_single_lock);
}

PR_PUBLIC_API(void) PR_MultiThread(void)
{
    PR_ExitMonitor(_pr_single_lock);

#ifdef XP_MAC
    ActivateTimer();
#endif
}

/************************************************************************/

/*
** Pick a new thread to run.
*/
void _PR_Schedule(void)
{
    PRThread *thread;
    int32 pri;
    unsigned long r;

    /* Interrupts must be off the entire time we are doing this */
    PR_ASSERT(_pr_intsOff != 0);

#ifdef PR_NO_PREEMPT
    /* Since we are rescheduling, we no longer want to */
    pr_want_resched = 0;
#endif

    /*
    ** Find highest priority thread to run. Bigger priority numbers are
    ** higher priority threads
    */
    r = _pr_runq_ready_mask;
    pri = 0;
    if (r >> 16) { pri += 16; r >>= 16; }
    if (r >> 8) { pri += 8; r >>= 8; }
    if (r >> 4) { pri += 4; r >>= 4; }
    if (r >> 2) { pri += 2; r >>= 2; }
    if (r >> 1) { pri++; }
    PR_ASSERT(_pr_runq[pri].next != &_pr_runq[pri]);

    /* Pull thread off of its run queue */
    thread = THREAD_PTR(_pr_runq[pri].next);
    PR_REMOVE_LINK(&thread->runqLinks);
    PR_INIT_CLIST(&thread->runqLinks);
    if (_pr_runq[pri].next == &_pr_runq[pri]) {
	_pr_runq_ready_mask &= ~(1L << pri);
    }

    /* Resume the thread */
    thread->state = _PR_RUNNING;

    /* _pr_current_thread is now updated inside of _MD_RESTORE_CONTEXT */
    _MD_RESTORE_CONTEXT(thread);

    PR_NOT_REACHED("No more processes to schedule -- deadlock.");
}

#ifdef XP_MAC
// FIX ME!!!!
//	This is a temporary hack!!!
#include <Types.h>
extern long gInTimerCallback;
#endif

void _PR_SwitchThread(PRThread *thread)
{
    PR_ASSERT(thread == _pr_current_thread);
    PR_ASSERT(_pr_intsOff != 0);
    
#ifdef XP_MAC
	if (gInTimerCallback)
		DebugStr("\pFATAL ERROR: Attempt to reschedule at interrupt time!");
#endif

    _MD_SWITCH_CONTEXT(thread);

    PR_ASSERT(thread == _pr_current_thread);
    PR_ASSERT(_pr_intsOff != 0);

#ifdef PURIFY
    pure_notice_context_switch(&thread->id);
#endif
}

/*
** Put thread on sleepQ in the right position for it's desired sleep time
*/
void _PR_PutOnSleepQ(PRThread *thread, int64 sleep)
{
    PRCList *qp;
    PRThread *t;

    PR_ASSERT(_pr_intsOff != 0);
    PR_ASSERT(LL_GE_ZERO(sleep));
    PR_ASSERT(PR_CLIST_IS_EMPTY(&thread->runqLinks));

    /* Sort thread into sleepQ at appropriate point */
    PR_LOG(SCHED, debug,
	   ("put %s on sleepQ for %lld", thread->name, sleep));
    qp = _pr_sleepq.next;
    while (qp != &_pr_sleepq) {
	t = THREAD_PTR(qp);
	if (LL_CMP(sleep, <, t->sleep)) {
	    /* Found sleeper to insert in front of */
	    break;
	}
	LL_SUB(sleep, sleep, t->sleep);
	qp = qp->next;
    }
    thread->sleep = sleep;
    PR_INSERT_BEFORE(&thread->runqLinks, qp);
    thread->flags |= _PR_ON_SLEEPQ;

    /*
    ** Subtract our sleeptime from the sleeper that follows us (if there
    ** is one) so that they remain relative to us.
    */
    if (thread->runqLinks.next != &_pr_sleepq) {
	t = THREAD_PTR(thread->runqLinks.next);
	PR_ASSERT(THREAD_PTR(t->runqLinks.prev) == thread);
	LL_SUB(t->sleep, t->sleep, sleep);
    }

#ifdef DEBUG
    {
	int i = 0;
	PR_LOG(SCHED, debug, ("_pr_sleepq: "));
	qp = _pr_sleepq.next;
	while (qp != &_pr_sleepq) {
	    t = THREAD_PTR(qp);
	    PR_LOG(SCHED, debug,
		   ("[%d] %s %lld", i, t->name, t->sleep));
	    i++;
	    qp = qp->next;
	}
    }
#endif
}

/*
** If there is a thread on the sleepQ following "thread", give it
** thread's remaining sleep time to keep the sleepQ accurate.
*/
void _PR_FixSleepQ(PRThread *thread)
{
    if (thread->runqLinks.next != &_pr_sleepq) {
	/* There is a thread after us */
	PRThread *t = THREAD_PTR(thread->runqLinks.next);
	PR_ASSERT(THREAD_PTR(t->runqLinks.prev) == thread);
	LL_ADD(t->sleep, t->sleep, thread->sleep);
    }
}

/************************************************************************/

#ifndef XP_UNIX
void _PR_Idle(void)
{
    for (;;) {
    PRThread *t = PR_CurrentThread();
	_PR_FreeZombies();
	PR_LOG_BEGIN(SCHED, warn, ("idle: begin waiting for i/o"));
        _PR_Pause();
	PR_LOG_END(SCHED, warn, ("idle: end waiting for i/o"));
    }
}
#else
/* Look in prunix.c */
extern void _PR_Idle(void);
#endif

static PRBool initialized;

PR_PUBLIC_API(PRBool) PR_Initialized(void)
{
    return initialized;
}

/*
** Initialize the kernel
*/
PR_PUBLIC_API(void) PR_Init(char *name, int priority, int maxCpus, int flags_notused)
{
    PRThread *thread;
    int i;
    PRThreadStack *ts;

    if (initialized) return;
    initialized = PR_TRUE;

#ifdef PURIFY
    pure_init_threads();
#endif

    _MD_InitOS(_MD_INIT_AT_START);

#ifdef DEBUG
    /*
    ** We need to pre-initialize these because the logging mechanism
    ** depends on them.
    */
    PR_LogInit(&SCHEDLog);
    PR_LogInit(&CLOCKLog);
#ifdef XP_UNIX
    PR_LogInit(&IOLog);
#endif
#endif 

    if (!pr_pageSize) {
	PR_InitPageStuff();
    }

    /* Setup various queue's */
    for (i = 0; i < 32; i++) {
	PR_INIT_CLIST(&_pr_runq[i]);
    }
    PR_INIT_CLIST(&_pr_suspendq);
    PR_INIT_CLIST(&_pr_sleepq);
    PR_INIT_CLIST(&_pr_zombieq);
    PR_INIT_CLIST(&_pr_monitorq);

    /* Create thread that represents the caller */
    ts = (PRThreadStack*) calloc(1, sizeof(PRThreadStack));
    PR_ASSERT(ts != 0);
#ifdef HAVE_STACK_GROWING_UP
    ts->stackTop = (char*)
	((((prword_t)&name) >> pr_pageShift) << pr_pageShift);
#else
#if defined(XP_PC) && !defined(_WIN32)
    _pr_top_of_task_stack = (char *) &name;
    ts->stackTop = (char *) &name;
    ts->stackSize = _MD_THREAD_STACK_SIZE;
#else
    ts->stackTop = (char*)
	((((prword_t)&name + pr_pageSize - 1) >> pr_pageShift) << pr_pageShift);
#endif
#endif
    thread = PR_AttachThread(name, priority, ts);
    PR_ASSERT(thread != 0);
    thread->state = _PR_RUNNING;
    thread->flags = 0;

    _pr_current_thread = thread;
    _pr_user_active++;

    /* Get misc locks now that enough init has been done */
    _pr_heap_lock = PR_NewNamedMonitor(0, "heap-lock");
    PR_ASSERT(_pr_heap_lock != 0);
    _pr_rusty_lock = PR_NewNamedMonitor(0, "rusty-lock");
    PR_ASSERT(_pr_rusty_lock != 0);
    _pr_dns_lock = PR_NewNamedMonitor(0, "dns-lock");
    PR_ASSERT(_pr_dns_lock != 0);
    _pr_child_intr = PR_NewNamedMonitor(0, "child-intr");
    PR_ASSERT(_pr_child_intr != 0);
    _pr_async_io_intr = PR_NewNamedMonitor(0, "async-io-intr");
    PR_ASSERT(_pr_async_io_intr != 0);
    _pr_TempName_lock = PR_NewNamedMonitor(0, "TempName-lock");
    PR_ASSERT(_pr_TempName_lock != 0);

    /* Get single/multi lock */
    _pr_single_lock = PR_NewNamedMonitor(0, "single-lock");
    PR_ASSERT(_pr_single_lock != 0);
    _PR_InitMonitorCache();
    _PR_InitIO();
    _PR_InitPrivateIDs();

    /* Create idle thread */
    _pr_idle = PR_CreateThread("idle", 0, 0);
    PR_ASSERT(_pr_idle != 0);
    _pr_idle->flags |= _PR_SYSTEM;
    if (PR_Start(_pr_idle, (void (*)(void*,void*))_PR_Idle, 0, 0) < 0) {
	fprintf(stderr, "PR_Init: can't start idle thread\n");
	PR_Abort();
    }

    /* Initialize current time and start the clock */
    _pr_now = PR_Now();
    _MD_StartClock();

    /* Finish up machine dependent os init */
    _MD_InitOS(_MD_INIT_READY);
}

/*
** Shutdown NSPR
*/
PR_PUBLIC_API(void) PR_Shutdown(void)
{
    /* Unload all loaded DLLs */
    _PR_ShutdownLinker();
}
#endif