Ben Coman wrote:
Hi Nicolai,�� I have something for you to try.
With the problem seeming to be stuck in the semaphore critical section,
I read around on mutexes and semaphores and found [1] enlightening,
which says: "The correct use of a semaphore is for signaling from one
task to another. A mutex is meant to be taken and released, always in
that order, by each task that uses the shared resource it protects. By
contrast, tasks that use semaphores either signal or wait���not both.
[...] Any two RTOS tasks that operate at **different priorities** and
coordinate via a mutex, create the opportunity for priority inversion
[...while...] semaphores [...] do not cause priority inversion when
used for signaling." [1]��
To summarize with an example, here's how to use a mutex:
/* Task 1 */
���� mutexWait(mutex_mens_room);
���������� // Safely use shared resource
���� mutexRelease(mutex_mens_room);
/* Task 2 */
���� mutexWait(mutex_mens_room);
���������� // Safely use shared resource
���� mutexRelease(mutex_mens_room);
By contrast, you should always use a semaphore like this:
/* Task 1 - Producer */
������ semPost(sem_power_btn);���� // Send the signal
/* Task 2 - Consumer */
������ semPend(sem_power_btn);�� // Wait for signal
I am out of my depth here, but in light [1] the way the current code
seems to mix semaphore/mutex concepts with
Semaphore>>forMutalExclusion and Semaphore>>critical:�� gave
me some concern.�� Anyway, the delay scheduling code seemed more like a
producer/consumer signaling problem than a shared resource mutal
exclusion problem, so I rewrote it like the former and fixes the
problem without causing any *obvious* problems.�� However I lack
experience here and it could definitely use some review.�� Can you take
the attached zip file and...
1. File in "Delay class-new delay code part 1.st"
������ Defines #enableNewDelayCode and #enableNewDelayCode:
������ Manually define the referenced class variables
2. File in "Delay class-new delay code part 2.st"
�������� Modifies #handleTimerEvent, guarded by enableNewDelayCode
3. File in "Delay-new delay code part 3.st"
������ Modifies #schedule and #unschedule, guarded by enableNewDelayCode
4. File in "BackgroundWorkDisplayMorph.st"
5. Save checkpoint Image to return to between trials.
6. Open System Browser on BackgroundWorkDisplayMorph>>initialize
������
Trial 1.
BackgroundWorkDisplayMorph new openInWorld.
Trial 2.
Delay enableNewDelayCode: true.
BackgroundWorkDisplayMorph new openInWorld.
If if its deemed worthwhile, I'll open a ticket to track this more
formally.�� One thing needed is to considered performance impact, I
haven't so far.
cheers -ben
[1] http://www.barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
[2] http://nerdfortress.com/2011/02/18/you-say-semaphore-i-say-mutex/
Juan, Nicolai,
I found I was overzealous in my guarding by looping on /beingWaitedOn/
in #schedule and #unschedule, which caused
DelayTest>>testSemaphore to fail.��
With the code in my previous zip file, you should modify the #ifTrue:
part of the following methods...
for Delay>>schedule
������ beingWaitedOn ifTrue: [^self error: 'This Delay has already been
scheduled.'].
������ "Assuming comparison, #ifTrue:ifFalse and assignment all inlined
bytecodes that can't be interrupted."
������ ScheduledDelay == nil
������ ������ ifTrue: [ ScheduledDelay := self ] "No other process will
overwrite "
������ ������ ifFalse:
������ ������ [ ������ "Assuming when semaphore is signalled to continue this
process, it cannot be interrupted again before assignment"
������ ������ ������ ScheduledDelayNilledSemaphore wait.
������ ������ ������ ScheduledDelay := self.
������ ������ ].
������ ������ "Signal semaphore in #handleTimerEvent (highest priority
process) to action ScheduledDelay,
������ ������ set ScheduledDelay to nil and signal
ScheduledDelayNilledSemaphore"
������ ������ TimingSemaphore signal.
for Delay>>unschedule
�������� "Assuming comparison, #ifTrue:ifFalse and assignment all inlined
bytecodes that can't be interrupted."
������ FinishedDelay == nil
������ ������ ifTrue: [ FinishedDelay := self ]
������ ������ ifFalse:
������ ������ [ ������ "Assuming when semaphore is signalled to continue this
process, it cannot be interrupted again before assignment"
������ ������ ������ FinishedDelayNilledSemaphore wait.
������ ������ ������ FinishedDelay := self.
������ ������ ��].
������ ������ "Signal semaphore in #handleTimerEvent (highest priority
process) to action FinishedDelay,
������ ������ set FinishedDelay to nil and signal
FinishedDelayNilledSemaphore"
������ ������ TimingSemaphore signal.
For reference, I've also attached a new zip of my Delay class.
cheers -ben