LIBATOMIC-STACK(3)

πŸ¦‹ LIBATOMIC-STACK(3) πŸ¦‹

okay so bestie, welcome to the glow-up manual page for libatomic-stack πŸ’… this is a 3 manpage (a library thing, iykyk), and we're about to make lock-free stacks feel like the most main-character data structure ever. no cap, it's giving ✨concurrency✨.


πŸŽ€ NAME πŸŽ€

libatomic-stack β€” the library that hands you a linked stack abstraction, and honestly? she ate πŸ’–


🌸 SYNOPSIS 🌸

like, first you gotta include the header, obviously:

#include <atomic_ops_stack.h>

and then when you're compiling, you link the thing in:

cc ... -latomic_ops_gpl
⚠️ important tea, bestie: the AO_stack implementation is licensed under the GPL, unlike the lower level routines. so like, different vibe, different license, don't get it twisted 🫢

and here are the three function signatures, exactly as they are, no editing:

void AO_stack_init(AO_stack_t *list);
void AO_stack_push_release(AO_stack_t *list, AO_t *new_element);
AO_t * AO_stack_pop_acquire(volatile AO_stack_t *list);

πŸ’œ DESCRIPTION πŸ’œ

libatomic-stack defines a linked stack abstraction. stacks, but make it concurrent. these stacks can be accessed by multiple concurrent threads at the same time, and she's built different.

the implementation is 1-lock-free, meaning it will keep making progress if at most one thread becomes inactive while it's operating on the data structure. one sleepy thread? fine. two? okay now we're in trouble, girl 😭

this is what makes it safe to access these data structures from non-reentrant signal handlers, as long as at most one non-signal-handler thread is accessing the data structure at the same time. and how do you guarantee that condition? you acquire an ordinary lock around the non-handler accesses to the data structure. simple as that. periodt πŸ’…

we use a fully lock-free implementation when the underlying hardware makes that less expensive, i.e. when we have a double-wide compare-and-swap operation available. (the fully lock-free implementation uses an AO_t-sized version count, and assumes it does not wrap during the time any given operation is active. this seems reasonably safe on 32-bit hardware, and very safe on 64-bit hardware πŸ”₯.) if a fully lock-free implementation is used, the macro AO_STACK_IS_LOCK_FREE will be defined. so like, vibe check your macros, bestie.

the cleanest way to use these routines is probably to define the stack node type with an initial AO_t link field, so that the conversion between the link-field pointer and the stack element pointer is just a compile-time cast. but other possibilities exist, no gatekeeping here. (this would be cleaner in C++ with templates, just saying πŸ™ˆ.)

a stack is represented by an AO_stack_t structure. (this is normally 2 or 3 times the size of a pointer, so she's got some heft to her ✨.) it may be statically initialized by setting it to AO_STACK_INITIALIZER, or dynamically initialized to an empty stack with AO_stack_init.

and there are only three operations for accessing stacks. iconic. minimal. efficient:

  • AO_stack_init β€” Initalise a stack πŸŽ€
  • AO_stack_push_release β€” Push new element onto the stack. up she goes πŸ’…
  • AO_stack_pop_acquire β€” Pop element off the stack. bye bestie πŸ‘‹

🧚 the rules (yes there are rules) 🧚

we require that the objects pushed as list elements remain addressable as long as any push or pop operation are in progress. (it is OK for an object to be "pop"ped off a stack and "deallocated" with a concurrent "pop" on the same stack still in progress, but only if "deallocation" leaves the object addressable. the second "pop" may still read the object, but the value it reads will not matter. so like, she might peek, but she won't care 🫢)

we require that the headers (AO_stack objects) remain allocated and valid as long as any operations on them are still in-flight. do not deallocate her mid-conversation, that's so rude 😭


πŸ“ the debugging macros πŸ“

we also provide macros AO_REAL_HEAD_PTR that converts an AO_stack_t to a pointer to the link field in the next element, and AO_REAL_NEXT_PTR that converts a link field to a real, dereferencable, pointer to the link field in the next element.

🚨 bestie listen: this is intended only for debugging, or to traverse the list after modification has ceased. there is otherwise no guarantee that walking a stack using this macro will produce any kind of consistent picture of the data structure. like, no promises, no vibes, nothing. you have been warned πŸ’€

🌈 SEE ALSO 🌈


πŸ‘‘ AUTHOR πŸ‘‘

this manual page was written by Ian Wienand <ianw@gelato.unsw.edu.au>, based on comments in the source code. it was written for the Debian project (but may be used by others). so like, shoutout Ian, absolute legend for documenting this 🫢


and that's the whole page, bestie πŸ’– Ian Wienand wrote this one, and it's dated June 2, 2005 β€” yeah, she's vintage, she's classic, she's LIBATOMIC-STACK(3) and she still slays. go forth and push and pop responsibly, mwah πŸ’…βœ¨πŸŽ€