1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2013-2020 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 #include "plugin-internal.h"
19 #include <libcouchbase/plugins/io/bsdio-inl.c>
20 
21 static my_uvreq_t *alloc_uvreq(my_sockdata_t *sock, generic_callback_t callback);
22 static void set_last_error(my_iops_t *io, int error);
23 static void socket_closed_callback(uv_handle_t *handle);
24 
25 static void wire_iops2(int version, lcb_loop_procs *loop, lcb_timer_procs *timer, lcb_bsd_procs *bsd, lcb_ev_procs *ev,
26                        lcb_completion_procs *iocp, lcb_iomodel_t *model);
27 
decref_iops(my_iops_t *io)28 static void decref_iops(my_iops_t *io)
29 {
30     lcb_assert(io->iops_refcount);
31     if (--io->iops_refcount) {
32         return;
33     }
34 
35     memset(io, 0xff, sizeof(*io));
36     free(io);
37 }
38 
iops_lcb_dtor(lcb_io_opt_t iobase)39 static void iops_lcb_dtor(lcb_io_opt_t iobase)
40 {
41     my_iops_t *io = (my_iops_t *)iobase;
42     if (io->startstop_noop) {
43         decref_iops(io);
44         return;
45     }
46 
47     while (io->iops_refcount > 1) {
48         UVC_RUN_ONCE(io->loop);
49     }
50 
51     if (io->external_loop == 0) {
52         uv_loop_delete(io->loop);
53     }
54 
55     decref_iops(io);
56 }
57 
58 /******************************************************************************
59  ******************************************************************************
60  ** Event Loop Functions                                                     **
61  ******************************************************************************
62  ******************************************************************************/
63 
64 #if UV_VERSION < 0x000900
do_run_loop(my_iops_t *io)65 static void do_run_loop(my_iops_t *io)
66 {
67     while (uv_run_once(io->loop) && io->do_stop == 0) {
68         /* nothing */
69     }
70     io->do_stop = 0;
71 }
do_stop_loop(my_iops_t *io)72 static void do_stop_loop(my_iops_t *io)
73 {
74     io->do_stop = 1;
75 }
76 #else
do_run_loop(my_iops_t *io)77 static void do_run_loop(my_iops_t *io)
78 {
79     uv_run(io->loop, UV_RUN_DEFAULT);
80 }
do_stop_loop(my_iops_t *io)81 static void do_stop_loop(my_iops_t *io)
82 {
83     uv_stop(io->loop);
84 }
85 #endif
86 
run_event_loop(lcb_io_opt_t iobase)87 static void run_event_loop(lcb_io_opt_t iobase)
88 {
89     my_iops_t *io = (my_iops_t *)iobase;
90 
91     if (!io->startstop_noop) {
92         do_run_loop(io);
93     }
94 }
95 
tick_event_loop(lcb_io_opt_t iobase)96 static void tick_event_loop(lcb_io_opt_t iobase)
97 {
98     my_iops_t *io = (my_iops_t *)iobase;
99     if (!io->startstop_noop) {
100 #if UV_VERSION < 0x000900
101         uv_run_once(io->loop);
102         io->do_stop = 0;
103 #else
104         uv_run(io->loop, UV_RUN_NOWAIT);
105 #endif
106     }
107 }
108 
stop_event_loop(lcb_io_opt_t iobase)109 static void stop_event_loop(lcb_io_opt_t iobase)
110 {
111     my_iops_t *io = (my_iops_t *)iobase;
112     if (!io->startstop_noop) {
113         do_stop_loop(io);
114     }
115 }
116 
117 LCBUV_API
lcb_create_libuv_io_opts(int version, lcb_io_opt_t *io, lcbuv_options_t *options)118 lcb_STATUS lcb_create_libuv_io_opts(int version, lcb_io_opt_t *io, lcbuv_options_t *options)
119 {
120     lcb_io_opt_t iop;
121     uv_loop_t *loop = NULL;
122     my_iops_t *ret;
123 
124     if (version != 0) {
125         return LCB_ERR_PLUGIN_VERSION_MISMATCH;
126     }
127 
128 #ifdef _WIN32
129     {
130         /** UV unloading on Windows doesn't work well */
131         HMODULE module;
132         /* We need to provide a symbol */
133         static int dummy;
134         BOOL result;
135         result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
136                                    (LPCSTR)&dummy, &module);
137         if (!result) {
138             return LCB_ERR_SDK_INTERNAL;
139         }
140     }
141 #endif
142 
143     ret = (my_iops_t *)calloc(1, sizeof(*ret));
144 
145     if (!ret) {
146         return LCB_ERR_NO_MEMORY;
147     }
148 
149     iop = &ret->base;
150     iop->version = 2;
151     iop->destructor = iops_lcb_dtor;
152     iop->v.v2.get_procs = wire_iops2;
153 
154     ret->iops_refcount = 1;
155 
156     *io = iop;
157     if (options) {
158         if (options->v.v0.loop) {
159             ret->external_loop = 1;
160             loop = options->v.v0.loop;
161         }
162         ret->startstop_noop = options->v.v0.startsop_noop;
163     }
164 
165     if (!loop) {
166         loop = uv_loop_new();
167     }
168 
169     ret->loop = loop;
170 
171     return LCB_SUCCESS;
172 }
173 
174 #define SOCK_INCR_PENDING(s, fld) (s)->pending.fld++
175 #define SOCK_DECR_PENDING(s, fld) (s)->pending.fld--
176 
177 #ifdef DEBUG
sock_dump_pending(my_sockdata_t *sock)178 static void sock_dump_pending(my_sockdata_t *sock)
179 {
180     printf("Socket %p:\n", (void *)sock);
181     printf("\tRead: %d\n", sock->pending.read);
182     printf("\tWrite: %d\n", sock->pending.write);
183 }
184 #endif
185 
sock_do_uv_close(my_sockdata_t *sock)186 static void sock_do_uv_close(my_sockdata_t *sock)
187 {
188     if (!sock->uv_close_called) {
189         sock->uv_close_called = 1;
190         uv_close((uv_handle_t *)&sock->tcp, socket_closed_callback);
191     }
192 }
193 
decref_sock(my_sockdata_t *sock)194 static void decref_sock(my_sockdata_t *sock)
195 {
196     lcb_assert(sock->refcount);
197 
198     if (--sock->refcount) {
199         return;
200     }
201     sock_do_uv_close(sock);
202 }
203 
204 #define incref_sock(sd) (sd)->refcount++
205 
206 /******************************************************************************
207  ******************************************************************************
208  ** Socket Functions                                                         **
209  ******************************************************************************
210  ******************************************************************************/
create_socket(lcb_io_opt_t iobase, int domain, int type, int protocol)211 static lcb_sockdata_t *create_socket(lcb_io_opt_t iobase, int domain, int type, int protocol)
212 {
213     my_sockdata_t *ret;
214     my_iops_t *io = (my_iops_t *)iobase;
215 
216     ret = (my_sockdata_t *)calloc(1, sizeof(*ret));
217     if (!ret) {
218         return NULL;
219     }
220 
221     uv_tcp_init(io->loop, &ret->tcp.t);
222     ret->base.socket = INVALID_SOCKET;
223 
224     incref_iops(io);
225     incref_sock(ret);
226 
227     set_last_error(io, 0);
228 
229     (void)domain;
230     (void)type;
231     (void)protocol;
232 
233     return (lcb_sockdata_t *)ret;
234 }
235 
236 /**
237  * This one is called from uv_close
238  */
socket_closed_callback(uv_handle_t *handle)239 static void socket_closed_callback(uv_handle_t *handle)
240 {
241     my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, handle, tcp);
242     my_iops_t *io = (my_iops_t *)sock->base.parent;
243 
244     if (sock->pending.read) {
245         CbREQ (&sock->tcp)(&sock->base, -1, sock->rdarg);
246     }
247 
248     memset(sock, 0xEE, sizeof(*sock));
249     free(sock);
250 
251     decref_iops(io);
252 }
253 
close_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase)254 static unsigned int close_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase)
255 {
256     my_sockdata_t *sock = (my_sockdata_t *)sockbase;
257     sock->uv_close_called = 1;
258     uv_close((uv_handle_t *)&sock->tcp, socket_closed_callback);
259     (void)iobase;
260     return 0;
261 }
262 
cntl_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, int mode, int option, void *arg)263 static int cntl_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, int mode, int option, void *arg)
264 {
265     my_sockdata_t *sd = (my_sockdata_t *)sockbase;
266     int rv;
267 
268     switch (option) {
269         case LCB_IO_CNTL_TCP_NODELAY:
270             if (mode == LCB_IO_CNTL_SET) {
271                 rv = uv_tcp_nodelay(&sd->tcp.t, *(int *)arg);
272                 if (rv != 0) {
273                     set_last_error((my_iops_t *)iobase, rv);
274                 }
275                 return rv;
276             } else {
277                 LCB_IOPS_ERRNO(iobase) = ENOTSUP;
278                 return -1;
279             }
280         default:
281             LCB_IOPS_ERRNO(iobase) = ENOTSUP;
282             return -1;
283     }
284 }
285 
286 /******************************************************************************
287  ******************************************************************************
288  ** Connection Functions                                                     **
289  ******************************************************************************
290  ******************************************************************************/
connect_callback(uv_connect_t *req, int status)291 static void connect_callback(uv_connect_t *req, int status)
292 {
293     my_uvreq_t *uvr = (my_uvreq_t *)req;
294 
295     set_last_error((my_iops_t *)uvr->socket->base.parent, status);
296 
297     if (uvr->cb.conn) {
298         uvr->cb.conn(&uvr->socket->base, status);
299     }
300 
301     decref_sock(uvr->socket);
302     free(uvr);
303 }
304 
start_connect(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, const struct sockaddr *name, unsigned int namelen, lcb_io_connect_cb callback)305 static int start_connect(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, const struct sockaddr *name,
306                          unsigned int namelen, lcb_io_connect_cb callback)
307 {
308     my_sockdata_t *sock = (my_sockdata_t *)sockbase;
309     my_iops_t *io = (my_iops_t *)iobase;
310     my_uvreq_t *uvr;
311     int ret;
312     int err_is_set = 0;
313 
314     uvr = alloc_uvreq(sock, (generic_callback_t)callback);
315     if (!uvr) {
316         return -1;
317     }
318 
319     if (namelen == sizeof(struct sockaddr_in)) {
320         ret = UVC_TCP_CONNECT(&uvr->uvreq.conn, &sock->tcp.t, name, connect_callback);
321 
322     } else if (namelen == sizeof(struct sockaddr_in6)) {
323         ret = UVC_TCP_CONNECT6(&uvr->uvreq.conn, &sock->tcp.t, name, connect_callback);
324 
325     } else {
326         io->base.v.v1.error = EINVAL;
327         ret = -1;
328         err_is_set = 1;
329     }
330 
331     if (ret) {
332         if (!err_is_set) {
333             set_last_error(io, ret);
334         }
335 
336         free(uvr);
337 
338     } else {
339         incref_sock(sock);
340     }
341 
342 #if UV_VERSION_HEX >= 0x010000
343     {
344         uv_os_fd_t fd = (uv_os_fd_t)INVALID_SOCKET;
345         /* Fetch socket descriptor for internal usage.
346          * For example to detect dead sockets. */
347         ret = uv_fileno((uv_handle_t *)&sock->tcp, &fd);
348         if (ret == 0) {
349             sock->base.socket = (lcb_socket_t)fd;
350         }
351     }
352 #endif
353 
354     return ret;
355 }
356 
357 /******************************************************************************
358  ******************************************************************************
359  ** Write Functions                                                          **
360  ******************************************************************************
361  ******************************************************************************/
write2_callback(uv_write_t *req, int status)362 static void write2_callback(uv_write_t *req, int status)
363 {
364     my_write_t *mw = (my_write_t *)req;
365     my_sockdata_t *sock = mw->sock;
366 
367     if (status != 0) {
368         set_last_error((my_iops_t *)sock->base.parent, status);
369     }
370 
371     mw->callback(&sock->base, status, mw->w.data);
372     free(mw);
373 }
374 
start_write2(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, struct lcb_iovec_st *iov, lcb_size_t niov, void *uarg, lcb_ioC_write2_callback callback)375 static int start_write2(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, struct lcb_iovec_st *iov, lcb_size_t niov,
376                         void *uarg, lcb_ioC_write2_callback callback)
377 {
378     my_write_t *w;
379     my_sockdata_t *sd = (my_sockdata_t *)sockbase;
380     int ret;
381 
382     w = (my_write_t *)calloc(1, sizeof(*w));
383     w->w.data = uarg;
384     w->callback = callback;
385     w->sock = sd;
386 
387     ret = uv_write(&w->w, (uv_stream_t *)&sd->tcp, (uv_buf_t *)iov, niov, write2_callback);
388 
389     if (ret != 0) {
390         free(w);
391         set_last_error((my_iops_t *)iobase, -1);
392     }
393 
394     return ret;
395 }
396 
397 /******************************************************************************
398  ******************************************************************************
399  ** Read Functions                                                           **
400  ******************************************************************************
401  ******************************************************************************/
402 
403 /**
404  * Currently we support a single IOV. In theory while we could support
405  * multiple IOVs, two problems arise:
406  *
407  * (1) Because UV does not guarantee that it'll utilize the first IOV completely
408  *     we may end up having a gap of unused space between IOVs. This may be
409  *     resolved by keeping an offset into the last-returned IOV and then
410  *     determining how much of this data was actually populated by UV itself.
411  *
412  * (2) In the event of an error, UV gives us "Undefined" behavior if we try
413  *     to utilize the socket again. The IOPS policy dictates that we deliver
414  *     any outstanding data to libcouchbase and _then_ deliver the pending
415  *     error. If we are forced to do this all in a single go, we'd be forced
416  *     to set up an 'async handle' to deliver the pending error, complicating
417  *     our code paths.
418  */
419 
UVC_ALLOC_CBnull420 static UVC_ALLOC_CB(alloc_cb)
421 {
422     UVC_ALLOC_CB_VARS()
423 
424     my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, handle, tcp);
425     buf->base = (char *)sock->iov.iov_base;
426     buf->len = sock->iov.iov_len;
427 
428     (void)suggested_size;
429     UVC_ALLOC_CB_RETURN();
430 }
431 
UVC_READ_CBnull432 static UVC_READ_CB(read_cb)
433 {
434     UVC_READ_CB_VARS()
435 
436     my_tcp_t *mt = (my_tcp_t *)stream;
437     my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, mt, tcp);
438     my_iops_t *io = (my_iops_t *)sock->base.parent;
439     lcb_ioC_read2_callback callback = CbREQ(mt);
440 
441     if (nread == 0) {
442         /* we have a fixed IOV between requests, so just retry again */
443         return;
444     }
445 
446     /**
447      * XXX:
448      * For multi-IOV support, we would require a counter to determine if this
449      * EAGAIN is spurious (i.e. no previous data in buffer), or actual. In
450      * the case of the former, we'd retry -- but in the latter it is a signal
451      * that there is no more pending data within the socket buffer AND we have
452      * outstanding data to deliver back to the caller.
453      */
454     SOCK_DECR_PENDING(sock, read);
455     uv_read_stop(stream);
456     CbREQ(mt) = NULL;
457 
458     if (nread < 0) {
459         set_last_error(io, uvc_last_errno(io->loop, nread));
460         if (uvc_is_eof(io->loop, nread)) {
461             nread = 0;
462         }
463     }
464     callback(&sock->base, nread, sock->rdarg);
465     decref_sock(sock);
466     (void)buf;
467 }
468 
start_read(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, lcb_IOV *iov, lcb_size_t niov, void *uarg, lcb_ioC_read2_callback callback)469 static int start_read(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, lcb_IOV *iov, lcb_size_t niov, void *uarg,
470                       lcb_ioC_read2_callback callback)
471 {
472     my_sockdata_t *sock = (my_sockdata_t *)sockbase;
473     my_iops_t *io = (my_iops_t *)iobase;
474     int ret;
475 
476     sock->iov = *iov;
477     sock->rdarg = uarg;
478     sock->tcp.callback = callback;
479 
480     ret = uv_read_start((uv_stream_t *)&sock->tcp.t, alloc_cb, read_cb);
481     set_last_error(io, ret);
482 
483     if (ret == 0) {
484         SOCK_INCR_PENDING(sock, read);
485         incref_sock(sock);
486     }
487     return ret;
488 }
489 
get_nameinfo(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, struct lcb_nameinfo_st *ni)490 static int get_nameinfo(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, struct lcb_nameinfo_st *ni)
491 {
492     my_sockdata_t *sock = (my_sockdata_t *)sockbase;
493     my_iops_t *io = (my_iops_t *)iobase;
494     uv_tcp_getpeername(&sock->tcp.t, ni->remote.name, ni->remote.len);
495     uv_tcp_getsockname(&sock->tcp.t, ni->local.name, ni->local.len);
496 
497     (void)io;
498     return 0;
499 }
500 
501 /******************************************************************************
502  ******************************************************************************
503  ** Timer Functions                                                          **
504  ** There are just copied from the old couchnode I/O code                    **
505  ******************************************************************************
506  ******************************************************************************/
UVC_TIMER_CBnull507 static UVC_TIMER_CB(timer_cb)
508 {
509     my_timer_t *mytimer = (my_timer_t *)timer;
510     if (mytimer->callback) {
511         mytimer->callback(-1, 0, mytimer->cb_arg);
512     }
513 }
514 
create_timer(lcb_io_opt_t iobase)515 static void *create_timer(lcb_io_opt_t iobase)
516 {
517     my_iops_t *io = (my_iops_t *)iobase;
518     my_timer_t *timer = (my_timer_t *)calloc(1, sizeof(*timer));
519     if (!timer) {
520         return NULL;
521     }
522 
523     timer->parent = io;
524     incref_iops(io);
525     uv_timer_init(io->loop, &timer->uvt);
526 
527     return timer;
528 }
529 
update_timer(lcb_io_opt_t iobase, void *timer_opaque, lcb_uint32_t usec, void *cbdata, v0_callback_t callback)530 static int update_timer(lcb_io_opt_t iobase, void *timer_opaque, lcb_uint32_t usec, void *cbdata,
531                         v0_callback_t callback)
532 {
533     my_timer_t *timer = (my_timer_t *)timer_opaque;
534 
535     timer->callback = callback;
536     timer->cb_arg = cbdata;
537 
538     (void)iobase;
539 
540     return uv_timer_start(&timer->uvt, timer_cb, usec / 1000, 0);
541 }
542 
delete_timer(lcb_io_opt_t iobase, void *timer_opaque)543 static void delete_timer(lcb_io_opt_t iobase, void *timer_opaque)
544 {
545     my_timer_t *timer = (my_timer_t *)timer_opaque;
546 
547     uv_timer_stop(&timer->uvt);
548     timer->callback = NULL;
549 
550     (void)iobase;
551 }
552 
timer_close_cb(uv_handle_t *handle)553 static void timer_close_cb(uv_handle_t *handle)
554 {
555     my_timer_t *timer = (my_timer_t *)handle;
556     decref_iops(timer->parent);
557     memset(timer, 0xff, sizeof(*timer));
558     free(timer);
559 }
560 
destroy_timer(lcb_io_opt_t io, void *timer_opaque)561 static void destroy_timer(lcb_io_opt_t io, void *timer_opaque)
562 {
563     delete_timer(io, timer_opaque);
564     uv_close((uv_handle_t *)timer_opaque, timer_close_cb);
565 }
566 
alloc_uvreq(my_sockdata_t *sock, generic_callback_t callback)567 static my_uvreq_t *alloc_uvreq(my_sockdata_t *sock, generic_callback_t callback)
568 {
569     my_uvreq_t *ret = (my_uvreq_t *)calloc(1, sizeof(*ret));
570     if (!ret) {
571         sock->base.parent->v.v1.error = ENOMEM;
572         return NULL;
573     }
574     ret->socket = sock;
575     ret->cb.cb_ = callback;
576     return ret;
577 }
578 
set_last_error(my_iops_t *io, int error)579 static void set_last_error(my_iops_t *io, int error)
580 {
581     io->base.v.v1.error = uvc_last_errno(io->loop, error);
582 }
583 
584 #if UV_VERSION_HEX >= 0x010000
check_closed(lcb_io_opt_t io, lcb_sockdata_t *sockbase, int flags)585 static int check_closed(lcb_io_opt_t io, lcb_sockdata_t *sockbase, int flags)
586 {
587     my_sockdata_t *sd = (my_sockdata_t *)sockbase;
588 
589     char buf = 0;
590     int rv = 0;
591     lcb_socket_t sock = sd->base.socket;
592 
593     if (sock == INVALID_SOCKET) {
594         return LCB_IO_SOCKCHECK_STATUS_UNKNOWN;
595     }
596 
597 GT_RETRY:
598     /* We can ignore flags for now, since both Windows and POSIX support MSG_PEEK */
599     rv = recv(sock, &buf, 1, MSG_PEEK);
600     if (rv == 1) {
601         if (flags & LCB_IO_SOCKCHECK_PEND_IS_ERROR) {
602             return LCB_IO_SOCKCHECK_STATUS_CLOSED;
603         } else {
604             return LCB_IO_SOCKCHECK_STATUS_OK;
605         }
606     } else if (rv == 0) {
607         /* Really closed! */
608         return LCB_IO_SOCKCHECK_STATUS_CLOSED;
609     } else {
610         int last_err;
611 #ifdef _WIN32
612         last_err = get_wserr(sock);
613 #else
614         last_err = errno;
615 #endif
616 
617         if (last_err == EINTR) {
618             goto GT_RETRY;
619         } else if (last_err == EWOULDBLOCK || last_err == EAGAIN) {
620             return LCB_IO_SOCKCHECK_STATUS_OK; /* Nothing to report. So we're good */
621         } else {
622             return LCB_IO_SOCKCHECK_STATUS_CLOSED;
623         }
624     }
625 }
626 #endif
627 
wire_iops2(int version, lcb_loop_procs *loop, lcb_timer_procs *timer, lcb_bsd_procs *bsd, lcb_ev_procs *ev, lcb_completion_procs *iocp, lcb_iomodel_t *model)628 static void wire_iops2(int version, lcb_loop_procs *loop, lcb_timer_procs *timer, lcb_bsd_procs *bsd, lcb_ev_procs *ev,
629                        lcb_completion_procs *iocp, lcb_iomodel_t *model)
630 {
631     *model = LCB_IOMODEL_COMPLETION;
632     loop->start = run_event_loop;
633     loop->stop = stop_event_loop;
634     loop->tick = tick_event_loop;
635 
636     timer->create = create_timer;
637     timer->cancel = delete_timer;
638     timer->schedule = update_timer;
639     timer->destroy = destroy_timer;
640 
641     iocp->close = close_socket;
642     iocp->socket = create_socket;
643     iocp->connect = start_connect;
644     iocp->nameinfo = get_nameinfo;
645     iocp->read2 = start_read;
646     iocp->write2 = start_write2;
647     iocp->cntl = cntl_socket;
648 #if UV_VERSION_HEX >= 0x010000
649     iocp->is_closed = check_closed;
650 #endif
651 
652     /** Stuff we don't use */
653     iocp->write = NULL;
654     iocp->wballoc = NULL;
655     iocp->wbfree = NULL;
656     iocp->serve = NULL;
657 
658     (void)bsd;
659     (void)version;
660     (void)ev;
661 }
662