SMR_LIST_INIT(9)

πŸ“¦ in nixpkgs unstable

πŸ’… SMR_LIST_INIT(9)

okay so bestie, this one is straight out of the Kernel Developer's Manual and it's a whole family of macros β€” not just the one in the title. The SMR_* list macros. Singly-linked lists, regular lists, and tail queues, all built to live inside the safe memory reclamation mechanism. It's giving concurrency, it's giving lock-free reader energy. Let's get into it 🫢


✨ NAME ✨

Literally all of these live on this page, no cap:

  • singly-linked list girlies: SMR_SLIST_ENTRY, SMR_SLIST_HEAD, SMR_SLIST_HEAD_INITIALIZER, SMR_SLIST_INIT, SMR_SLIST_FIRST, SMR_SLIST_NEXT, SMR_SLIST_FOREACH, SMR_SLIST_FIRST_LOCKED, SMR_SLIST_NEXT_LOCKED, SMR_SLIST_EMPTY_LOCKED, SMR_SLIST_FOREACH_LOCKED, SMR_SLIST_FOREACH_SAFE_LOCKED, SMR_SLIST_INSERT_HEAD_LOCKED, SMR_SLIST_INSERT_AFTER_LOCKED, SMR_SLIST_REMOVE_HEAD_LOCKED, SMR_SLIST_REMOVE_AFTER_LOCKED, SMR_SLIST_REMOVE_LOCKED
  • doubly-linked list girlies: SMR_LIST_ENTRY, SMR_LIST_HEAD, SMR_LIST_HEAD_INITIALIZER, SMR_LIST_INIT, SMR_LIST_FIRST, SMR_LIST_NEXT, SMR_LIST_FOREACH, SMR_LIST_FIRST_LOCKED, SMR_LIST_NEXT_LOCKED, SMR_LIST_EMPTY_LOCKED, SMR_LIST_FOREACH_LOCKED, SMR_LIST_FOREACH_SAFE_LOCKED, SMR_LIST_INSERT_HEAD_LOCKED, SMR_LIST_INSERT_AFTER_LOCKED, SMR_LIST_INSERT_BEFORE_LOCKED, SMR_LIST_REMOVE_LOCKED
  • tail queue girlies: SMR_TAILQ_ENTRY, SMR_TAILQ_HEAD, SMR_TAILQ_HEAD_INITIALIZER, SMR_TAILQ_INIT, SMR_TAILQ_FIRST, SMR_TAILQ_NEXT, SMR_TAILQ_FOREACH, SMR_TAILQ_FIRST_LOCKED, SMR_TAILQ_NEXT_LOCKED, SMR_TAILQ_LAST_LOCKED, SMR_TAILQ_EMPTY_LOCKED, SMR_TAILQ_FOREACH_LOCKED, SMR_TAILQ_FOREACH_SAFE_LOCKED, SMR_TAILQ_INSERT_HEAD_LOCKED, SMR_TAILQ_INSERT_TAIL_LOCKED, SMR_TAILQ_INSERT_AFTER_LOCKED, SMR_TAILQ_INSERT_BEFORE_LOCKED, SMR_TAILQ_REMOVE_LOCKED

β€” also known, collectively, as the SMR list macros. Iconic behavior 🌈


🌸 SYNOPSIS 🌸

#include <sys/smr.h>

SMR_SLIST_ENTRY(TYPE);

void
SMR_SLIST_INIT(SMR_SLIST_HEAD *head);

TYPE *
SMR_SLIST_FIRST(SMR_SLIST_HEAD *head);

TYPE *
SMR_SLIST_NEXT(TYPE *elm, FIELDNAME);

SMR_SLIST_FOREACH(VARNAME, SMR_SLIST_HEAD *head, FIELDNAME);

TYPE *
SMR_SLIST_FIRST_LOCKED(SMR_SLIST_HEAD *head);

TYPE *
SMR_SLIST_NEXT_LOCKED(TYPE *elm, FIELDNAME);

