pub struct NonceStack<T>where
T: Send,{ /* private fields */ }Implementations§
Source§impl<T> NonceStack<T>where
T: Send,
impl<T> NonceStack<T>where
T: Send,
pub fn push(&self, val: T)
Sourcepub fn pop(&self) -> Option<T>
pub fn pop(&self) -> Option<T>
Almost-correct example of using a nonce to implement pop().
This method contains a use-after-free on the line head.head=(*ret).next.
This would be safe in a garbage collected system, or in an embedded system without memory protection, since head.head will be discarded if the stack has been changed, and *ret can only be freed after it is popped from the stack. Since *ret may have been freed, it may have been returned to the operating system, leading to a segmentation fault when accessed here.
If you need a stack similar to NonceStack, consider using an epoch collector such as crossbeam_epoch, or by maintaining a pool of reusable objects. For instance, you could use a second stack to store the pool, and return objects to it after they are popped from this stack. If the pool stack is empty, then allocate a new object.
Once you are sure that no thread will access either stack, you can safely empty both stacks and free the objects they contain.
Stacks with nonces are also sometimes used to implement slot allocators. A slot allocator is initialized at startup with a finite number of tokens (such as file handles, or some other finite resource). When a thread needs a resource, it pops from the stack. If the stack is empty, then the thread goes async. Atomically checking that the stack is empty and registering oneself for future wakeup is left as an exercise to the reader, as it is exactly the sort of thing atomic_try_update excels at.
TODO: Implement a double-stack structure and/or slot such as the ones above, so we have correct examples of the NonceStack pattern.