On 19 January 2013 02:02, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 18, 2013 at 2:38 PM, Frank Shearar <frank.shearar@gmail.com> wrote:
On 18 January 2013 21:49, Eliot Miranda <eliot.miranda@gmail.com> wrote:
phhh, let me try again :)
On Fri, Jan 18, 2013 at 8:47 AM, Frank Shearar <frank.shearar@gmail.com> wrote:
On 17 January 2013 17:45, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Thu, Jan 17, 2013 at 3:25 AM, Igor Stasenko <siguctua@gmail.com> wrote:
After some thought i decided to contribute my 2cents.
First, i think it is impossible to introduce a (re)initialization logic which would suit all different scenarios. Because it is really depends on what is stored in class variables and/or pools and if you add subclasses and then instances into the soup, you might end up with something which is really hard to safely reinitialize, because it may have some inconsistent state at different stages of initialization. It also, sometimes an order of initialization is important.
Yes, but on *can* write idempotent class initialization code. So that if the system does reinitialize on every load old code may break until its fixed, but new code will have the advantage of always being correctly initialized. Note that in the VMMaker class reinitializations of the core VM classes (the interpreter, garbage collector, jit, etc) are done *on each launch of the simulator*, and *each vm generation*, let alone on each package load :). [ I'm not recommending this :) ].
One thing I do is when creating singletons for sentinels etc I check whether they've been initialized. e.g.
initialize Sentinel ifNil: [Sentinel := Object new]
Knowing full well that I know next to nothing about concurrency in the image, and so realising that this might be a complete non-issue: lazy initialization and concurrency do not mix well.
Agreed, but the two can be decoupled. For example one can build the state and then assign it. Assignment is currently atomic so there are no multicore issues here (yet :) ). Further, assignments are not suspension points so a sequence of (just) assignments are guaranteed to occur without interruption. And (I'm sure you realize, for give the pedantry) the above example isn't lazy, it is eager. It simply refuses to create another Sentinel if one exists already.
So because of the assignment guarantees we won't have two processes entering the ifNil: block? (I'll try extract one foot from my mouth and maybe replace it with another...)
In this particular case things seem pretty innocuous: let's say two processes enter the #initialize. They both manage to enter the ifNil: (I'm aware this is compiled away to jumps, but the impact of this I don't know) through a time-of-check-time-of-use race, and you end up generating two objects. But one's simply lost. As long as references to Sentinel don't escape the class, no one need know anything at all.
right. but good point. i hadn't thought of this case. the scenarios i'm thinking of are those where one is loading/updating a monticello or fuel package or similar or redefining a class initialize method. in these scenarios there's only likely to be one thread of control running the initialize.
I've been bitten too many times not to be leery of that particular case :). I had a nasty bug with a lazy initialisation in a 3rd party library where a TCP server kept a thread pool. If the thread pool was nil it would make a new one (so the Delphi equivalent of f ifNil: [f := blah]). On application shutdown, a thing running in that threadpool could call back to the TCP server. In particular, the TCP server would ask all threads to terminate and then nil the pool... and one of the threads would call back and reinitialise a whole new threadpool. The last thing the application did was wait for all threads to finish, and knew this by decrementing a counter. Only that cause-a-new-threadpool thread would try decrement the counter of a brand new threadpool. The application would happily wait forever for a notification from a thread that was long dead! The solution was simplicity itself: strip out the lazy initialization, and you don't get a second threadpool accidentally made. frank
frank
But I do agree with your sentiment: idempotency is highly desirable, and somehow forcing non-idempotent class initialisation to fail quickly and noisily sounds like a good plan.
frank
-- best, Eliot
-- best, Eliot