queue: Add atomic variants for *_EMPTY

In some places, these macros are used without a lock, under the
assumption that they are naturally atomic.  After commit
34740937f7 ("queue: New debug macros for STAILQ"), this assumption is
false.

Provide *_EMPTY_ATOMIC for such cases.  This lets us include extra debug
checks for the non-atomic case, and gives us a way to explicitly
annotate unlocked checks, which generally deserve extra scrutiny and
might otherwise raise reports from KCSAN.

Reviewed by:	kib, olce (previous version)
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D48899
This commit is contained in:
Mark Johnston
2025-02-14 15:45:11 +00:00
parent fbacadf103
commit d2870b8666
2 changed files with 37 additions and 1 deletions
+25 -1
View File
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd April 8, 2024 .Dd February 10, 2025
.Dt QUEUE 3 .Dt QUEUE 3
.Os .Os
.Sh NAME .Sh NAME
@@ -33,6 +33,7 @@
.Nm SLIST_CLASS_HEAD , .Nm SLIST_CLASS_HEAD ,
.Nm SLIST_CONCAT , .Nm SLIST_CONCAT ,
.Nm SLIST_EMPTY , .Nm SLIST_EMPTY ,
.Nm SLIST_EMPTY_ATOMIC ,
.Nm SLIST_ENTRY , .Nm SLIST_ENTRY ,
.Nm SLIST_FIRST , .Nm SLIST_FIRST ,
.Nm SLIST_FOREACH , .Nm SLIST_FOREACH ,
@@ -53,6 +54,7 @@
.Nm STAILQ_CLASS_HEAD , .Nm STAILQ_CLASS_HEAD ,
.Nm STAILQ_CONCAT , .Nm STAILQ_CONCAT ,
.Nm STAILQ_EMPTY , .Nm STAILQ_EMPTY ,
.Nm STAILQ_EMPTY_ATOMIC ,
.Nm STAILQ_ENTRY , .Nm STAILQ_ENTRY ,
.Nm STAILQ_FIRST , .Nm STAILQ_FIRST ,
.Nm STAILQ_FOREACH , .Nm STAILQ_FOREACH ,
@@ -75,6 +77,7 @@
.Nm LIST_CLASS_HEAD , .Nm LIST_CLASS_HEAD ,
.Nm LIST_CONCAT , .Nm LIST_CONCAT ,
.Nm LIST_EMPTY , .Nm LIST_EMPTY ,
.Nm LIST_EMPTY_ATOMIC ,
.Nm LIST_ENTRY , .Nm LIST_ENTRY ,
.Nm LIST_FIRST , .Nm LIST_FIRST ,
.Nm LIST_FOREACH , .Nm LIST_FOREACH ,
@@ -96,6 +99,7 @@
.Nm TAILQ_CLASS_HEAD , .Nm TAILQ_CLASS_HEAD ,
.Nm TAILQ_CONCAT , .Nm TAILQ_CONCAT ,
.Nm TAILQ_EMPTY , .Nm TAILQ_EMPTY ,
.Nm TAILQ_EMPTY_ATOMIC ,
.Nm TAILQ_ENTRY , .Nm TAILQ_ENTRY ,
.Nm TAILQ_FIRST , .Nm TAILQ_FIRST ,
.Nm TAILQ_FOREACH , .Nm TAILQ_FOREACH ,
@@ -128,6 +132,7 @@ lists and tail queues
.Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn SLIST_CONCAT "SLIST_HEAD *head1" "SLIST_HEAD *head2" "TYPE" "SLIST_ENTRY NAME" .Fn SLIST_CONCAT "SLIST_HEAD *head1" "SLIST_HEAD *head2" "TYPE" "SLIST_ENTRY NAME"
.Fn SLIST_EMPTY "SLIST_HEAD *head" .Fn SLIST_EMPTY "SLIST_HEAD *head"
.Fn SLIST_EMPTY_ATOMIC "SLIST_HEAD *head"
.Fn SLIST_ENTRY "TYPE" .Fn SLIST_ENTRY "TYPE"
.Fn SLIST_FIRST "SLIST_HEAD *head" .Fn SLIST_FIRST "SLIST_HEAD *head"
.Fn SLIST_FOREACH "TYPE *var" "SLIST_HEAD *head" "SLIST_ENTRY NAME" .Fn SLIST_FOREACH "TYPE *var" "SLIST_HEAD *head" "SLIST_ENTRY NAME"
@@ -149,6 +154,7 @@ lists and tail queues
.Fn STAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn STAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn STAILQ_CONCAT "STAILQ_HEAD *head1" "STAILQ_HEAD *head2" .Fn STAILQ_CONCAT "STAILQ_HEAD *head1" "STAILQ_HEAD *head2"
.Fn STAILQ_EMPTY "STAILQ_HEAD *head" .Fn STAILQ_EMPTY "STAILQ_HEAD *head"
.Fn STAILQ_EMPTY_ATOMIC "STAILQ_HEAD *head"
.Fn STAILQ_ENTRY "TYPE" .Fn STAILQ_ENTRY "TYPE"
.Fn STAILQ_FIRST "STAILQ_HEAD *head" .Fn STAILQ_FIRST "STAILQ_HEAD *head"
.Fn STAILQ_FOREACH "TYPE *var" "STAILQ_HEAD *head" "STAILQ_ENTRY NAME" .Fn STAILQ_FOREACH "TYPE *var" "STAILQ_HEAD *head" "STAILQ_ENTRY NAME"
@@ -172,6 +178,7 @@ lists and tail queues
.Fn LIST_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn LIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn LIST_CONCAT "LIST_HEAD *head1" "LIST_HEAD *head2" "TYPE" "LIST_ENTRY NAME" .Fn LIST_CONCAT "LIST_HEAD *head1" "LIST_HEAD *head2" "TYPE" "LIST_ENTRY NAME"
.Fn LIST_EMPTY "LIST_HEAD *head" .Fn LIST_EMPTY "LIST_HEAD *head"
.Fn LIST_EMPTY_ATOMIC "LIST_HEAD *head"
.Fn LIST_ENTRY "TYPE" .Fn LIST_ENTRY "TYPE"
.Fn LIST_FIRST "LIST_HEAD *head" .Fn LIST_FIRST "LIST_HEAD *head"
.Fn LIST_FOREACH "TYPE *var" "LIST_HEAD *head" "LIST_ENTRY NAME" .Fn LIST_FOREACH "TYPE *var" "LIST_HEAD *head" "LIST_ENTRY NAME"
@@ -194,6 +201,7 @@ lists and tail queues
.Fn TAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn TAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn TAILQ_CONCAT "TAILQ_HEAD *head1" "TAILQ_HEAD *head2" "TAILQ_ENTRY NAME" .Fn TAILQ_CONCAT "TAILQ_HEAD *head1" "TAILQ_HEAD *head2" "TAILQ_ENTRY NAME"
.Fn TAILQ_EMPTY "TAILQ_HEAD *head" .Fn TAILQ_EMPTY "TAILQ_HEAD *head"
.Fn TAILQ_EMPTY_ATOMIC "TAILQ_HEAD *head"
.Fn TAILQ_ENTRY "TYPE" .Fn TAILQ_ENTRY "TYPE"
.Fn TAILQ_FIRST "TAILQ_HEAD *head" .Fn TAILQ_FIRST "TAILQ_HEAD *head"
.Fn TAILQ_FOREACH "TYPE *var" "TAILQ_HEAD *head" "TAILQ_ENTRY NAME" .Fn TAILQ_FOREACH "TYPE *var" "TAILQ_HEAD *head" "TAILQ_ENTRY NAME"
@@ -425,6 +433,10 @@ high-usage code paths or to operate on long lists.
The macro The macro
.Nm SLIST_EMPTY .Nm SLIST_EMPTY
evaluates to true if there are no elements in the list. evaluates to true if there are no elements in the list.
The
.Nm SLIST_EMPTY_ATOMIC
variant has the same behavior, but can be safely used in contexts where it is
possible that a different thread is concurrently updating the list.
.Pp .Pp
The macro The macro
.Nm SLIST_ENTRY .Nm SLIST_ENTRY
@@ -633,6 +645,10 @@ removing all entries from the former.
The macro The macro
.Nm STAILQ_EMPTY .Nm STAILQ_EMPTY
evaluates to true if there are no items on the tail queue. evaluates to true if there are no items on the tail queue.
The
.Nm STAILQ_EMPTY_ATOMIC
variant has the same behavior, but can be safely used in contexts where it is
possible that a different thread is concurrently updating the queue.
.Pp .Pp
The macro The macro
.Nm STAILQ_ENTRY .Nm STAILQ_ENTRY
@@ -866,6 +882,10 @@ high-usage code paths or to operate on long lists.
The macro The macro
.Nm LIST_EMPTY .Nm LIST_EMPTY
evaluates to true if there are no elements in the list. evaluates to true if there are no elements in the list.
The
.Nm LIST_EMPTY_ATOMIC
variant has the same behavior, but can be safely used in contexts where it is
possible that a different thread is concurrently updating the list.
.Pp .Pp
The macro The macro
.Nm LIST_ENTRY .Nm LIST_ENTRY
@@ -1084,6 +1104,10 @@ removing all entries from the former.
The macro The macro
.Nm TAILQ_EMPTY .Nm TAILQ_EMPTY
evaluates to true if there are no items on the tail queue. evaluates to true if there are no items on the tail queue.
The
.Nm TAILQ_EMPTY_ATOMIC
variant has the same behavior, but can be safely used in contexts where it is
possible that a different thread is concurrently updating the queue.
.Pp .Pp
The macro The macro
.Nm TAILQ_ENTRY .Nm TAILQ_ENTRY
+12
View File
@@ -226,6 +226,9 @@ struct { \
#define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_EMPTY_ATOMIC(head) \
(atomic_load_ptr(&(head)->slh_first) == NULL)
#define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_FOREACH(var, head, field) \ #define SLIST_FOREACH(var, head, field) \
@@ -387,6 +390,9 @@ struct { \
STAILQ_FIRST(head) == NULL; \ STAILQ_FIRST(head) == NULL; \
}) })
#define STAILQ_EMPTY_ATOMIC(head) \
(atomic_load_ptr(&(head)->stqh_first) == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \ #define STAILQ_FOREACH(var, head, field) \
@@ -575,6 +581,9 @@ struct { \
#define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_EMPTY_ATOMIC(head) \
(atomic_load_ptr(&(head)->lh_first) == NULL)
#define LIST_FIRST(head) ((head)->lh_first) #define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \ #define LIST_FOREACH(var, head, field) \
@@ -779,6 +788,9 @@ struct { \
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_EMPTY_ATOMIC(head) \
(atomic_load_ptr(&(head)->tqh_first) == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \ #define TAILQ_FOREACH(var, head, field) \