finalizing processes?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

finalizing processes?

Yoshiki Ohshima-2
  Hello,

I have trouble terminating some processes I make.  More specifically,
I'm trying to terminate some processes when the their associated
"owner" object is garbage collected.  The problem seems that these
processes hold back pointers to my owner object via outerContext of
the closures so the owner object does not get garbage collected when
nobody else is pointing to it.

Attached is my test case.  After filing in this to a trunk image,
evaluate:

        k := KEventStream new.
        KEventStream beTimer: k with: 100.

in a workspace.  You'll see a little counter going on the left edge of
screen.  What I want to do is to stop the counter by evaluating:

        k := nil. Smalltalk garbageCollect.

but the processes hold onto the closure, which in turn holds onto the
context for executing #beTimer:with:, and that context holds onto 'k',
or 'anObject'.

I thought that closure could just be detached from the outer context but
it is not the case here.  Why?

Thank you!

-- Yoshiki






KEventStream.st (2K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: finalizing processes?

Andreas.Raab
On 12/1/2011 0:32, Yoshiki Ohshima wrote:

>    Hello,
>
> I have trouble terminating some processes I make.  More specifically,
> I'm trying to terminate some processes when the their associated
> "owner" object is garbage collected.  The problem seems that these
> processes hold back pointers to my owner object via outerContext of
> the closures so the owner object does not get garbage collected when
> nobody else is pointing to it.
>
> Attached is my test case.  After filing in this to a trunk image,
> evaluate:
>
> k := KEventStream new.
> KEventStream beTimer: k with: 100.
>
> in a workspace.  You'll see a little counter going on the left edge of
> screen.  What I want to do is to stop the counter by evaluating:
>
> k := nil. Smalltalk garbageCollect.
>
> but the processes hold onto the closure, which in turn holds onto the
> context for executing #beTimer:with:, and that context holds onto 'k',
> or 'anObject'.
>
> I thought that closure could just be detached from the outer context but
> it is not the case here.  Why?

Just guessing: Is it possibly because you're using a shared variable
(sema) in #beTimer:with:? In order to reference the shared variable from
the process the outer scope needs to be retained. You'd have to pass the
variable into the process to avoid requiring the outer context for sema,
along the lines of:


        timerProcess := [:sema|
                [true] whileTrue: [(Delay forMilliseconds: interval) wait. sema signal]
        ] newProcessWith: {semaphore}.
        timerProcess priority: Processor timingPriority.
        timerProcess resume.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: finalizing processes?

Yoshiki Ohshima-2
At Thu, 01 Dec 2011 09:43:13 +0100,
Andreas Raab wrote:

>
> On 12/1/2011 0:32, Yoshiki Ohshima wrote:
> >    Hello,
> >
> > I have trouble terminating some processes I make.  More specifically,
> > I'm trying to terminate some processes when the their associated
> > "owner" object is garbage collected.  The problem seems that these
> > processes hold back pointers to my owner object via outerContext of
> > the closures so the owner object does not get garbage collected when
> > nobody else is pointing to it.
> >
> > Attached is my test case.  After filing in this to a trunk image,
> > evaluate:
> >
> > k := KEventStream new.
> > KEventStream beTimer: k with: 100.
> >
> > in a workspace.  You'll see a little counter going on the left edge of
> > screen.  What I want to do is to stop the counter by evaluating:
> >
> > k := nil. Smalltalk garbageCollect.
> >
> > but the processes hold onto the closure, which in turn holds onto the
> > context for executing #beTimer:with:, and that context holds onto 'k',
> > or 'anObject'.
> >
> > I thought that closure could just be detached from the outer context but
> > it is not the case here.  Why?
>
> Just guessing: Is it possibly because you're using a shared variable
> (sema) in #beTimer:with:? In order to reference the shared variable from
> the process the outer scope needs to be retained. You'd have to pass the
> variable into the process to avoid requiring the outer context for sema,
> along the lines of:
>
>
> timerProcess := [:sema|
> [true] whileTrue: [(Delay forMilliseconds: interval) wait. sema signal]
> ] newProcessWith: {semaphore}.
> timerProcess priority: Processor timingPriority.
> timerProcess resume.

  Ah, I did not know about newProcessWith, but this approach does not
seem to solve the problem.  (Before reading this, I tried something
along the line of:

        timerProcess := [
                mySema := sema.
                myInterval := interval.
                [true] whileTrue: [(Delay forMilliseconds: myInterval) wait. mySema signal]] forkAt: Processor timingPriority.

but this did not work either.  As far as I can tell, 'outerContext' link
survives so the context does not go away.)

Now, my solution is to pass "Array with: anObject" to beTimer:with:
instead of "anObject", and in beTimer:with:, I assign nil to the
slot.  So, the outer context remains but it only holds onto an Array
with nil, and a finalization does kicks in now.

Thanks!

-- Yoshiki

If anybody is interested in, the code looks like:

beTimer: anObjectContainer with: interval

        | dict sema timerProcess updateProcess updater target anObject |
        sema := Semaphore new.
        updater := #display:.
        anObject := anObjectContainer at: 1.
        anObjectContainer at: 1 put: nil.
        target := WeakArray with: anObject.
        timerProcess := [
                [true] whileTrue: [(Delay forMilliseconds: interval) wait. sema signal]] forkAt: Processor timingPriority.
        updateProcess := [
                [true] whileTrue: [sema wait. (target at: 1) ifNotNil: [:t | t perform: updater with: Time millisecondClockValue]]] forkAt: Processor timingPriority.
        anObject property: #sema put: sema.
        anObject property: #timerProcess put: timerProcess.
        anObject property: #updateProcess put: updateProcess.

        dict := IdentityDictionary new.
        dict at: #timerProcess put: timerProcess.
        dict at: #updateProcess put: updateProcess.
        anObject toFinalizeSend: #finalize: to: self with: dict.
        ^ anObject.