Irfan
Irfan

Reputation: 303

returning a value with help of semaphore

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.

Upvotes: 0

Views: 266

Answers (2)

Tobias
Tobias

Reputation: 3110

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.

Upvotes: 1

Damien Cassou
Damien Cassou

Reputation: 2589

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.

Upvotes: 2

Related Questions