diff --git a/mps/code/awlutth.c b/mps/code/awlutth.c index cecdc77bfbc..8e317d6c0ae 100644 --- a/mps/code/awlutth.c +++ b/mps/code/awlutth.c @@ -19,7 +19,7 @@ #include "mpsw3.h" #endif #include -#ifdef MPS_OS_LI +#if defined(MPS_OS_LI) || defined(MPS_OS_FR) #include #endif @@ -274,7 +274,7 @@ static void *setup(void *v, size_t s) "Weak AP Create\n"); die(mps_ap_create(&bogusap, tablepool, MPS_RANK_EXACT), "Bogus AP Create\n"); - + test(leafap, exactap, weakap, bogusap); mps_ap_destroy(bogusap); diff --git a/mps/code/eventcnv.c b/mps/code/eventcnv.c index 5d13c00de63..fd17d442702 100644 --- a/mps/code/eventcnv.c +++ b/mps/code/eventcnv.c @@ -125,12 +125,12 @@ static char *parseArgs(int argc, char *argv[]) { char *name = "mpsio.log"; int i = 1; - + if (argc >= 1) prog = argv[0]; else prog = "unknown"; - + while (i < argc) { /* consider argument i */ if (argv[i][0] == '-') { /* it's an option argument */ switch (argv[i][1]) { @@ -138,9 +138,9 @@ static char *parseArgs(int argc, char *argv[]) ++ i; if (i == argc) usageError(); - else + else name = argv[i]; - break; + break; case 'p': /* partial log */ partialLog = TRUE; break; @@ -154,7 +154,7 @@ static char *parseArgs(int argc, char *argv[]) usageError(); else parseEventSpec(argv[i]); - } break; + } break; case 'b': { /* bucket size */ ++ i; if (i == argc) @@ -165,7 +165,7 @@ static char *parseArgs(int argc, char *argv[]) n = sscanf(argv[i], "%lu", &bucketSize); if (n != 1) usageError(); } - } break; + } break; case 'S': /* style */ style = argv[i][2]; /* '\0' for human-readable, 'L' for Lisp, */ break; /* 'C' for CDF. */ @@ -192,7 +192,7 @@ static void processEvent(EventProc proc, Event event, Word etime) if (res != ResOK) error("Can't record event: error %d.", res); switch(event->any.code) { - default: + default: break; } } @@ -246,7 +246,7 @@ static void reportEventResults(eventCountArray eventCounts) { EventCode i; unsigned long total = 0; - + for(i = 0; i <= EventCodeMAX; ++i) { total += eventCounts[i]; if (eventEnabled[i]) @@ -441,7 +441,7 @@ static void readLog(EventProc proc) printf("%u", (unsigned)code); break; } - + switch (style) { case '\0': printf(" %8lu", (ulong)eventTime); break; @@ -591,11 +591,16 @@ int main(int argc, char *argv[]) EventProc proc; Res res; +#if !defined(MPS_OS_FR) + /* GCC -ansi -pedantic -Werror on FreeBSD will fail here + * with the warning "statement with no effect". */ + assert(CHECKCONV(ulong, Word)); assert(CHECKCONV(ulong, Addr)); assert(CHECKCONV(ulong, void *)); assert(CHECKCONV(unsigned, EventCode)); assert(CHECKCONV(Addr, void *)); /* for labelled pointers */ +#endif filename = parseArgs(argc, argv); diff --git a/mps/code/eventpro.c b/mps/code/eventpro.c index 9cb8700fe8d..dae0aecf1c9 100644 --- a/mps/code/eventpro.c +++ b/mps/code/eventpro.c @@ -19,7 +19,6 @@ #include /* size_t */ #include /* strcmp */ - struct EventProcStruct { Bool partialLog; /* Is this a partial log? */ EventProcReader reader; /* reader fn */ @@ -91,7 +90,7 @@ static eventRecord eventTypes[] = { static size_t eventType2Index(EventType type) { size_t i; - + for(i = 0; i < eventTypeCount; ++i) if (eventTypes[i].type == type) return i; @@ -105,7 +104,7 @@ static size_t eventType2Index(EventType type) static size_t eventCode2Index(EventCode code, Bool errorp) { size_t i; - + for(i = 0; i < eventTypeCount; ++i) if (eventTypes[i].code == code) return i; @@ -120,7 +119,7 @@ static size_t eventCode2Index(EventCode code, Bool errorp) EventCode EventName2Code(char *name) { size_t i; - + for(i = 0; i < eventTypeCount; ++i) if (strcmp(eventTypes[i].name, name) == 0) { assert(eventTypes[i].code <= EventCodeMAX); @@ -250,7 +249,7 @@ EventString LabelText(EventProc proc, Word id) Res EventRead(Event *eventReturn, EventProc proc) { - size_t index, length; + size_t eventIndex, length; Res res; EventType type; Event event; @@ -259,8 +258,8 @@ Res EventRead(Event *eventReturn, EventProc proc) res = proc->reader(proc->readerP, &type, sizeof(EventType)); if (res != ResOK) return res; - index = eventType2Index(type); - length = eventTypes[index].length; + eventIndex = eventType2Index(type); + length = eventTypes[eventIndex].length; if (proc->cachedEvent != NULL) { event = proc->cachedEvent; proc->cachedEvent = NULL; @@ -306,7 +305,7 @@ Res EventRecord(EventProc proc, Event event, Word etime) switch(event->any.code) { case EventIntern: { /* id, label */ Symbol sym = malloc(sizeof(symbolStruct)); - + if (sym == NULL) return ResMEMORY; sym->id = event->ws.w0; res = eventStringCopy(&(sym->name), &(event->ws.s1)); @@ -332,7 +331,7 @@ Res EventRecord(EventProc proc, Event event, Word etime) else res = TableDefine(proc->labelTable, (Word)label->addr, label); } break; - default: + default: res = ResOK; break; } @@ -386,7 +385,11 @@ Res EventProcCreate(EventProc *procReturn, Bool partial, assert(CHECKFIELD(EventUnion, any.code, EventWSStruct, code)); assert(CHECKFIELD(EventUnion, any.clock, EventWSStruct, clock)); /* check use of labelTable */ +#if !defined(MPS_OS_FR) + /* GCC -ansi -pedantic -Werror on FreeBSD will fail here + * with the warning "statement with no effect". */ assert(sizeof(Word) >= sizeof(Addr)); +#endif proc->partialLog = partial; proc->reader = reader; proc->readerP = readerP; diff --git a/mps/code/fri4gc.gmk b/mps/code/fri4gc.gmk new file mode 100644 index 00000000000..fadadd13e69 --- /dev/null +++ b/mps/code/fri4gc.gmk @@ -0,0 +1,25 @@ +# impl.gmk.fri4gc: BUILD FOR FreeBSD/INTEL/GCC PLATFORM +# +# $HopeName: MMsrc!lii4gc.gmk(trunk.5) $ +# Copyright (C) 2000 Harlequin Limited. All rights reserved. + +PFM = fri4gc + +PFMDEFS = -D_REENTRANT + +MPMPF = mpsliban.c mpsioan.c lockfr.c thfri4.c pthrdext.c vmfr.c \ + protfr.c protfri3.c prmcan.c ssfri3.c span.c +SWPF = than.c vmfr.c protsw.c prmcan.c ssan.c + +LIBS = -lm -pthread + +include gc.gmk + +CC = cc + +# Suppress some warnings (SuSE). +# .void: -Wpointer-arith cannot be used because the string.h header does +# arithmetic on void*. +CFLAGSCOMPILER := $(subst -Wpointer-arith,,$(CFLAGSCOMPILER)) + +include comm.gmk diff --git a/mps/code/lockfr.c b/mps/code/lockfr.c new file mode 100644 index 00000000000..10030afe675 --- /dev/null +++ b/mps/code/lockfr.c @@ -0,0 +1,236 @@ +/* impl.c.lockfr: RECURSIVE LOCKS FOR POSIX SYSTEMS + * + * $HopeName$ + * Copyright (C) 2000 Harlequin Limited. All rights reserved. + * + * .freebsd: This implementation supports FreeBSD (platform + * MPS_OS_FR). + * + * .posix: In fact, the implementation should be reusable for most POSIX + * implementations, but may need some customization for each. + * + * .design: These locks are implemented using mutexes. + * + * .recursive: Mutexes support both non-recursive and recursive + * locking, but only at initialization time. This doesn't match the + * API of MPS Lock module, which chooses at locking time, so all locks + * are made (non-recursive) errorchecking. Recursive locks are + * implemented by checking the error code. + * + * .claims: During use the claims field is updated to remember the + * number of claims acquired on a lock. This field must only be + * modified while we hold the mutex. */ + +#include +#include +#include + +#include "mpmtypes.h" +#include "lock.h" +#include "config.h" + + +#ifndef MPS_OS_FR +#error "lockfr.c is FreeBSD specific but MPS_OS_FR not defined" +#endif + +SRCID(lockfr, "$HopeName$"); + + +/* LockStruct -- the MPS lock structure + * + * .lock.posix: Posix lock structure; uses a mutex. + */ + +typedef struct LockStruct { + Sig sig; /* design.mps.sig */ + unsigned long claims; /* # claims held by owner */ + pthread_mutex_t mut; /* the mutex itself */ +} LockStruct; + + +/* LockSize -- size of a LockStruct */ + +size_t LockSize(void) +{ + return sizeof(LockStruct); +} + + +/* LockCheck -- check a lock */ + +Bool LockCheck(Lock lock) +{ + CHECKS(Lock, lock); + /* While claims can't be very large, I don't dare to put a limit on it. */ + /* There's no way to test the mutex, or check if it's held by somebody. */ + return TRUE; +} + + +/* LockInit -- initialize a lock */ + +void LockInit(Lock lock) +{ + pthread_mutexattr_t attr; + int res; + + AVER(lock != NULL); + lock->claims = 0; + res = pthread_mutexattr_init(&attr); + AVER(res == 0); + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + AVER(res == 0); + res = pthread_mutex_init(&lock->mut, &attr); + AVER(res == 0); + res = pthread_mutexattr_destroy(&attr); + AVER(res == 0); + lock->sig = LockSig; + AVERT(Lock, lock); +} + + +/* LockFinish -- finish a lock */ + +void LockFinish(Lock lock) +{ + int res; + + AVERT(Lock, lock); + /* Lock should not be finished while held */ + AVER(lock->claims == 0); + res = pthread_mutex_destroy(&lock->mut); + AVER(res == 0); + lock->sig = SigInvalid; +} + + +/* LockClaim -- claim a lock (non-recursive) */ + +void LockClaim(Lock lock) +{ + int res; + + AVERT(Lock, lock); + + res = pthread_mutex_lock(&lock->mut); + /* pthread_mutex_lock will error if we own the lock already. */ + AVER(res == 0); + + /* This should be the first claim. Now we own the mutex */ + /* it is ok to check this. */ + AVER(lock->claims == 0); + lock->claims = 1; +} + + +/* LockReleaseMPM -- release a lock (non-recursive) */ + +void LockReleaseMPM(Lock lock) +{ + int res; + + AVERT(Lock, lock); + AVER(lock->claims == 1); /* The lock should only be held once */ + lock->claims = 0; /* Must set this before releasing the lock */ + res = pthread_mutex_unlock(&lock->mut); + /* pthread_mutex_unlock will error if we didn't own the lock. */ + AVER(res == 0); +} + + +/* LockClaimRecursive -- claim a lock (recursive) */ + +void LockClaimRecursive(Lock lock) +{ + int res; + + AVERT(Lock, lock); + + res = pthread_mutex_lock(&lock->mut); + /* pthread_mutex_lock will return: */ + /* 0 if we have just claimed the lock */ + /* EDEADLK if we own the lock already. */ + AVER((res == 0 && lock->claims == 0) || + (res == EDEADLK && lock->claims > 0)); + + ++lock->claims; + AVER(lock->claims > 0); +} + + +/* LockReleaseRecursive -- release a lock (recursive) */ + +void LockReleaseRecursive(Lock lock) +{ + int res; + + AVERT(Lock, lock); + AVER(lock->claims > 0); + --lock->claims; + if (lock->claims == 0) { + res = pthread_mutex_unlock(&lock->mut); + /* pthread_mutex_unlock will error if we didn't own the lock. */ + AVER(res == 0); + } +} + + +/* Global locks + * + * .global: The two "global" locks are statically allocated normal locks. + */ + +static LockStruct globalLockStruct; +static LockStruct globalRecLockStruct; +static Lock globalLock = &globalLockStruct; +static Lock globalRecLock = &globalRecLockStruct; +static pthread_once_t isGlobalLockInit = PTHREAD_ONCE_INIT; + +static void globalLockInit(void) +{ + LockInit(globalLock); + LockInit(globalRecLock); +} + + +/* LockClaimGlobalRecursive -- claim the global recursive lock */ + +void LockClaimGlobalRecursive(void) +{ + int res; + + /* Ensure the global lock has been initialized */ + res = pthread_once(&isGlobalLockInit, globalLockInit); + AVER(res == 0); + LockClaimRecursive(globalRecLock); +} + + +/* LockReleaseGlobalRecursive -- release the global recursive lock */ + +void LockReleaseGlobalRecursive(void) +{ + LockReleaseRecursive(globalRecLock); +} + + +/* LockClaimGlobal -- claim the global non-recursive lock */ + +void LockClaimGlobal(void) +{ + int res; + + /* Ensure the global lock has been initialized */ + res = pthread_once(&isGlobalLockInit, globalLockInit); + AVER(res == 0); + LockClaim(globalLock); +} + + +/* LockReleaseGlobal -- release the global non-recursive lock */ + +void LockReleaseGlobal(void) +{ + LockReleaseMPM(globalLock); +} diff --git a/mps/code/mpstd.h b/mps/code/mpstd.h index 2365ceb1a3f..766e0da1980 100644 --- a/mps/code/mpstd.h +++ b/mps/code/mpstd.h @@ -160,7 +160,7 @@ /* GCC 2.7.2.1, gcc -E -dM -traditional-cpp and + * ools/Preprocessor/Preprocessor.[ef].html> */ #elif defined(__APPLE__) && defined(__ppc__) && defined(__MACH__) && defined(__GNUC__) @@ -291,6 +291,20 @@ #define MPS_WORD_SHIFT 5 #define MPS_PF_ALIGN 8 /* @@@@ not tested */ +/* GCC 2.95.3, gcc -E -dM + */ + +#elif defined(__FreeBSD__) && defined (__i386__) && defined (__GNUC__) +#define MPS_PF_FRI4GC +#define MPS_PF_STRING "fri4gc" +#define MPS_OS_FR +#define MPS_ARCH_I4 +#define MPS_BUILD_GC +#define MPS_T_WORD unsigned long +#define MPS_WORD_WIDTH 32 +#define MPS_WORD_SHIFT 5 +#define MPS_PF_ALIGN 4 + #else #error "Unable to detect target platform" #endif diff --git a/mps/code/prmcfr.h b/mps/code/prmcfr.h new file mode 100644 index 00000000000..85c6ba0c14c --- /dev/null +++ b/mps/code/prmcfr.h @@ -0,0 +1,21 @@ +/* impl.h.prmcfr: PROTECTION MUTATOR CONTEXT (FREEBSD) + * + * $HopeName: $ + * Copyright (C) 1998 The Harlequin Group Limited. All rights reserved. + * + * .readership: MPS developers. + */ + +#ifndef prmcfr_h +#define prmcfr_h + +#include "mpm.h" + +#include + +typedef struct MutatorFaultContextStruct { /* Protection fault context data */ + ucontext_t *ucontext; +} MutatorFaultContextStruct; + + +#endif /* prmcfr_h */ diff --git a/mps/code/protfr.c b/mps/code/protfr.c new file mode 100644 index 00000000000..5e0ef52665b --- /dev/null +++ b/mps/code/protfr.c @@ -0,0 +1,93 @@ +/* impl.c.protfr: PROTECTION FOR FreeBSD + * + * $HopeName: $ + * Copyright (C) 1995,1999 Harlequin Group, all rights reserved + * + */ + +#include "mpm.h" + +#ifndef MPS_OS_FR +#error "protfr.c is FreeBSD specific, but MPS_OS_FR is not set" +#endif +#ifndef PROTECTION +#error "protfr.c implements protection, but PROTECTION is not set" +#endif + +#include +#include +#include +#include +#include + +SRCID(protfr, "$HopeName: $"); + + +/* ProtSet -- set protection + * + * This is just a thin veneer on top of mprotect(2). + */ + +void ProtSet(Addr base, Addr limit, AccessSet mode) +{ + int flags; + int res; + + AVER(sizeof(int) == sizeof(Addr)); /* should be redundant; will fail on Alpha */ + AVER(base < limit); + AVER(base != 0); + AVER(AddrOffset(base, limit) <= INT_MAX); /* should be redundant */ + +#if 0 + /* .flags.trouble: This less strict version of flags (which allows write + * access unless explicitly told not to) caused mmqa test 37 to fail. + * This might be a bug in MPS, so for now we go with the stricter + * version that matches the Win32 implementation. */ + /* .flags.trouble.freebsd: the above comment was in the Linux version + * of this code; I haven't verified it for FreeBSD. */ + flags = 0; + if((mode & AccessREAD) == 0) + flags |= PROT_READ | PROT_EXEC; + if((mode & AccessWRITE) == 0) + flags |= PROT_WRITE; +#endif + flags = PROT_READ | PROT_WRITE | PROT_EXEC; + if((mode & AccessWRITE) != 0) + flags = PROT_READ | PROT_EXEC; + if((mode & AccessREAD) != 0) + flags = 0; + + res = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags); + AVER(res == 0); +} + + +/* ProtSync -- synchronize protection settings with hardware + * + * This does nothing under FreeBSD. + */ + +void ProtSync(Arena arena) +{ + NOOP; +} + + + +/* ProtTramp -- protection trampoline + * + * The protection trampoline is trivial under FreeBSD, as there is + * nothing that needs to be done in the dynamic context of the mutator + * in order to catch faults. (Contrast this with Win32 Structured + * Exception Handling.) + */ + +void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), + void *p, size_t s) +{ + AVER(resultReturn != NULL); + AVER(FUNCHECK(f)); + /* Can't check p and s as they are interpreted by the client */ + + *resultReturn = (*f)(p, s); +} diff --git a/mps/code/protfri3.c b/mps/code/protfri3.c new file mode 100644 index 00000000000..4f30a79a249 --- /dev/null +++ b/mps/code/protfri3.c @@ -0,0 +1,137 @@ +/* impl.c.protfri3: PROTECTION FOR FREEBSD (INTEL 386) + * + * $HopeName: MMsrc!protlii3.c(trunk.3) $ + * Copyright (C) 2000 Harlequin Limited. All rights reserved. + * + * SOURCES + * + * .source.i486: Intel486 Microprocessor Family Programmer's + * Reference Manual + * + * .source.man: sigaction(2): FreeBSD System Calls Manual. + */ + +#include "prmcfr.h" + +#ifndef MPS_OS_FR +#error "protfri3.c is FreeBSD-specific, but MPS_OS_FR is not set" +#endif +#if !defined(MPS_ARCH_I3) && !defined(MPS_ARCH_I4) +#error "protfri3.c is Intel-specific, but MPS_ARCH_I3 or MPS_ARCH_I4 is not set" +#endif +#ifndef PROTECTION +#error "protfri3.c implements protection, but PROTECTION is not set" +#endif + +#include +#include + +SRCID(protfri3, "$HopeName: MMsrc!protlii3.c(trunk.3) $"); + + +/* The previously-installed signal action, as returned by */ +/* sigaction(3). See ProtSetup. */ + +static struct sigaction sigNext; + +/* sigHandle -- protection signal handler + * + * This is the signal handler installed by ProtSetup to deal with + * protection faults. It is installed on the SIGBUS signal. It + * decodes the protection fault details from the signal context and + * passes them to ArenaAccess, which attempts to handle the fault and + * remove its cause. If the fault is handled, then the handler + * returns and execution resumes. If it isn't handled, then + * sigHandle does its best to pass the signal on to the previously + * installed signal handler (sigNext). + * + * .sigh.args: The sigaction manual page .source.man documents three + * different handler prototypes: ANSI C sa_handler, traditional BSD + * sa_handler, and POSIX SA_SIGINFO sa_sigaction. The ANSI C + * prototype isn't powerful enough for us (can't get addresses), and + * the manual page deprecates the BSD sa_handler in favour of the + * POSIX SA_SIGINFO sa_sigaction. In that prototype, the arguments + * are: signal number, pointer to signal info structure, pointer to + * signal context structure. + * + * .sigh.context: We only know how to handle signals with code + * BUS_PAGE_FAULT, where info->si_addr gives the fault address. + * + * .sigh.mode: The fault type (read/write) does not appear to be + * available to the signal handler (see mail archive). + */ + +static void sigHandle(int sig, siginfo_t *info, void *context) /* .sigh.args */ +{ + AVER(sig == SIGBUS); + + if(info->si_code == BUS_PAGE_FAULT) { /* .sigh.context */ + AccessSet mode; + Addr base, limit; + + mode = AccessREAD | AccessWRITE; /* .sigh.mode */ + + /* We assume that the access is for one word at the address. */ + base = (Addr)info->si_addr; /* .sigh.context */ + limit = AddrAdd(base, (Size)sizeof(Addr)); + + /* Offer each protection structure the opportunity to handle the */ + /* exception. If it succeeds, then allow the mutator to continue. */ + if(ArenaAccess(base, mode, NULL)) + return; + } + + /* The exception was not handled by any known protection structure, */ + /* so throw it to the previously installed handler. */ + + /* @@@@ This is really weak. */ + /* Need to implement rest of the contract of sigaction */ + /* We might also want to set SA_RESETHAND in the flags and explicitly */ + /* reinstall the handler from withint itself so the SIG_DFL/SIG_IGN */ + /* case can work properly by just returning. */ + switch ((int)sigNext.sa_handler) { + case (int)SIG_DFL: + case (int)SIG_IGN: + abort(); + NOTREACHED; + break; + default: + if ((int)sigNext.sa_handler & SA_SIGINFO) { + (*sigNext.sa_sigaction)(sig, info, context); + } else { + /* @@@@ what if the previous handler is BSD-style? */ + /* We don't have a struct sigcontext to pass to it. + The second argument (the code) is just info->si_code + but the third argument (the sigcontext) we would have to + fake from the ucontext. We could do that. */ + (*sigNext.sa_handler)(sig); + } + break; + } +} + + +/* ProtSetup -- global protection setup + * + * Under FreeBSD, the global setup involves installing a signal + * handler on SIGBUS to catch and handle page faults (see + * sigHandle). The previous handler is recorded so that it can be + * reached from sigHandle if it fails to handle the fault. + * + * NOTE: There are problems with this approach: + * 1. we can't honor the sa_flags for the previous handler, + * 2. what if this thread is suspended just after calling signal(3)? + * The sigNext variable will never be initialized! */ + +void ProtSetup(void) +{ + struct sigaction sa; + int result; + + sa.sa_sigaction = sigHandle; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + + result = sigaction(SIGBUS, &sa, &sigNext); + AVER(result == 0); +} diff --git a/mps/code/pthrdext.c b/mps/code/pthrdext.c index 2651e06db36..3ba128d44b5 100644 --- a/mps/code/pthrdext.c +++ b/mps/code/pthrdext.c @@ -13,9 +13,13 @@ */ +#include "mpm.h" + +#if defined(MPS_OS_LI) /* open sesame magic */ #define _BSD_SOURCE 1 #define _POSIX_C_SOURCE 1 +#endif #include #include @@ -26,7 +30,6 @@ #include #include "pthrdext.h" -#include "mpm.h" SRCID(pthreadext, "$HopeName$"); @@ -37,7 +40,7 @@ SRCID(pthreadext, "$HopeName$"); */ #define PTHREADEXT_SIGSUSPEND SIGXFSZ -#define PTHREADEXT_SIGRESUME SIGPWR +#define PTHREADEXT_SIGRESUME SIGXCPU /* Static data initiatialized on first use of the module @@ -63,9 +66,89 @@ static PThreadext suspendingVictim = NULL; /* current victim */ static RingStruct suspendedRing; /* PThreadext suspend ring */ -static void suspendSignalHandler(int sig, struct sigcontext scp); -static void resumeSignalHandler(int sig); +/* suspendSignalHandler -- signal handler called when suspending a thread + * + * See design.mps.pthreadext.impl.suspend-handler + * + * The interface for determining the MFC might be platform specific. + * + * Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until + * receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both + * PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having + * PTHREADEXT_SIGRESUME blocked prevents a resume before we can finish the + * suspend protocol. + */ +#if defined(MPS_OS_LI) + +#include "prmcli.h" + +static void suspendSignalHandler(int sig, struct sigcontext scp) +{ + sigset_t signal_set; + MutatorFaultContextStruct mfContext; + + AVER(sig == PTHREADEXT_SIGSUSPEND); + UNUSED(sig); + + AVER(suspendingVictim != NULL); + mfContext.scp = &scp; + suspendingVictim->suspendedMFC = &mfContext; + /* Block all signals except PTHREADEXT_SIGRESUME while suspended. */ + sigfillset(&signal_set); + sigdelset(&signal_set, PTHREADEXT_SIGRESUME); + sem_post(&pthreadextSem); + sigsuspend(&signal_set); + + /* Once here, the resume signal handler has run to completion. */ + return; +} + +#elif defined(MPS_OS_FR) + +#include "prmcfr.h" + +static void suspendSignalHandler(int sig, + siginfo_t *info, + void *context) +{ + sigset_t signal_set; + ucontext_t ucontext; + MutatorFaultContextStruct mfContext; + + AVER(sig == PTHREADEXT_SIGSUSPEND); + UNUSED(sig); + + AVER(suspendingVictim != NULL); + /* copy the ucontext structure so we definitely have it on our stack, + * not (e.g.) shared with other threads. */ + ucontext = *(ucontext_t *)context; + mfContext.ucontext = &ucontext; + suspendingVictim->suspendedMFC = &mfContext; + /* Block all signals except PTHREADEXT_SIGRESUME while suspended. */ + sigfillset(&signal_set); + sigdelset(&signal_set, PTHREADEXT_SIGRESUME); + sem_post(&pthreadextSem); + sigsuspend(&signal_set); + + /* Once here, the resume signal handler has run to completion. */ + return; +} + +#endif + + +/* resumeSignalHandler -- signal handler called when resuming a thread + * + * See design.mps.pthreadext.impl.suspend-handler + */ + +static void resumeSignalHandler(int sig) +{ + AVER(sig == PTHREADEXT_SIGRESUME); + UNUSED(sig); + return; +} /* PThreadextModuleInit -- Initialize the PThreadext module * @@ -96,13 +179,20 @@ static void PThreadextModuleInit(void) /* PTHREADEXT_SIGRESUME signal cannot be delivered before the */ /* target thread calls sigsuspend.) */ - pthreadext_sigsuspend.sa_flags = 0; - pthreadext_sigsuspend.sa_handler = (__sighandler_t)suspendSignalHandler; status = sigemptyset(&pthreadext_sigsuspend.sa_mask); AVER(status == 0); status = sigaddset(&pthreadext_sigsuspend.sa_mask, PTHREADEXT_SIGRESUME); AVER(status == 0); +#if defined(MPS_OS_LI) + pthreadext_sigsuspend.sa_flags = 0; + pthreadext_sigsuspend.sa_handler = (__sighandler_t)suspendSignalHandler; + +#elif defined(MPS_OS_FR) + pthreadext_sigsuspend.sa_flags = SA_SIGINFO; + pthreadext_sigsuspend.sa_sigaction = suspendSignalHandler; +#endif + pthreadext_sigresume.sa_flags = 0; pthreadext_sigresume.sa_handler = resumeSignalHandler; status = sigemptyset(&pthreadext_sigresume.sa_mask); @@ -131,7 +221,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext) /* can't check ID */ CHECKL(RingCheck(&pthreadext->threadRing)); CHECKL(RingCheck(&pthreadext->idRing)); - if (pthreadext->suspendedScp == NULL) { + if (pthreadext->suspendedMFC == NULL) { /* not suspended */ CHECKL(RingIsSingle(&pthreadext->threadRing)); CHECKL(RingIsSingle(&pthreadext->idRing)); @@ -142,7 +232,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext) RING_FOR(node, &pthreadext->idRing, next) { PThreadext pt = RING_ELT(PThreadext, idRing, node); CHECKL(pt->id == pthreadext->id); - CHECKL(pt->suspendedScp == pthreadext->suspendedScp); + CHECKL(pt->suspendedMFC == pthreadext->suspendedMFC); } } status = pthread_mutex_unlock(&pthreadextMut); @@ -163,7 +253,7 @@ extern void PThreadextInit(PThreadext pthreadext, pthread_t id) AVER(status == 0); pthreadext->id = id; - pthreadext->suspendedScp = NULL; + pthreadext->suspendedMFC = NULL; RingInit(&pthreadext->threadRing); RingInit(&pthreadext->idRing); pthreadext->sig = PThreadextSig; @@ -185,7 +275,7 @@ extern void PThreadextFinish(PThreadext pthreadext) status = pthread_mutex_lock(&pthreadextMut); AVER(status == 0); - if(pthreadext->suspendedScp == NULL) { + if(pthreadext->suspendedMFC == NULL) { AVER(RingIsSingle(&pthreadext->threadRing)); AVER(RingIsSingle(&pthreadext->idRing)); } else { @@ -205,60 +295,12 @@ extern void PThreadextFinish(PThreadext pthreadext) } -/* suspendSignalHandler -- signal handler called when suspending a thread - * - * See design.mps.pthreadext.impl.suspend-handler - * - * The interface for determining the sigcontext might be platform specific. - * - * Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until - * receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both - * PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having - * PTHREADEXT_SIGRESUME blocked prevents a resume before we can finish the - * suspend protocol. - */ - -static void suspendSignalHandler(int sig, struct sigcontext scp) -{ - sigset_t signal_set; - - AVER(sig == PTHREADEXT_SIGSUSPEND); - UNUSED(sig); - - /* Tell caller about the sigcontext. */ - AVER(suspendingVictim != NULL); - suspendingVictim->suspendedScp = &scp; - - /* Block all signals except PTHREADEXT_SIGRESUME while suspended. */ - sigfillset(&signal_set); - sigdelset(&signal_set, PTHREADEXT_SIGRESUME); - sem_post(&pthreadextSem); - sigsuspend(&signal_set); - - /* Once here, the resume signal handler has run to completion. */ - return; -} - - -/* resumeSignalHandler -- signal handler called when resuming a thread - * - * See design.mps.pthreadext.impl.suspend-handler - */ - -static void resumeSignalHandler(int sig) -{ - AVER(sig == PTHREADEXT_SIGRESUME); - UNUSED(sig); - return; -} - - /* PThreadextSuspend -- suspend a thread * * See design.mps.pthreadext.impl.suspend */ -Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn) +Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn) { Ring node, next; Res res; @@ -266,7 +308,7 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn) AVERT(PThreadext, target); AVER(contextReturn != NULL); - AVER(target->suspendedScp == NULL); /* multiple suspends illegal */ + AVER(target->suspendedMFC == NULL); /* multiple suspends illegal */ /* Serialize access to suspend, makes life easier */ status = pthread_mutex_lock(&pthreadextMut); @@ -280,7 +322,7 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn) PThreadext alreadySusp = RING_ELT(PThreadext, threadRing, node); if (alreadySusp->id == target->id) { RingAppend(&alreadySusp->idRing, &target->idRing); - target->suspendedScp = alreadySusp->suspendedScp; + target->suspendedMFC = alreadySusp->suspendedMFC; goto noteSuspended; } } @@ -302,9 +344,9 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn) } noteSuspended: - AVER(target->suspendedScp != NULL); + AVER(target->suspendedMFC != NULL); RingAppend(&suspendedRing, &target->threadRing); - *contextReturn = target->suspendedScp; + *contextReturn = target->suspendedMFC; res = ResOK; unlock: @@ -316,7 +358,7 @@ unlock: /* PThreadextResume -- resume a suspended thread - * + * * See design.mps.pthreadext.impl.resume */ @@ -327,7 +369,7 @@ Res PThreadextResume(PThreadext target) AVERT(PThreadext, target); AVER(pthreadextModuleInitialized); /* must have been a prior suspend */ - AVER(target->suspendedScp != NULL); + AVER(target->suspendedMFC != NULL); /* Serialize access to suspend, makes life easier. */ status = pthread_mutex_lock(&pthreadextMut); @@ -353,7 +395,7 @@ Res PThreadextResume(PThreadext target) noteResumed: /* Remove the thread from the suspended ring */ RingRemove(&target->threadRing); - target->suspendedScp = NULL; + target->suspendedMFC = NULL; res = ResOK; unlock: diff --git a/mps/code/pthrdext.h b/mps/code/pthrdext.h index 4ec4daa99f9..32fec1a0c7d 100644 --- a/mps/code/pthrdext.h +++ b/mps/code/pthrdext.h @@ -26,13 +26,13 @@ typedef struct PThreadextStruct *PThreadext; /* PThreadextStruct -- structure definition * - * Should be embedded in a client structure + * Should be embedded in a client structure */ typedef struct PThreadextStruct { Sig sig; /* design.mps.sig */ pthread_t id; /* Thread ID */ - struct sigcontext *suspendedScp; /* sigcontext if suspended */ + MutatorFaultContext suspendedMFC; /* context if suspended */ RingStruct threadRing; /* ring of suspended threads */ RingStruct idRing; /* duplicate suspensions for id */ } PThreadextStruct; @@ -56,8 +56,8 @@ extern void PThreadextFinish(PThreadext pthreadext); /* PThreadextSuspend -- Suspend a pthreadext and return its context. */ -extern Res PThreadextSuspend(PThreadext pthreadext, - struct sigcontext **contextReturn); +extern Res PThreadextSuspend(PThreadext pthreadext, + MutatorFaultContext *contextReturn); /* PThreadextResume -- Resume a suspended pthreadext */ diff --git a/mps/code/ssfri3.c b/mps/code/ssfri3.c new file mode 100644 index 00000000000..a887fba4ec8 --- /dev/null +++ b/mps/code/ssfri3.c @@ -0,0 +1,58 @@ +/* impl.c.ssfri3: FREEBSD/INTEL STACK SCANNING + * + * $HopeName: MMsrc!sslii3.c(trunk.1) $ + * Copyright (C) 1999. Harlequin Group plc. All rights reserved. + * + * This scans the stack and fixes the registers which may contain + * roots. See design.mps.thread-manager + * + * The registers edi, esi, ebx are the registers defined to be preserved + * across function calls and therefore may contain roots. + * These are pushed on the stack for scanning. + * + * SOURCES + * + * .source.callees.saves: Set of callee-saved registers taken from + * CALL_USED_REGISTERS in /config/i386/i386.h. + * + * ASSUMPTIONS + * + * .assume.align: The stack pointer is assumed to be aligned on a word + * boundary. + * + * .assume.asm.stack: The compiler must not do wacky things with the + * stack pointer around a call since we need to ensure that the + * callee-save regs are visible during TraceScanArea. + * + * .assume.asm.order: The volatile modifier should prevent movement + * of code, which might break .assume.asm.stack. + * + */ + + +#include "mpm.h" + +SRCID(ssfri3, "$HopeName: MMsrc!sslii3.c(trunk.1) $"); + +/* .assume.asm.order */ +#define ASMV(x) __asm__ volatile (x) + + +Res StackScan(ScanState ss, Addr *stackBot) +{ + Addr *stackTop; + Res res; + + /* .assume.asm.stack */ + ASMV("push %ebx"); /* These registers are callee-saved */ + ASMV("push %esi"); /* and so may contain roots. They are pushed */ + ASMV("push %edi"); /* for scanning. See .source.callees.saves. */ + ASMV("mov %%esp, %0" : "=r" (stackTop) :); /* stackTop = esp */ + + AVER(AddrIsAligned((Addr)stackTop, sizeof(Addr))); /* .assume.align */ + res = TraceScanArea(ss, stackTop, stackBot); + + ASMV("add $12, %esp"); /* pop 3 regs to restore the stack pointer */ + + return res; +} diff --git a/mps/code/thfri4.c b/mps/code/thfri4.c new file mode 100644 index 00000000000..fa792eb69eb --- /dev/null +++ b/mps/code/thfri4.c @@ -0,0 +1,303 @@ +/* impl.c.thfri3: Threads Manager for Intel x86 systems on FreeBSD + * + * $HopeName: MMsrc!thlii4.c(trunk.2) $ + * Copyright (C) 2000 Harlequin Limited. All rights reserved. + * + * .purpose: This is a pthreads implementation of the threads manager. + * This implements impl.h.th. + * + * .design: See design.mps.thread-manager. + * + * .thread.id: The thread id is used to identify the current thread. + * + * ASSUMPTIONS + * + * .error.resume: PThreadextResume is assumed to succeed unless the thread + * has been destroyed. + * .error.suspend: PThreadextSuspend is assumed to succeed unless the thread + * has been destroyed. In this case, the suspend context is set to NULL; + * + * .stack.full-descend: assumes full descending stack. + * i.e. stack pointer points to the last allocated location; + * stack grows downwards. + * + * .stack.below-bottom: it's legal for the stack pointer to be at a + * higher address than the registered bottom of stack. This might + * happen if the stack of another thread doesn't contain any frames + * belonging to the client language. In this case, the stack should + * not be scanned. + * + * .stack.align: assume roots on the stack are always word-aligned, + * but don't assume that the stack pointer is necessarily + * word-aligned at the time of reading the context of another thread. + * + * .sp: The stack pointer in the context is ESP. + * .context.regroots: The root regs are EDI, ESI, EBX, EDX, ECX, EAX are + * assumed to be recorded in the context at pointer-aligned boundaries. + */ + +#include "prmcfr.h" +#include "mpm.h" + +#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I4) +#error "Compiling thfri4 when MPS_OS_FR or MPS_ARCH_I4 not defined." +#endif + +#include +#include "pthrdext.h" + +SRCID(thfri4, "$HopeName: MMsrc!thlii4.c(trunk.2) $"); + + +/* ThreadStruct -- thread desriptor */ + +typedef struct ThreadStruct { /* PThreads thread structure */ + Sig sig; /* design.mps.sig */ + Serial serial; /* from arena->threadSerial */ + Arena arena; /* owning arena */ + RingStruct arenaRing; /* threads attached to arena */ + PThreadextStruct thrextStruct; /* PThreads extension */ + pthread_t id; /* Pthread object of thread */ + MutatorFaultContext mfc; /* Context if thread is suspended */ +} ThreadStruct; + + +/* ThreadCheck -- check a thread */ + +Bool ThreadCheck(Thread thread) +{ + CHECKS(Thread, thread); + CHECKU(Arena, thread->arena); + CHECKL(thread->serial < thread->arena->threadSerial); + CHECKL(RingCheck(&thread->arenaRing)); + CHECKD(PThreadext, &thread->thrextStruct); + return TRUE; +} + +Bool ThreadCheckSimple(Thread thread) +{ + CHECKS(Thread, thread); + return TRUE; +} + + +/* ThreadRegister -- register a thread with an arena */ + +Res ThreadRegister(Thread *threadReturn, Arena arena) +{ + Res res; + Thread thread; + void *p; + + AVER(threadReturn != NULL); + AVERT(Arena, arena); + + res = ControlAlloc(&p, arena, sizeof(ThreadStruct), + /* withReservoirPermit */ FALSE); + if(res != ResOK) + return res; + thread = (Thread)p; + + thread->id = pthread_self(); + + RingInit(&thread->arenaRing); + + thread->sig = ThreadSig; + thread->serial = arena->threadSerial; + ++arena->threadSerial; + thread->arena = arena; + thread->mfc = NULL; + + PThreadextInit(&thread->thrextStruct, thread->id); + + AVERT(Thread, thread); + + RingAppend(ArenaThreadRing(arena), &thread->arenaRing); + + *threadReturn = thread; + return ResOK; +} + + +/* ThreadDeregister -- deregister a thread from an arena */ + +void ThreadDeregister(Thread thread, Arena arena) +{ + AVERT(Thread, thread); + AVERT(Arena, arena); + + RingRemove(&thread->arenaRing); + + thread->sig = SigInvalid; + + RingFinish(&thread->arenaRing); + + PThreadextFinish(&thread->thrextStruct); + + ControlFree(arena, thread, sizeof(ThreadStruct)); +} + + +/* mapThreadRing -- map over threads on ring calling a function on each one + * except the current thread + */ + +static void mapThreadRing(Ring threadRing, void (*func)(Thread)) +{ + Ring node, next; + pthread_t self; + + AVERT(Ring, threadRing); + + self = pthread_self(); + RING_FOR(node, threadRing, next) { + Thread thread = RING_ELT(Thread, arenaRing, node); + AVERT(Thread, thread); + if(! pthread_equal(self, thread->id)) /* .thread.id */ + (*func)(thread); + } +} + + +/* ThreadRingSuspend -- suspend all threads on a ring, expect the current one */ + + +static void threadSuspend(Thread thread) +{ + /* .error.suspend */ + /* In the error case (PThreadextSuspend returning ResFAIL), we */ + /* assume the thread has been destroyed. */ + /* In which case we simply continue. */ + Res res; + res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc); + if(res != ResOK) + thread->mfc = NULL; +} + + + +void ThreadRingSuspend(Ring threadRing) +{ + mapThreadRing(threadRing, threadSuspend); +} + + +/* ThreadRingResume -- resume all threads on a ring (expect the current one) */ + + +static void threadResume(Thread thread) +{ + /* .error.resume */ + /* If the previous suspend failed (thread->mfc == NULL), */ + /* or in the error case (PThreadextResume returning ResFAIL), */ + /* assume the thread has been destroyed. */ + /* In which case we simply continue. */ + if(thread->mfc != NULL) { + (void)PThreadextResume(&thread->thrextStruct); + thread->mfc = NULL; + } +} + +void ThreadRingResume(Ring threadRing) +{ + mapThreadRing(threadRing, threadResume); +} + + +/* ThreadRingThread -- return the thread at the given ring element */ + +Thread ThreadRingThread(Ring threadRing) +{ + Thread thread; + AVERT(Ring, threadRing); + thread = RING_ELT(Thread, arenaRing, threadRing); + AVERT(Thread, thread); + return thread; +} + + +/* ThreadArena -- get the arena of a thread + * + * Must be thread-safe. See design.mps.interface.c.thread-safety. + */ + +Arena ThreadArena(Thread thread) +{ + /* Can't check thread as that would not be thread-safe. */ + return thread->arena; +} + + +/* ThreadScan -- scan the state of a thread (stack and regs) */ + +Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +{ + pthread_t self; + Res res; + + AVERT(Thread, thread); + self = pthread_self(); + if(pthread_equal(self, thread->id)) { + /* scan this thread's stack */ + res = StackScan(ss, stackBot); + if(res != ResOK) + return res; + } else { + MutatorFaultContext mfc; + Addr *stackBase, *stackLimit, stackPtr; + + mfc = thread->mfc; + if(mfc == NULL) { + /* .error.suspend */ + /* We assume that the thread must have been destroyed. */ + /* We ignore the situation by returning immediately. */ + return ResOK; + } + + stackPtr = (Addr)mfc->ucontext->uc_mcontext.mc_esp; /* .i3.sp */ + /* .stack.align */ + stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr)); + stackLimit = (Addr *)stackBot; + if (stackBase >= stackLimit) + return ResOK; /* .stack.below-bottom */ + + /* scan stack inclusive of current sp and exclusive of + * stackBot (.stack.full-descend) + */ + res = TraceScanAreaTagged(ss, stackBase, stackLimit); + if(res != ResOK) + return res; + + /* (.context.regroots) + * This scans the root registers (.context.regroots). It also + * unecessarily scans the rest of the context. The optimisation + * to scan only relevent parts would be machine dependent. + */ + res = TraceScanAreaTagged(ss, (Addr *)mfc->ucontext, + (Addr *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext)))); + if(res != ResOK) + return res; + } + + return ResOK; +} + + +/* ThreadDescribe -- describe a thread */ + +Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) +{ + Res res; + + res = WriteF(stream, + "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, + " arena $P ($U)\n", + (WriteFP)thread->arena, (WriteFU)thread->arena->serial, + " id $U\n", (WriteFU)thread->id, + "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, + NULL); + if(res != ResOK) + return res; + + return ResOK; +} diff --git a/mps/code/thlii4.c b/mps/code/thlii4.c index f42e7a1f52a..f4384d611fe 100644 --- a/mps/code/thlii4.c +++ b/mps/code/thlii4.c @@ -21,14 +21,14 @@ * i.e. stack pointer points to the last allocated location; * stack grows downwards. * - * .stack.below-bottom: it's legal for the stack pointer to be at a - * higher address than the registered bottom of stack. This might - * happen if the stack of another thread doesn't contain any frames + * .stack.below-bottom: it's legal for the stack pointer to be at a + * higher address than the registered bottom of stack. This might + * happen if the stack of another thread doesn't contain any frames * belonging to the client language. In this case, the stack should * not be scanned. * * .stack.align: assume roots on the stack are always word-aligned, - * but don't assume that the stack pointer is necessarily + * but don't assume that the stack pointer is necessarily * word-aligned at the time of reading the context of another thread. * * .sp: The stack pointer in the context is ESP. @@ -58,7 +58,7 @@ typedef struct ThreadStruct { /* PThreads thread structure */ RingStruct arenaRing; /* threads attached to arena */ PThreadextStruct thrextStruct; /* PThreads extension */ pthread_t id; /* Pthread object of thread */ - struct sigcontext *scpSusp; /* Context if thread is suspended */ + MutatorFaultContext mfc; /* Context if thread is suspended */ } ThreadStruct; @@ -92,9 +92,9 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVER(threadReturn != NULL); AVERT(Arena, arena); - res = ControlAlloc(&p, arena, sizeof(ThreadStruct), + res = ControlAlloc(&p, arena, sizeof(ThreadStruct), /* withReservoirPermit */ FALSE); - if(res != ResOK) + if(res != ResOK) return res; thread = (Thread)p; @@ -106,7 +106,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; thread->arena = arena; - thread->scpSusp = NULL; + thread->mfc = NULL; PThreadextInit(&thread->thrextStruct, thread->id); @@ -169,9 +169,9 @@ static void threadSuspend(Thread thread) /* assume the thread has been destroyed. */ /* In which case we simply continue. */ Res res; - res = PThreadextSuspend(&thread->thrextStruct, &thread->scpSusp); + res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc); if(res != ResOK) - thread->scpSusp = NULL; + thread->mfc = NULL; } @@ -188,13 +188,13 @@ void ThreadRingSuspend(Ring threadRing) static void threadResume(Thread thread) { /* .error.resume */ - /* If the previous suspend failed (thread->scpSusp == NULL), */ + /* If the previous suspend failed (thread->mfc == NULL), */ /* or in the error case (PThreadextResume returning ResFAIL), */ /* assume the thread has been destroyed. */ /* In which case we simply continue. */ - if(thread->scpSusp != NULL) { + if(thread->mfc != NULL) { (void)PThreadextResume(&thread->thrextStruct); - thread->scpSusp = NULL; + thread->mfc = NULL; } } @@ -246,7 +246,7 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) struct sigcontext *scp; Addr *stackBase, *stackLimit, stackPtr; - scp = thread->scpSusp; + scp = thread->mfc->scp; if(scp == NULL) { /* .error.suspend */ /* We assume that the thread must have been destroyed. */ @@ -288,15 +288,15 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) { Res res; - + res = WriteF(stream, "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, - " arena $P ($U)\n", + " arena $P ($U)\n", (WriteFP)thread->arena, (WriteFU)thread->arena->serial, " id $U\n", (WriteFU)thread->id, "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, NULL); - if(res != ResOK) + if(res != ResOK) return res; return ResOK; diff --git a/mps/code/vmfr.c b/mps/code/vmfr.c new file mode 100644 index 00000000000..ca0c9edda27 --- /dev/null +++ b/mps/code/vmfr.c @@ -0,0 +1,278 @@ +/* impl.c.vmfr: VIRTUAL MEMORY MAPPING FOR FreeBSD + * + * $HopeName: MMsrc!vmli.c(trunk.7) $ + * Copyright (C) 2000 Harlequin Limited. All rights reserved. + * + * .purpose: This is the implementation of the virtual memory mapping + * interface (vm.h) for FreeBSD. It was created by copying vmli.c (the + * DIGITAL UNIX implementation) as that seemed to be closest. + * + * .design: See design.mps.vm. .design.freebsd: mmap(2) is used to + * reserve address space by creating a mapping with page access none. + * mmap(2) is used to map pages onto store by creating a copy-on-write + * (MAP_PRIVATE) mapping with the flag MAP_ANON. + * + * .assume.not-last: The implementation of VMCreate assumes that + * mmap() will not choose a region which contains the last page + * in the address space, so that the limit of the mapped area + * is representable. + * + * .assume.mmap.err: ENOMEM is the only error we really expect to + * get from mmap. The others are either caused by invalid params + * or features we don't use. See mmap(2) for details. + * + * .remap: Possibly this should use mremap to reduce the number of + * distinct mappings. According to our current testing, it doesn't + * seem to be a problem. + */ + +/* for mmap(2), munmap(2) */ +#include +#include + +/* for errno(2) */ +#include + +/* for getpagesize(3) */ +#include + +#include "mpm.h" + + +#ifndef MPS_OS_FR +#error "vmfr.c is FreeBSD specific, but MPS_OS_FR is not set" +#endif + +SRCID(vmfr, "$HopeName: MMsrc!vmli.c(trunk.7) $"); + + +/* VMStruct -- virtual memory structure */ + +#define VMSig ((Sig)0x519B3999) /* SIGnature VM */ + +typedef struct VMStruct { + Sig sig; /* design.mps.sig */ + Align align; /* page size */ + Addr base, limit; /* boundaries of reserved space */ + Size reserved; /* total reserved address space */ + Size mapped; /* total mapped memory */ +} VMStruct; + + +/* VMAlign -- return page size */ + +Align VMAlign(VM vm) +{ + return vm->align; +} + + +/* VMCheck -- check a VM */ + +Bool VMCheck(VM vm) +{ + CHECKS(VM, vm); + CHECKL(vm->base != 0); + CHECKL(vm->limit != 0); + CHECKL(vm->base < vm->limit); + CHECKL(vm->mapped <= vm->reserved); + CHECKL(SizeIsP2(vm->align)); + CHECKL(AddrIsAligned(vm->base, vm->align)); + CHECKL(AddrIsAligned(vm->limit, vm->align)); + return TRUE; +} + + +/* VMCreate -- reserve some virtual address space, and create a VM structure */ + +Res VMCreate(VM *vmReturn, Size size) +{ + Align align; + VM vm; + int pagesize; + void *addr; + Res res; + + AVER(vmReturn != NULL); + + /* Find out the page size from the OS */ + pagesize = getpagesize(); + /* check the actual returned pagesize will fit in an object of */ + /* type Align. */ + AVER(pagesize > 0); + AVER((unsigned long)pagesize <= (unsigned long)(Align)-1); + /* Note implicit conversion from "int" to "Align". */ + align = pagesize; + AVER(SizeIsP2(align)); + size = SizeAlignUp(size, align); + if((size == 0) || (size > (Size)(size_t)-1)) + return ResRESOURCE; + + /* Map in a page to store the descriptor on. */ + addr = mmap(0, (size_t)SizeAlignUp(sizeof(VMStruct), align), + PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, + -1, 0); + if(addr == MAP_FAILED) { + int e = errno; + AVER(e == ENOMEM); /* .assume.mmap.err */ + return ResMEMORY; + } + vm = (VM)addr; + + vm->align = align; + + /* See .assume.not-last. */ + addr = mmap(0, (size_t)size, + PROT_NONE, MAP_ANON | MAP_PRIVATE, + -1, 0); + if(addr == MAP_FAILED) { + int e = errno; + AVER(e == ENOMEM); /* .assume.mmap.err */ + res = ResRESOURCE; + goto failReserve; + } + + vm->base = (Addr)addr; + vm->limit = AddrAdd(vm->base, size); + vm->reserved = size; + vm->mapped = (Size)0; + + vm->sig = VMSig; + + AVERT(VM, vm); + + EVENT_PAA(VMCreate, vm, vm->base, vm->limit); + + *vmReturn = vm; + return ResOK; + +failReserve: + (void)munmap((void *)vm, (size_t)SizeAlignUp(sizeof(VMStruct), align)); + return res; +} + + +/* VMDestroy -- release all address space and destroy VM structure */ + +void VMDestroy(VM vm) +{ + int r; + + AVERT(VM, vm); + AVER(vm->mapped == (Size)0); + + /* This appears to be pretty pointless, since the descriptor */ + /* page is about to vanish completely. However, munmap might fail */ + /* for some reason, and this would ensure that it was still */ + /* discovered if sigs were being checked. */ + vm->sig = SigInvalid; + + r = munmap((void *)vm->base, (size_t)AddrOffset(vm->base, vm->limit)); + AVER(r == 0); + r = munmap((void *)vm, + (size_t)SizeAlignUp(sizeof(VMStruct), vm->align)); + AVER(r == 0); + + EVENT_P(VMDestroy, vm); +} + + +/* VMBase -- return the base address of the memory reserved */ + +Addr VMBase(VM vm) +{ + AVERT(VM, vm); + + return vm->base; +} + + +/* VMLimit -- return the limit address of the memory reserved */ + +Addr VMLimit(VM vm) +{ + AVERT(VM, vm); + + return vm->limit; +} + + +/* VMReserved -- return the amount of memory reserved */ + +Size VMReserved(VM vm) +{ + AVERT(VM, vm); + + return vm->reserved; +} + + +/* VMMapped -- return the amount of memory actually mapped */ + +Size VMMapped(VM vm) +{ + AVERT(VM, vm); + + return vm->mapped; +} + + +/* VMMap -- map the given range of memory */ + +Res VMMap(VM vm, Addr base, Addr limit) +{ + Size size; + + AVERT(VM, vm); + AVER(sizeof(void *) == sizeof(Addr)); + AVER(base < limit); + AVER(base >= vm->base); + AVER(limit <= vm->limit); + AVER(AddrIsAligned(base, vm->align)); + AVER(AddrIsAligned(limit, vm->align)); + + size = AddrOffset(base, limit); + + if(mmap((void *)base, (size_t)size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, + -1, 0) + == MAP_FAILED) { + AVER(errno == ENOMEM); /* .assume.mmap.err */ + return ResMEMORY; + } + + vm->mapped += size; + + EVENT_PAA(VMMap, vm, base, limit); + return ResOK; +} + + +/* VMUnmap -- unmap the given range of memory */ + +void VMUnmap(VM vm, Addr base, Addr limit) +{ + Size size; + void *addr; + + AVERT(VM, vm); + AVER(base < limit); + AVER(base >= vm->base); + AVER(limit <= vm->limit); + AVER(AddrIsAligned(base, vm->align)); + AVER(AddrIsAligned(limit, vm->align)); + + size = AddrOffset(base, limit); + + /* see design.mps.vmo1.fun.unmap.offset */ + addr = mmap((void *)base, (size_t)size, + PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, + -1, 0); + AVER(addr == (void *)base); + + vm->mapped -= size; + + EVENT_PAA(VMUnmap, vm, base, limit); +}