int
SMR_SLIST_EMPTY_LOCKED(SMR_SLIST_HEAD *head);

SMR_SLIST_FOREACH_LOCKED(VARNAME, SMR_SLIST_HEAD *head, FIELDNAME);

SMR_SLIST_FOREACH_SAFE_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME,
    TEMP_VARNAME);

void
SMR_SLIST_INSERT_HEAD_LOCKED(SMR_SLIST_HEAD *head, struct TYPE *elm,
    FIELDNAME);

void
SMR_SLIST_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
    FIELDNAME);

void
SMR_SLIST_REMOVE_HEAD_LOCKED(SMR_SLIST_HEAD *head, FIELDNAME);

void
SMR_SLIST_REMOVE_AFTER_LOCKED(struct TYPE *elm, FIELDNAME);

void
SMR_SLIST_REMOVE_LOCKED(SMR_SLIST_HEAD *head, struct TYPE *elm, TYPE,
    FIELDNAME);

SMR_LIST_ENTRY(TYPE);

void
SMR_LIST_INIT(SMR_LIST_HEAD *head);

TYPE *
SMR_LIST_FIRST(SMR_LIST_HEAD *head);

TYPE *
SMR_LIST_NEXT(TYPE *elm, FIELDNAME);

TYPE *
SMR_LIST_FIRST_LOCKED(SMR_LIST_HEAD *head);

TYPE *
SMR_LIST_NEXT_LOCKED(TYPE *elm, FIELDNAME);

int
SMR_LIST_EMPTY_LOCKED(SMR_LIST_HEAD *head);

SMR_LIST_FOREACH(VARNAME, SMR_LIST_HEAD *head, FIELDNAME);

SMR_LIST_FOREACH_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME);

SMR_LIST_FOREACH_SAFE_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME,
    TEMP_VARNAME);

void
SMR_LIST_INSERT_HEAD_LOCKED(SMR_LIST_HEAD *head, struct TYPE *elm,
    FIELDNAME);

void
SMR_LIST_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
    FIELDNAME);

void
SMR_LIST_INSERT_BEFORE_LOCKED(struct TYPE *listelm, struct TYPE *elm,
    FIELDNAME);

void
SMR_LIST_REMOVE_LOCKED(struct TYPE *elm, FIELDNAME);

SMR_TAILQ_ENTRY(TYPE);

void
SMR_TAILQ_INIT(SMR_TAILQ_HEAD *head);

TYPE *
SMR_TAILQ_FIRST(SMR_TAILQ_HEAD *head);

TYPE *
SMR_TAILQ_NEXT(TYPE *elm, FIELDNAME);

TYPE *
SMR_TAILQ_FIRST_LOCKED(SMR_TAILQ_HEAD *head);

TYPE *
SMR_TAILQ_NEXT_LOCKED(TYPE *elm, FIELDNAME);

TYPE *
SMR_TAILQ_LAST_LOCKED(SMR_TAILQ_HEAD *head);

SMR_TAILQ_FOREACH(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME);

SMR_TAILQ_FOREACH_LOCKED(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME);

SMR_TAILQ_FOREACH_SAFE_LOCKED(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME,
    TEMP_VARNAME);

void
SMR_TAILQ_INSERT_HEAD_LOCKED(SMR_TAILQ_HEAD *head, struct TYPE *elm,
    FIELDNAME);

void
SMR_TAILQ_INSERT_TAIL_LOCKED(SMR_TAILQ_HEAD *head, struct TYPE *elm,
    FIELDNAME);

void
SMR_TAILQ_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
    FIELDNAME);

void
SMR_TAILQ_INSERT_BEFORE_LOCKED(struct TYPE *listelm, struct TYPE *elm,
    FIELDNAME);

void
SMR_TAILQ_REMOVE_LOCKED(struct TYPE *elm, FIELDNAME);

πŸ¦‹ DESCRIPTION πŸ¦‹

