Line data Source code
1 : /* posix-lock.c - GPGRT lock functions for POSIX systems
2 : Copyright (C) 2005-2009 Free Software Foundation, Inc.
3 : Copyright (C) 2014 g10 Code GmbH
4 :
5 : This file is part of libgpg-error.
6 :
7 : libgpg-error is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public License
9 : as published by the Free Software Foundation; either version 2.1 of
10 : the License, or (at your option) any later version.
11 :
12 : libgpg-error is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this program; if not, see <https://www.gnu.org/licenses/>.
19 :
20 : Parts of the code, in particular use_pthreads_p, are based on code
21 : from gettext, written by Bruno Haible <bruno@clisp.org>, 2005.
22 : */
23 :
24 : #if HAVE_CONFIG_H
25 : #include <config.h>
26 : #endif
27 :
28 : #ifdef HAVE_W32_SYSTEM
29 : # error This module may not be build for Windows.
30 : #endif
31 :
32 : #include <stdlib.h>
33 : #include <stdio.h>
34 : #include <string.h>
35 : #include <errno.h>
36 : #include <assert.h>
37 :
38 : #if USE_POSIX_THREADS
39 : # include <pthread.h>
40 : #endif
41 :
42 : #include "gpg-error.h"
43 : #include "lock.h"
44 : #include "posix-lock-obj.h"
45 :
46 :
47 : /*
48 : * Functions called before and after blocking syscalls.
49 : * gpgrt_set_syscall_clamp is used to set them.
50 : */
51 : static void (*pre_lock_func)(void);
52 : static void (*post_lock_func)(void);
53 :
54 :
55 : #if USE_POSIX_THREADS
56 : # if USE_POSIX_THREADS_WEAK
57 : /* On ELF systems it is easy to use pthreads using weak
58 : references. Take care not to test the address of a weak
59 : referenced function we actually use; some GCC versions have a
60 : bug were &foo != NULL is always evaluated to true in PIC mode. */
61 : # pragma weak pthread_cancel
62 : # pragma weak pthread_mutex_init
63 : # pragma weak pthread_mutex_lock
64 : # pragma weak pthread_mutex_trylock
65 : # pragma weak pthread_mutex_unlock
66 : # pragma weak pthread_mutex_destroy
67 : # if ! PTHREAD_IN_USE_DETECTION_HARD
68 : # define use_pthread_p() (!!pthread_cancel)
69 : # endif
70 : # else /*!USE_POSIX_THREADS_WEAK*/
71 : # if ! PTHREAD_IN_USE_DETECTION_HARD
72 : # define use_pthread_p() (1)
73 : # endif
74 : # endif /*!USE_POSIX_THREADS_WEAK*/
75 : # if PTHREAD_IN_USE_DETECTION_HARD
76 : /* The function to be executed by a dummy thread. */
77 : static void *
78 : dummy_thread_func (void *arg)
79 : {
80 : return arg;
81 : }
82 :
83 : static int
84 : use_pthread_p (void)
85 : {
86 : static int tested;
87 : static int result; /* 1: linked with -lpthread, 0: only with libc */
88 :
89 : if (!tested)
90 : {
91 : pthread_t thread;
92 :
93 : if (pthread_create (&thread, NULL, dummy_thread_func, NULL))
94 : result = 0; /* Thread creation failed. */
95 : else
96 : {
97 : /* Thread creation works. */
98 : void *retval;
99 : if (pthread_join (thread, &retval) != 0)
100 : {
101 : assert (!"pthread_join");
102 : abort ();
103 : }
104 : result = 1;
105 : }
106 : tested = 1;
107 : }
108 : return result;
109 : }
110 : #endif /*PTHREAD_IN_USE_DETECTION_HARD*/
111 : #endif /*USE_POSIX_THREADS*/
112 :
113 :
114 : /* Helper to set the clamp functions. This is called as a helper from
115 : * _gpgrt_set_syscall_clamp to keep the function pointers local. */
116 : void
117 6 : _gpgrt_lock_set_lock_clamp (void (*pre)(void), void (*post)(void))
118 : {
119 6 : pre_lock_func = pre;
120 6 : post_lock_func = post;
121 6 : }
122 :
123 :
124 :
125 : static _gpgrt_lock_t *
126 : get_lock_object (gpgrt_lock_t *lockhd)
127 : {
128 : _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
129 :
130 38846 : if (lock->vers != LOCK_ABI_VERSION)
131 : {
132 0 : assert (!"lock ABI version");
133 : abort ();
134 : }
135 : if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
136 : {
137 : assert (!"sizeof lock obj");
138 : abort ();
139 : }
140 :
141 : return lock;
142 : }
143 :
144 :
145 : gpg_err_code_t
146 7 : _gpgrt_lock_init (gpgrt_lock_t *lockhd)
147 : {
148 : _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
149 : int rc;
150 :
151 : /* If VERS is zero we assume that no static initialization has been
152 : done, so we setup our ABI version right here. The caller might
153 : have called us to test whether lock support is at all available. */
154 7 : if (!lock->vers)
155 : {
156 : if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
157 : {
158 : assert (!"sizeof lock obj");
159 : abort ();
160 : }
161 6 : lock->vers = LOCK_ABI_VERSION;
162 : }
163 : else /* Run the usual check. */
164 : lock = get_lock_object (lockhd);
165 :
166 : #if USE_POSIX_THREADS
167 7 : if (use_pthread_p())
168 : {
169 7 : rc = pthread_mutex_init (&lock->u.mtx, NULL);
170 7 : if (rc)
171 0 : rc = gpg_err_code_from_errno (rc);
172 : }
173 : else
174 : rc = 0; /* Threads are not used. */
175 : #else /* Unknown thread system. */
176 : rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
177 : #endif /* Unknown thread system. */
178 :
179 7 : return rc;
180 : }
181 :
182 :
183 : gpg_err_code_t
184 19149 : _gpgrt_lock_lock (gpgrt_lock_t *lockhd)
185 : {
186 : _gpgrt_lock_t *lock = get_lock_object (lockhd);
187 : int rc;
188 :
189 : #if USE_POSIX_THREADS
190 19149 : if (use_pthread_p())
191 : {
192 19173 : if (pre_lock_func)
193 0 : pre_lock_func ();
194 19173 : rc = pthread_mutex_lock (&lock->u.mtx);
195 19683 : if (rc)
196 0 : rc = gpg_err_code_from_errno (rc);
197 19683 : if (post_lock_func)
198 0 : post_lock_func ();
199 : }
200 : else
201 : rc = 0; /* Threads are not used. */
202 : #else /* Unknown thread system. */
203 : rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
204 : #endif /* Unknown thread system. */
205 :
206 19659 : return rc;
207 : }
208 :
209 :
210 : gpg_err_code_t
211 0 : _gpgrt_lock_trylock (gpgrt_lock_t *lockhd)
212 : {
213 : _gpgrt_lock_t *lock = get_lock_object (lockhd);
214 : int rc;
215 :
216 : #if USE_POSIX_THREADS
217 0 : if (use_pthread_p())
218 : {
219 0 : rc = pthread_mutex_trylock (&lock->u.mtx);
220 0 : if (rc)
221 0 : rc = gpg_err_code_from_errno (rc);
222 : }
223 : else
224 : rc = 0; /* Threads are not used. */
225 : #else /* Unknown thread system. */
226 : rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
227 : #endif /* Unknown thread system. */
228 :
229 0 : return rc;
230 : }
231 :
232 :
233 : gpg_err_code_t
234 19687 : _gpgrt_lock_unlock (gpgrt_lock_t *lockhd)
235 : {
236 : _gpgrt_lock_t *lock = get_lock_object (lockhd);
237 : int rc;
238 :
239 : #if USE_POSIX_THREADS
240 19687 : if (use_pthread_p())
241 : {
242 19683 : rc = pthread_mutex_unlock (&lock->u.mtx);
243 19419 : if (rc)
244 0 : rc = gpg_err_code_from_errno (rc);
245 : }
246 : else
247 : rc = 0; /* Threads are not used. */
248 : #else /* Unknown thread system. */
249 : rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
250 : #endif /* Unknown thread system. */
251 :
252 19423 : return rc;
253 : }
254 :
255 :
256 : /* Note: Use this function only if no other thread holds or waits for
257 : this lock. */
258 : gpg_err_code_t
259 9 : _gpgrt_lock_destroy (gpgrt_lock_t *lockhd)
260 : {
261 : _gpgrt_lock_t *lock = get_lock_object (lockhd);
262 : int rc;
263 :
264 : #if USE_POSIX_THREADS
265 9 : if (use_pthread_p())
266 : {
267 9 : rc = pthread_mutex_destroy (&lock->u.mtx);
268 9 : if (rc)
269 0 : rc = gpg_err_code_from_errno (rc);
270 : else
271 : {
272 : /* Re-init the mutex so that it can be re-used. */
273 9 : gpgrt_lock_t tmp = GPGRT_LOCK_INITIALIZER;
274 9 : memcpy (lockhd, &tmp, sizeof tmp);
275 : }
276 : }
277 : else
278 : rc = 0; /* Threads are not used. */
279 : #else /* Unknown thread system. */
280 : rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
281 : #endif /* Unknown thread system. */
282 :
283 9 : return rc;
284 : }
|