0

I have class named Myclass which has a 'Step' method and the other method as 'timer'. Below is the code for both methods. 'initialize' method starts Stepping. Aim is to calculate amount of time (in milliseconds) it took for stepping.

Myclass>> step  
self bounds: ((self bounds)  expandBy:1).
[(self extent )> (200@200) ifTrue:[self stopStepping.
                     tend:= Time millisecondClockValue.
                     result:= (tend-tstart).
                     Transcript cr; show: 'Semaphore signaled'.
                    sem signal. ]] fork.

Myclass>>timer
tstart:=Time millisecondClockValue.
[sem:= Semaphor new.
 sem wait.
 Transcript show: result.
 "^ result"] fork.

Above code is working fine , but when i try to return the value of result, it gives me an error saying block cannot return. Is it possible to make process wait until result gets updated and get the value of result.

Irfan
  • 303
  • 1
  • 8

2 Answers2

2

When you send the message fork in MyClass>>timer, a new process is created to evaluate the block and the timer method exits immediately. That means you just can not return something from within the block because nobody is waiting for a value. If you explain us what you want to achieve, we might be able to better help.

Damien Cassou
  • 2,555
  • 1
  • 16
  • 21
  • I want the process to wait until stepping is completed so that i can get the value of result back. Any other way i can achieve this?? – Irfan Sep 11 '13 at 15:36
1

Frame Rate

If all you want is know the time it took since last stepping, have a look at the FrameRateMorph. It holds two instance variables, lastDisplayTime and framesSinceLastDisplay, which are calculated in the Morph’s #step method:

FrameRateMorph>>#step
"Compute and display (every half second or so) the current framerate"

| now mSecs mSecsPerFrame framesPerSec newContents |
framesSinceLastDisplay := framesSinceLastDisplay + 1.
now := Time millisecondClockValue.
mSecs := now - lastDisplayTime.
(mSecs > 500 or: [mSecs < 0 "clock wrap-around"]) ifTrue: 
    [mSecsPerFrame := mSecs // framesSinceLastDisplay.
    framesPerSec := (framesSinceLastDisplay * 1000) // mSecs.
    "…"
    lastDisplayTime := now.
    framesSinceLastDisplay := 0]

You can use similar logic in your morph.

Note that FrameRateMorph implements #stepTime to return 0, so that it is called as often as possible. You might need to adjust your calculations according to this number.

Process synchronization

If your goal cannot be achieved by the means above, you have three options.

omit the block/fork

Do you really need the fork in #timer? What about this:

Myclass>>#timer
    tstart:=Time millisecondClockValue.
    sem:= Semaphor new.
    sem wait.
    Transcript show: result.
    ^ result

This will block until your result is ready.

fork and wait

If you insist on using a forked block, consider #forkAndWait:

Myclass>>#timer
    tstart:=Time millisecondClockValue.
    [sem:= Semaphor new.
    sem wait.
    Transcript show: result] forkAndWait.
    ^ result

This will also block until your result is ready.

make a callback

You could proactively call code once your result is ready

Callback via argument

Pass a one-argument-block to a changed timer function and operate on the result:

Myclass>>#timerDo: aBlock
    tstart:=Time millisecondClockValue.
    [sem:= Semaphor new.
    sem wait.
    Transcript show: result.
    aBlock value: result] fork.

and then

| obj |
" assume that obj is an instance of Myclass"
obj timerDo: [:result |
    "do something meaningful with the result, eg, show it "
    blaObject showResult: result.].

Callback block via instance variable

Add an instance variable, eg callBack to Myclass and change #timer to

Myclass>>#timer
    tstart:=Time millisecondClockValue.
    [sem:= Semaphor new.
    sem wait.
    Transcript show: result.
    callBack value: result] fork.

and then use it like

| obj |
" assume that obj is an instance of Myclass"
obj callBack: [:result |
    "do something meaningful with the result, eg, show it "
    blaObject showResult: result.].
obj timer.

Callback via message send

Note this might be dangerous and not what you are after

The third option is not to save a block as callback but send a message to an object directly upon result arrival.

Add two instance variable, eg target and selector to Myclass and change #timer to

Myclass>>#timer
    tstart:=Time millisecondClockValue.
    [sem:= Semaphor new.
    sem wait.
    Transcript show: result.
    target perform: selector with: result] fork.

and then use it like

| obj |
" assume that obj is an instance of Myclass"
obj
    target: blaObject;
    selector: #showResult: .
obj timer.

However, with all process synchronization you can get yourself into all different kinds of trouble, so if possible, please try the first option first.

Tobias
  • 3,026
  • 20
  • 34
  • FrameRateMorph is no longer loaded by default in a Pharo smalltalk. You can simply file it out of a Squeak image (or an old Pharo image, it was in a Pier 1.2 one click) and file it in (drag and drop). – Stephan Eggermont Sep 14 '13 at 21:45
  • Ah well. I hope the `#step` method above covert the point nevertheless. Thanks for the pointer. – Tobias Sep 15 '13 at 13:32
  • I tried 3 options, freezes my Pharo image. I can't find FrameRateMorph class in Pharo 2.0.. Thanks for the help – Irfan Sep 17 '13 at 23:20
  • @Irfan as @stephan-eggermont said, it is no longer in Pharo, file it out from earlier Pharo or Squeak. For thr freezing image, please edit your post and show _how your code/the `#timer` message is used_ so I can show how to help. – Tobias Sep 18 '13 at 08:35