2004-03-10 Marcus Brinkmann <marcus@g10code.de>
[libgcrypt.git] / src / ath.c
1 /* ath.c - Thread-safeness library.
2    Copyright (C) 2002, 2003, 2004 g10 Code GmbH
3
4    This file is part of Libgcrypt.
5  
6    Libgcrypt is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation; either version 2.1 of
9    the License, or (at your option) any later version.
10  
11    Libgcrypt is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15  
16    You should have received a copy of the GNU Lesser General Public
17    License along with Libgcrypt; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA.  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <assert.h>
26 #include <unistd.h>
27 #ifdef HAVE_SYS_SELECT_H
28 # include <sys/select.h>
29 #else
30 # include <sys/time.h>
31 #endif
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35
36 #include "ath.h"
37
38 \f
39 /* The interface table.  */
40 static struct ath_ops ops;
41
42 /* True if we should use the external callbacks.  */
43 static int ops_set;
44
45
46 /* For the dummy interface.  */
47 #define MUTEX_UNLOCKED  ((ath_mutex_t) 0)
48 #define MUTEX_LOCKED    ((ath_mutex_t) 1)
49 #define MUTEX_DESTROYED ((ath_mutex_t) 2)
50
51
52 \f
53 /* The lock we take while checking for lazy lock initialization.  */
54 static ath_mutex_t check_init_lock = ATH_MUTEX_INITIALIZER;
55
56 int
57 ath_init (void)
58 {
59   int err = 0;
60
61   if (ops_set)
62     {
63       if (ops.init)
64         err = (*ops.init) ();
65       if (err)
66         return err;
67       err = (*ops.mutex_init) (&check_init_lock);
68     }
69   return err;
70 }
71
72
73 /* Initialize the locking library.  Returns 0 if the operation was
74    successful, EINVAL if the operation table was invalid and EBUSY if
75    we already were initialized.  */
76 gpg_err_code_t
77 ath_install (struct ath_ops *ath_ops, int check_only)
78 {
79   if (check_only)
80     {
81       enum ath_thread_option option = ATH_THREAD_OPTION_DEFAULT;
82       
83       /* Check if the requested thread option is compatible to the
84          thread option we are already committed to.  */
85       if (ath_ops)
86         option = ath_ops->option;
87
88       if (!ops_set && option)
89         return GPG_ERR_NOT_SUPPORTED;
90
91       if (ops.option == ATH_THREAD_OPTION_USER
92           || option == ATH_THREAD_OPTION_USER
93           || ops.option != option)
94         return GPG_ERR_NOT_SUPPORTED;
95
96       return 0;
97     }
98     
99   if (ath_ops)
100     {
101       /* It is convenient to not require DESTROY.  */
102       if (!ath_ops->mutex_init || !ath_ops->mutex_lock
103           || !ath_ops->mutex_unlock)
104         return GPG_ERR_INV_ARG;
105
106       ops = *ath_ops;
107       ops_set = 1;
108     }
109   else
110     ops_set = 0;
111
112   return 0;
113 }
114
115
116 static int
117 mutex_init (ath_mutex_t *lock, int just_check)
118 {
119   int err = 0;
120
121   if (just_check)
122     (*ops.mutex_lock) (&check_init_lock);
123   if (*lock == ATH_MUTEX_INITIALIZER || !just_check)
124     err = (*ops.mutex_init) (lock);
125   if (just_check)
126     (*ops.mutex_unlock) (&check_init_lock);
127   return err;
128 }
129
130
131 int
132 ath_mutex_init (ath_mutex_t *lock)
133 {
134   if (ops_set)
135     return mutex_init (lock, 0);
136
137 #ifndef NDEBUG
138   *lock = MUTEX_UNLOCKED;
139 #endif
140   return 0;
141 }
142
143
144 int
145 ath_mutex_destroy (ath_mutex_t *lock)
146 {
147   if (ops_set)
148     {
149       int err = mutex_init (lock, 1);
150
151       if (err)
152         return err;
153
154       if (ops.mutex_destroy)
155         return (*ops.mutex_destroy) (lock);
156       else
157         return 0;
158     }
159
160 #ifndef NDEBUG
161   assert (*lock == MUTEX_UNLOCKED);
162
163   *lock = MUTEX_DESTROYED;
164 #endif
165   return 0;
166 }
167
168
169 int
170 ath_mutex_lock (ath_mutex_t *lock)
171 {
172   if (ops_set)
173     {
174       int ret = mutex_init (lock, 1);
175       if (ret)
176         return ret;
177       return (*ops.mutex_lock) (lock);
178     }
179
180 #ifndef NDEBUG
181   assert (*lock == MUTEX_UNLOCKED);
182
183   *lock = MUTEX_LOCKED;
184 #endif
185   return 0;
186 }
187
188
189 int
190 ath_mutex_unlock (ath_mutex_t *lock)
191 {
192   if (ops_set)
193     {
194       int ret = mutex_init (lock, 1);
195       if (ret)
196         return ret;
197       return (*ops.mutex_unlock) (lock);
198     }
199
200 #ifndef NDEBUG
201   assert (*lock == MUTEX_LOCKED);
202
203   *lock = MUTEX_UNLOCKED;
204 #endif
205   return 0;
206 }
207
208
209 ssize_t
210 ath_read (int fd, void *buf, size_t nbytes)
211 {
212   if (ops_set && ops.read)
213     return (*ops.read) (fd, buf, nbytes);
214   else
215     return read (fd, buf, nbytes);
216 }
217
218
219 ssize_t
220 ath_write (int fd, const void *buf, size_t nbytes)
221 {
222   if (ops_set && ops.write)
223     return (*ops.write) (fd, buf, nbytes);
224   else
225     return write (fd, buf, nbytes);
226 }
227
228
229 ssize_t
230 ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset,
231             struct timeval *timeout)
232 {
233   if (ops_set && ops.select)
234     return (*ops.select) (nfd, rset, wset, eset, timeout);
235   else
236     return select (nfd, rset, wset, eset, timeout);
237 }
238
239  
240 ssize_t
241 ath_waitpid (pid_t pid, int *status, int options)
242 {
243   if (ops_set && ops.waitpid)
244     return (*ops.waitpid) (pid, status, options);
245   else
246     return waitpid (pid, status, options);
247 }
248
249
250 int
251 ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr)
252 {
253   if (ops_set && ops.accept)
254     return (*ops.accept) (s, addr, length_ptr);
255   else
256     return accept (s, addr, length_ptr);
257 }
258
259
260 int
261 ath_connect (int s, struct sockaddr *addr, socklen_t length)
262 {
263   if (ops_set && ops.connect)
264     return (*ops.connect) (s, addr, length);
265   else
266     return connect (s, addr, length);
267 }
268
269
270 int
271 ath_sendmsg (int s, const struct msghdr *msg, int flags)
272 {
273   if (ops_set && ops.sendmsg)
274     return (*ops.sendmsg) (s, msg, flags);
275   else
276     return sendmsg (s, msg, flags);
277 }
278
279
280 int
281 ath_recvmsg (int s, struct msghdr *msg, int flags)
282 {
283   if (ops_set && ops.recvmsg)
284     return (*ops.recvmsg) (s, msg, flags);
285   else
286     return recvmsg (s, msg, flags);
287 }