martin@mwtech.UUCP (Martin Weitzel) (06/05/90)
In article <475@van-bc.wimsey.bc.ca> sl@van-bc.wimsey.bc.ca (Stuart Lynne) writes: [about locking from interface programs of the SysV spool system] > >This script shows how this could be done, except that there is a race >condition to create the lock file. > >A C program would be a much better solution. > [script deleted, basically the existence of some file was used as lock condition; because `test -f lockfile was done before `echo ... >lockfile', there remained a small chance for two simultaneous executions of the script both to "think" they have won - or technically spoken, they succeeded locking the resource] As the poster allready pointed out, there is a race condition with this method. Without changing much, this could be avoided by `link'ing to the lockfile instead of creating it. Eg. write to a temporary file first, then place the lock with `/etc/link'; this operation will replace the test for existence in the script. (Note that for some newer "ln"-commands it is no error if the destination file exists, though there's a "-f" option which seems at first glance to give the choice to the user ... :-(). Linking (and unlinking) provide some "semaphor"-mechanism for shell scripts, which works even for the priviledged processes. (Note that the latter is *not* the case for the more common technique of creating a file without access permissions.) If your system supports FIFOs, there's another interresting technique, which even avoids the usual trade off of "busy waiting" vs. "quick response". (This is not so much a problem in the spooler example, but may be in other applications.) Locking with FIFOs works by creating a named pipe with some daemon process, then write a single new-line character into it and sleep forever. If you don't want to program in C, the latter may be done by executing repeated `sleep 30000' from a never ending loop. It is essential that the daemon keeps the FIFO open for read and write, so that the character in it is never discarded! All in all it's a bit tricky to get this started, as you can open a FIFO from the shell only for read OR for write and each request would wait for the other, but you can get around this with: mknod LOCKFIFO p echo >LOCKFIFO & while sleep 30000 do :; done <LOCKFIFO & Some other process then "places a lock" by reading from the FIFO. If the FIFO contains a new-line character, this can be done by the shell's read command. (In fact, this is the only reason why a new-line was written to the FIFO, otherwise every character would have served similar well). Now there is no more character in the FIFO, so that a third (and forth, fifth, ...) process which will try to read is blocked. As this is done by the kernel, no CPU-time is consumed in this situation. When the first process leaves its critical region, it writes a new-line character back into the FIFO, which eventually will be read by some other process (a waiting one or one that starts later; remember: the daemon constitutes a `possible consumer' for data in the FIFO, so that the written character will never be lost). The disadvantages of this technique are similar to using a lockfile, especially the case that the process which placed the lock dies without releasing it must be solved. But because the race condition is avoided now, any other technique could be used. Note that the `FIRST writes PID to file - SECOND issues kill PID to see if FIRST is still alive' is one choice, though this technique has a (very) small chance to go wrong. (Assume that one process dies, then the PIDs wrap around and it happens that a very long running process picks just that PID that is still in the lockfile). If you want to make it "really water-proof", you can assign the role of a "watchdog" to the daemon. For that purpose a process that successfully placed the lock should write its PID into some other FIFO which is read by the daemon. After this read returns, the daemon goes into a loop and periodically checks the validity of the PID (eg. with the `kill PID' approach). If this shows that the locking process died without releasing the lock, the daemon could easily write a new new-line into the FIFO. Everyone's happy now? - Not me. What I want to know from the readership is if anyone allready uses "FIFO-Locking", as I have never found in mentioned in any literature (maybe there's a leak of memory in my head :-), but at least IMHO). Do you have pointers if someone else allready described it or did you yourself "invent" it. Further: Has anybody allready added possible improvements, eg. a "first come first served" scheme for competing processes (the kernel gives *no* guarantee which of several blocked-on-read processes will be unblocked when a character becomes available in the FIFO.) -- Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83