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
.\" SUCH DAMAGE.
.\"
.Dd April 8, 2024
.Dd February 10, 2025
.Dt QUEUE 3
.Os
.Sh NAME
@@ -33,6 +33,7 @@
.Nm SLIST_CLASS_HEAD ,
.Nm SLIST_CONCAT ,
.Nm SLIST_EMPTY ,
.Nm SLIST_EMPTY_ATOMIC ,
.Nm SLIST_ENTRY ,
.Nm SLIST_FIRST ,
.Nm SLIST_FOREACH ,
@@ -53,6 +54,7 @@
.Nm STAILQ_CLASS_HEAD ,
.Nm STAILQ_CONCAT ,
.Nm STAILQ_EMPTY ,
.Nm STAILQ_EMPTY_ATOMIC ,
.Nm STAILQ_ENTRY ,
.Nm STAILQ_FIRST ,
.Nm STAILQ_FOREACH ,
@@ -75,6 +77,7 @@
.Nm LIST_CLASS_HEAD ,
.Nm LIST_CONCAT ,
.Nm LIST_EMPTY ,
.Nm LIST_EMPTY_ATOMIC ,
.Nm LIST_ENTRY ,
.Nm LIST_FIRST ,
.Nm LIST_FOREACH ,
@@ -96,6 +99,7 @@
.Nm TAILQ_CLASS_HEAD ,
.Nm TAILQ_CONCAT ,
.Nm TAILQ_EMPTY ,
.Nm TAILQ_EMPTY_ATOMIC ,
.Nm TAILQ_ENTRY ,
.Nm TAILQ_FIRST ,
.Nm TAILQ_FOREACH ,
@@ -128,6 +132,7 @@ lists and tail queues
.Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn SLIST_CONCAT "SLIST_HEAD *head1" "SLIST_HEAD *head2" "TYPE" "SLIST_ENTRY NAME"
.Fn SLIST_EMPTY "SLIST_HEAD *head"
.Fn SLIST_EMPTY_ATOMIC "SLIST_HEAD *head"
.Fn SLIST_ENTRY "TYPE"
.Fn SLIST_FIRST "SLIST_HEAD *head"
.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_CONCAT "STAILQ_HEAD *head1" "STAILQ_HEAD *head2"
.Fn STAILQ_EMPTY "STAILQ_HEAD *head"
.Fn STAILQ_EMPTY_ATOMIC "STAILQ_HEAD *head"
.Fn STAILQ_ENTRY "TYPE"
.Fn STAILQ_FIRST "STAILQ_HEAD *head"
.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_CONCAT "LIST_HEAD *head1" "LIST_HEAD *head2" "TYPE" "LIST_ENTRY NAME"
.Fn LIST_EMPTY "LIST_HEAD *head"
.Fn LIST_EMPTY_ATOMIC "LIST_HEAD *head"
.Fn LIST_ENTRY "TYPE"
.Fn LIST_FIRST "LIST_HEAD *head"
.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_CONCAT "TAILQ_HEAD *head1" "TAILQ_HEAD *head2" "TAILQ_ENTRY NAME"
.Fn TAILQ_EMPTY "TAILQ_HEAD *head"
.Fn TAILQ_EMPTY_ATOMIC "TAILQ_HEAD *head"
.Fn TAILQ_ENTRY "TYPE"
.Fn TAILQ_FIRST "TAILQ_HEAD *head"
.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
.Nm SLIST_EMPTY
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
The macro
.Nm SLIST_ENTRY
@@ -633,6 +645,10 @@ removing all entries from the former.
The macro
.Nm STAILQ_EMPTY
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
The macro
.Nm STAILQ_ENTRY
@@ -866,6 +882,10 @@ high-usage code paths or to operate on long lists.
The macro
.Nm LIST_EMPTY
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
The macro
.Nm LIST_ENTRY
@@ -1084,6 +1104,10 @@ removing all entries from the former.
The macro
.Nm TAILQ_EMPTY
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
The macro
.Nm TAILQ_ENTRY
+12
View File
@@ -226,6 +226,9 @@ struct { \
#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_FOREACH(var, head, field) \
@@ -387,6 +390,9 @@ struct { \
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_FOREACH(var, head, field) \
@@ -575,6 +581,9 @@ struct { \
#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_FOREACH(var, head, field) \
@@ -779,6 +788,9 @@ struct { \
#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_FOREACH(var, head, field) \