Will add the lock and the TODO.
Btw, the bug I was running into was that "threadObject.inspect" was
incorrectly binding to KernelOps.Inspect rather than ThreadOps.Inspect when
executed on a second thread. The first thread was in the middle of calling
EnsureInitialized on the Thread class.
Thanks,
Shri
From: Tomas Matousek
Sent: Tuesday, December 16, 2008 7:59 AM
To: Shri Borde
Subject: RE: Lock during module initialization
This seems to be a good temporary workaround and it might be close to the right
long term solution (could you add a TODO comment next to the lock statement?).
There are no cycles in inheritance hierarchy (a module inclusion cannot be
cyclic) and any time an uninitialized module is accessed we need to initialize
only its ancestors. (Actually, the _mixins array should contain all mixins
serialized into a list - i.e. all mixins included into the mixins in the array
are contained in the array as well. That implies we might not need to call the
virtual EnsureInitialized method on the _mixins''s elements. There are
more such improvements to be made in module initialization and they should lead
to better performance and thread-safety as well.
There has been a discussion on Ruby list recently on how
''require'' should work in a multi-threaded program. The
conclusion seems to be similar to the Python''s approach (a global mutex
for require). ''require'' is thread-unsafe in today''s
MRI. In general, MRI''s behavior is mostly undefined in multi-threaded
environment today.
(BTW: The built-ins initialization we are doing is a little bit more complicated
than in MRI since we are postponing it until it is really needed. In MRI all
built-ins are initialized during startup. We initialize them as they are
accessed.)
Tomas
From: Shri Borde
Sent: Monday, December 15, 2008 11:20 PM
To: Tomas Matousek
Subject: Lock during module initialization
IronRuby code was misbehaving and calling an incorrect method in the presence of
multiple threads. This was because RubyModule.EnsureInitialized just returns if
_state is, for example, State.Initializing. I add the lock shown below which
fixes the problem.
internal virtual void EnsureInitialized() {
lock(this) {
if (_state == State.Uninitialized) {
for (int i = 0; i < _mixins.Length; i++) {
_mixins[i].EnsureInitialized();
}
if (_state == State.Uninitialized) {
InitializeMembers();
}
}
}
}
This probably opens up the possibility of deadlocks if there are
mutually-dependent modules or some such complicated scheme. Is the lock good
enough for now since it fixes wrong behavior which is hard to track down? A
deadlock will be easier to debug.
What is the long term story here? Python allows module importing on only one
thread at a time. The CLR maintains a list of types being initialized and knows
to break cycles of mutually dependent cctors. What does MRI do? I don''t
think green threads make the problem any simpler. What does JRuby do?
Thanks,
Shri
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://rubyforge.org/pipermail/ironruby-core/attachments/20081216/3e8b7737/attachment.html>