WARNING / ACHTUNG / 警告
This page is intended solely as a space to jot notes onto. Do not make any decisions based on the content here. Some portions came from cursory reviews & may, in fact, be incorrect.
open() - increments a channel's refcount field. (Only referenced when attempting to unload the device & detecting that channels are still open.)
close() - decrements a channels refcount field.
- read(), write()
- Channel lock acquired/held for duration of operation, except for a temporary release around uiomove().
Parent PCM device's inprog counter incremented on entrance, decremented on exit.
- poll(), mmap() - Channel lock acquired/held for duration of operation.
By default, no locking is performed. Typically, a channel lock is acquired in order to complete a few instructions, then is released. (E.g., acquire, set buffer size, release.) This is satisfactory in many cases, but operations which touch other channels make things a little tricky (ex: SNDCTL_AUDIOINFO).
Another toughy are SNDCTL_DSP_SYNCGROUP and SNDCTL_DSP_SYNCSTART. Syncgroups are singly-linked lists of PCM channels set to begin playing at the same time. These lists are also kept in a singly linked list, since the number of entries is small and nearly all insert/remove operations are done at the head. The master list (and all member group lists) are protected by a single mutex, so it is important that a caller acquire this mutex first without any channel locks held. This is necessary for the SYNCSTART operation which iterates over all channels in a group, locks them, and begins playback/recording.
Hack of a solution: in addition to a mutex, each channel maintains a conditional variable (cv) and in progress counter (inprog). For operations which might temporarily give up the mutex, inprog is incremented before releasing the mutex. Any other thread that acquires the channel lock should inspect inprog - if it is non-zero, that thread should call cv_wait() on the channel's lock & cv. (This is typically done in a loop.) When the owning thread reacquires the lock, it should decrement inprog and call cv_signal() on the lock & cv.
Implementing that scheme would require updating channel.c::chn_lock() to include the cv handling, then updating CHN_LOCK/CHN_UNLOCK macros to make use of these checks.