Branch data Line data Source code
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 : 1 : do_once (gpointer data)
37 : : {
38 : : static gint i = 0;
39 : :
40 : 1 : i++;
41 : :
42 : 1 : return GINT_TO_POINTER (i);
43 : : }
44 : :
45 : : static void
46 : 1 : test_once_single_threaded (void)
47 : : {
48 : 1 : GOnce once = G_ONCE_INIT;
49 : : gpointer res;
50 : :
51 : 1 : g_test_summary ("Test g_once() usage from a single thread");
52 : :
53 : 1 : g_assert_cmpint (once.status, ==, G_ONCE_STATUS_NOTCALLED);
54 : :
55 [ - + ]: 1 : res = g_once (&once, do_once, NULL);
56 : 1 : g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
57 : :
58 : 1 : g_assert_cmpint (once.status, ==, G_ONCE_STATUS_READY);
59 : :
60 [ + - ]: 1 : res = g_once (&once, do_once, NULL);
61 : 1 : g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
62 : 1 : }
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 : 1 : 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 : 1 : old_value = g_atomic_int_add (&once_multi_threaded_counter, 1);
80 : :
81 : 1 : return GINT_TO_POINTER (old_value + 1);
82 : : }
83 : :
84 : : static gpointer
85 : 100 : once_thread_func (gpointer data)
86 : : {
87 : : gpointer res;
88 : 100 : 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 : 100 : g_mutex_lock (&once_multi_threaded_mutex);
94 : :
95 : 100 : once_multi_threaded_n_threads_waiting++;
96 : 100 : g_cond_broadcast (&once_multi_threaded_cond);
97 : :
98 [ + + ]: 1122 : while (once_multi_threaded_n_threads_waiting < n_threads_expected)
99 : 1022 : g_cond_wait (&once_multi_threaded_cond, &once_multi_threaded_mutex);
100 : 100 : g_mutex_unlock (&once_multi_threaded_mutex);
101 : :
102 : : /* Actually run the test. */
103 [ + + ]: 100 : res = g_once (&once_multi_threaded, do_once_multi_threaded, NULL);
104 : 100 : g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
105 : :
106 : 100 : return NULL;
107 : : }
108 : :
109 : : static void
110 : 1 : test_once_multi_threaded (void)
111 : : {
112 : : guint i;
113 : : GThread *threads[THREADS];
114 : :
115 : 1 : g_test_summary ("Test g_once() usage from multiple threads");
116 : :
117 [ + + ]: 101 : for (i = 0; i < G_N_ELEMENTS (threads); i++)
118 : 100 : 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 : 1 : g_cond_broadcast (&once_multi_threaded_cond);
124 : :
125 [ + + ]: 101 : for (i = 0; i < G_N_ELEMENTS (threads); i++)
126 : 100 : g_thread_join (threads[i]);
127 : :
128 : 1 : g_assert_cmpint (g_atomic_int_get (&once_multi_threaded_counter), ==, 1);
129 : 1 : }
130 : :
131 : : static void
132 : 1 : test_once_init_single_threaded (void)
133 : : {
134 : : static gsize init = 0;
135 : :
136 : 1 : g_test_summary ("Test g_once_init_{enter,leave}() usage from a single thread");
137 : :
138 [ + - + - : 1 : if (g_once_init_enter (&init))
+ - ]
139 : : {
140 : : g_assert (TRUE);
141 : 1 : g_once_init_leave (&init, 1);
142 : : }
143 : :
144 : 1 : g_assert_cmpint (init, ==, 1);
145 [ - + - - : 1 : if (g_once_init_enter (&init))
- + ]
146 : : {
147 : : g_assert_not_reached ();
148 : : g_once_init_leave (&init, 2);
149 : : }
150 : 1 : g_assert_cmpint (init, ==, 1);
151 : 1 : }
152 : :
153 : : static gint64 shared;
154 : :
155 : : static void
156 : 100 : init_shared (void)
157 : : {
158 : : static gsize init = 0;
159 : :
160 [ + + + - : 100 : if (g_once_init_enter (&init))
+ + ]
161 : : {
162 : 1 : shared += 42;
163 : :
164 : 1 : g_once_init_leave (&init, 1);
165 : : }
166 : 100 : }
167 : :
168 : : static gpointer
169 : 100 : thread_func (gpointer data)
170 : : {
171 : 100 : init_shared ();
172 : :
173 : 100 : return NULL;
174 : : }
175 : :
176 : : static void
177 : 1 : test_once_init_multi_threaded (void)
178 : : {
179 : : gsize i;
180 : : GThread *threads[THREADS];
181 : :
182 : 1 : g_test_summary ("Test g_once_init_{enter,leave}() usage from multiple threads");
183 : :
184 : 1 : shared = 0;
185 : :
186 [ + + ]: 101 : for (i = 0; i < G_N_ELEMENTS (threads); i++)
187 : 100 : threads[i] = g_thread_new ("once-init-multi-threaded", thread_func, NULL);
188 : :
189 [ + + ]: 101 : for (i = 0; i < G_N_ELEMENTS (threads); i++)
190 : 100 : g_thread_join (threads[i]);
191 : :
192 : 1 : g_assert_cmpint (shared, ==, 42);
193 : 1 : }
194 : :
195 : : static void
196 : 1 : test_once_init_string (void)
197 : : {
198 : : static gchar *val;
199 : :
200 : 1 : g_test_summary ("Test g_once_init_{enter,leave}() usage with a string");
201 : :
202 [ + - + - : 1 : if (g_once_init_enter_pointer (&val))
+ - ]
203 : 1 : g_once_init_leave_pointer (&val, "foo");
204 : :
205 : 1 : g_assert_cmpstr (val, ==, "foo");
206 : 1 : }
207 : :
208 : : int
209 : 1 : main (int argc, char *argv[])
210 : : {
211 : 1 : g_test_init (&argc, &argv, NULL);
212 : :
213 : 1 : g_test_add_func ("/once/single-threaded", test_once_single_threaded);
214 : 1 : g_test_add_func ("/once/multi-threaded", test_once_multi_threaded);
215 : 1 : g_test_add_func ("/once-init/single-threaded", test_once_init_single_threaded);
216 : 1 : g_test_add_func ("/once-init/multi-threaded", test_once_init_multi_threaded);
217 : 1 : g_test_add_func ("/once-init/string", test_once_init_string);
218 : :
219 : 1 : return g_test_run ();
220 : : }
|