-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathgai_async.c
191 lines (182 loc) · 4.73 KB
/
gai_async.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* gai_async.c
*
* a simple wrapper for getaddrinfo using pthreads to make it asynchronous.
*
* Gunnar Zötl <[email protected]>, 2013-2015
* Released under the terms of the MIT license. See file LICENSE for details.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "gai_async.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* handler for asynchronous getaddrinfo() requests. Carries everything
* getaddrinfo() needs and also some housekeeping stuff like the worker
* thread id, the run status of the request and the error code returned
* by getaddrinfo() upon completion.
*/
struct gai_request {
pthread_t worker;
int done;
int err;
char *node;
char *service;
struct addrinfo *hints;
struct addrinfo *res;
};
/* helper for gai_worker: this cleanup handler marks the request as done
* when it completed normally and also when it was canceled.
*/
static void gai_cleanup(void *arg)
{
struct gai_request *rq = (struct gai_request*) arg;
rq->done = 1;
}
/* gai_worker
*
* perform the getaddrinfo request. Started as a thread by gai_start.
*
* Arguments:
* arg pointer to a gai_request structure
*
* Returns:
* 0, is ignored by the call to pthread_join() in gai_finalize()
*/
static void *gai_worker(void *arg)
{
struct gai_request *rq = (struct gai_request*) arg;
pthread_cleanup_push(gai_cleanup, rq);
rq->err = getaddrinfo(rq->node, rq->service, rq->hints, &rq->res);
pthread_cleanup_pop(1);
return 0;
}
/* gai_start
*
* start the getaddrinfo worker thread.
*
* Arguments:
* node node name, as for getaddrinfo()
* service service name, as for getaddrinfo()
* hints criteria for name resolving, as for getaddrinfo()
*
* Returns:
* a pointer to the request handler on success, NULL on failure. If this
* function returns NULL, it is always because the worker thread could
* not be spawned.
*/
struct gai_request *gai_start(const char *node, const char *service, const struct addrinfo *hints)
{
struct gai_request *rq = malloc(sizeof(struct gai_request));
if (rq) {
rq->done = 0;
rq->node = node ? strdup(node) : NULL;
rq->service = service ? strdup(service) : NULL;
if (hints) {
rq->hints = malloc(sizeof(struct addrinfo));
memcpy(rq->hints, hints, sizeof(struct addrinfo));
} else
rq->hints = NULL;
rq->res = NULL;
int _err = pthread_create(&rq->worker, NULL, gai_worker, (void*) rq);
if (_err) {
free(rq);
rq = 0;
}
}
return rq;
}
/* gai_poll
*
* check whether the request has finished.
*
* Arguments:
* rq gai_request to check.
*
* Returns:
* 1 if finished, 0 if still running, and EAI_SYSTEM on error. An error
* can only occur if rq is invalid (a.k.a. NULL), in which case errno
* is set to EINVAL
*/
int gai_poll(struct gai_request *rq)
{
if (rq)
return rq->done;
errno = EINVAL;
return EAI_SYSTEM;
}
/* gai_cancel
*
* cancel a pending getaddrinfo request.
*
* Arguments:
* rq gai_request to cancel.
*
* Returns:
* 0 on success, EAI_SYSTEM on error. Errno will be set accordingly, either
* EINVAL if the rq pointer was NULL, or any error pthread_cancel returned,
* which, according to its manpage, will be ESRCH if there was no active
* thread with the id stored in the rq structure.
*
* Note: you must still call gai_finalize on a canceled request. No guarantee
* can be made about when the request will be canceled.
*/
int gai_cancel(struct gai_request *rq)
{
if (rq) {
int err = pthread_cancel(rq->worker);
if (err)
errno = err;
else
return 0;
} else
errno = EINVAL;
return EAI_SYSTEM;
}
/* gai_finalize
*
* finalize getaddrinfo request, clean up the gai_request structure and
* fetch the result from getaddrinfo.
*
* Arguments:
* rq gai_request to get results from
* res pointer to pointer for result, like the last argument to getaddrinfo()
*
* Returns:
* 0 on success, one of the EAI error codes if the request terminated
* successful, but getaddrinfo failed, or EAI_SYSTEM with errno set to
* EAGAIN if the request is not yet terminated, or to EINVAL if rq was
* NULL. If the request has previously been canceled, errno will be set
* to ECANCELED.
*
* After the call to gai_finalize, the gai_request handler in rq is no
* longer valid.
*/
int gai_finalize(struct gai_request *rq, struct addrinfo **res)
{
*res = NULL;
if (rq) {
if (rq->done) {
void *rv;
pthread_join(rq->worker, &rv);
int err = rq->err;
if (rq->node) free(rq->node);
if (rq->service) free(rq->service);
if (rq->hints) free(rq->hints);
*res = rq->res;
free(rq);
if (rv == PTHREAD_CANCELED) {
if (*res) freeaddrinfo(*res);
errno = ECANCELED;
*res = NULL;
} else
return err;
} else
errno = EAGAIN;
} else {
errno = EINVAL;
}
return EAI_SYSTEM;
}