pub struct Signal { /* private fields */ }Expand description
A cache-line padded 64-bit atomic bitmap for tracking queue readiness.
Each Signal represents a group of up to 64 queues, where each bit indicates
whether the corresponding queue has work available. Multiple Signal instances
are coordinated via a SignalWaker to form a complete two-level bitmap.
§Design
Signal (64-bit AtomicU64)
┌───┬───┬───┬───┬─────┬───┐
│ 0 │ 1 │ 0 │ 1 │ ... │ 0 │ Each bit = one queue's scheduled state
└───┴───┴───┴───┴─────┴───┘
Q0 Q1 Q2 Q3 ... Q63§Cache Optimization
The inner state is wrapped in Arc<CachePadded<...>> to:
- Allow cheap cloning (single pointer copy)
- Prevent false sharing between different signals
- Optimize for hot paths (producers setting bits, executor clearing bits)
§Thread Safety
All operations use atomic instructions. Multiple producers can concurrently set
bits (via set()), and the executor can concurrently acquire/clear bits (via
acquire() or try_acquire()).
§Cloning
Signal is cheaply clonable via Arc. All clones share the same underlying
atomic bitmap, making it suitable for distribution across multiple producer threads.
Implementations§
Source§impl Signal
impl Signal
Sourcepub fn index(&self) -> u64
pub fn index(&self) -> u64
Returns the signal’s index within the SignalWaker’s signal array.
This index is used to:
- Map this signal to the corresponding bit in the summary bitmap
- Identify which group of 64 queues this signal represents
§Example
let signal = Signal::with_index(5);
assert_eq!(signal.index(), 5);
// This signal controls queues 320-383 (5 * 64 through (5+1) * 64 - 1)Sourcepub fn value(&self) -> &AtomicU64
pub fn value(&self) -> &AtomicU64
Returns a reference to the underlying atomic value.
Provides direct access to the 64-bit bitmap for advanced use cases that need custom atomic operations beyond the provided methods.
§Use Cases
- Custom bit manipulation patterns
- Debugging (observing raw bitmap state)
- Integration with external synchronization primitives
§Example
let signal = Signal::new();
signal.set(10);
let raw_value = signal.value().load(Ordering::Relaxed);
assert_eq!(raw_value & (1 << 10), 1 << 10); // Bit 10 is setSource§impl Signal
impl Signal
Sourcepub fn with_index(index: u64) -> Self
pub fn with_index(index: u64) -> Self
Sourcepub fn with_value(index: u64, value: u64) -> Self
pub fn with_value(index: u64, value: u64) -> Self
Creates a new Signal with the specified index and initial bitmap value.
This is primarily used for testing or restoring state. In normal operation, signals start with all bits cleared.
§Parameters
index: Position in the SignalWaker’s signal array (0-61)value: Initial 64-bit bitmap value
§Example
// Create signal with bits 0, 5, and 10 already set
let signal = Signal::with_value(3, (1 << 0) | (1 << 5) | (1 << 10));
assert_eq!(signal.size(), 3);
assert!(signal.is_set(0));
assert!(signal.is_set(5));
assert!(signal.is_set(10));Sourcepub fn load(&self, ordering: Ordering) -> u64
pub fn load(&self, ordering: Ordering) -> u64
Loads the current bitmap value with the specified memory ordering.
§Parameters
ordering: Memory ordering for the load operation
§Returns
The 64-bit bitmap value where each set bit represents a ready queue.
§Example
signal.set(5);
signal.set(10);
let value = signal.load(Ordering::Acquire);
assert_eq!(value, (1 << 5) | (1 << 10));Sourcepub fn size(&self) -> u64
pub fn size(&self) -> u64
Returns the number of set bits (ready queues) in this signal.
Equivalent to popcount(bitmap), this counts how many queues in this
signal group currently have work available.
§Performance
Uses the POPCNT instruction on x86_64 (~3 cycles), making it very efficient.
§Example
let signal = Signal::new();
signal.set(0);
signal.set(5);
signal.set(63);
assert_eq!(signal.size(), 3);Sourcepub fn set(&self, index: u64) -> (bool, bool)
pub fn set(&self, index: u64) -> (bool, bool)
Atomically sets a bit in the bitmap using fetch_or.
This is the primary method for producers to signal that a queue has work available.
§Parameters
index: Bit position to set (0-63)
§Returns
A tuple (was_empty, was_set):
was_empty:trueif this was the first bit set (signal transitioned from empty to non-empty)was_set:trueif the bit was successfully set (wasn’t already set)
§Use Cases
The return values are used for summary bitmap updates:
let (was_empty, was_set) = signal.set(queue_bit);
if was_empty && was_set {
// This signal was empty, now has work - update summary
waker.mark_active(signal.index());
}§Performance
~5-10 ns (one atomic fetch_or operation)
§Example
let signal = Signal::new();
let (was_empty, was_set) = signal.set(5);
assert!(was_empty); // Signal was empty
assert!(was_set); // Bit 5 was not previously set
let (was_empty, was_set) = signal.set(5);
assert!(!was_empty); // Signal already had bits set
assert!(!was_set); // Bit 5 was already setSourcepub fn set_with_bit(&self, bit: u64) -> u64
pub fn set_with_bit(&self, bit: u64) -> u64
Atomically sets a bit using a precomputed bitmask.
Similar to set(), but takes a precomputed 1 << index value for cases
where the bit position is computed once and reused.
§Parameters
bit: Precomputed bitmask with exactly one bit set (e.g.,1 << 5)
§Returns
The previous bitmap value before setting the bit.
§Example
let signal = Signal::new();
let bit_mask = 1u64 << 10;
let prev = signal.set_with_bit(bit_mask);
assert_eq!(prev, 0); // Was empty
assert!(signal.is_set(10));Sourcepub fn acquire(&self, index: u64) -> bool
pub fn acquire(&self, index: u64) -> bool
Atomically clears a bit if it is currently set (CAS-based).
This is the primary method for the executor to claim ownership of a ready queue. Uses a CAS loop to ensure the bit is cleared atomically.
§Parameters
index: Bit position to clear (0-63)
§Returns
true: Bit was set and has been successfully cleared (queue acquired)false: Bit was not set (queue not ready or already acquired)
§Use Cases
// Executor loop
if signal.acquire(queue_bit) {
// Successfully acquired queue, process it
process_queue(queue_id);
}§Performance
~10-20 ns (CAS loop, typically succeeds on first iteration)
§Example
let signal = Signal::new();
signal.set(5);
assert!(signal.acquire(5)); // Successfully cleared bit 5
assert!(!signal.acquire(5)); // Bit already clear, returns falseSourcepub fn try_acquire(&self, index: u64) -> (u64, u64, bool)
pub fn try_acquire(&self, index: u64) -> (u64, u64, bool)
Attempts to atomically clear a bit, returning detailed state information.
Similar to acquire(), but provides additional information about the
before/after state of the bitmap, useful for debugging or advanced scheduling.
§Parameters
index: Bit position to clear (0-63)
§Returns
A tuple (before, after, success):
before: Bitmap value before the operationafter: Bitmap value after the operation (if successful)success:trueif the bit was cleared,falseif it wasn’t set
§Example
let signal = Signal::with_value(0, 0b101010); // Bits 1, 3, 5 set
let (before, after, success) = signal.try_acquire(3);
assert_eq!(before, 0b101010);
assert_eq!(after, 0b100010); // Bit 3 cleared
assert!(success);Sourcepub fn is_set(&self, index: u64) -> bool
pub fn is_set(&self, index: u64) -> bool
Checks if a specific bit is set without modifying the bitmap.
Non-atomic read followed by bit test. Suitable for non-critical checks where races are acceptable.
§Parameters
index: Bit position to check (0-63)
§Returns
true if the bit is set, false otherwise.
§Example
let signal = Signal::new();
assert!(!signal.is_set(5));
signal.set(5);
assert!(signal.is_set(5));