1 /* Test of async-safe sequential list data type implementation.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2021. */
19 /* There are two notions of async-safe for a list:
20 * A list is "strongly async-safe" if it can be iterated in any signal
21 handler, and the signal handler will see
22 - in the single-threaded case: either the value after or the value
23 before the current in-progress change that was interrupted,
24 - in the multi-threaded case: one of the most recent values for the
26 * A list is "weakly async-safe" if it can be iterated in any signal
28 - in the single-threaded case: the signal handler will see either
29 the value after or the value before the current in-progress change
31 - in the multi-threaded case:
32 - elements which were present in the list throughout the iteration
33 will be seen by the iterator,
34 - elements which were absent in the list throughout the iteration
35 will be unseen by the iterator,
36 - no statement regarding the elements which are being added or
37 removed during the iteration.
39 This unit test attempts to verify that the 'linked-list' implementation of
40 lists is weakly async-safe.
42 It fails the test in the multi-threaded cases (test == 2 or 3). */
46 /* Work around GCC bug 44511. */
47 #if _GL_GNUC_PREREQ (4, 3)
48 # pragma GCC diagnostic ignored "-Wreturn-type"
51 #include "gl_linked_list.h"
55 # if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS /* || USE_WINDOWS_THREADS */
56 /* This test works only with POSIX compatible threads.
57 With Windows threads, send_signal() has to be implemented as
59 hence only test == 3 tests anything, and in fact only 5 signals arrive.
60 This small test is not able to detect a buggy implementation. Therefore
61 mark the test as skipped in this case. */
69 # include "glthread/thread.h"
70 # include "glthread/yield.h"
74 /* The signal we use for testing.
75 For portability, it should be one of SIGINT, SIGFPE, SIGTERM.
76 If we use SIGINT, it prevents debugging with gdb.
77 If we use SIGFPE, it does not work right with valgrind.
78 If we use SIGTERM, it could interfere with a system shutdown.
80 # define MY_SIGNAL SIGTERM
82 /* The number of different objects we can store in a bag.
83 Must be a multiple of 32. */
86 # define RANDOM(n) (rand () % (n))
87 # define RANDOM_OBJECT() ((void *) (uintptr_t) RANDOM (BAG_SIZE))
89 /* Representation of a bag as a bit set. */
90 typedef struct { uint32_t bits
[BAG_SIZE
/ 32]; } bag_t
;
92 /* Initializes a bag to empty. */
94 init_bag_empty (bag_t
*bagp
)
98 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
102 /* Adds an element to a bag. */
104 add_to_bag (uintptr_t x
, bag_t
*bagp
)
108 bagp
->bits
[x
/ 32] |= (1U << (x
% 32));
111 /* Returns a bag that contains the elements of the given list. */
113 bag_from_list (gl_list_t list
)
117 init_bag_empty (&bag
);
119 gl_list_iterator_t iter
= gl_list_iterator (list
);
122 while (gl_list_iterator_next (&iter
, &elt
, NULL
))
123 add_to_bag ((uintptr_t) elt
, &bag
);
124 gl_list_iterator_free (&iter
);
130 /* Returns true if and only if the given bag is empty. */
131 _GL_ATTRIBUTE_MAYBE_UNUSED
static bool
132 bag_is_empty (bag_t bag
)
136 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
137 if (bag
.bits
[i
] != 0)
142 /* Returns true if and only if BAG1 is a subset of BAG2. */
144 bag_is_subset (bag_t bag1
, bag_t bag2
)
148 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
149 if ((bag1
.bits
[i
] & ~bag2
.bits
[i
]) != 0)
154 /* Returns true if and only if the two given bags have the same elements. */
156 bag_equals (bag_t bag1
, bag_t bag2
)
160 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
161 if (bag1
.bits
[i
] != bag2
.bits
[i
])
166 /* Returns a bag that contains the elements of BAG1 and the elements of
168 _GL_ATTRIBUTE_MAYBE_UNUSED
static bag_t
169 bag_or (bag_t bag1
, bag_t bag2
)
174 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
175 bag
.bits
[i
] = bag1
.bits
[i
] | bag2
.bits
[i
];
180 /* Returns a bag that contains the elements of BAG1 that are not in BAG2
181 and the elements of BAG2 that are not in BAG1. */
183 bag_xor (bag_t bag1
, bag_t bag2
)
188 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
189 bag
.bits
[i
] = bag1
.bits
[i
] ^ bag2
.bits
[i
];
194 /* Returns a bag that contains the elements of BAG1 that are not in BAG2. */
195 _GL_ATTRIBUTE_MAYBE_UNUSED
static bag_t
196 bag_and_not (bag_t bag1
, bag_t bag2
)
201 for (i
= 0; i
< BAG_SIZE
/ 32; i
++)
202 bag
.bits
[i
] = bag1
.bits
[i
] & ~bag2
.bits
[i
];
207 /* test == 1: signals are executed in the mutator thread.
208 test == 2: signals are executed in an idle thread.
209 test == 3: signals are executed in the signal-sending thread. */
210 static int volatile test
;
212 /* Store the newest list's bag representation, the previous list's bag
213 representation, and so on in a circular buffer. Store also the
214 changes in a circular buffer. */
215 # define NUM_RECENT_BAGS 1024 /* can be up to (BAG_SIZE * BAG_SIZE) */
216 static bag_t
volatile recent_bags
[NUM_RECENT_BAGS
];
217 static uintptr_t volatile recent_changes
[NUM_RECENT_BAGS
];
218 /* 0 <= recent_oldest_index <= recent_newest_index
219 and recent_newest_index - recent_oldest_index < NUM_RECENT_BAGS.
220 For each i with recent_oldest_index <= i <= recent_newest_index, the bag
221 representation of the list[i] is stored at recent_bags[i % NUM_RECENT_BAGS].
222 For each i with recent_oldest_index <= i < recent_newest_index, the object
223 of the change between list[i] and list[i+1] is stored at
224 recent_changes[i % NUM_RECENT_BAGS]. */
225 static size_t volatile recent_newest_index
;
226 static size_t volatile recent_oldest_index
;
229 store_change (uintptr_t x
, gl_list_t list
)
231 recent_oldest_index
+=
232 (recent_newest_index
- recent_oldest_index
) == NUM_RECENT_BAGS
- 1;
233 recent_changes
[recent_newest_index
% NUM_RECENT_BAGS
] = x
;
234 recent_bags
[(recent_newest_index
+ 1) % NUM_RECENT_BAGS
] = bag_from_list (list
);
235 recent_newest_index
++;
238 static gl_list_t
volatile list1
;
240 static gl_thread_t
volatile signal_target
;
243 static unsigned int volatile num_mutations
;
244 static unsigned int volatile num_signals_sent
;
245 static unsigned int volatile num_signals_arrived
;
247 /* Blocks the MY_SIGNAL signal in the current thread. */
254 sigaddset (&mask
, MY_SIGNAL
);
255 sigprocmask (SIG_BLOCK
, &mask
, NULL
); /* equivalent to pthread_sigmask */
258 /* This thread is idle. */
260 idle_thread (void *arg
)
268 /* The signal handler iterates through the list and verifies that the sum of
269 all elements in the list is one of the allowed values. */
270 static _GL_ASYNC_SAFE
void
271 sigint_handler (int signo
)
273 num_signals_arrived
++;
276 init_bag_empty (&bag
);
278 gl_list_iterator_t iter
= gl_list_iterator (list1
);
281 while (gl_list_iterator_next (&iter
, &elt
, NULL
))
282 add_to_bag ((uintptr_t) elt
, &bag
);
283 gl_list_iterator_free (&iter
);
289 /* The signal handler executes in a different thread than the mutator
290 thread. By the time we get here, the mutator thread can have done
291 any number of mutations; it is reasonable to assume that this number
292 of mutations is small. */
294 bag_t changes_from_i_to_newest
;
295 init_bag_empty (&changes_from_i_to_newest
);
297 for (i
= recent_newest_index
;;)
299 if (bag_is_subset (bag_xor (bag
, recent_bags
[i
% NUM_RECENT_BAGS
]),
300 changes_from_i_to_newest
)
301 && i
>= recent_oldest_index
)
306 if (i
<= recent_oldest_index
)
309 add_to_bag (recent_changes
[i
% NUM_RECENT_BAGS
],
310 &changes_from_i_to_newest
);
315 /* The signal handler executes in the mutator thread. Its execution
316 interrupts a single mutation. The allowed bag is either the newest
317 or the previous one. */
318 size_t i
= recent_newest_index
;
319 found
= (bag_equals (bag
, recent_bags
[i
% NUM_RECENT_BAGS
])
320 || (i
> recent_oldest_index
321 && bag_equals (bag
, recent_bags
[(i
- 1) % NUM_RECENT_BAGS
])
322 && i
> recent_oldest_index
));
326 /* POSIX does not allow uses of stdio from within a signal handler, but
327 it should work here, because the rest of the program does not use
329 fprintf (stderr
, "unexpected bag (after %u mutations and %u signals)\n",
330 num_mutations
, num_signals_arrived
);
336 /* Sends a MY_SIGNAL signal to the current process. */
341 /* This does not work for test != 3, because raise() sends the signal to the
342 current thread always, and if test != 3 the signal is eternally blocked
343 in the current thread; thus it will never get delivered. */
346 /* This works: Either
347 kill (getpid (), MY_SIGNAL);
349 pthread_kill (signal_target, MY_SIGNAL);
350 The difference is that for kill(), the OS determines the (only) thread that
351 has not blocked the signal, whereas for pthread_kill() we have manually
352 determined this thread. */
353 kill (getpid (), MY_SIGNAL
);
357 /* This thread permanently sends MY_SIGNAL signals. */
359 signal_sending_thread (void *arg
)
366 for (repeat
= 1000; repeat
> 0; repeat
--)
368 num_signals_sent
++; send_signal ();
369 num_signals_sent
++; send_signal ();
370 num_signals_sent
++; send_signal ();
371 num_signals_sent
++; send_signal ();
372 num_signals_sent
++; send_signal ();
375 ts
.tv_sec
= 0; ts
.tv_nsec
= 1000000;
376 nanosleep (&ts
, NULL
);
381 printf ("Sent %u signals. Received %u signals. Done after %u mutations.\n",
382 num_signals_sent
, num_signals_arrived
, num_mutations
);
384 exit (test_exit_status
);
389 /* This thread repeatedly adds and removes objects from the list. */
391 mutator_thread (void *arg
)
396 gl_list_t list2
= (gl_list_t
) arg
;
398 for (num_mutations
= 0; ; num_mutations
++)
400 unsigned int operation
= RANDOM (2);
403 case 0: /* Add an element. */
405 const void *obj
= RANDOM_OBJECT ();
406 ASSERT (gl_list_nx_add_first (list2
, obj
) != NULL
);
407 store_change ((uintptr_t) obj
, list2
);
408 ASSERT (gl_list_nx_add_first (list1
, obj
) != NULL
);
411 case 1: /* Remove an element. */
412 if (gl_list_size (list2
) > 0)
414 size_t index
= RANDOM (gl_list_size (list2
));
415 const void *obj
= gl_list_get_at (list2
, index
);
416 ASSERT (gl_list_remove (list2
, obj
));
417 store_change ((uintptr_t) obj
, list2
);
418 ASSERT (gl_list_remove (list1
, obj
));
428 main (int argc
, char *argv
[])
430 test
= atoi (argv
[1]);
432 /* Allow the user to provide a non-default random seed on the command line. */
434 srand (atoi (argv
[2]));
437 /* Initialize list1 and list2. */
439 size_t initial_size
= RANDOM (50);
440 const void **contents
=
441 (const void **) malloc (initial_size
* sizeof (const void *));
444 for (i
= 0; i
< initial_size
; i
++)
445 contents
[i
] = RANDOM_OBJECT ();
447 list1
= gl_list_nx_create_empty (GL_LINKED_LIST
, NULL
, NULL
, NULL
, true);
448 ASSERT (list1
!= NULL
);
449 for (i
= 0; i
< initial_size
; i
++)
450 ASSERT (gl_list_nx_add_first (list1
, contents
[i
]) != NULL
);
452 list2
= gl_list_nx_create_empty (GL_LINKED_LIST
, NULL
, NULL
, NULL
, true);
453 ASSERT (list2
!= NULL
);
454 for (i
= 0; i
< initial_size
; i
++)
455 ASSERT (gl_list_nx_add_last (list2
, contents
[i
]) != NULL
);
457 recent_oldest_index
= 0;
458 recent_bags
[0] = bag_from_list (list2
);
459 recent_newest_index
= 0;
462 /* Install the signal handler.
463 It needs to be done with sigaction(), not signal(), otherwise on Solaris
464 and AIX the program crashes at the second incoming MY_SIGNAL. */
466 signal (MY_SIGNAL
, sigint_handler
);
469 struct sigaction action
;
470 action
.sa_handler
= sigint_handler
;
471 action
.sa_flags
= SA_RESTART
| SA_NODEFER
;
472 sigemptyset (&action
.sa_mask
);
473 ASSERT (sigaction (MY_SIGNAL
, &action
, NULL
) == 0);
477 /* Launch the threads. */
482 signal_target
= gl_thread_create (mutator_thread
, list2
);
483 signal_sending_thread (NULL
);
489 signal_target
= gl_thread_create (idle_thread
, NULL
);
490 gl_thread_create (mutator_thread
, list2
);
491 signal_sending_thread (NULL
);
497 gl_thread_create (mutator_thread
, list2
);
498 signal_target
= gl_thread_self (); signal_sending_thread (NULL
);
514 fputs ("Skipping test: POSIX compatible multithreading not enabled\n", stderr
);
527 fputs ("Skipping test: signal-safety of linked lists is not enabled\n", stderr
);