Cross-process semaphores with timeouts on OS X
Someone on darwin-dev recently asked how to go about obtaining a cross-process semaphore that can be waited on with a timeout on OS X.
POSIX semaphores currently don’t support this feature on OS X; nor do System V semaphores. Mach semaphores do support timeouts (see /usr/include/mach/semaphore.h), but it isn’t immediately obvious how to pass one to another process.
Anyway, I thought I’d stick together a simple Mach server to implement named semaphores; the idea is very simple… when you want a semaphore, you ask the server to create it for you, giving it a name. If it already exists, the server will simply return the existing semaphore object. If not, it will create a new Mach semaphore and return that.
Once you have the semaphore_t port value, you use it just as if you had called semaphore_create() yourself. When you’re done with it, you can use mach_port_destroy() to release the port in your own process. You won’t be able to destroy the semaphore using semaphore_destroy(), because your task doesn’t own it.
A few notes:
If you’re going to use this code directly in your program, please don’t change the
.defsfile without altering the definition ofSEMSRV_SERVICEto a name that starts with your own reverse domain prefix. Doing that would break anything that uses this code literally as-is.In a normal program, you probably want to try spawning the
semsrvprocess in the background. If another program has already started the server with the same service name, your copy will fail, but that’s OK because you can just use the other copy.The
semsrvprogram doesn’t daemonize itself. This might matter to you, or it might not.It’s possible that you might be interested in re-writing the server to use launchd so that it starts automatically and you don’t need to worry about point 2 (above). Note though that doing that will mean your program needs to install a suitable launch daemon property list, which might be more of a headache than it’s worth.
This code is only really intended as a sample, and has had limited testing. There is obviously no warranty or anything like that.
The code is in the Public Domain. It is not subject to copyright and therefore has no license. If you work for someone too stupid to understand the words Public Domain, you can simply slap a BSD or MIT license on it and carry on with what you were doing.
The code can be built by typing make at a command prompt after changing into its directory. It has a very simple (and not perfect) Makefile, but I can’t be bothered with anything more elaborate. You could add the files to an Xcode project instead if you wanted, in which case you’d need to make Xcode run mig if it doesn’t already know to do that.
To try a few simple experiments, build the code, then enter ./semsrv & at a Terminal prompt to start the server. You can then use the ./semtest program to try various things, e.g.
$ tar xjf semsrv.tar.bz2 $ cd semsrv $ make mig semsrv.defs cc -g -W -Wall -c -o semsrvServer.o semsrvServer.c g++ -g -W -Wall -c -o server.o server.cc g++ -g -W -Wall semsrvServer.o server.o -o semsrv cc -g -W -Wall -c -o client.o client.c cc -g -W -Wall -c -o semsrvUser.o semsrvUser.c cc -g -W -Wall client.o semsrvUser.o -o semtest $ ./semsrv & $ ./semtest create foo Created foo: 4099 $ ./semtest signal foo Signalled foo $ ./semtest wait foo Waiting for foo...done $ ./semtest get foo Got foo: 4099 $ ./semtest create foo Got foo (already exists): 4099 $ ./semtest destroy foo Destroyed foo
Obviously to test more interesting behaviour, you’ll want more than one Terminal window…
The code is in this archive file.
The libdispatch semaphores also support timeouts.
I'm not sold on using Mach though, given the ambiguity of whether it's supported API or unsupported SPI, at least when I've asked.
It shouldn't be too hard to put an IPC semaphore together using existing IPC methods though, I might add that to my list of things to put together when I have time ^_^