Dan Sugalski wrote:

I'm pretty sure the POSIX docs say that you can't call mutex routines from within interrupt code, which makes sense--the last thing you want is for an interrupt handler to block on a mutex aquisition.

I haven't got a copy, but I'd be surprised if they explicitly forbade it - I think however you *do* need to be very careful if you need to mix mutexes and signals, so that you don't self-deadlock.


This isn't actually a big deal, as it's reasonably straightforward to write code that can handle multiple user-space readers and writers (which can guard themselves with mutexes) and a single interrupt-time writer. Proper ordering and checking of operations inside the code makes this straightforward, if a pain. (And I know the ordering's doable, otherwise every single OS on the planet with interrupt code would fall down and go boom :) The nasty part comes in with multiple interrupt-time writers. I'm not sure if it's common to be able to have multiple concurrent interrupt handlers running, but I do know it's possible.

You *cannot* achieve what you want by 'proper ordering and checking of operations', as this will *not* work on a SMP machine, where the ordering of loads and stores is *not* guaranteed, *irrespective* of what appears to be the order of instructions produced by the compiler. To achieve what you want you need a memory barrier, which is other thing a mutex provides in addition to synchronization. For details, see either section 3.4 "Memory visibility between threads" in "Programming with Posix Threads" by David Butenhof, or follow the links below:


http://www.math.cmu.edu/Parallel_Cluster/smp/Lthreadsfaq.htm#Q118
http://www.math.cmu.edu/Parallel_Cluster/smp/Lthreadsfaq.htm#Q156
http://www.math.cmu.edu/Parallel_Cluster/smp/Lthreadsfaq.htm#Q162
http://www.math.cmu.edu/Parallel_Cluster/smp/Lthreadsfaq.htm#Q180
http://www.math.cmu.edu/Parallel_Cluster/smp/Lthreadsfaq.htm#Q202

I'd considered just up and disabling interrupts entirely but I didn't want to take the performance hit from it. I'm thinking now that decision was a bad one, but luckily one that's not welded into anything anywhere (and wouldn't have been even if I'd kept going this way, as all we would've guaranteed is thread-safe interrupt-safe queues) so it's simple enough to change.

Disabling signals and interrupts is still going to be platform-specific, but that's not a big deal.

I think that what you are trying to do is ensure that the queue operations are async-signal safe - here is what our threads expert has to say about this:


| For a function to be async-signal safe (that is, to be callable both
| at main level and from a signal handler) it has to block all signals
| before acquiring any locks (including any locks acquired by any
| library functions it calls). Otherwise, it could grab a lock at main
| level, take a signal, and attempt to acquire the same lock at
| interrupt level, thereby deadlocking itself.
|
| And, no, recursive locks are not the answer; all that would do is
| invite data corruption: the lock is being held because the data
| invariants are being modified; allowing a signal handler to re-modify
| them just corrupts them.
|
| Now, if there is a lock that is acquired *only* while in a signal
| handler, then it is sufficient to block all signals while in the signal
| handler (and this can be accomplished with no additional overhead by
| doing sigfillset(&action.sa_mask) before using 'action' to set up
| the signal handler).

Hope that helps,

--
Alan Burlison
--



Reply via email to