1c43e99fdSEd Maste /*
2c43e99fdSEd Maste * Copyright 2007-2012 Niels Provos and Nick Mathewson
3c43e99fdSEd Maste * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
4c43e99fdSEd Maste * Copyright 2003 Michael A. Davis <mike@datanerds.net>
5c43e99fdSEd Maste *
6c43e99fdSEd Maste * Redistribution and use in source and binary forms, with or without
7c43e99fdSEd Maste * modification, are permitted provided that the following conditions
8c43e99fdSEd Maste * are met:
9c43e99fdSEd Maste * 1. Redistributions of source code must retain the above copyright
10c43e99fdSEd Maste * notice, this list of conditions and the following disclaimer.
11c43e99fdSEd Maste * 2. Redistributions in binary form must reproduce the above copyright
12c43e99fdSEd Maste * notice, this list of conditions and the following disclaimer in the
13c43e99fdSEd Maste * documentation and/or other materials provided with the distribution.
14c43e99fdSEd Maste * 3. The name of the author may not be used to endorse or promote products
15c43e99fdSEd Maste * derived from this software without specific prior written permission.
16c43e99fdSEd Maste *
17c43e99fdSEd Maste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18c43e99fdSEd Maste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19c43e99fdSEd Maste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20c43e99fdSEd Maste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21c43e99fdSEd Maste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22c43e99fdSEd Maste * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23c43e99fdSEd Maste * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24c43e99fdSEd Maste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25c43e99fdSEd Maste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26c43e99fdSEd Maste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27c43e99fdSEd Maste */
28c43e99fdSEd Maste #include "event2/event-config.h"
29c43e99fdSEd Maste #include "evconfig-private.h"
30c43e99fdSEd Maste
31c43e99fdSEd Maste #ifdef _WIN32
32c43e99fdSEd Maste
33c43e99fdSEd Maste #include <winsock2.h>
34c43e99fdSEd Maste #include <windows.h>
35c43e99fdSEd Maste #include <sys/types.h>
36c43e99fdSEd Maste #include <sys/queue.h>
37c43e99fdSEd Maste #include <limits.h>
38c43e99fdSEd Maste #include <signal.h>
39c43e99fdSEd Maste #include <stdio.h>
40c43e99fdSEd Maste #include <stdlib.h>
41c43e99fdSEd Maste #include <string.h>
42c43e99fdSEd Maste #include <errno.h>
43c43e99fdSEd Maste
44c43e99fdSEd Maste #include "event2/util.h"
45c43e99fdSEd Maste #include "util-internal.h"
46c43e99fdSEd Maste #include "log-internal.h"
47c43e99fdSEd Maste #include "event2/event.h"
48c43e99fdSEd Maste #include "event-internal.h"
49c43e99fdSEd Maste #include "evmap-internal.h"
50c43e99fdSEd Maste #include "event2/thread.h"
51c43e99fdSEd Maste #include "evthread-internal.h"
52c43e99fdSEd Maste #include "time-internal.h"
53c43e99fdSEd Maste
54c43e99fdSEd Maste #define XFREE(ptr) do { if (ptr) mm_free(ptr); } while (0)
55c43e99fdSEd Maste
56c43e99fdSEd Maste extern struct event_list timequeue;
57c43e99fdSEd Maste extern struct event_list addqueue;
58c43e99fdSEd Maste
59c43e99fdSEd Maste struct win_fd_set {
60c43e99fdSEd Maste unsigned int fd_count;
61c43e99fdSEd Maste SOCKET fd_array[1];
62c43e99fdSEd Maste };
63c43e99fdSEd Maste
64c43e99fdSEd Maste /* MSDN says this is required to handle SIGFPE */
65c43e99fdSEd Maste volatile double SIGFPE_REQ = 0.0f;
66c43e99fdSEd Maste
67c43e99fdSEd Maste struct idx_info {
68c43e99fdSEd Maste int read_pos_plus1;
69c43e99fdSEd Maste int write_pos_plus1;
70c43e99fdSEd Maste };
71c43e99fdSEd Maste
72c43e99fdSEd Maste struct win32op {
73c43e99fdSEd Maste unsigned num_fds_in_fd_sets;
74c43e99fdSEd Maste int resize_out_sets;
75c43e99fdSEd Maste struct win_fd_set *readset_in;
76c43e99fdSEd Maste struct win_fd_set *writeset_in;
77c43e99fdSEd Maste struct win_fd_set *readset_out;
78c43e99fdSEd Maste struct win_fd_set *writeset_out;
79c43e99fdSEd Maste struct win_fd_set *exset_out;
80c43e99fdSEd Maste unsigned signals_are_broken : 1;
81c43e99fdSEd Maste };
82c43e99fdSEd Maste
83c43e99fdSEd Maste static void *win32_init(struct event_base *);
84c43e99fdSEd Maste static int win32_add(struct event_base *, evutil_socket_t, short old, short events, void *idx_);
85c43e99fdSEd Maste static int win32_del(struct event_base *, evutil_socket_t, short old, short events, void *idx_);
86c43e99fdSEd Maste static int win32_dispatch(struct event_base *base, struct timeval *);
87c43e99fdSEd Maste static void win32_dealloc(struct event_base *);
88c43e99fdSEd Maste
89c43e99fdSEd Maste struct eventop win32ops = {
90c43e99fdSEd Maste "win32",
91c43e99fdSEd Maste win32_init,
92c43e99fdSEd Maste win32_add,
93c43e99fdSEd Maste win32_del,
94c43e99fdSEd Maste win32_dispatch,
95c43e99fdSEd Maste win32_dealloc,
96c43e99fdSEd Maste 0, /* doesn't need reinit */
97c43e99fdSEd Maste 0, /* No features supported. */
98c43e99fdSEd Maste sizeof(struct idx_info),
99c43e99fdSEd Maste };
100c43e99fdSEd Maste
101c43e99fdSEd Maste #define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET)))
102c43e99fdSEd Maste
103c43e99fdSEd Maste static int
grow_fd_sets(struct win32op * op,unsigned new_num_fds)104c43e99fdSEd Maste grow_fd_sets(struct win32op *op, unsigned new_num_fds)
105c43e99fdSEd Maste {
106c43e99fdSEd Maste size_t size;
107c43e99fdSEd Maste
108c43e99fdSEd Maste EVUTIL_ASSERT(new_num_fds >= op->readset_in->fd_count &&
109c43e99fdSEd Maste new_num_fds >= op->writeset_in->fd_count);
110c43e99fdSEd Maste EVUTIL_ASSERT(new_num_fds >= 1);
111c43e99fdSEd Maste
112c43e99fdSEd Maste size = FD_SET_ALLOC_SIZE(new_num_fds);
113c43e99fdSEd Maste if (!(op->readset_in = mm_realloc(op->readset_in, size)))
114c43e99fdSEd Maste return (-1);
115c43e99fdSEd Maste if (!(op->writeset_in = mm_realloc(op->writeset_in, size)))
116c43e99fdSEd Maste return (-1);
117c43e99fdSEd Maste op->resize_out_sets = 1;
118c43e99fdSEd Maste op->num_fds_in_fd_sets = new_num_fds;
119c43e99fdSEd Maste return (0);
120c43e99fdSEd Maste }
121c43e99fdSEd Maste
122c43e99fdSEd Maste static int
do_fd_set(struct win32op * op,struct idx_info * ent,evutil_socket_t s,int read)123c43e99fdSEd Maste do_fd_set(struct win32op *op, struct idx_info *ent, evutil_socket_t s, int read)
124c43e99fdSEd Maste {
125c43e99fdSEd Maste struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
126c43e99fdSEd Maste if (read) {
127c43e99fdSEd Maste if (ent->read_pos_plus1 > 0)
128c43e99fdSEd Maste return (0);
129c43e99fdSEd Maste } else {
130c43e99fdSEd Maste if (ent->write_pos_plus1 > 0)
131c43e99fdSEd Maste return (0);
132c43e99fdSEd Maste }
133c43e99fdSEd Maste if (set->fd_count == op->num_fds_in_fd_sets) {
134c43e99fdSEd Maste if (grow_fd_sets(op, op->num_fds_in_fd_sets*2))
135c43e99fdSEd Maste return (-1);
136c43e99fdSEd Maste /* set pointer will have changed and needs reiniting! */
137c43e99fdSEd Maste set = read ? op->readset_in : op->writeset_in;
138c43e99fdSEd Maste }
139c43e99fdSEd Maste set->fd_array[set->fd_count] = s;
140c43e99fdSEd Maste if (read)
141c43e99fdSEd Maste ent->read_pos_plus1 = set->fd_count+1;
142c43e99fdSEd Maste else
143c43e99fdSEd Maste ent->write_pos_plus1 = set->fd_count+1;
144c43e99fdSEd Maste return (set->fd_count++);
145c43e99fdSEd Maste }
146c43e99fdSEd Maste
147c43e99fdSEd Maste static int
do_fd_clear(struct event_base * base,struct win32op * op,struct idx_info * ent,int read)148c43e99fdSEd Maste do_fd_clear(struct event_base *base,
149c43e99fdSEd Maste struct win32op *op, struct idx_info *ent, int read)
150c43e99fdSEd Maste {
151c43e99fdSEd Maste int i;
152c43e99fdSEd Maste struct win_fd_set *set = read ? op->readset_in : op->writeset_in;
153c43e99fdSEd Maste if (read) {
154c43e99fdSEd Maste i = ent->read_pos_plus1 - 1;
155c43e99fdSEd Maste ent->read_pos_plus1 = 0;
156c43e99fdSEd Maste } else {
157c43e99fdSEd Maste i = ent->write_pos_plus1 - 1;
158c43e99fdSEd Maste ent->write_pos_plus1 = 0;
159c43e99fdSEd Maste }
160c43e99fdSEd Maste if (i < 0)
161c43e99fdSEd Maste return (0);
162c43e99fdSEd Maste if (--set->fd_count != (unsigned)i) {
163c43e99fdSEd Maste struct idx_info *ent2;
164c43e99fdSEd Maste SOCKET s2;
165c43e99fdSEd Maste s2 = set->fd_array[i] = set->fd_array[set->fd_count];
166c43e99fdSEd Maste
167c43e99fdSEd Maste ent2 = evmap_io_get_fdinfo_(&base->io, s2);
168c43e99fdSEd Maste
169c43e99fdSEd Maste if (!ent2) /* This indicates a bug. */
170c43e99fdSEd Maste return (0);
171c43e99fdSEd Maste if (read)
172c43e99fdSEd Maste ent2->read_pos_plus1 = i+1;
173c43e99fdSEd Maste else
174c43e99fdSEd Maste ent2->write_pos_plus1 = i+1;
175c43e99fdSEd Maste }
176c43e99fdSEd Maste return (0);
177c43e99fdSEd Maste }
178c43e99fdSEd Maste
179c43e99fdSEd Maste #define NEVENT 32
180c43e99fdSEd Maste void *
win32_init(struct event_base * base)181c43e99fdSEd Maste win32_init(struct event_base *base)
182c43e99fdSEd Maste {
183c43e99fdSEd Maste struct win32op *winop;
184c43e99fdSEd Maste size_t size;
185c43e99fdSEd Maste if (!(winop = mm_calloc(1, sizeof(struct win32op))))
186c43e99fdSEd Maste return NULL;
187c43e99fdSEd Maste winop->num_fds_in_fd_sets = NEVENT;
188c43e99fdSEd Maste size = FD_SET_ALLOC_SIZE(NEVENT);
189c43e99fdSEd Maste if (!(winop->readset_in = mm_malloc(size)))
190c43e99fdSEd Maste goto err;
191c43e99fdSEd Maste if (!(winop->writeset_in = mm_malloc(size)))
192c43e99fdSEd Maste goto err;
193c43e99fdSEd Maste if (!(winop->readset_out = mm_malloc(size)))
194c43e99fdSEd Maste goto err;
195c43e99fdSEd Maste if (!(winop->writeset_out = mm_malloc(size)))
196c43e99fdSEd Maste goto err;
197c43e99fdSEd Maste if (!(winop->exset_out = mm_malloc(size)))
198c43e99fdSEd Maste goto err;
199c43e99fdSEd Maste winop->readset_in->fd_count = winop->writeset_in->fd_count = 0;
200c43e99fdSEd Maste winop->readset_out->fd_count = winop->writeset_out->fd_count
201c43e99fdSEd Maste = winop->exset_out->fd_count = 0;
202c43e99fdSEd Maste
203c43e99fdSEd Maste if (evsig_init_(base) < 0)
204c43e99fdSEd Maste winop->signals_are_broken = 1;
205c43e99fdSEd Maste
206c43e99fdSEd Maste evutil_weakrand_seed_(&base->weakrand_seed, 0);
207c43e99fdSEd Maste
208c43e99fdSEd Maste return (winop);
209c43e99fdSEd Maste err:
210c43e99fdSEd Maste XFREE(winop->readset_in);
211c43e99fdSEd Maste XFREE(winop->writeset_in);
212c43e99fdSEd Maste XFREE(winop->readset_out);
213c43e99fdSEd Maste XFREE(winop->writeset_out);
214c43e99fdSEd Maste XFREE(winop->exset_out);
215c43e99fdSEd Maste XFREE(winop);
216c43e99fdSEd Maste return (NULL);
217c43e99fdSEd Maste }
218c43e99fdSEd Maste
219c43e99fdSEd Maste int
win32_add(struct event_base * base,evutil_socket_t fd,short old,short events,void * idx_)220c43e99fdSEd Maste win32_add(struct event_base *base, evutil_socket_t fd,
221c43e99fdSEd Maste short old, short events, void *idx_)
222c43e99fdSEd Maste {
223c43e99fdSEd Maste struct win32op *win32op = base->evbase;
224c43e99fdSEd Maste struct idx_info *idx = idx_;
225c43e99fdSEd Maste
226c43e99fdSEd Maste if ((events & EV_SIGNAL) && win32op->signals_are_broken)
227c43e99fdSEd Maste return (-1);
228c43e99fdSEd Maste
229c43e99fdSEd Maste if (!(events & (EV_READ|EV_WRITE)))
230c43e99fdSEd Maste return (0);
231c43e99fdSEd Maste
232c43e99fdSEd Maste event_debug(("%s: adding event for %d", __func__, (int)fd));
233c43e99fdSEd Maste if (events & EV_READ) {
234c43e99fdSEd Maste if (do_fd_set(win32op, idx, fd, 1)<0)
235c43e99fdSEd Maste return (-1);
236c43e99fdSEd Maste }
237c43e99fdSEd Maste if (events & EV_WRITE) {
238c43e99fdSEd Maste if (do_fd_set(win32op, idx, fd, 0)<0)
239c43e99fdSEd Maste return (-1);
240c43e99fdSEd Maste }
241c43e99fdSEd Maste return (0);
242c43e99fdSEd Maste }
243c43e99fdSEd Maste
244c43e99fdSEd Maste int
win32_del(struct event_base * base,evutil_socket_t fd,short old,short events,void * idx_)245c43e99fdSEd Maste win32_del(struct event_base *base, evutil_socket_t fd, short old, short events,
246c43e99fdSEd Maste void *idx_)
247c43e99fdSEd Maste {
248c43e99fdSEd Maste struct win32op *win32op = base->evbase;
249c43e99fdSEd Maste struct idx_info *idx = idx_;
250c43e99fdSEd Maste
251c43e99fdSEd Maste event_debug(("%s: Removing event for "EV_SOCK_FMT,
252c43e99fdSEd Maste __func__, EV_SOCK_ARG(fd)));
253c43e99fdSEd Maste if (events & EV_READ)
254c43e99fdSEd Maste do_fd_clear(base, win32op, idx, 1);
255c43e99fdSEd Maste if (events & EV_WRITE)
256c43e99fdSEd Maste do_fd_clear(base, win32op, idx, 0);
257c43e99fdSEd Maste
258c43e99fdSEd Maste return 0;
259c43e99fdSEd Maste }
260c43e99fdSEd Maste
261c43e99fdSEd Maste static void
fd_set_copy(struct win_fd_set * out,const struct win_fd_set * in)262c43e99fdSEd Maste fd_set_copy(struct win_fd_set *out, const struct win_fd_set *in)
263c43e99fdSEd Maste {
264c43e99fdSEd Maste out->fd_count = in->fd_count;
265c43e99fdSEd Maste memcpy(out->fd_array, in->fd_array, in->fd_count * (sizeof(SOCKET)));
266c43e99fdSEd Maste }
267c43e99fdSEd Maste
268c43e99fdSEd Maste /*
269c43e99fdSEd Maste static void dump_fd_set(struct win_fd_set *s)
270c43e99fdSEd Maste {
271c43e99fdSEd Maste unsigned int i;
272c43e99fdSEd Maste printf("[ ");
273c43e99fdSEd Maste for(i=0;i<s->fd_count;++i)
274c43e99fdSEd Maste printf("%d ",(int)s->fd_array[i]);
275c43e99fdSEd Maste printf("]\n");
276c43e99fdSEd Maste }
277c43e99fdSEd Maste */
278c43e99fdSEd Maste
279c43e99fdSEd Maste int
win32_dispatch(struct event_base * base,struct timeval * tv)280c43e99fdSEd Maste win32_dispatch(struct event_base *base, struct timeval *tv)
281c43e99fdSEd Maste {
282c43e99fdSEd Maste struct win32op *win32op = base->evbase;
283c43e99fdSEd Maste int res = 0;
284c43e99fdSEd Maste unsigned j, i;
285c43e99fdSEd Maste int fd_count;
286c43e99fdSEd Maste SOCKET s;
287c43e99fdSEd Maste
288c43e99fdSEd Maste if (win32op->resize_out_sets) {
289c43e99fdSEd Maste size_t size = FD_SET_ALLOC_SIZE(win32op->num_fds_in_fd_sets);
290c43e99fdSEd Maste if (!(win32op->readset_out = mm_realloc(win32op->readset_out, size)))
291c43e99fdSEd Maste return (-1);
292c43e99fdSEd Maste if (!(win32op->exset_out = mm_realloc(win32op->exset_out, size)))
293c43e99fdSEd Maste return (-1);
294c43e99fdSEd Maste if (!(win32op->writeset_out = mm_realloc(win32op->writeset_out, size)))
295c43e99fdSEd Maste return (-1);
296c43e99fdSEd Maste win32op->resize_out_sets = 0;
297c43e99fdSEd Maste }
298c43e99fdSEd Maste
299c43e99fdSEd Maste fd_set_copy(win32op->readset_out, win32op->readset_in);
300c43e99fdSEd Maste fd_set_copy(win32op->exset_out, win32op->writeset_in);
301c43e99fdSEd Maste fd_set_copy(win32op->writeset_out, win32op->writeset_in);
302c43e99fdSEd Maste
303c43e99fdSEd Maste fd_count =
304c43e99fdSEd Maste (win32op->readset_out->fd_count > win32op->writeset_out->fd_count) ?
305c43e99fdSEd Maste win32op->readset_out->fd_count : win32op->writeset_out->fd_count;
306c43e99fdSEd Maste
307c43e99fdSEd Maste if (!fd_count) {
308c43e99fdSEd Maste long msec = tv ? evutil_tv_to_msec_(tv) : LONG_MAX;
309c43e99fdSEd Maste /* Sleep's DWORD argument is unsigned long */
310c43e99fdSEd Maste if (msec < 0)
311c43e99fdSEd Maste msec = LONG_MAX;
312c43e99fdSEd Maste /* Windows doesn't like you to call select() with no sockets */
313c43e99fdSEd Maste Sleep(msec);
314c43e99fdSEd Maste return (0);
315c43e99fdSEd Maste }
316c43e99fdSEd Maste
317c43e99fdSEd Maste EVBASE_RELEASE_LOCK(base, th_base_lock);
318c43e99fdSEd Maste
319c43e99fdSEd Maste res = select(fd_count,
320c43e99fdSEd Maste (struct fd_set*)win32op->readset_out,
321c43e99fdSEd Maste (struct fd_set*)win32op->writeset_out,
322c43e99fdSEd Maste (struct fd_set*)win32op->exset_out, tv);
323c43e99fdSEd Maste
324c43e99fdSEd Maste EVBASE_ACQUIRE_LOCK(base, th_base_lock);
325c43e99fdSEd Maste
326c43e99fdSEd Maste event_debug(("%s: select returned %d", __func__, res));
327c43e99fdSEd Maste
328c43e99fdSEd Maste if (res <= 0) {
329*b50261e2SCy Schubert event_debug(("%s: %s", __func__,
330*b50261e2SCy Schubert evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())));
331c43e99fdSEd Maste return res;
332c43e99fdSEd Maste }
333c43e99fdSEd Maste
334c43e99fdSEd Maste if (win32op->readset_out->fd_count) {
335c43e99fdSEd Maste i = evutil_weakrand_range_(&base->weakrand_seed,
336c43e99fdSEd Maste win32op->readset_out->fd_count);
337c43e99fdSEd Maste for (j=0; j<win32op->readset_out->fd_count; ++j) {
338c43e99fdSEd Maste if (++i >= win32op->readset_out->fd_count)
339c43e99fdSEd Maste i = 0;
340c43e99fdSEd Maste s = win32op->readset_out->fd_array[i];
341c43e99fdSEd Maste evmap_io_active_(base, s, EV_READ);
342c43e99fdSEd Maste }
343c43e99fdSEd Maste }
344c43e99fdSEd Maste if (win32op->exset_out->fd_count) {
345c43e99fdSEd Maste i = evutil_weakrand_range_(&base->weakrand_seed,
346c43e99fdSEd Maste win32op->exset_out->fd_count);
347c43e99fdSEd Maste for (j=0; j<win32op->exset_out->fd_count; ++j) {
348c43e99fdSEd Maste if (++i >= win32op->exset_out->fd_count)
349c43e99fdSEd Maste i = 0;
350c43e99fdSEd Maste s = win32op->exset_out->fd_array[i];
351c43e99fdSEd Maste evmap_io_active_(base, s, EV_WRITE);
352c43e99fdSEd Maste }
353c43e99fdSEd Maste }
354c43e99fdSEd Maste if (win32op->writeset_out->fd_count) {
355c43e99fdSEd Maste i = evutil_weakrand_range_(&base->weakrand_seed,
356c43e99fdSEd Maste win32op->writeset_out->fd_count);
357c43e99fdSEd Maste for (j=0; j<win32op->writeset_out->fd_count; ++j) {
358c43e99fdSEd Maste if (++i >= win32op->writeset_out->fd_count)
359c43e99fdSEd Maste i = 0;
360c43e99fdSEd Maste s = win32op->writeset_out->fd_array[i];
361c43e99fdSEd Maste evmap_io_active_(base, s, EV_WRITE);
362c43e99fdSEd Maste }
363c43e99fdSEd Maste }
364c43e99fdSEd Maste return (0);
365c43e99fdSEd Maste }
366c43e99fdSEd Maste
367c43e99fdSEd Maste void
win32_dealloc(struct event_base * base)368c43e99fdSEd Maste win32_dealloc(struct event_base *base)
369c43e99fdSEd Maste {
370c43e99fdSEd Maste struct win32op *win32op = base->evbase;
371c43e99fdSEd Maste
372c43e99fdSEd Maste evsig_dealloc_(base);
373c43e99fdSEd Maste if (win32op->readset_in)
374c43e99fdSEd Maste mm_free(win32op->readset_in);
375c43e99fdSEd Maste if (win32op->writeset_in)
376c43e99fdSEd Maste mm_free(win32op->writeset_in);
377c43e99fdSEd Maste if (win32op->readset_out)
378c43e99fdSEd Maste mm_free(win32op->readset_out);
379c43e99fdSEd Maste if (win32op->writeset_out)
380c43e99fdSEd Maste mm_free(win32op->writeset_out);
381c43e99fdSEd Maste if (win32op->exset_out)
382c43e99fdSEd Maste mm_free(win32op->exset_out);
383c43e99fdSEd Maste /* XXXXX free the tree. */
384c43e99fdSEd Maste
385c43e99fdSEd Maste memset(win32op, 0, sizeof(*win32op));
386c43e99fdSEd Maste mm_free(win32op);
387c43e99fdSEd Maste }
388c43e99fdSEd Maste
389c43e99fdSEd Maste #endif
390