summaryrefslogtreecommitdiffstats
path: root/gl/pthread-once.c
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-12-28 12:13:40 +0100
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-12-28 12:13:40 +0100
commitb0afb8fe0ff1d87165af9df61501197a06240dda (patch)
tree274ac6a96c53ef4c19ab4974ce24a06a233128c5 /gl/pthread-once.c
parent68fc05381ee5fa0aee1413118fbb3d81ca888b09 (diff)
downloadmonitoring-plugins-b0afb8fe0ff1d87165af9df61501197a06240dda.tar.gz
Sync with Gnulib stable-202507 code (a8ac9f9ce5)
Diffstat (limited to 'gl/pthread-once.c')
-rw-r--r--gl/pthread-once.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/gl/pthread-once.c b/gl/pthread-once.c
new file mode 100644
index 00000000..b19dae50
--- /dev/null
+++ b/gl/pthread-once.c
@@ -0,0 +1,148 @@
1/* POSIX once-only control.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 This file 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 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Bruno Haible <bruno@clisp.org>, 2019. */
18
19#include <config.h>
20
21/* Specification. */
22#include <pthread.h>
23
24#if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
25# include "windows-once.h"
26#endif
27
28#if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
29/* Use Windows threads. */
30
31int
32pthread_once (pthread_once_t *once_control, void (*initfunction) (void))
33{
34 glwthread_once (once_control, initfunction);
35 return 0;
36}
37
38#elif HAVE_PTHREAD_H
39/* Provide workarounds for POSIX threads. */
40
41# if defined __CYGWIN__
42
43# include <stdlib.h>
44
45int
46pthread_once (pthread_once_t *once_control, void (*initfunction) (void))
47{
48 /* In this implementation, we reuse the type
49 typedef struct { pthread_mutex_t mutex; int state; } pthread_once_t;
50 #define PTHREAD_ONCE_INIT { PTHREAD_MUTEX_INITIALIZER, 0 }
51 while assigning the following meaning to the state:
52 state = (<number of waiting threads> << 16) + <1 if done>
53 In other words:
54 state = { unsigned int num_threads : 16; unsigned int done : 16; }
55 */
56 struct actual_state
57 {
58 _Atomic unsigned short num_threads;
59 /* done == 0: initial state
60 done == 1: initfunction executed, lock still active
61 done == 2: initfunction executed, lock no longer usable */
62 _Atomic unsigned short done;
63 };
64 struct actual_state *state_p = (struct actual_state *) &once_control->state;
65 /* This test is not necessary. It's only an optimization, to establish
66 a fast path for the common case that the 'done' word is already > 0. */
67 if (state_p->done == 0)
68 {
69 /* Increment num_threads (atomically), to indicate that this thread will
70 possibly take the lock. */
71 state_p->num_threads += 1;
72 /* Test the 'done' word. */
73 if (state_p->done == 0)
74 {
75 /* The 'done' word is still zero. Now take the lock. */
76 pthread_mutex_lock (&once_control->mutex);
77 /* Test the 'done' word again. */
78 if (state_p->done == 0)
79 {
80 /* Execute the initfunction. */
81 (*initfunction) ();
82 /* Set the 'done' word to 1 (atomically). */
83 state_p->done = 1;
84 }
85 /* Now the 'done' word is 1. Release the lock. */
86 pthread_mutex_unlock (&once_control->mutex);
87 }
88 /* Here, done is > 0. */
89 /* Decrement num_threads (atomically). */
90 if ((state_p->num_threads -= 1) == 0)
91 {
92 /* num_threads is now zero, and done is > 0.
93 No other thread will need to use the lock.
94 We can therefore destroy the lock, to free resources. */
95 if (__sync_bool_compare_and_swap (&state_p->done, 1, 2))
96 pthread_mutex_destroy (&once_control->mutex);
97 }
98 }
99 /* Proof of correctness:
100 * num_threads is incremented and then decremented by some threads.
101 Therefore, num_threads always stays >= 0, and is == 0 at the end.
102 * The 'done' word, once > 0, stays > 0 (since it is never assigned 0).
103 * The 'done' word is changed from == 0 to > 0 only while the lock
104 is taken. Therefore, only the first thread that succeeds in taking
105 the lock executes the initfunction and sets the 'done' word to a
106 value > 0; the other threads that take the lock do no side effects
107 between taking and releasing the lock.
108 * The 'done' word does not change any more once it is 2.
109 Therefore, it can be changed from 1 to 2 only once.
110 * pthread_mutex_destroy gets invoked right after 'done' has been changed
111 from 1 to 2. Therefore, pthread_mutex_destroy gets invoked only once.
112 * After a moment where num_threads was 0 and done was > 0, no thread can
113 reach the pthread_mutex_lock invocation. Proof:
114 - At such a moment, no thread is in the code range between
115 state_p->num_threads += 1
116 and
117 state_p->num_threads -= 1
118 - After such a moment, some thread can increment num_threads, but from
119 there they cannot reach the pthread_mutex_lock invocation, because the
120 if (state_p->done == 0)
121 test prevents that.
122 * From this it follows that:
123 - pthread_mutex_destroy cannot be executed while the lock is taken
124 (because pthread_mutex_destroy is only executed after a moment where
125 num_threads was 0 and done was > 0).
126 - Once pthread_mutex_destroy has been executed, the lock is not used any
127 more.
128 */
129 return 0;
130}
131
132# endif
133
134#else
135/* Provide a dummy implementation for single-threaded applications. */
136
137int
138pthread_once (pthread_once_t *once_control, void (*initfunction) (void))
139{
140 if (*once_control == 0)
141 {
142 *once_control = ~ 0;
143 initfunction ();
144 }
145 return 0;
146}
147
148#endif