Alastair’s Place

Software development, Cocoa, Objective-C, life. Stuff like that.


Cross-process semaphores with timeouts on OS X

OS X provides three kinds of semaphores by default; POSIX semaphores, System V sempahores, and Mach semaphores. Of those, neither the POSIX form nor the System V form support any kind of timeout, and while Mach semaphores do support a timeout, it isn’t immediately obvious how to pass one to another process.

semsrv is here to solve this problem. It’s a simple Mach server that implements named semaphores; 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 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:

  1. If you’re going to use this code directly in your program, please don’t change the .defs file without altering the definition of SEMSRV_SERVICE to a name that starts with your own reverse domain prefix. Doing that would break anything that uses this code literally as-is.

  2. In a normal program, you probably want to try spawning the semsrv process 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.

  3. The semsrv program doesn’t daemonize itself. This might matter to you, or it might not.

  4. 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 that doing that will mean your program needs to install a suitable launch daemon property list.

  5. This code is only really intended as a sample, and has had limited testing.

The code can be built by typing make at a command prompt. It has a very simple (and not perfect) Makefile, but I can’t be bothered with anything more sophisticated.

If you just want to kick the tyres, try:

$ hg clone
$ cd semsrv
$ make
mig semsrv.defs
cc -g -W -Wall   -c -o semsrvServer.o semsrvServer.c
g++ -g -W -Wall   -c -o server.o
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…

You can grab the code from the mercurial repository.