So the SMR list macros define and operate on singly-linked lists, lists and tail queues β€” and the whole point is that they're usable with the safe memory reclamation mechanism. A data structure built with these macros can be accessed concurrently by multiple readers and a single writer. Multiple readers, one writer. That's the arrangement. πŸŽ€

Readers have to access the data structure inside an SMR read-side critical section. You enter that critical section with smr_read_enter(9) and you leave it with smr_read_leave(9). That's it, that's the vibe check.

Writers must ensure exclusive write access. Do that with a lock, like mutex(9) or rwlock(9). Note the mutual exclusion of writers does not need to apply to readers β€” that's the whole magic trick ✨

⚠️ Okay listen, this part matters: when an element has been removed from the data structure, the element must not be deleted or re-inserted before all reader references to it have disappeared. The writer has to use either smr_barrier(9) or smr_call(9) to make sure the element can no longer be accessed by readers. No skipping this step, I'm serious 😭

πŸ”— Singly-Linked Lists

The SMR_SLIST_ENTRY() macro declares a structure that connects the elements in the list. That's the little link-y bit πŸ‘‘

  • SMR_SLIST_INIT() initializes the list head to an empty state.
  • SMR_SLIST_FIRST() and SMR_SLIST_FIRST_LOCKED() return the first element on the list head, or NULL if the list is empty.
  • SMR_SLIST_NEXT() and SMR_SLIST_NEXT_LOCKED() return the successor of the element elm, or NULL if there are no more elements on the list.
  • SMR_SLIST_EMPTY_LOCKED() returns true if the list head is empty.
  • SMR_SLIST_FOREACH() and SMR_SLIST_FOREACH_LOCKED() traverse the list head in forward direction.
  • SMR_SLIST_FOREACH_SAFE_LOCKED() traverses the list head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the list and defer its freeing using smr_call(9).
  • SMR_SLIST_INSERT_HEAD_LOCKED() inserts the new element elm at the head of the list.
  • SMR_SLIST_INSERT_AFTER_LOCKED() inserts the new element elm after the element listelm.
  • SMR_SLIST_REMOVE_HEAD_LOCKED() removes the first element of the list head.
  • SMR_SLIST_REMOVE_AFTER_LOCKED() removes the list element immediately following elm.
  • SMR_SLIST_REMOVE_LOCKED() removes the list element elm from the list head.

🧚 Linked Lists

The SMR_LIST_ENTRY() macro declares a structure that connects the elements in the list. Same deal, doubly-linked edition πŸ’–

  • SMR_LIST_INIT() initializes the list head to an empty state.
  • SMR_LIST_FIRST() and SMR_LIST_FIRST_LOCKED() return the first element on the list head, or NULL if the list is empty.
  • SMR_LIST_NEXT() and SMR_LIST_NEXT_LOCKED() return the successor of the element elm, or NULL if there are no more elements on the list.
  • SMR_LIST_EMPTY_LOCKED() returns true if the list head is empty.
  • SMR_LIST_FOREACH() and SMR_LIST_FOREACH_LOCKED() traverse the list head in forward direction.
  • SMR_LIST_FOREACH_SAFE_LOCKED() traverses the list head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the list and defer its freeing using smr_call(9).
  • SMR_LIST_INSERT_HEAD_LOCKED() inserts the new element elm at the head of the list.
  • SMR_LIST_INSERT_AFTER_LOCKED() inserts the new element elm after the element listelm.
  • SMR_LIST_INSERT_BEFORE_LOCKED() inserts the new element elm before the element listelm.
  • SMR_LIST_REMOVE_LOCKED() removes the element elm from the list head.

πŸ“ Tail Queues

