1 | /* Unit tests for GThread |
2 | * Copyright (C) 2011 Red Hat, Inc |
3 | * Author: Matthias Clasen |
4 | * |
5 | * This work is provided "as is"; redistribution and modification |
6 | * in whole or in part, in any medium, physical or electronic is |
7 | * permitted without restriction. |
8 | * |
9 | * This work 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. |
12 | * |
13 | * In no event shall the authors or contributors be liable for any |
14 | * direct, indirect, incidental, special, exemplary, or consequential |
15 | * damages (including, but not limited to, procurement of substitute |
16 | * goods or services; loss of use, data, or profits; or business |
17 | * interruption) however caused and on any theory of liability, whether |
18 | * in contract, strict liability, or tort (including negligence or |
19 | * otherwise) arising in any way out of the use of this software, even |
20 | * if advised of the possibility of such damage. |
21 | */ |
22 | |
23 | #include <config.h> |
24 | #include <errno.h> |
25 | |
26 | #ifdef HAVE_SYS_TIME_H |
27 | #include <sys/time.h> |
28 | #endif |
29 | #include <sys/types.h> |
30 | #ifdef HAVE_SYS_PRCTL_H |
31 | #include <sys/prctl.h> |
32 | #endif |
33 | |
34 | #include <glib.h> |
35 | |
36 | #include "glib/glib-private.h" |
37 | |
38 | #ifdef G_OS_UNIX |
39 | #include <unistd.h> |
40 | #include <sys/resource.h> |
41 | #endif |
42 | |
43 | #ifdef THREADS_POSIX |
44 | #include <pthread.h> |
45 | #endif |
46 | |
47 | static gpointer |
48 | thread1_func (gpointer data) |
49 | { |
50 | g_thread_exit (GINT_TO_POINTER (1)); |
51 | |
52 | g_assert_not_reached (); |
53 | |
54 | return NULL; |
55 | } |
56 | |
57 | /* test that g_thread_exit() works */ |
58 | static void |
59 | test_thread1 (void) |
60 | { |
61 | gpointer result; |
62 | GThread *thread; |
63 | GError *error = NULL; |
64 | |
65 | thread = g_thread_try_new (name: "test" , func: thread1_func, NULL, error: &error); |
66 | g_assert_no_error (error); |
67 | |
68 | result = g_thread_join (thread); |
69 | |
70 | g_assert_cmpint (GPOINTER_TO_INT (result), ==, 1); |
71 | } |
72 | |
73 | static gpointer |
74 | thread2_func (gpointer data) |
75 | { |
76 | return g_thread_self (); |
77 | } |
78 | |
79 | /* test that g_thread_self() works */ |
80 | static void |
81 | test_thread2 (void) |
82 | { |
83 | gpointer result; |
84 | GThread *thread; |
85 | |
86 | thread = g_thread_new (name: "test" , func: thread2_func, NULL); |
87 | |
88 | g_assert (g_thread_self () != thread); |
89 | |
90 | result = g_thread_join (thread); |
91 | |
92 | g_assert (result == thread); |
93 | } |
94 | |
95 | static gpointer |
96 | thread3_func (gpointer data) |
97 | { |
98 | GThread *peer = data; |
99 | gint retval; |
100 | |
101 | retval = 3; |
102 | |
103 | if (peer) |
104 | { |
105 | gpointer result; |
106 | |
107 | result = g_thread_join (thread: peer); |
108 | |
109 | retval += GPOINTER_TO_INT (result); |
110 | } |
111 | |
112 | return GINT_TO_POINTER (retval); |
113 | } |
114 | |
115 | /* test that g_thread_join() works across peers */ |
116 | static void |
117 | test_thread3 (void) |
118 | { |
119 | gpointer result; |
120 | GThread *thread1, *thread2, *thread3; |
121 | |
122 | thread1 = g_thread_new (name: "a" , func: thread3_func, NULL); |
123 | thread2 = g_thread_new (name: "b" , func: thread3_func, data: thread1); |
124 | thread3 = g_thread_new (name: "c" , func: thread3_func, data: thread2); |
125 | |
126 | result = g_thread_join (thread: thread3); |
127 | |
128 | g_assert_cmpint (GPOINTER_TO_INT(result), ==, 9); |
129 | } |
130 | |
131 | /* test that thread creation fails as expected, |
132 | * by setting RLIMIT_NPROC ridiculously low |
133 | */ |
134 | static void |
135 | test_thread4 (void) |
136 | { |
137 | #ifdef _GLIB_ADDRESS_SANITIZER |
138 | g_test_incomplete ("FIXME: Leaks a GSystemThread's name, see glib#2308" ); |
139 | #elif defined(HAVE_PRLIMIT) |
140 | struct rlimit ol, nl; |
141 | GThread *thread; |
142 | GError *error; |
143 | gint ret; |
144 | |
145 | getrlimit (RLIMIT_NPROC, rlimits: &nl); |
146 | nl.rlim_cur = 1; |
147 | |
148 | if ((ret = prlimit (pid: getpid (), RLIMIT_NPROC, new_limit: &nl, old_limit: &ol)) != 0) |
149 | g_error ("prlimit failed: %s" , g_strerror (errno)); |
150 | |
151 | error = NULL; |
152 | thread = g_thread_try_new (name: "a" , func: thread1_func, NULL, error: &error); |
153 | |
154 | if (thread != NULL) |
155 | { |
156 | gpointer result; |
157 | |
158 | /* Privileged processes might be able to create new threads even |
159 | * though the rlimit is too low. There isn't much we can do about |
160 | * this; we just can't test this failure mode in this situation. */ |
161 | g_test_skip (msg: "Unable to test g_thread_try_new() failing with EAGAIN " |
162 | "while privileged (CAP_SYS_RESOURCE, CAP_SYS_ADMIN or " |
163 | "euid 0?)" ); |
164 | result = g_thread_join (thread); |
165 | g_assert_cmpint (GPOINTER_TO_INT (result), ==, 1); |
166 | } |
167 | else |
168 | { |
169 | g_assert (thread == NULL); |
170 | g_assert_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN); |
171 | g_error_free (error); |
172 | } |
173 | |
174 | if ((ret = prlimit (pid: getpid (), RLIMIT_NPROC, new_limit: &ol, NULL)) != 0) |
175 | g_error ("resetting RLIMIT_NPROC failed: %s" , g_strerror (errno)); |
176 | #endif |
177 | } |
178 | |
179 | static void |
180 | test_thread5 (void) |
181 | { |
182 | GThread *thread; |
183 | |
184 | thread = g_thread_new (name: "a" , func: thread3_func, NULL); |
185 | g_thread_ref (thread); |
186 | g_thread_join (thread); |
187 | g_thread_unref (thread); |
188 | } |
189 | |
190 | static gpointer |
191 | thread6_func (gpointer data) |
192 | { |
193 | #if defined (HAVE_PTHREAD_SETNAME_NP_WITH_TID) && defined (HAVE_PTHREAD_GETNAME_NP) |
194 | char name[16]; |
195 | |
196 | pthread_getname_np (target_thread: pthread_self(), buf: name, buflen: 16); |
197 | |
198 | g_assert_cmpstr (name, ==, data); |
199 | #endif |
200 | |
201 | return NULL; |
202 | } |
203 | |
204 | static void |
205 | test_thread6 (void) |
206 | { |
207 | GThread *thread; |
208 | |
209 | thread = g_thread_new (name: "abc" , func: thread6_func, data: "abc" ); |
210 | g_thread_join (thread); |
211 | } |
212 | |
213 | int |
214 | main (int argc, char *argv[]) |
215 | { |
216 | g_test_init (argc: &argc, argv: &argv, NULL); |
217 | |
218 | g_test_add_func (testpath: "/thread/thread1" , test_func: test_thread1); |
219 | g_test_add_func (testpath: "/thread/thread2" , test_func: test_thread2); |
220 | g_test_add_func (testpath: "/thread/thread3" , test_func: test_thread3); |
221 | g_test_add_func (testpath: "/thread/thread4" , test_func: test_thread4); |
222 | g_test_add_func (testpath: "/thread/thread5" , test_func: test_thread5); |
223 | g_test_add_func (testpath: "/thread/thread6" , test_func: test_thread6); |
224 | |
225 | return g_test_run (); |
226 | } |
227 | |