Hi ben, thank you for looking at this. 2014-07-22 20:17 GMT+02:00 <[email protected]>:
> I thought this might be interesting to learn, so I've gave it a go. I > had some success at the end, but I'll give a progressive report. > > First I thought I'd try moving the update of StringMorph outside the > worker-process using a Morph's #step method as follows... > > Morph subclass: #BackgroundWorkDisplayMorph > instanceVariableNames: 'interProcessString stringMorph' > classVariableNames: '' > category: 'BenPlay' > "---------" > > BackgroundWorkDisplayMorph>>initializeMorph > self color: Color red. > stringMorph := StringMorph new. > self addMorphBack: stringMorph. > self extent:(300@50). > "---------" > > BackgroundWorkDisplayMorph>>newWorkerProcess > ^[ > | work | > work := 0. > [ 20 milliSeconds asDelay wait. > work := work + 1. > interProcessString := work asString. > ] repeat. > ] newProcess. > "---------" > > BackgroundWorkDisplayMorph>>step > stringMorph contents: interProcessString. > "---------" > > BackgroundWorkDisplayMorph>>stepTime > ^50 > "---------" > > BackgroundWorkDisplayMorph>>initialize > | workerProcess running | > super initialize. > self initializeMorph. > > workerProcess := self newWorkerProcess. > running := false. > > self on: #mouseUp send: #value to: > [ (running := running not) > ifTrue: [ workerProcess resume. self color: Color green. ] > ifFalse: [ workerProcess suspend. self color: Color red. ] > ] > "---------" > > > > But evaluating "BackgroundWorkDisplayMorph new openInWorld" found this > exhibited the same problematic behavior you reported... Clicking on the > morph worked a few times and then froze the UI until Cmd-. pressed a few > times. > > However I found the following never locked the GUI. > > BackgroundWorkDisplayMorph>>initialize > "BackgroundWorkDisplayMorph new openInWorld" > | workerProcess running | > super initialize. > self initializeMorph. > > workerProcess := self newWorkerProcess. > running := false. > > [ [ (running := running not) > ifTrue: [ workerProcess resume. self color: Color green ] > ifFalse: [ workerProcess suspend. self color: Color red ]. > 10 milliSeconds asDelay wait. > ] repeat ] fork. > "---------" > > This locks the UI as well. Not every timet hough. I did this 5 times, every time in a freshly loaded image and it happens two times. > So the problem seemed to not be with #suspend/#resume or with the shared > variable /interProcessString/. Indeed, since in the worker thread > /interProcessString/ is atomically assigned a copy via #asString, and the > String never updated, I think there is no need to surround use of it with a > critical section. > > The solution then was to move the "#resume/#suspend" away from the "#on: > #mouseUp send: #value to:" as follows... > > BackgroundWorkDisplayMorph>>initialize > "BackgroundWorkDisplayMorph new openInWorld" > | workerProcess running lastRunning | > super initialize. > self initializeMorph. > > workerProcess := self newWorkerProcess. > lastRunning := running := false. > > [ [ lastRunning = running ifFalse: > [ running > ifTrue: [ workerProcess resume ] > ifFalse: [ workerProcess suspend ]. > lastRunning := running. > ]. > 10 milliSeconds asDelay wait. > ] repeat ] fork. > > self on: #mouseUp send: #value to: > [ (running := running not) > ifTrue: [ self color: Color green. ] > ifFalse: [ self color: Color red. ] > ] > "---------" > And this too :( > > And finally remove the busy loop. > > BackgroundWorkDisplayMorph>>initialize > "BackgroundWorkDisplayMorph new openInWorld" > | workerProcess running lastRunning semaphore | > super initialize. > self initializeMorph. > > workerProcess := self newWorkerProcess. > lastRunning := running := false. > semaphore := Semaphore new. > > [ [ semaphore wait. > running > ifTrue: [ workerProcess resume ] > ifFalse: [ workerProcess suspend ]. > ] repeat ] fork. > > self on: #mouseUp send: #value to: > [ (running := running not) > ifTrue: [ self color: Color green. ] > ifFalse: [ self color: Color red. ]. > semaphore signal. > ] > "---------" > > And this locks the UI too. (Loaded the code 20 times, every time after a fresh image start up. Two times I got a locked ui after the first two clicks). And I don't understand this code :) > Now I can't say how close that is to how it "should" be done. Its the > first time I used sempahores and just what I discovered hacking around. > But hey! it works :) > > cheers -ben > > > > Nicolai Hess wrote: > > I am still struggling with it. > > Any ideas? > > > 2014-07-09 11:19 GMT+02:00 Nicolai Hess <[email protected]>: > >> >> >> >> 2014-07-09 2:07 GMT+02:00 Eliot Miranda <[email protected]>: >> >> Hi Nicolai, >>> >>> >>> On Tue, Jul 8, 2014 at 7:19 AM, Nicolai Hess <[email protected]> >>> wrote: >>> >>>> I want to create a process doing some work and call #changed on a >>>> Morph. >>>> I want to start/suspend/resume or stop this process. >>>> But sometimes, suspending the process locks the UI-Process, >>>> and I don't know why. Did I miss something or do I have to care when to >>>> call suspend? >>>> >>>> Wrapping the "morph changed" call in >>>> UIManager default defer:[ morph changed]. >>>> Does not change anything. >>>> >>>> Here is an example to reproduce it. >>>> Create the process, >>>> call resume, call supsend. It works, most of the time, >>>> but sometimes, calling suspend locks the ui. >>>> >>>> p:=[[true] whileTrue:[ Transcript crShow: (DateAndTime now asString). >>>> 30 milliSeconds asDelay wait]] newProcess. >>>> >>> p resume. >>>> p suspend. >>>> >>> >>> If you simply suspend this process at random form a user-priority >>> process you'll never be able to damage the Delay machinery you're using, >>> but chances are you'll suspend the process inside the critical section that >>> Transcript uses to make itself thread-safe, and that'll lock up the >>> Transcript. >>> >> >> Thank you Eliot >> yes I guessed it locks up the critical section, but I hoped with would >> not happen if I the use UIManager defer call. >> >> >> >>> >>> ThreadSafeTranscript>>nextPutAll: value >>> accessSemaphore >>> critical: [stream nextPutAll: value]. >>> ^value >>> >>> So instead you need to use a semaphore. e.g. >>> >>> | p s wait | >>> s := Semaphore new. >>> p:=[[true] whileTrue:[wait ifTrue: [s wait]. Transcript crShow: >>> (DateAndTime now asString). 30 milliSeconds asDelay wait]] newProcess. >>> wait := true. >>> 30 milliSeconds asDelay wait. >>> wait := false. >>> s signal >>> >>> etc... >>> >> >> Is this a common pattern I can find in pharos classes. Or I need some >> help understanding this. The semaphore >> wait/signal is used instead of process resume/suspend? >> >> What I want is a process doing repeatly some computation, >> calls or triggers an update on a morph, and I want to suspend and resume >> this process. >> >> I would stop this discussion if someone tells me, "No your are doing it >> wrong, go this way ..", BUT what strikes me: >> in this example, that reproduces my problem more closely: >> >> |p m s running| >> running:=false. >> m:=Morph new color:Color red. >> s:= StringMorph new. >> m addMorphBack:s. >> p:=[[true]whileTrue:[20 milliSeconds asDelay wait. s >> contents:(DateAndTime now asString). m changed]] newProcess. >> m on:#mouseUp send:#value to:[ >> running ifTrue:[p suspend. m color:Color red.] >> ifFalse:[p resume.m color:Color green.]. >> running := running not]. >> m extent:(300@50). >> m openInWorld >> >> >> clicking on the morph will stop or resume the process, if it locks up I >> can still press alt+dot -> >> - a Debugger opens but the UI is still not responsive. I can click with >> the mouse on the debuggers close icon. >> - nothing happens, as the UI is still blocked. >> - pressing alt+Dot again, the mouse click on the close icon is >> processed and the first debugger window closes >> - maybe other debuggers open. >> >> Repeating this steps, at some time the system is *fully* responsive again! >> And miraculously, it works after that without further blockages. >> What's happening here? >> >> >> Nicolai >> >> >> >>> >>> HTH >>> >>> regards >>>> Nicolai >>>> >>> >>> >>> >>> -- >>> best, >>> Eliot >>> >> >> > >
