web page created Wednesday 25th February 2004 evening
AmigaOS semaphores are a bit tricky to program, the example here which is really a simplest possible usage was quite an effort to get right.
Download the archive, the program is
semaphores
. Using the program is straightforward, writing it is
not. Parallel programming is fraught with difficulties and sometimes its
better to just write a unitasking unithreaded program because its more
likely to be correct! Anyway one strategy to deal with the trickiness
of such things is to try and contain the tricky stuff in subroutines and
only access the mechanism through the subroutines, this is what I have
done here, the difficulties are contained within semaphores
so when you use it you are protected from the "issues".
Its a bit like how the Amiga has pre-emptive multitasking but most user programs are unitasking, ie the programmer is shielded from the problems of multitasking code.
The best way to understand semaphores
and mutexing
in general is via some experiments!:
============= Experiment 1 =============
Open 3 shells and have them all visible say A, B and C.
In shell A type:
semaphores hello e
this will return immediately, but has in fact obtained an exclusive lock on a public AmigaOS Semaphore called hello. You can have as many as you want with any name. This program requires the names to be made of characters a-z, A-Z, 0-9, _,
Now in shell B type:
semaphores hello e
this doesnt return! it has been queued up to wait for the semaphore "hello". Likewise in shell C type the same, this will also be waiting. This is all because exactly one task can possess an exclusive lock on a particular semaphore.
Now in shell A, type:
semaphores hello u
This unlocks the Semaphore, ie relinquishes it, you will now notice that shell B immediately returns to a prompt but C doesnt! This is because B is next in the queue. B now has the exclusive lock.
Now in shell B, type:
semaphores hello u
and shell C will immediately return: its now C's turn.
Try typing semaphores hello u
in shell B and
see what happens.
Lastly type semaphores hello u
in shell C.
The semaphore "hello" is now fully relinquished, try
typing semaphores hello u
again from any of the 3 shells
and see what happens.
If you type eg
semaphores ?
it will tell you all the options. The option s gives you a shared lock and E attempts to get an exclusive lock without waiting. If the E attempt fails you get a "warn" return for "if warn" in shell scripts.
Try experimenting with eg s in a similar manner to see what it is about.
=========== Experiment 2 =========
Find a large directory say xyz:
that takes a long time for the command
list xyz: all
Now create a script file expt2
semaphores expt2 e list xyz: all semaphores expt2 u
Now open 2 shells A and B, in each type
expt2
You will see that the second shell waits till the first shell has completely finished before it starts. This shows how semaphores can be used as a queue-up mechanism for shell scripts.
Now I show 2 practical uses of all this:
======= assign counts done correctly ========
create a script file assign_plus
:
.k name___,dirlist___ .bra { .ket } semaphores {name___} e ; obtain exclusive lock ; because this is not Forbid(); we can take ; as long as we like! if not exists env:{name___} echo "0" to env:{name___} noline endif eval ${name___} + 1 to env:{name___} ; counts no of times assign is done assign {name___}: {dirlist___} semaphores {name___} u ;
create another script file assign_minus
:
.k name___ .bra { .ket } semaphores {name___} e ; obtain exclusive lock ; because this is not Forbid(); we can take ; as long as we like! if not exists env:{name___} echo "assign {name___}: doesnt exist" quit 5 ; warn endif eval ${name___} - 1 to env:{name___} ; counts no of times assign is done if ${name___} eq 0 assign {name___}: ; only remove the assign when count reaches 0 endif semaphores {name___} u ;
Example usage of the scripts is:
assign_plus xyz "ram: df0: cd0: Workbench:s" ; assigns xyz: to the list ram: df0: etc assign_minus xyz ; to undo the above,
============ text editor problem example ========
I often have many copies of Memacs running with each one showing a different file. eg 1 Memacs may have some C, another a header file, another some assembler. If I am editing several files then its possible to lose track of which files are currently being edited. So if you are not careful you might load some file twice and thus be making changes to the wrong copy! So when you save and exit some of your changes are lost.
To get around this you need a script that prevents a file being editted twice. A naive script could be:
.k name___ .bra { .ket } if exists "{name___}.flag" echo "*"{name___}*"" is already being editted, quitting, quit 5 ; warn endif echo "hello" to "{name___}.flag" Memacs "{name___}" delete "{name___}.flag"
This looks ok, but the problem is that if 2 scripts were run in quick succession you would end up with 2 copies running because the second script looked for {name___}.flag before the first script created it.
What is needed here is an "indivisible read modify write",
the simplest way to do it is via 1 global Semaphore called Memacs
.k name___ .bra { .ket } semaphores Memacs e if exists "{name___}.flag" echo "*"{name___}*"" is already being editted, quitting, semaphores Memacs u quit 5 ; warn endif echo "hello" to "{name___}.flag" semaphores Memacs u Memacs "{name___}" delete "{name___}.flag"
I think this is now ok, although Memacs
is a global Semaphore,
it is only locked for a small interval of time, with the lockers
queuing up.
Being able to queue up shell scripts is a useful facility eg sometimes its better for copying actions to be one at a time because parallel copying to or from a given volume can be exceedingly inefficient as the disk heads have to keep zig zagging.
Semaphores and Mutexes are a bit like a local anaesthetic,
you just create local paralysis not global. If you experiment with
the above semaphores
program, you will understand
why they call it a Semaphore, it really feels like the 1 shell
is "signalling" the other. The term is very appropriate but only
when you have seen it in action, not when you read about it!