Administrator
|
If my object is using a process internally that should run only as long as the object is around, how do I do that? I searched the ml's for "Finalization", but couldn't quite make sense of the process.
Thanks!
Cheers,
Sean |
On 2013-04-23, at 05:17, "Sean P. DeNigris" <[hidden email]> wrote: > If my object is using a process internally that should run only as long as > the object is around, how do I do that? I searched the ml's for > "Finalization", but couldn't quite make sense of the process. If your process refers to this Object (e.g. methods of said Object are evaluated in a this thread) the thing you want to do is not possible. Processes are root objects for the GarbageCollector hence all the objects in its execution stack won't get garbage collected. Otherwise check WeakRegistry |
In reply to this post by Sean P. DeNigris
On 23 April 2013 05:16, Sean P. DeNigris <[hidden email]> wrote:
> If my object is using a process internally that should run only as long as > the object is around, how do I do that? I searched the ml's for > "Finalization", but couldn't quite make sense of the process. > you don't need finalization.. just a weak reference. something like this: array := WeakArray with: myObject. [[ array first notNil] whileTrue: [ ...... ]] fork. > Thanks! > > ----- > Cheers, > Sean > -- > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > -- Best regards, Igor Stasenko. |
Administrator
|
Timer>>start | array | array := WeakArray with: self. process := [ [ array first notNil ] whileTrue: [ interval asDelay wait. self value: self value + 1. self announcer announce: (TimerValueChanged to: self value) ] ] fork. Timer new start. Smalltalk garbageCollect. The process is still there... I'm attaching (to Nabble) a screenshot of #pointersTo
Cheers,
Sean |
I think it is as Camillo said:
the Timer object holds the Process object the block in the Process holds the Timer object in its closure. the Process is held by the system => neither can become garbage Maybe Igor meant to not put the process in an instance variable… But I fear that won't do: the block closure keeps on referring to self, which is your timer. On 23 Apr 2013, at 14:17, "Sean P. DeNigris" <[hidden email]> wrote: > Igor Stasenko wrote >> you don't need finalization.. just a weak reference. something like this: >> >> array := WeakArray with: myObject. >> >> [[ array first notNil] whileTrue: [ ...... ]] fork. > > Timer>>start > > | array | > array := WeakArray with: self. > process := [ > [ array first notNil ] whileTrue: [ > interval asDelay wait. > self value: self value + 1. > self announcer announce: (TimerValueChanged to: self value) ] ] fork. > > Timer new start. > Smalltalk garbageCollect. > > The process is still there... > I'm attaching (to Nabble) a screenshot of #pointersTo > <http://forum.world.st/file/n4683095/Screen_Shot_2013-04-23_at_8.16.01_AM.png> > > > > ----- > Cheers, > Sean > -- > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683095.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > |
In reply to this post by Sean P. DeNigris
On 23 April 2013 14:17, Sean P. DeNigris <[hidden email]> wrote:
> Igor Stasenko wrote >> you don't need finalization.. just a weak reference. something like this: >> >> array := WeakArray with: myObject. >> >> [[ array first notNil] whileTrue: [ ...... ]] fork. > > Timer>>start > > | array | > array := WeakArray with: self. > process := [ > [ array first notNil ] whileTrue: [ > interval asDelay wait. > self value: self value + 1. > self announcer announce: (TimerValueChanged to: self value) ] ] fork. > > Timer new start. > Smalltalk garbageCollect. > > The process is still there... but what you expecting? you refer to 'self' multiple times. sure thing it cannot be GC-ed, neither process will die because of plenty of references on stack. Even block closure which you creating for fork will have home context which keeps reference to 'self', e.g. receiver. > I'm attaching (to Nabble) a screenshot of #pointersTo > <http://forum.world.st/file/n4683095/Screen_Shot_2013-04-23_at_8.16.01_AM.png> > > > > ----- > Cheers, > Sean > -- > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683095.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > -- Best regards, Igor Stasenko. |
Hi Sean, We experienced that also with Noury and Jannik. The trick has been to extract the block in a class side method. Example: Timer>>start
process := self class createProcessFor: self Timer class>>createProcessFor: aTimer ^[ aTimer value: aTimer value + 1.......] fork Now: t := Timer new start. t := nil makes the process dispear as well. ok not really beautiful but it works ;-)
Cheers, Luc #Luc 2013/4/23 Igor Stasenko <[hidden email]>
|
Hi Luc, Sean, Igor,
I still don't think that is right (although I did not actually try to run your approach): you still make the block closure of the process refer strongly to the timer object. I tried it as follows (.st file included at the end): Object subclass: #TestTimer instanceVariableNames: 'counter interval' TestTimer>>#initialize interval := 5 seconds asDelay. counter := 0. self class start: (WeakArray with: self) TestTimer>>#tick interval wait. counter := counter + 1 TestTimer class>>#start: reference [ [ reference first isNil ] whileFalse: [ reference first tick ] ] forkAt: Processor userBackgroundPriority named: 'Test Timer' Sadly it still does not work, and I don't know why ;-) TestTimer new. 3 timesRepeat: [ Smalltalk garbageCollect ]. TestTimer allInstances You can see the timer count going up every 5 seconds when inspecting the instance, but you have to close the inspector otherwise that is another reference. Exploring strong pointers lists the WeakArray instance... Sven On 23 Apr 2013, at 16:01, Luc Fabresse <[hidden email]> wrote: > Hi Sean, > > We experienced that also with Noury and Jannik. > The trick has been to extract the block in a class side method. > Example: > > Timer>>start > process := self class createProcessFor: self > > Timer class>>createProcessFor: aTimer > ^[ aTimer value: aTimer value + 1.......] fork > > > Now: > > t := Timer new start. > t := nil > > makes the process dispear as well. > ok not really beautiful but it works ;-) > > Cheers, > > Luc > > > > > #Luc > > > 2013/4/23 Igor Stasenko <[hidden email]> > On 23 April 2013 14:17, Sean P. DeNigris <[hidden email]> wrote: > > Igor Stasenko wrote > >> you don't need finalization.. just a weak reference. something like this: > >> > >> array := WeakArray with: myObject. > >> > >> [[ array first notNil] whileTrue: [ ...... ]] fork. > > > > Timer>>start > > > > | array | > > array := WeakArray with: self. > > process := [ > > [ array first notNil ] whileTrue: [ > > interval asDelay wait. > > self value: self value + 1. > > self announcer announce: (TimerValueChanged to: self value) ] ] fork. > > > > Timer new start. > > Smalltalk garbageCollect. > > > > The process is still there... > > but what you expecting? > you refer to 'self' multiple times. sure thing it cannot be GC-ed, > neither process will die because > of plenty of references on stack. > Even block closure which you creating for fork will have home context > which keeps reference to 'self', > e.g. receiver. > > > > I'm attaching (to Nabble) a screenshot of #pointersTo > > <http://forum.world.st/file/n4683095/Screen_Shot_2013-04-23_at_8.16.01_AM.png> > > > > > > > > ----- > > Cheers, > > Sean > > -- > > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683095.html > > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > > > > > > -- > Best regards, > Igor Stasenko. > > TestTimer.st (1K) Download Attachment |
Administrator
|
In reply to this post by Igor Stasenko
I'm expecting magic apparently because I am stupid when it comes to processes ;) I changed all the "self"s to array first, which also didn't work, but then I read your statement about the home context... Luc's class-side trick seems to work: Timer>>start | array | array := WeakArray with: self. process := self class createProcessFor: array. "tried passing self here, but it seemed to keep the process around" Timer class>>createProcessFor: aWeakArray [ [ aWeakArray first notNil ] whileTrue: [ | timer | aWeakArray first interval asDelay wait. "We have to check again if the timer was garbage collected during the wait" aWeakArray first ifNotNil: [ aWeakArray first value: aWeakArray first value + 1. aWeakArray first announcer announce: (TimerValueChanged to: aWeakArray first value) ] ] ] fork. Wow, it would seem that an object using a process internally that is only valid when the object is around is a fairly common use case. I can't believe how complicated this is :/
Cheers,
Sean |
Sean,
On 23 Apr 2013, at 18:02, "Sean P. DeNigris" <[hidden email]> wrote: > Luc's class-side trick seems to work: > Timer>>start > > | array | > array := WeakArray with: self. > process := self class createProcessFor: array. "tried passing self here, > but it seemed to keep the process around" > > Timer class>>createProcessFor: aWeakArray > > [ [ aWeakArray first notNil ] whileTrue: [ > | timer | > aWeakArray first interval asDelay wait. > "We have to check again if the timer was garbage collected during the > wait" > aWeakArray first ifNotNil: [ > aWeakArray first value: aWeakArray first value + 1. > aWeakArray first announcer announce: (TimerValueChanged to: aWeakArray > first value) ] ] ] fork. How do you test ? In my code, I tried to do exactly what you are doing, except for the announcement, but I don't think it works. I tried to formalize a test, but you probably have to use the #debug: on TestCase otherwise the test is run twice and fails on the first assert. It could well be that the test is bogus (does the finalization get a chance to run ?). It only works if I terminate the process via the Process Browser. Any ideas ? These are old school file outs in _UnpackagedPackage: Sven |
Administrator
|
> Are you sure it works ?
> How do you test ? Pretty sure. I'm attaching the actual code and some tests. If you run TimerTest>>#testStart from Nautilus with an open Process Browser (with auto-update enabled), you'll see the process appear and then disappear. MorphicExtrasSpd-Leds.st (6K) Download Attachment
Cheers,
Sean |
Administrator
|
Although if I evaluate the snippet in the class comment of SpdLedMorph, the process hangs around. Arggghhhh. This is crazy!!!
Cheers,
Sean |
In reply to this post by Sean P. DeNigris
On 23 April 2013 18:02, Sean P. DeNigris <[hidden email]> wrote:
> Igor Stasenko wrote >> but what you expecting? >> you refer to 'self' multiple times. sure thing it cannot be GC-ed, >> neither process will die because >> of plenty of references on stack. >> Even block closure which you creating for fork will have home context >> which keeps reference to 'self', >> e.g. receiver. > > I'm expecting magic apparently because I am stupid when it comes to > processes ;) I changed all the "self"s to array first, which also didn't > work, but then I read your statement about the home context... > > Luc's class-side trick seems to work: > Timer>>start > > | array | > array := WeakArray with: self. > process := self class createProcessFor: array. "tried passing self here, > but it seemed to keep the process around" > closure captures method's arguments, and holding them strongly.. > Timer class>>createProcessFor: aWeakArray > > [ [ aWeakArray first notNil ] whileTrue: [ > | timer | > aWeakArray first interval asDelay wait. > "We have to check again if the timer was garbage collected during the > wait" > aWeakArray first ifNotNil: [ > aWeakArray first value: aWeakArray first value + 1. > aWeakArray first announcer announce: (TimerValueChanged to: aWeakArray > first value) ] ] ] fork. > > Wow, it would seem that an object using a process internally that is only > valid when the object is around is a fairly common use case. I can't believe > how complicated this is :/ > you can also simply pass a copy of 'self' to process. as long as copy accessing same announcer, your ticker process will work perfectly. then all you would need to do is: | copy | copy := self copy. process := self class createProcessFor: copy. WeakRegistry default add: self executor: copy. and implement: finalize process terminate. process := nil. Then createProcessFor: will be simple: Timer class>>createProcessFor: timer [ [ timer wait. " make Demeter a bit happier" timer tick. "this should increment counter and announce TimerValueChanged, like that we're not make Demeter unhappy" ] whileTrue ] fork. (actually , to make mr.Demeter completely happy, i would do [ timer waitAndTick ] whileTrue. ) and, tick, will be something like that: tick process ifNil: [ ^ false ]. self incrementCounter. self announce: (MyGreatAnnouncement with: whatever). ^ true. cheerz :) > ----- > Cheers, > Sean > -- > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683144.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > -- Best regards, Igor Stasenko. |
On 23 April 2013 20:32, Igor Stasenko <[hidden email]> wrote:
> On 23 April 2013 18:02, Sean P. DeNigris <[hidden email]> wrote: >> Igor Stasenko wrote >>> but what you expecting? >>> you refer to 'self' multiple times. sure thing it cannot be GC-ed, >>> neither process will die because >>> of plenty of references on stack. >>> Even block closure which you creating for fork will have home context >>> which keeps reference to 'self', >>> e.g. receiver. >> >> I'm expecting magic apparently because I am stupid when it comes to >> processes ;) I changed all the "self"s to array first, which also didn't >> work, but then I read your statement about the home context... >> >> Luc's class-side trick seems to work: >> Timer>>start >> >> | array | >> array := WeakArray with: self. >> process := self class createProcessFor: array. "tried passing self here, >> but it seemed to keep the process around" >> > of course, because the home context (will be the method below) of > closure captures method's arguments, > and holding them strongly.. > >> Timer class>>createProcessFor: aWeakArray >> >> [ [ aWeakArray first notNil ] whileTrue: [ >> | timer | >> aWeakArray first interval asDelay wait. >> "We have to check again if the timer was garbage collected during the >> wait" >> aWeakArray first ifNotNil: [ >> aWeakArray first value: aWeakArray first value + 1. >> aWeakArray first announcer announce: (TimerValueChanged to: aWeakArray >> first value) ] ] ] fork. >> >> Wow, it would seem that an object using a process internally that is only >> valid when the object is around is a fairly common use case. I can't believe >> how complicated this is :/ >> > it is not apparent to me why this is common use case. :) > > you can also simply pass a copy of 'self' to process. > as long as copy accessing same announcer, your ticker process will > work perfectly. > then all you would need to do is: > > | copy | > copy := self copy. > > process := self class createProcessFor: copy. > WeakRegistry default add: self executor: copy. > > and implement: > > finalize > process terminate. > process := nil. small correction.. while writing this, i first used #terminate, but then added, 'process := nil' , which is enough, because it letting the process exit from loop graciously and terminate by itself. > > Then createProcessFor: will be simple: > > Timer class>>createProcessFor: timer > [ > [ timer wait. " make Demeter a bit happier" > timer tick. "this should increment counter and announce TimerValueChanged, > like that we're not make Demeter unhappy" > ] whileTrue ] fork. > > (actually , to make mr.Demeter completely happy, i would do > [ timer waitAndTick ] whileTrue. > ) > > and, tick, will be something like that: > > tick > process ifNil: [ ^ false ]. > self incrementCounter. > self announce: (MyGreatAnnouncement with: whatever). > ^ true. > > cheerz :) > >> ----- >> Cheers, >> Sean >> -- >> View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683144.html >> Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. >> > > > > -- > Best regards, > Igor Stasenko. -- Best regards, Igor Stasenko. |
Administrator
|
In reply to this post by Igor Stasenko
In the sense that Smalltalk is prototype Dynabook software, part of the purpose of which is to do simulations ;) Also, I've seen this come up quite a bit and usually the suggestion is to hijack Morphic stepping, which seems to me like too ugly a hack.
Cheers,
Sean |
In reply to this post by Igor Stasenko
Hi all,
@Sven, yes of course I forgot the WeakArray to save the hard ref in my previous explanation. 2013/4/23 Igor Stasenko <[hidden email]>
@Igor. It will not work here I think. because you make the shallow copy before initializing the process and the finalize message is then sent to the copy.
as Sven proposed, a WeakArray would be good but I never deeply test it. Anyway, I always fight with the finalization mechanism. With Noury we wrote an ActiveObject class some time ago that has an internal process that is stopped and the activeobject is finalized.
The only thing is to be **really** carreful when giving the block to the ActiveObject to not introduce references. Here one of the test case that is green: testDoesStopBlockUponFinalizationForLowestPriority |counter startSemaphore finalizationSemaphore process | counter := 0.
startSemaphore := Semaphore new. finalizationSemaphore := Semaphore new. activeObject := ActiveObject
do: [ startSemaphore signal. [(Delay forMilliseconds: 50) wait]repeat
] ensure: [ counter := 1. finalizationSemaphore signal.
]. activeObject priority: Processor lowestPriority;
start. self deny: (startSemaphore waitTimeoutSeconds: 2). process := activeObject process.
activeObject := nil. Smalltalk garbageCollect. self deny: (finalizationSemaphore waitTimeoutSeconds: 2).
self assert: counter = 1. self assert: process isTerminated Here the whole code: MCHttpRepository
user: '' password: '' Luc
|
Administrator
|
Yes, I had to copy the process variable to the copy, but it just gets uglier and uglier... copy := self copy. process := self class createProcessFor: copy. copy process: process. WeakRegistry default add: self executor: copy.
Cheers,
Sean |
In reply to this post by Sean P. DeNigris
On 23 Apr 2013, at 19:59, "Sean P. DeNigris" <[hidden email]> wrote: > Sean P. DeNigris wrote >> If you run TimerTest>>#testStart from Nautilus with an open Process >> Browser (with auto-update enabled), you'll see the process appear and then >> disappear. > > Although if I evaluate the snippet in the class comment of SpdLedMorph, the > process hangs around. Arggghhhh. This is crazy!!! I loaded your code and I have the impression that it works, both in the test and in a workspace. But it drives me crazy that my version does not seem to work. I will have to come back to this tomorrow with a fresh mind. Sven |
In reply to this post by Sean P. DeNigris
On 23 April 2013 21:47, Sean P. DeNigris <[hidden email]> wrote:
> Luc Fabresse wrote >> @Igor. >> It will not work here I think. >> because you make the shallow copy before initializing the process and the >> finalize message is then sent to the copy. > > Yes, I had to copy the process variable to the copy, but it just gets uglier > and uglier... > copy := self copy. > process := self class createProcessFor: copy. > copy process: process. > WeakRegistry default add: self executor: copy. > no need to pass it back to sender. > > > ----- > Cheers, > Sean > -- > View this message in context: http://forum.world.st/Terminating-a-Process-when-an-object-dies-tp4683036p4683183.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > -- Best regards, Igor Stasenko. |
Administrator
|
I need the return value because I need to suspend and resume the process from the original Timer, which is what the GUI holds a pointer to.
Cheers,
Sean |
Free forum by Nabble | Edit this page |