Locking and Synchronization
Spinlocks
Internals
- they effectively spin until the lock becomes released. In each spin (while iteration) they check if the lock has been released yet; if not, they just spin further.
- optimization for UP: do nothing.
new lock holder spins already, not sleeping, so no need to wakeup
Usage
very lightweight, but should be held only for very short time (as other contenders spin and don't sleep!)
- read spinlocks (effectively shared) are also available, but not recommended for new implementations
- can not sleep while holding a spinlock; scheduler will panic about spinlocks being held by yielding thread XXX
- can never recurse!
- used to protect structures
LWKT serializing tokens
Internals
- uses atomic_cmpset* internally
- UP optimization: only check to see if preemption is happening, in which case acquisition of token fails.
- deeply integrated with lwkt scheduler (lwkt_yield, ...)
- scheduler takes care of acquiring the token before rescheduling
- so a thread won't run unless the scheduler can acquire all the tokens for it.
- tokens are not owned by the thread but by the CPU. threads only get token references
no explicit wakeup when yielding a token reference
Usage
a same thread can acquire multiple token references, but if that's the case, all tokens have to be acquired before the lwkt is scheduled!
- if thread sleeps while holding token references, other threads can acquire a token reference and run; so not completely safe to sleep
- used to protect heavier processing than spinlocks; but mostly also to protect data structures. Often used for global lists (allproc, etc)
- when acquiring token, not available, go to sleep, lose tokens
Lockmgr
Internals
- uses a spinlock inside
- sleeps while acquiring (at least if NO_WAIT is not specified) XXX
- heavyweight
- wakeup used to activate new lock holder
no UP optimization
Usage
supports shared locks or exclusive locks, shared locks can be upgraded, exclusive can be downgraded
- can be acquired recursively, if the thread is the same and LK_CANRECURSE is specified
- can sleep while holding the lock.
- used when there is a requirement or possibility of blocking/sleep/recursion/...
- used for portability of freebsd code that uses lockmgr locks
MTX
Internals
- based around atomic_cmpset_int instead of spinlocks
- uses wakeup to activate new lock holder
- much more lightweight than lockmgr (faster, much less space)
no UP optimization
Usage
can always be recursive
- can be shared/exclusive, so upgradable
- can be held across blocking/sleeps
- can pass lock ownership directly to new owner, so no wakeup needed and is guaranteed to reach the intended destination
MPLock
Internals
API is a wrapper for mp token
Usage
should be avoided at all cost
- must be held as little as possible
- one for the whole system (all CPUs!!)
LWKT Messages
Internals
- messages are passed by queueing them to the destination thread's message queue, then waking up the listener.
- messages have to be allocated/deallocated (typically using objcache_*)
- (usually use a drain to deallocate; set the replyport to be the drain and then just reply to the message)
rather lightweight, except for inter-processor messages
Usage
isn't a locking mechanism, rather serialization as everything can be processed in one thread if the others just send it to that one
- can be used to avoid races: just do all the processing in one single thread
- send all the work from other threads/entry points to that one thread using lwkt messages.
- requires no locking
Critical Sections
Internals
- Changes priority of current thread to TDPRIT_CRIT, effectively avoiding preemption of the thread
are per-cpu
Usage
avoid anything else happening on that CPU due to the disabled preemption
- are no synchronization/locking between different CPUs!
- should be used if some code has to run uninterruptedly
Condvars
Internals
uses spinlocks internally on the condvar
Usage
can interlock sleep when given a lockmgr lock to avoid missing changes to it, or just regular tsleep
- used to wait for conditions to happen (threads doing this are waiters)
can wakeup all waiters or just one, effectively notifying that a change has occured
Notes
could (should?) be torn out and replaced with other locking primitives and tsleep/wakeup interlocks
Serializers
Usage
serialize access to various hardware and other subsystems
Notes
could (should?) be torn out and carefully replaced with mtx locks
