Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / glib / tests / once.c
1
2 /* Unit tests for GOnce and friends
3  * Copyright (C) 2011 Red Hat, Inc
4  * Author: Matthias Clasen
5  *
6  * SPDX-License-Identifier: LicenseRef-old-glib-tests
7  *
8  * This work is provided "as is"; redistribution and modification
9  * in whole or in part, in any medium, physical or electronic is
10  * permitted without restriction.
11  *
12  * This work is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * In no event shall the authors or contributors be liable for any
17  * direct, indirect, incidental, special, exemplary, or consequential
18  * damages (including, but not limited to, procurement of substitute
19  * goods or services; loss of use, data, or profits; or business
20  * interruption) however caused and on any theory of liability, whether
21  * in contract, strict liability, or tort (including negligence or
22  * otherwise) arising in any way out of the use of this software, even
23  * if advised of the possibility of such damage.
24  */
25
26 #include <glib.h>
27 #include "../gvalgrind.h"
28
29 #if GLIB_SIZEOF_VOID_P > 4 && !defined(ENABLE_VALGRIND)
30 #define THREADS 1000
31 #else
32 #define THREADS 100
33 #endif
34
35 static gpointer
36 do_once (gpointer data)
37 {
38   static gint i = 0;
39
40   i++;
41
42   return GINT_TO_POINTER (i);
43 }
44
45 static void
46 test_once_single_threaded (void)
47 {
48   GOnce once = G_ONCE_INIT;
49   gpointer res;
50
51   g_test_summary ("Test g_once() usage from a single thread");
52
53   g_assert_cmpint (once.status, ==, G_ONCE_STATUS_NOTCALLED);
54
55   res = g_once (&once, do_once, NULL);
56   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
57
58   g_assert_cmpint (once.status, ==, G_ONCE_STATUS_READY);
59
60   res = g_once (&once, do_once, NULL);
61   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
62 }
63
64 static GOnce once_multi_threaded = G_ONCE_INIT;
65 static gint once_multi_threaded_counter = 0;
66 static GCond once_multi_threaded_cond;
67 static GMutex once_multi_threaded_mutex;
68 static guint once_multi_threaded_n_threads_waiting = 0;
69
70 static gpointer
71 do_once_multi_threaded (gpointer data)
72 {
73   gint old_value;
74
75   /* While this function should only ever be executed once, by one thread,
76    * we should use atomics to ensure that if there were a bug, writes to
77    * `once_multi_threaded_counter` from multiple threads would not get lost and
78    * mean the test erroneously succeeded. */
79   old_value = g_atomic_int_add (&once_multi_threaded_counter, 1);
80
81   return GINT_TO_POINTER (old_value + 1);
82 }
83
84 static gpointer
85 once_thread_func (gpointer data)
86 {
87   gpointer res;
88   guint n_threads_expected = GPOINTER_TO_UINT (data);
89
90   /* Don’t immediately call g_once(), otherwise the first thread to be created
91    * will end up calling the once-function, and there will be very little
92    * contention. */
93   g_mutex_lock (&once_multi_threaded_mutex);
94
95   once_multi_threaded_n_threads_waiting++;
96   g_cond_broadcast (&once_multi_threaded_cond);
97
98   while (once_multi_threaded_n_threads_waiting < n_threads_expected)
99     g_cond_wait (&once_multi_threaded_cond, &once_multi_threaded_mutex);
100   g_mutex_unlock (&once_multi_threaded_mutex);
101
102   /* Actually run the test. */
103   res = g_once (&once_multi_threaded, do_once_multi_threaded, NULL);
104   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
105
106   return NULL;
107 }
108
109 static void
110 test_once_multi_threaded (void)
111 {
112   guint i;
113   GThread *threads[THREADS];
114
115   g_test_summary ("Test g_once() usage from multiple threads");
116
117   for (i = 0; i < G_N_ELEMENTS (threads); i++)
118     threads[i] = g_thread_new ("once-multi-threaded",
119                                once_thread_func,
120                                GUINT_TO_POINTER (G_N_ELEMENTS (threads)));
121
122   /* All threads have started up, so start the test. */
123   g_cond_broadcast (&once_multi_threaded_cond);
124
125   for (i = 0; i < G_N_ELEMENTS (threads); i++)
126     g_thread_join (threads[i]);
127
128   g_assert_cmpint (g_atomic_int_get (&once_multi_threaded_counter), ==, 1);
129 }
130
131 static void
132 test_once_init_single_threaded (void)
133 {
134   static gsize init = 0;
135
136   g_test_summary ("Test g_once_init_{enter,leave}() usage from a single thread");
137
138   if (g_once_init_enter (&init))
139     {
140       g_assert (TRUE);
141       g_once_init_leave (&init, 1);
142     }
143
144   g_assert_cmpint (init, ==, 1);
145   if (g_once_init_enter (&init))
146     {
147       g_assert_not_reached ();
148       g_once_init_leave (&init, 2);
149     }
150   g_assert_cmpint (init, ==, 1);
151 }
152
153 static gint64 shared;
154
155 static void
156 init_shared (void)
157 {
158   static gsize init = 0;
159
160   if (g_once_init_enter (&init))
161     {
162       shared += 42;
163
164       g_once_init_leave (&init, 1);
165     }
166 }
167
168 static gpointer
169 thread_func (gpointer data)
170 {
171   init_shared ();
172
173   return NULL;
174 }
175
176 static void
177 test_once_init_multi_threaded (void)
178 {
179   gsize i;
180   GThread *threads[THREADS];
181
182   g_test_summary ("Test g_once_init_{enter,leave}() usage from multiple threads");
183
184   shared = 0;
185
186   for (i = 0; i < G_N_ELEMENTS (threads); i++)
187     threads[i] = g_thread_new ("once-init-multi-threaded", thread_func, NULL);
188
189   for (i = 0; i < G_N_ELEMENTS (threads); i++)
190     g_thread_join (threads[i]);
191
192   g_assert_cmpint (shared, ==, 42);
193 }
194
195 static void
196 test_once_init_string (void)
197 {
198   static gchar *val;
199
200   g_test_summary ("Test g_once_init_{enter,leave}() usage with a string");
201
202   if (g_once_init_enter (&val))
203     g_once_init_leave (&val, "foo");
204
205   g_assert_cmpstr (val, ==, "foo");
206 }
207
208 int
209 main (int argc, char *argv[])
210 {
211   g_test_init (&argc, &argv, NULL);
212
213   g_test_add_func ("/once/single-threaded", test_once_single_threaded);
214   g_test_add_func ("/once/multi-threaded", test_once_multi_threaded);
215   g_test_add_func ("/once-init/single-threaded", test_once_init_single_threaded);
216   g_test_add_func ("/once-init/multi-threaded", test_once_init_multi_threaded);
217   g_test_add_func ("/once-init/string", test_once_init_string);
218
219   return g_test_run ();
220 }