Hi Todd (et al);
"Todd L. Miller" wrote:
> findStaticInit() is called in the resolution phase of
> class-loading. It basically could reside in ur_java_class without any
> complications (though I haven't tried and made sure), and isn't there
> currently because it started out as part of the resolution functions
> taking place in class_loader.cc, and was moved out as it became necessary
> to call it last. All that it does is check if a static initializer
> function exists, and if it does, creates a thread to run, should that
> static initialization become necessary.
When I wrote my previous message, I did not understand what was going on
here. Let me repeat it back to you to see if I understand now.
Basically, when we attempt to resolve a ur_java_class, we check to see
if there's a static initializer. If there is, we hang a java_thread
which will run the static initializers off of the (in process of being
resolved) java_class. When running invoke (or under some other
circumstances I haven't run under the debugger to see who calls) or
getstatic opcodes for a thread, we check to see if this slot in the
java_thread's corresponding java_class is non-NULL, and, if so, we then
run the initializer thread to the bitter end.
If I am reasonably close to understanding how this works now, on to my
concerns...
> > (1) It looks like a new, fresh thread is spawned to run the static
> > initializers. However, the thread that caused the classload doesn't
> > seem to wait for the new thread to finish actually running those
> > initializers (right?).
>
> `while(jt->runOpcode());' will run until the thread finishes,
> which is (normally) when the function on the bottom of the frame stack
> (the static init) returns.
(1) Well, from an OS and maybe debugger perspective, the thread that's
running in this while loop is actually not the thread the scheduler
thinks is running, right? I mean, the scheduler thinks the thread that
is running is the thread that caused the invocation that required the
class-with-static-initializers to the loaded, resolved, and initialized,
NOT the thread spawned (however ephemerally) to actually run the
initializers. Right? If so, if there are any scheduler-related calls
made by the static initializers, the scheduler's notion of who's running
is actually wrong -- I don't specifically know what could go wrong
(given the almost infinite variety of code that could be run in the
static initializer), but, in general, having your scheduler be flat
wrong about both what the current process is, the count of active
processes, and the existence of the ephemeral process, could all combine
in nefarious ways. It just feels like the potential exists for some
really weird (and hard to debug) things to happen.
(2) As currently envisioned, we will not pre-empt during the static
initializations, because the while loops will run to the bitter end
without "sleeping" this thread. However, what happens if the bytecode
does something that requires another thread to be invoked (e.g.,
classloading off of a TCP stream, which would, say, require our thread
to go to sleep, and fire up an inetd-equivalent)? Does the requesting
thread get "slept" or "waited" properly, and the Right Thing happen
later after the bits get loaded?
(3) Speaking somewhat hypothetically and prematurely, what happens when
we get real multithreading? Won't we want to pre-empt the static
initializers?
> > This could be bad, right? (Actually, there seem
> > to be a couple of multithreading hazards here in specific and in the
> > class_loader abstraction in general -- basically, many threads might
> > share the same classloader -- that might have to be addressed regardless
> > of the answer to my next question.)
>
> One would hope many threads share the same classloader -- where
> would this be a problem? (Something I'm missing, probably...)
Oh, I was probably wrong-thinking about having multiple threads sitting
in classloader machinery methods/FSMs simulataneously. I was thinking
we'd be pre-empting the static initializers, and maybe have to put
concurrency controls around the hashtables, but I think my concerns are
all premature given the lack of multithreading (I mean, time-slicing
pre-emption).
> > (2) Is there some reason we don't just run those initializers within the
> > context of the current (requesting) thread? Maybe just "push" their
> > stack frames onto the stack so the vanilla machinery can tackle the
> > problem? Is this a Java-ism? Is there some other advantage to spawning
> > a new thread? (It pays to actually understand something before you
> > attempt to muck with it, eh?)
>
> Because it would be Very Annoying to arrange to push a method
> frame in the middle of one bytecode and then return to that same point in
> the bytecode after that method frame finishes executing; and there are
> four such locations where static initialization is done.
Well, I agree that you never ever want to leave a thread "in the middle
of" a bytecode execution. I believe you'd have to back it out all the
way and wait for a retry. The second part, about pushing a frame on top
of the current stack, I will have to think about how bad that is (I
forget what the stack discipline is like, and how the frames get
popped). I would think it could be kind of like an "interrupt"
happened, and the interrupt affects the runnability (sp?) of the pending
opcode (by side effect of resolving the required class), and then just
goes away and cleans up after itself. However, a debugger might show
the extra stack stuff on top of the explicitly-called stuff.
How do "normal" Java systems/debuggers treat this event (he asked,
ignorantly)?
> Moving the method frame onto the stack (we could store a pointer
> to the frame instead of a pointer to a thread) would allow for the
> scheduler to take care of timeslicing the initialization but there are two
> problems: (A) we'd have to take care to check at each static init point if
> some other thread was mucking about with the static init also (perhaps, if
> static_init == 1?) and if so, sleep until it finished -- the sleeping part
These are the multithreading hazards I mentioned before.
> would probably be difficult to arrange; and (B) unwinding the stack & the
> program counter would be annoying and result in much duplicated work for
> these four locations.
>
> It might, however, be wise to adjust the current system in some
> way: should we ever implement native processes, so that one thread sitting
> in its static_init loop *could* be interrupted by another, we'd have the
> same problems as if the method frame had been pushed onto the frame stack.
> Of course, alot of things would have to change if we ever did that, so I'm
> not sure that we want to worry about it.
Just asking. Given that I guess we're making a trade-off decision
(deciding to not decide to do something is the same as deciding not to
do it), I just wanted to know what we were trading-off.
> IMHO, since it (seems to) work, there's no need to change it.
> Counter-arguments?
Just the concerns I had above. In short summary, which thread is really
running? And, what should a debugger show?
-jm
--
==== John Morrison
==== MaK Technologies Inc.
==== 185 Alewife Brook Parkway, Cambridge, MA 02138
==== http://www.mak.com/welcome.html
==== vox:617-876-8085 x115
==== fax:617-876-9208
==== [EMAIL PROTECTED]
_______________________________________________
Kernel maillist - [EMAIL PROTECTED]
http://jos.org/mailman/listinfo/kernel