1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2011-2012 Couchbase, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /**
19 * This file contains IO operations that use libevent
20 *
21 * @author Trond Norbye
22 * @todo add more documentation
23 */
24 #define LCB_IOPS_V12_NO_DEPRECATE
25 #include "config.h"
26 #include <event.h>
27 #include "libevent_io_opts.h"
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <libcouchbase/plugins/io/bsdio-inl.c>
32
33 struct libevent_cookie {
34 struct event_base *base;
35 int allocated;
36 };
37
38
39 #ifndef HAVE_LIBEVENT2
40 /* libevent 1.x compatibility layer */
41 #ifndef evutil_socket_t
42 #define evutil_socket_t int
43 #endif
44
45 typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
46
47 static int
event_assign(struct event * ev,struct event_base * base,evutil_socket_t fd,short events,event_callback_fn callback,void * arg)48 event_assign(struct event *ev,
49 struct event_base *base,
50 evutil_socket_t fd,
51 short events,
52 event_callback_fn callback,
53 void *arg)
54 {
55 ev->ev_callback = callback;
56 ev->ev_arg = arg;
57 ev->ev_fd = fd;
58 ev->ev_events = events;
59 ev->ev_res = 0;
60 ev->ev_flags = EVLIST_INIT;
61 ev->ev_ncalls = 0;
62 ev->ev_pncalls = NULL;
63 event_base_set(base, ev);
64
65 return 0;
66 }
67
68 static struct event *
event_new(struct event_base * base,evutil_socket_t fd,short events,event_callback_fn cb,void * arg)69 event_new(struct event_base *base,
70 evutil_socket_t fd,
71 short events,
72 event_callback_fn cb,
73 void *arg) {
74 struct event *ev;
75 ev = malloc(sizeof(struct event));
76 if (ev == NULL) {
77 return NULL;
78 }
79 if (event_assign(ev, base, fd, events, cb, arg) < 0) {
80 free(ev);
81 return NULL;
82 }
83 return ev;
84 }
85
86 static void
event_free(struct event * ev)87 event_free(struct event *ev)
88 {
89 /* make sure that this event won't be coming back to haunt us. */
90 free(ev);
91
92 }
93 static short
event_get_events(const struct event * ev)94 event_get_events(const struct event *ev)
95 {
96 return ev->ev_events;
97 }
98
99 static event_callback_fn
event_get_callback(const struct event * ev)100 event_get_callback(const struct event *ev)
101 {
102 return ev->ev_callback;
103 }
104 #endif
105
lcb_io_create_event(struct lcb_io_opt_st * iops)106 static void *lcb_io_create_event(struct lcb_io_opt_st *iops)
107 {
108 return event_new(((struct libevent_cookie *)iops->v.v2.cookie)->base,
109 INVALID_SOCKET, 0, NULL, NULL);
110 }
111
lcb_io_update_event(struct lcb_io_opt_st * iops,lcb_socket_t sock,void * event,short flags,void * cb_data,void (* handler)(lcb_socket_t sock,short which,void * cb_data))112 static int lcb_io_update_event(struct lcb_io_opt_st *iops,
113 lcb_socket_t sock,
114 void *event,
115 short flags,
116 void *cb_data,
117 void (*handler)(lcb_socket_t sock,
118 short which,
119 void *cb_data))
120 {
121 flags |= EV_PERSIST;
122 if (flags == event_get_events(event) &&
123 handler == event_get_callback(event)) {
124 /* no change! */
125 return 0;
126 }
127
128 if (event_pending(event, EV_READ | EV_WRITE, 0)) {
129 event_del(event);
130 }
131
132 event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, sock, flags, handler, cb_data);
133 return event_add(event, NULL);
134 }
135
136
lcb_io_delete_timer(struct lcb_io_opt_st * iops,void * event)137 static void lcb_io_delete_timer(struct lcb_io_opt_st *iops,
138 void *event)
139 {
140 (void)iops;
141 if (event_pending(event, EV_TIMEOUT, 0) != 0 && event_del(event) == -1) {
142 iops->v.v2.error = EINVAL;
143 }
144 event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL);
145 }
146
lcb_io_update_timer(struct lcb_io_opt_st * iops,void * timer,lcb_uint32_t usec,void * cb_data,void (* handler)(lcb_socket_t sock,short which,void * cb_data))147 static int lcb_io_update_timer(struct lcb_io_opt_st *iops,
148 void *timer,
149 lcb_uint32_t usec,
150 void *cb_data,
151 void (*handler)(lcb_socket_t sock,
152 short which,
153 void *cb_data))
154 {
155 short flags = EV_TIMEOUT | EV_PERSIST;
156 struct timeval tmo;
157 if (flags == event_get_events(timer) &&
158 handler == event_get_callback(timer)) {
159 /* no change! */
160 return 0;
161 }
162
163 if (event_pending(timer, EV_TIMEOUT, 0)) {
164 event_del(timer);
165 }
166
167 event_assign(timer, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, flags, handler, cb_data);
168 tmo.tv_sec = usec / 1000000;
169 tmo.tv_usec = usec % 1000000;
170 return event_add(timer, &tmo);
171 }
172
lcb_io_destroy_event(struct lcb_io_opt_st * iops,void * event)173 static void lcb_io_destroy_event(struct lcb_io_opt_st *iops,
174 void *event)
175 {
176 (void)iops;
177 if (event_pending(event, EV_READ | EV_WRITE | EV_TIMEOUT, 0)) {
178 event_del(event);
179 }
180 event_free(event);
181 }
182
lcb_io_delete_event(struct lcb_io_opt_st * iops,lcb_socket_t sock,void * event)183 static void lcb_io_delete_event(struct lcb_io_opt_st *iops,
184 lcb_socket_t sock,
185 void *event)
186 {
187 (void)iops;
188 (void)sock;
189 if (event_del(event) == -1) {
190 iops->v.v2.error = EINVAL;
191 }
192 event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL);
193 }
194
lcb_io_stop_event_loop(struct lcb_io_opt_st * iops)195 static void lcb_io_stop_event_loop(struct lcb_io_opt_st *iops)
196 {
197 event_base_loopbreak(((struct libevent_cookie *)iops->v.v2.cookie)->base);
198 }
199
lcb_io_run_event_loop(struct lcb_io_opt_st * iops)200 static void lcb_io_run_event_loop(struct lcb_io_opt_st *iops)
201 {
202 event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base, 0);
203 }
204
lcb_io_tick_event_loop(struct lcb_io_opt_st * iops)205 static void lcb_io_tick_event_loop(struct lcb_io_opt_st *iops)
206 {
207 event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base,
208 EVLOOP_NONBLOCK);
209 }
210
lcb_destroy_io_opts(struct lcb_io_opt_st * iops)211 static void lcb_destroy_io_opts(struct lcb_io_opt_st *iops)
212 {
213 if (((struct libevent_cookie *)iops->v.v2.cookie)->allocated) {
214 event_base_free(((struct libevent_cookie *)iops->v.v2.cookie)->base);
215 }
216 free(iops->v.v2.cookie);
217 free(iops);
218 }
219
220 static void
procs2_lnt_callback(int version,lcb_loop_procs * loop_procs,lcb_timer_procs * timer_procs,lcb_bsd_procs * bsd_procs,lcb_ev_procs * ev_procs,lcb_completion_procs * completion_procs,lcb_iomodel_t * iomodel)221 procs2_lnt_callback(int version, lcb_loop_procs *loop_procs,
222 lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs,
223 lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs,
224 lcb_iomodel_t *iomodel)
225 {
226 ev_procs->create = lcb_io_create_event;
227 ev_procs->destroy = lcb_io_destroy_event;
228 ev_procs->watch = lcb_io_update_event;
229 ev_procs->cancel = lcb_io_delete_event;
230
231 timer_procs->create = lcb_io_create_event;
232 timer_procs->destroy = lcb_io_destroy_event;
233 timer_procs->schedule = lcb_io_update_timer;
234 timer_procs->cancel = lcb_io_delete_timer;
235
236 loop_procs->start = lcb_io_run_event_loop;
237 loop_procs->stop = lcb_io_stop_event_loop;
238 loop_procs->tick = lcb_io_tick_event_loop;
239
240 *iomodel = LCB_IOMODEL_EVENT;
241
242 wire_lcb_bsd_impl2(bsd_procs, version);
243 (void)completion_procs;
244 }
245
246 LIBCOUCHBASE_API
lcb_create_libevent_io_opts(int version,lcb_io_opt_t * io,void * arg)247 lcb_error_t lcb_create_libevent_io_opts(int version, lcb_io_opt_t *io, void *arg)
248 {
249 struct event_base *base = arg;
250 struct lcb_io_opt_st *ret;
251 struct libevent_cookie *cookie;
252 if (version != 0) {
253 return LCB_PLUGIN_VERSION_MISMATCH;
254 }
255
256 ret = calloc(1, sizeof(*ret));
257 cookie = calloc(1, sizeof(*cookie));
258 if (ret == NULL || cookie == NULL) {
259 free(ret);
260 free(cookie);
261 return LCB_CLIENT_ENOMEM;
262 }
263
264 /* setup io iops! */
265 ret->version = 3;
266 ret->dlhandle = NULL;
267 ret->destructor = lcb_destroy_io_opts;
268 /* consider that struct isn't allocated by the library,
269 * `need_cleanup' flag might be set in lcb_create() */
270 ret->v.v3.need_cleanup = 0;
271
272 if (base == NULL) {
273 if ((cookie->base = event_base_new()) == NULL) {
274 free(ret);
275 free(cookie);
276 return LCB_CLIENT_ENOMEM;
277 }
278 cookie->allocated = 1;
279 } else {
280 cookie->base = base;
281 cookie->allocated = 0;
282 }
283
284 ret->v.v3.cookie = cookie;
285 ret->v.v3.get_procs = procs2_lnt_callback;
286
287 /* For back-compat */
288 wire_lcb_bsd_impl(ret);
289
290 *io = ret;
291 return LCB_SUCCESS;
292 }
293