The SMR_TAILQ_ENTRY() macro declares a structure that connects the elements in the tail queue. Bestie's favourite, honestly β€” you get a tail AND a head πŸŽ€

  • SMR_TAILQ_INIT() initializes the tail queue head to an empty state.
  • SMR_TAILQ_FIRST() and SMR_TAILQ_FIRST_LOCKED() return the first element in the queue head, or NULL if the queue is empty.
  • SMR_TAILQ_NEXT() and SMR_TAILQ_NEXT_LOCKED() return the successor of the element elm, or NULL if there are no more elements in the queue.
  • SMR_TAILQ_EMPTY_LOCKED() returns true if the queue head is empty.
  • SMR_TAILQ_FOREACH() and SMR_TAILQ_FOREACH_LOCKED() traverse the queue head in forward direction.
  • SMR_TAILQ_FOREACH_SAFE_LOCKED() traverses the queue head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the queue and defer its freeing using smr_call(9).
  • SMR_TAILQ_INSERT_HEAD_LOCKED() inserts the new element elm at the head of the queue.
  • SMR_TAILQ_INSERT_TAIL_LOCKED() inserts the new element elm at the tail of the queue.
  • SMR_TAILQ_INSERT_AFTER_LOCKED() inserts the new element elm after the element listelm.
  • SMR_TAILQ_INSERT_BEFORE_LOCKED() inserts the new element elm before the element listelm.
  • SMR_TAILQ_REMOVE_LOCKED() removes the element elm from the queue head.

πŸ’€ CONTEXT πŸ’€

All SMR list macros can be used during autoconf, from process context, or from interrupt context. All of them. Every single one. Flexible queens πŸ’…

Can be used from an SMR read-side critical section:

  • SMR_SLIST_FIRST, SMR_SLIST_NEXT, SMR_SLIST_FOREACH
  • SMR_LIST_FIRST, SMR_LIST_NEXT, SMR_LIST_FOREACH
  • SMR_TAILQ_FIRST, SMR_TAILQ_NEXT, SMR_TAILQ_FOREACH

Can be used from writer context:

  • SMR_SLIST_INIT, SMR_SLIST_FIRST_LOCKED, SMR_SLIST_NEXT_LOCKED, SMR_SLIST_EMPTY_LOCKED, SMR_SLIST_FOREACH_LOCKED, SMR_SLIST_FOREACH_SAFE_LOCKED, SMR_SLIST_INSERT_HEAD_LOCKED, SMR_SLIST_INSERT_AFTER_LOCKED, SMR_SLIST_REMOVE_HEAD_LOCKED, SMR_SLIST_REMOVE_AFTER_LOCKED, SMR_SLIST_REMOVE_LOCKED
  • SMR_LIST_INIT, SMR_LIST_FIRST_LOCKED, SMR_LIST_NEXT_LOCKED, SMR_LIST_EMPTY_LOCKED, SMR_LIST_FOREACH_LOCKED, SMR_LIST_FOREACH_SAFE_LOCKED, SMR_LIST_INSERT_HEAD_LOCKED, SMR_LIST_INSERT_AFTER_LOCKED, SMR_LIST_INSERT_BEFORE_LOCKED, SMR_LIST_REMOVE_LOCKED
  • SMR_TAILQ_INIT, SMR_TAILQ_FIRST_LOCKED, SMR_TAILQ_NEXT_LOCKED, SMR_TAILQ_EMPTY_LOCKED, SMR_TAILQ_FOREACH_LOCKED, SMR_TAILQ_FOREACH_SAFE_LOCKED, SMR_TAILQ_INSERT_HEAD_LOCKED, SMR_TAILQ_INSERT_TAIL_LOCKED, SMR_TAILQ_INSERT_AFTER_LOCKED, SMR_TAILQ_INSERT_BEFORE_LOCKED, SMR_TAILQ_REMOVE_LOCKED

🌟 SEE ALSO 🌟

go read those too, iykyk 🫢


πŸ‘‘ HISTORY πŸ‘‘

The SMR list macros first appeared in OpenBSD 6.5. Periodt. A whole era πŸ’œ


and that's the whole page bestie β€” written by the Debian crew, dated January 16, 2022, and served to you with love. mwah πŸ’‹

πŸ“¦ where this page comes from