Description
I've been trying to understand the SegQueue
implementation to reply to #675. These are some notes I took along the way:
- Might be UB? Need to know whether or not padding bytes within the structs being uninitialized (even if zeroed) counts as UB. Also seems like you could achieve better performance by using actually uninit memory in the slot value instead of zeroing it out.
SegQueue::new
should pre-allocate a block to match theInjector
implementation and remove some initialization code frompush
andpop
.- Internally document the memory layout and the magic of the head and tail indexes. Basically, the index skips over one value which is used as a fence to perform block allocation/deallocation. So if LAP=2, BLOCK_CAP=1, and tail=0, two competing threads will result in one of them writing its value into slot 0, bumping tail to 1, and then allocating a new block and bumping tail to 2. The other thread will see that its CAS failed and get a tail of 1 which means it spins/yields until tail is 2. Same but opposite for head.
- Extract out
offset + 1 == BLOCK_CAP
into a variable with the explanation from the previous bullet point. crossbeam/crossbeam-queue/src/seg_queue.rs
Line 206 in b11f1a8
crossbeam/crossbeam-queue/src/seg_queue.rs
Line 229 in b11f1a8
crossbeam/crossbeam-queue/src/seg_queue.rs
Line 297 in b11f1a8
crossbeam/crossbeam-queue/src/seg_queue.rs
Line 300 in b11f1a8
- There's a bunch of
LAP - 1
that would be clearer asBLOCK_CAP
- All shifting of tail should be able to be removed as no metadata is stored in the tail index. The one issue would be making sure head and tail wrap around at the same rate, so maybe tail could be shifted before addition and then unshifted again.
I'm planning on looking into this stuff so that Tokio can use SegQueue
: tokio-rs/tokio#2528. Given #746's lack of progress, my main concern is that any opened PR would just get ignored, so Tokio might end up copying SegQueue
with its own fixes (which would be a bummer).