OK to postpone initFiles until threads are initialized? **************************************************************** For nextFrame, must get stack bounds out of NSPR? Right now, I'm kludging it. Save the address of a variable in a stack frame on the bottom of the stack, take the address of a variable on top of the stack, and those are bounds on any legitimate pointer that the rest of Kaffe has to whatever's in the middle. However, if the JIT could just put a sentinel value at the end of the chain of exceptions, all of this nonsense could be avoided. While I'm at it, I should note that following Godmar, the count-frames function always returns 0. I'm not sure what, if anything, this screws up, but it can't be good. **************************************************************** A nasty piece of work I haven't figured out how to do: NSPR does not allow asynchronous interruption of threads. Kaffe (and Java!) requires this to support Thread.stop(). Not at all clear how to deal with this. Worse come to worst, check a bit in all the runtime functions (syscall, mem allocator, etc.) to see if an "asynchronous" interrupt has occured --- but a tight loop that never sleeps, blocks, explicitly yields, or allocates storage would be unkillable. NSPR interrupts *might* be good for this. (They might be good to implement Java interrupts too --- but I don't see any hooks at all for that in the runtime! A flag in the thread_glue saying what kind of exception to throw is all that's needed to distinguish the two). To complicate matters further, nswait has critical sections (involving manipulations of the counts on counted mutexes) which must be guarded against asynchronous termination... which go around the release and reacquisition of the mutex inside PR_wait! UGH... Godmar's implementation of this has a similar (though a lot less serious bug) --- a thread which is blocked in a wait() cannot be stopped until the wait ends (either on notify or timeout). BTW, anything that uses findFileSlot has a similar problem --- it needs to either fill in a legitimate value or free the slot before dying. **************************************************************** More ugliness; there's this spinon and spinoff nonsense which *appears* to be used, at this point, solely to guard a single hash table inside Kaffe (relating objects to their locks); this is manipulated on creation or destruction of a lock. So, *if* I'm right about this, just creating a mutex of our own to deal with it should do the job... **************************************************************** Yet more. freeLock in locks.c does *not* call anything which allows the machine-dependant layer to free its own data structures associated with the lock. This is a serious memory leak. More minor grip --- Tspinoffall (associated with the above spinlock nonsense) does not go through the dispatch tables. **************************************************************** Walking objects has a special-case for thread objects. Presumably this is just to mark the stack, but should check. In fact, it also marks the thread's exceptObj. Isn't this... redundant? **************************************************************** First entry in syscall slot is fixfd function which is used to register random file descriptors with the I/O system. The implication here is that the encapsulation of system calls is less than perfect, and that file descriptors come off of raw UNIX syscalls in *addition* to going through the open, socket, etc. entries in the Kaffe_SystemCallInterface. In fact, I can find only one use of the thing, by a quick grep; this is in libraries/clib/native/UNIXProcess.c --- where it is used, unsurprisingly, to rig up the streams underlying pipes. To investigate --- handling of stdin, stdout and stderr. NB there is a PR_CreatePipe; I could just use that! However, this leaves me worrying about what to do with waitpid (NB, why is that in the syscall table, but *not* fork() and exec()? I know that the native Kaffe thread implementation doesn't need to do much with the latter two, but even so, they pretty obviously go together...). **************************************************************** Need to figure out error handling returns from syscalls in the Kaffe stuff... It turns out that Kaffe never seems to even look at it except in findInJar.c, where it just assumes that the static variable does the right thing (clearly incorrect in case of preemptable jthreads); NSPR supplies PR_GetError, but there is no obvious place to put the results. Note that in cases where we want to *set* the error code, there is a PR_SetError (probably meant for internal use, but hey, it's there). **************************************************************** set/getsockopt not yet implemented. See PR_Get/SetSocketOption. **************************************************************** My select() implementation (which perforce calls NSPR_Poll) is profligate with stack space. Once things are working, allocate a smaller array, and dynamically handle overflow. (Or alloca()?) **************************************************************** GC modularization requires serious work. Items: GC_WRITE invoked in the threads stuff, expands to soft_addreference --- direct invocation of an internal GC function. This should be calling gc_add_ref. But I'm replacing the threads-internal stuff anyway. **************************************************************** More GC implementation notes --- there is a GC_IN_HEAP macro which can be used for conservative walkers... Weirdness. The scan function is supposed to call something called PR_LiveObject which I can't find... It looks like this should just expand to _pr_gcData.livePointer. More precisely... extern GCInfo _pr_gcData; #define PR_LiveObject (_pr_gcData.livePointer) And there is also a liveBlock entry which may be useful. But should I check? Better yet, punt. The NSPR gc is incompletely packaged, and the hooks for the thread package are a lot more straightforward than the hooks for the GC. The key things are: all threads (except perhaps the GC itself) must be created "GCable" GC process must call PR_SuspendAll, then use PR_ScanStackPointers to scan stacks of all active threads. Finish up, call PR_ResumeAll. It turns out that gc_malloc.c in kaffe/kaffevm/mem *is* capable of using malloc(), but it tries to use sbrk() to be "smart". To hell with that; force it off and use malloc() always. Then turn that into PR_Malloc, and we should be set. **************************************************************** Changes made to *non*-system files: 1) #ifdef'ed out the exit() at the end of main() in kaffe/main.c; just let it run off the end. 2) gc-mem.c --- get new memory by calling PR_Malloc, *not* by calling sbrk().