/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "primpl.h"
#include <Types.h>
#include <Timer.h>
#include <OSUtils.h>
#include <LowMem.h>
// This should be in LowMem.h
#ifndef LMSetStackLowPoint
#define LMSetStackLowPoint(value) \
*((UInt32 *)(0x110)) = (value)
#endif
PRThread *gPrimaryThread = NULL;
PR_IMPLEMENT(PRThread *) PR_GetPrimaryThread()
{
return gPrimaryThread;
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark CREATING MACINTOSH THREAD STACKS
/*
** Allocate a new memory segment. We allocate it from our figment heap. Currently,
** it is being used for per thread stack space.
**
** Return the segment's access rights and size. vaddr is used on Unix platforms to
** map an existing address for the segment.
*/
PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
{
PR_ASSERT(seg != 0);
PR_ASSERT(size != 0);
PR_ASSERT(vaddr == 0);
/*
** Take the actual memory for the segment out of our Figment heap.
*/
seg->vaddr = (char *)malloc(size);
if (seg->vaddr == NULL) {
#if DEBUG
DebugStr("\p_MD_AllocSegment failed.");
#endif
return PR_FAILURE;
}
seg->size = size;
return PR_SUCCESS;
}
/*
** Free previously allocated memory segment.
*/
void _MD_FreeSegment(PRSegment *seg)
{
PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
if (seg->vaddr != NULL)
DisposePtr((Ptr)(seg->vaddr));
}
/*
** The thread's stack has been allocated and its fields are already properly filled
** in by PR. Perform any debugging related initialization here.
**
** Put a recognizable pattern so that we can find it from Macsbug.
** Put a cookie at the top of the stack so that we can find it from Macsbug.
*/
void _MD_InitStack(PRThreadStack *ts, int redZoneBytes)
{
#pragma unused (redZoneBytes)
#if DEVELOPER_DEBUG
// Put a cookie at the top of the stack so that we can find
// it from Macsbug.
memset(ts->allocBase, 0xDC, ts->stackSize);
((UInt32 *)ts->stackTop)[-1] = 0xBEEFCAFE;
((UInt32 *)ts->stackTop)[-2] = (UInt32)gPrimaryThread;
((UInt32 *)ts->stackTop)[-3] = (UInt32)(ts);
((UInt32 *)ts->stackBottom)[0] = 0xCAFEBEEF;
#else
#pragma unused (ts)
#endif
/*
** Turn off the snack stiffer. The NSPR stacks are allocated in the
** application's heap; this throws the stack sniffer for a tizzy.
** Note that the sniffer does not run on machines running the thread manager.
** Yes, we will blast the low-mem every time a new stack is created. We can afford
** a couple extra cycles.
*/
LMSetStackLowPoint(0);
}
extern void _MD_ClearStack(PRThreadStack *ts)
{
#if DEVELOPER_DEBUG
// Clear out our cookies.
memset(ts->allocBase, 0xEF, ts->allocSize);
((UInt32 *)ts->stackTop)[-1] = 0;
((UInt32 *)ts->stackTop)[-2] = 0;
((UInt32 *)ts->stackTop)[-3] = 0;
((UInt32 *)ts->stackBottom)[0] = 0;
#else
#pragma unused (ts)
#endif
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark TIME MANAGER-BASED CLOCK
TMTask gTimeManagerTaskElem;
extern void _MD_IOInterrupt(void);
_PRInterruptTable _pr_interruptTable[] = {
{ "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, },
{ "i/o", _PR_MISSED_IO, _MD_IOInterrupt, },
{ 0 }
};
pascal void TimerCallback(TMTaskPtr tmTaskPtr)
{
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
if (_PR_MD_GET_INTSOFF()) {
cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
return;
}
_PR_MD_SET_INTSOFF(1);
// And tell nspr that a clock interrupt occured.
_PR_ClockInterrupt();
if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
_PR_SET_RESCHED_FLAG();
_PR_MD_SET_INTSOFF(0);
// Reset the clock timer so that we fire again.
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
}
#if GENERATINGCFM
RoutineDescriptor gTimerCallbackRD = BUILD_ROUTINE_DESCRIPTOR(uppTimerProcInfo, &TimerCallback);
#else
asm void gTimerCallbackRD(void)
{
// Check out LocalA5. If it is zero, then
// it is our first time through, and we should
// store away our A5. If not, then we are
// a real time manager callback, so we should
// store away A5, set up our local a5, jsr
// to our callback, and then restore the
// previous A5.
lea LocalA5, a0
move.l (a0), d0
cmpi.l #0, d0
bne TimerProc
move.l a5, (a0)
rts
TimerProc:
// Save A5, restore our local A5
move.l a5, -(sp)
move.l d0, a5
// Jump to our C routine
move.l a1, -(sp)
jsr TimerCallback
// Restore the previous A5
move.l (sp)+, a5
rts
LocalA5:
dc.l 0
}
#endif
void _MD_StartInterrupts(void)
{
gPrimaryThread = _PR_MD_CURRENT_THREAD();
// If we are not generating CFM-happy code, then make sure that
// we call our callback wrapper once so that we can
// save away our A5.
#if !GENERATINGCFM
gTimerCallbackRD();
#endif
// Fill in the Time Manager queue element
gTimeManagerTaskElem.tmAddr = (TimerUPP)&gTimerCallbackRD;
gTimeManagerTaskElem.tmCount = 0;
gTimeManagerTaskElem.tmWakeUp = 0;
gTimeManagerTaskElem.tmReserved = 0;
// Make sure that our time manager task is ready to go.
InsTime((QElemPtr)&gTimeManagerTaskElem);
PrimeTime((QElemPtr)&gTimeManagerTaskElem, kMacTimerInMiliSecs);
}
void _MD_StopInterrupts(void)
{
if (gTimeManagerTaskElem.tmAddr != NULL) {
RmvTime((QElemPtr)&gTimeManagerTaskElem);
gTimeManagerTaskElem.tmAddr = NULL;
}
}
void _MD_PauseCPU(PRIntervalTime timeout)
{
#pragma unused (timeout)
unsigned long finalTicks;
if (timeout != PR_INTERVAL_NO_WAIT) {
Delay(1,&finalTicks);
(void) _MD_IOInterrupt();
}
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark THREAD SUPPORT FUNCTIONS
#include <OpenTransport.h> /* for error codes */
PRStatus _MD_InitThread(PRThread *thread)
{
thread->md.asyncIOLock = PR_NewLock();
PR_ASSERT(thread->md.asyncIOLock != NULL);
thread->md.asyncIOCVar = PR_NewCondVar(thread->md.asyncIOLock);
PR_ASSERT(thread->md.asyncIOCVar != NULL);
if (thread->md.asyncIOLock == NULL || thread->md.asyncIOCVar == NULL)
return PR_FAILURE;
else
return PR_SUCCESS;
}
PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout)
{
#pragma unused (timeout)
_MD_SWITCH_CONTEXT(thread);
return PR_SUCCESS;
}
void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
{
intn is;
PRIntervalTime timein = PR_IntervalNow();
PRStatus status = PR_SUCCESS;
_PR_INTSOFF(is);
PR_Lock(thread->md.asyncIOLock);
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
while ((thread->io_pending) && (status == PR_SUCCESS))
status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
} else {
while ((thread->io_pending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout))
status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
}
if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
thread->md.osErrCode = kEINTRErr;
} else if (thread->io_pending) {
thread->md.osErrCode = kETIMEDOUTErr;
PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
}
PR_Unlock(thread->md.asyncIOLock);
_PR_FAST_INTSON(is);
}
void DoneWaitingOnThisThread(PRThread *thread)
{
intn is;
_PR_INTSOFF(is);
PR_Lock(thread->md.asyncIOLock);
thread->io_pending = PR_FALSE;
/* let the waiting thread know that async IO completed */
PR_NotifyCondVar(thread->md.asyncIOCVar);
PR_Unlock(thread->md.asyncIOLock);
_PR_FAST_INTSON(is);
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark PROCESS SUPPORT FUNCTIONS
PRProcess * _MD_CreateProcess(
const char *path,
char *const *argv,
char *const *envp,
const PRProcessAttr *attr)
{
#pragma unused (path, argv, envp, attr)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
return NULL;
}
PRStatus _MD_DetachProcess(PRProcess *process)
{
#pragma unused (process)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
return PR_FAILURE;
}
PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode)
{
#pragma unused (process, exitCode)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
return PR_FAILURE;
}
PRStatus _MD_KillProcess(PRProcess *process)
{
#pragma unused (process)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
return PR_FAILURE;
}
syntax highlighted by Code2HTML, v. 0.9.1