Here's a hypothetical broken class method that does lazy initialization of a 
class inst var:

fileTypes
        fileTypes ifNil: [
                fileTypes := Dictionary new.
                fileTypes
                        at: 'txt' put: 'Text File';
                        at: 'html' put: 'Web Page';
                        at: 'pdf' put: 'Portable Document Format File';
                        at: 'doc' put: 'Microsoft Word Document'].
        ^ fileTypes.

Because the assignment is done first and the initialization is done after with 
a cascade of interruptable sends of #at:put:, there's a window after the 
assignment where 'fileTypes' is not nil but also not fully initialized--a race 
condition.

The fix is simple. Do the initialization before the atomic assignment takes 
place, so the var is only ever bound to nil or a fully initialized object:

fileTypes
        fileTypes ifNil: [
                fileTypes :=
                        Dictionary new
                                at: 'txt' put: 'Text File';
                                at: 'html' put: 'Web Page';
                                at: 'pdf' put: 'Portable Document Format File';
                                at: 'doc' put: 'Microsoft Word Document';
                                yourself].
        ^ fileTypes.

The fixed code is still vulnerable to duplicate initialization, because the 
initialization sequence is interruptable and 'fileTypes' is nil during it, but 
as long as the initialization is cheap enough, has no side effects that 
restrict how often it can be done, and it's enough that the initialized objects 
are equal (but not identical), that's OK.

If it's too complex for a single statement, you can use a temp vars or put it 
in a separate factory method:

fileTypes
        fileTypes ifNil: [
                fileTypes := self newFileTypes].
        ^ fileTypes.

Similar precautions (given how easy) might as well be taken with explicit 
initialization of class state too. Of course if the object is mutated later (in 
other methods), then Mutexes or other constructs are needed to guard access. 
But for immutable class state, ensuring initialization is done before 
assignment should be enough.

> Sent: Tuesday, August 01, 2017 at 7:36 AM
> From: "Stephane Ducasse" <stepharo.s...@gmail.com>
> To: "Any question about pharo is welcome" <pharo-users@lists.pharo.org>
> Subject: Re: [Pharo-users] Threads safety in Pharo
>
> I would love to have an analysis of assumptions made in some code.
> Because my impression is that the concurrent code is sometimes defined
> knowing the underlying logic of scheduler and this is not good.
> As I said to abdel privately in french it would be great to start from
> my french squeak book (Yes I wrote one long time ago) chapter on
> concurrent programming and turn it into a pharo chapter.
> 
> Stef
> 
> On Tue, Aug 1, 2017 at 1:31 PM, Ben Coman <b...@openinworld.com> wrote:
> > Not sure I'll have what you're looking for, but to start, do you mean
> > Pharo's green threads or vm native threads?
> > cheers -ben
> >
> > On Mon, Jul 31, 2017 at 7:38 AM, Alidra Abdelghani via Pharo-users
> > <pharo-users@lists.pharo.org> wrote:
> >>
> >>
> >>
> >> ---------- Forwarded message ----------
> >> From: Alidra Abdelghani <alidran...@yahoo.fr>
> >> To: pharo-users@lists.pharo.org
> >> Cc: "St├ęphane Ducasse" <stephane.duca...@inria.fr>, farid arfi
> >> <arf...@hotmail.com>
> >> Bcc:
> >> Date: Mon, 31 Jul 2017 01:38:58 +0200
> >> Subject: Threads safety in Pharo
> >> Hi,
> >>
> >> Somebody once evoked the problem of threads safety in Pharo. With a friend
> >> of mine who is expert in formal methods and process scheduling, we would
> >> like to have a look on it.
> >> Does anyone knows a good document describing the problem of Pharo with
> >> threads safety or at least any document that we can start with?
> >>
> >> Thanks in advance,
> >> Abdelghani
> >>
> >>
> >>
> >
>

Reply via email to