1 /*
2  * This file is subject to the terms and conditions of the LGPL V2.1
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2019 Kalray Inc.
7  */
8 
9 #include "pthreadP.h"
10 #include <lowlevellock.h>
11 
12 unsigned long int __fork_generation attribute_hidden;
13 
14 static void
clear_once_control(void * arg)15 clear_once_control (void *arg)
16 {
17 	pthread_once_t *once_control = (pthread_once_t *) arg;
18 
19 	*once_control = 0;
20 	lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
21 }
22 
23 
24 int
__pthread_once(once_control,init_routine)25 __pthread_once (once_control, init_routine)
26 	pthread_once_t *once_control;
27 	void (*init_routine) (void);
28 {
29 	while (1)
30 	{
31 		int oldval, val, newval;
32 
33 		val = *once_control;
34 		do
35 		{
36 			/* Check if the initialized has already been done. */
37 			if ((val & 2) != 0)
38 				return 0;
39 
40 			oldval = val;
41 			newval = (oldval & 3) | __fork_generation | 1;
42 			val = atomic_compare_and_exchange_val_acq (once_control, newval, oldval);
43 		} while (__builtin_expect (val != oldval, 0));
44 
45 		/* Check if another thread already runs the initializer. */
46 		if ((oldval & 1) != 0)
47 		{
48 		/* Check whether the initializer execution was interrupted
49 		 * by a fork. */
50 			if (((oldval ^ newval) & -4) == 0)
51 			{
52 				/* Same generation, some other thread was faster. Wait. */
53 				lll_futex_wait (once_control, newval, LLL_PRIVATE);
54 				continue;
55 			}
56 		}
57 		/* This thread is the first here.  Do the initialization.
58 		 * Register a cleanup handler so that in case the thread gets
59 		 * interrupted the initialization can be restarted. */
60 		pthread_cleanup_push (clear_once_control, once_control);
61 
62 		init_routine ();
63 
64 		pthread_cleanup_pop (0);
65 
66 		/* Add one to *once_control. */
67 		atomic_increment (once_control);
68 
69 		/* Wake up all other threads. */
70 		lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
71 		break;
72 	}
73 
74 	return 0;
75 }
76 weak_alias (__pthread_once, pthread_once)
77 strong_alias (__pthread_once, __pthread_once_internal)
78