aboutsummaryrefslogtreecommitdiff
path: root/src/lib/eventloop.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-08-15 12:17:25 +0000
committerChristian Grothoff <christian@grothoff.org>2011-08-15 12:17:25 +0000
commit3e6963bdcf7ebc28bcae5bce1e2544d19f491657 (patch)
treeb645cb6c94cce4e992416230e644052fee07893d /src/lib/eventloop.c
parentbcf7ddfdbd225608e84b892dd1a970400ba2371e (diff)
downloadgnunet-gtk-3e6963bdcf7ebc28bcae5bce1e2544d19f491657.tar.gz
gnunet-gtk-3e6963bdcf7ebc28bcae5bce1e2544d19f491657.zip
draft for new libgnunetgtk
Diffstat (limited to 'src/lib/eventloop.c')
-rw-r--r--src/lib/eventloop.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/src/lib/eventloop.c b/src/lib/eventloop.c
new file mode 100644
index 00000000..7eb7e060
--- /dev/null
+++ b/src/lib/eventloop.c
@@ -0,0 +1,809 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/lib/eventloop.c
23 * @brief code for merging GNUnet scheduler and Gtk Main Loop event loops
24 * @author Christian Grothoff
25 */
26#include "gnunet_gtk.h"
27
28
29/**
30 * Main context for our event loop.
31 */
32struct GNUNET_GTK_MainLoop
33{
34
35 /**
36 * Our configuration.
37 */
38 const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40 /**
41 * Name of the glade file for the main window
42 */
43 const char *main_window_file;
44
45 /**
46 * Initial task to run to setup the system.
47 */
48 GNUNET_SCHEDULER_Task main_task;
49
50 /**
51 * Builder for the main window.
52 */
53 GtkBuilder *builder;
54
55 /**
56 * Gib's Main loop.
57 */
58 GMainLoop *gml;
59
60 /**
61 * GTK's main context.
62 */
63 GMainContext *gmc;
64
65 /**
66 * Read set.
67 */
68 struct GNUNET_NETWORK_FDSet *rs;
69
70 /**
71 * Write set.
72 */
73 struct GNUNET_NETWORK_FDSet *ws;
74
75 /**
76 * Recycled array of polling descriptors.
77 */
78 GPollFD *cached_poll_array;
79
80 /**
81 * Size of the 'cached_poll_array'.
82 */
83 guint cached_poll_array_size;
84
85 /**
86 * Return value from last g_main_context_query call.
87 */
88 guint poll_array_active;
89
90 /**
91 * Maximum GTK priority.
92 */
93 gint max_priority;
94
95
96#if WINDOWS
97 /**
98 * Array to hold pipe handles during a select() call
99 */
100 struct GNUNET_DISK_FileHandle **read_array;
101
102 /**
103 * Allocated length of read_array
104 */
105 int read_array_length;
106
107 /**
108 * Event to fire when a socket is ready for reading
109 */
110 HANDLE hEventRead;
111
112 /**
113 * Event to fire when a socket is ready for writing
114 */
115 HANDLE hEventWrite;
116
117 /**
118 * Event to fire when a socket had an error
119 */
120 HANDLE hEventException;
121
122 /**
123 * Event that is permanently enabled and is used to signal a pipe
124 * that is ready for writing (asynchronous pipes are always writable)
125 */
126 HANDLE hEventPipeWrite;
127
128 /**
129 * Event that is permanently enabled and is used to signal a pipe
130 * that is ready for reading (used to wake up early on a pipe that
131 * is known to be readable)
132 */
133 HANDLE hEventReadReady;
134
135 /**
136 * A list to hold file handles that are ready for reading
137 */
138 struct GNUNET_CONTAINER_SList *handles_read;
139
140 /**
141 * A list to hold file handles that are ready for writing
142 */
143 struct GNUNET_CONTAINER_SList *handles_write;
144
145 /**
146 * A list to hold file handles that are broken
147 */
148 struct GNUNET_CONTAINER_SList *handles_except;
149#endif
150
151};
152
153
154/**
155 * Get the configuration.
156 *
157 * @param ml handle to the main loop
158 * @return handle to the configuration, never NULL
159 */
160const struct GNUNET_CONFIGURATION_Handle *
161GNUNET_GTK_main_loop_get_configuration (struct GNUNET_GTK_MainLoop *ml)
162{
163 return ml->cfg;
164}
165
166
167/**
168 * Trigger shutdown of the GUI and exit the main loop.
169 *
170 * @param ml handle to the main loop
171 */
172void
173GNUNET_GTK_main_loop_quit (struct GNUNET_GTK_MainLoop *ml)
174{
175 g_main_loop_quit (ml->gml);
176}
177
178
179/**
180 * Get an object from the main window.
181 *
182 * @param ml handle to the main loop
183 * @param name name of the object
184 * @return NULL on error, otherwise the object
185 */
186GObject *
187GNUNET_GTK_main_loop_get_object (struct GNUNET_GTK_MainLoop *ml,
188 const char *name)
189{
190 return gtk_builder_get_object (ml->builder, name);
191}
192
193
194/**
195 * Task to run Gtk events (within a GNUnet scheduler task).
196 *
197 * @param cls the main loop handle
198 * @param tc scheduler context
199 */
200static void
201gnunet_gtk_dispatch_task (void *cls,
202 const struct GNUNET_SCHEDULER_TaskContext *tc)
203{
204 struct GNUNET_GTK_MainLoop *ml = cls;
205 g_main_context_dispatch (ml->gmc);
206}
207
208
209#ifndef FD_COPY
210#define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set)))
211#endif
212
213/**
214 * Replacement for the GNUnet scheduler's "select" that integrates the
215 * Gtk event loop. We merge Gtk's events with those from GNUnet's
216 * scheduler and then use 'g_poll' on both. Then we process the Gtk
217 * events (by adding a task to do so to the GNUnet scheduler), and, if
218 * applicable, return the GNUnet-scheduler events back to GNUnet.
219 *
220 * @param cls the 'struct GNUNET_GTK_MainLoop'
221 * @param rfds set of sockets to be checked for readability
222 * @param wfds set of sockets to be checked for writability
223 * @param efds set of sockets to be checked for exceptions
224 * @param timeout relative value when to return
225 * @return number of selected sockets, GNUNET_SYSERR on error
226 */
227static int
228gnunet_gtk_select (void *cls,
229 struct GNUNET_NETWORK_FDSet *rfds,
230 struct GNUNET_NETWORK_FDSet *wfds,
231 struct GNUNET_NETWORK_FDSet *efds,
232 const struct GNUNET_TIME_Relative timeout)
233{
234 struct GNUNET_GTK_MainLoop *ml = cls;
235 int max_nfds;
236 gint poll_result;
237
238 GPollFD *gfds;
239 gint delay;
240
241 guint i;
242 guint fd_counter = 0;
243
244 guint allocated_nfds, need_gfds;
245
246 fd_set aread, awrite, aexcept;
247 int pre_ret = 0;
248 int result = 0;
249 int socks = 0;
250 int sock_read = 0, sock_write = 0, sock_err = 0;
251
252#if WINDOWS
253 int always_ready_write_fd = -1;
254
255 int select_ret = 0;
256
257 int read_handles = 0;
258 DWORD waitstatus;
259#endif
260
261 if (TRUE != g_main_loop_is_running (ml->gml))
262 return GNUNET_NETWORK_socket_select (rfds, wfds, efds, timeout);
263
264 FD_ZERO (&aread);
265 FD_ZERO (&awrite);
266 FD_ZERO (&aexcept);
267 if (rfds)
268 FD_COPY (&rfds->sds, &aread);
269 if (wfds)
270 FD_COPY (&wfds->sds, &awrite);
271 if (efds)
272 FD_COPY (&efds->sds, &aexcept);
273
274#if WINDOWS
275 ResetEvent (ml->hEventRead);
276 ResetEvent (ml->hEventWrite);
277 ResetEvent (ml->hEventException);
278
279 GNUNET_CONTAINER_slist_clear (ml->handles_read);
280 GNUNET_CONTAINER_slist_clear (ml->handles_write);
281 GNUNET_CONTAINER_slist_clear (ml->handles_except);
282#endif
283
284
285 if (rfds != NULL)
286 max_nfds = rfds->nsds;
287 else
288 max_nfds = -1;
289 if (wfds != NULL && max_nfds < wfds->nsds)
290 max_nfds = wfds->nsds;
291 if (efds != NULL && max_nfds < efds->nsds)
292 max_nfds = efds->nsds;
293
294 allocated_nfds = ml->cached_poll_array_size;
295 gfds = ml->cached_poll_array;
296 if (allocated_nfds == 0)
297 {
298 /* TODO: get some statistics, find the maximum number of fds ever
299 * polled during normal gnunet-gtk operation, and set this to that number.
300 */
301 ml->cached_poll_array = gfds = g_new (GPollFD, 30);
302 ml->cached_poll_array_size = allocated_nfds = 30;
303 }
304
305 while (1)
306 {
307 fd_counter = 0;
308#if !WINDOWS
309 gboolean need_realloc = FALSE;
310 for (i = 0; !need_realloc && i < max_nfds; i += 1)
311 {
312 int isset[3];
313
314 isset[0] = (rfds == NULL) ? 0 : FD_ISSET (i, &rfds->sds);
315 isset[1] = (wfds == NULL) ? 0 : FD_ISSET (i, &wfds->sds);
316 isset[2] = (efds == NULL) ? 0 : FD_ISSET (i, &efds->sds);
317 if (!isset[0] && !isset[1] && !isset[2])
318 continue;
319 if (fd_counter >= allocated_nfds)
320 {
321 need_realloc = TRUE;
322 break;
323 }
324 gfds[fd_counter].fd = i;
325 gfds[fd_counter].events = (isset[0] ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0)
326 | (isset[1] ? G_IO_OUT | G_IO_ERR : 0) | (isset[2] ? G_IO_ERR : 0);
327 fd_counter += 1;
328 }
329 if (need_realloc)
330 {
331 ml->cached_poll_array = gfds = g_renew (GPollFD, gfds, ml->cached_poll_array_size * 2);
332 ml->cached_poll_array_size = allocated_nfds = ml->cached_poll_array_size * 2;
333 fd_counter = 0;
334 need_realloc = FALSE;
335 }
336 else
337 break;
338#else
339 struct GNUNET_CONTAINER_SList_Iterator *t;
340 /* We might overshoot a little, but that won't hurt very much */
341 int need_nfds = (rfds->sds.fd_count + rfds->sds.fd_count + rfds->sds.fd_count > 0 ? 3 : 0)
342 + (rfds == NULL ? 0 : GNUNET_CONTAINER_slist_count (rfds->handles))
343 + (wfds == NULL ? 0 : 1)
344 + 1;
345 if (need_nfds >= allocated_nfds)
346 {
347 /* Since there are also gmainloop's own fds, just need_nfds won't be
348 * enough, so make it twice as long.
349 */
350 ml->cached_poll_array = gfds = g_renew (GPollFD, gfds, need_nfds * 2);
351 ml->cached_poll_array_size = allocated_nfds = need_nfds * 2;
352 }
353 if (ml->read_array_length < GNUNET_CONTAINER_slist_count (rfds->handles))
354 {
355 ml->read_array = GNUNET_realloc (ml->read_array, GNUNET_CONTAINER_slist_count (rfds->handles) * sizeof (struct GNUNET_DISK_FileHandle *));
356 ml->read_array_length = GNUNET_CONTAINER_slist_count (rfds->handles);
357 }
358 if (rfds != NULL)
359 {
360 for (t = GNUNET_CONTAINER_slist_begin (rfds->handles), i = 0;
361 GNUNET_CONTAINER_slist_end (t) != GNUNET_YES;
362 GNUNET_CONTAINER_slist_next (t), i += 1)
363 {
364 struct GNUNET_DISK_FileHandle *fh;
365 fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL);
366 if (fh->type == GNUNET_PIPE)
367 {
368 if (!ReadFile (fh->h, NULL, 0, NULL, fh->oOverlapRead))
369 {
370 DWORD error_code = GetLastError();
371 if (error_code == ERROR_IO_PENDING)
372 {
373#if DEBUG_NETWORK
374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the pipe's 0x%x overlapped event to the array as %d\n", fh->h, nhandles);
375#endif
376 gfds[fd_counter].fd = (intptr_t) fh->oOverlapRead->hEvent;
377 /* On W32 .events makes no sense - g_poll will just OR its
378 * contents into .revents when the .fd event fires.
379 * So we'll use it in the way that suits us the best.
380 */
381 gfds[fd_counter].events = G_IO_IN;
382 fd_counter += 1;
383 ml->read_array[read_handles] = fh;
384 read_handles += 1;
385 }
386 else
387 {
388 gfds[fd_counter].fd = (intptr_t) ml->hEventReadReady;
389 gfds[fd_counter].events = G_IO_HUP;
390 fd_counter += 1;
391 ml->read_array[read_handles] = fh;
392 read_handles += 1;
393 }
394 }
395 else
396 {
397#if DEBUG_NETWORK
398 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the read ready event to the array as %d\n", nhandles);
399#endif
400 gfds[fd_counter].fd = (intptr_t) ml->hEventReadReady;
401 gfds[fd_counter].events = G_IO_IN;
402 fd_counter += 1;
403 ml->read_array[read_handles] = fh;
404 read_handles += 1;
405 }
406 }
407 else
408 {
409 GNUNET_CONTAINER_slist_add (ml->handles_read,
410 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
411 fh, sizeof (struct GNUNET_DISK_FileHandle));
412 pre_ret += 1;
413 }
414 }
415 }
416 if (wfds != NULL && GNUNET_CONTAINER_slist_count (wfds->handles) > 0)
417 {
418 gfds[fd_counter].fd = (intptr_t) ml->hEventPipeWrite;
419 gfds[fd_counter].events = G_IO_OUT;
420 always_ready_write_fd = fd_counter;
421 fd_counter += 1;
422 }
423 if (efds != NULL)
424 {
425 for (t = GNUNET_CONTAINER_slist_begin (efds->handles), i = 0;
426 GNUNET_CONTAINER_slist_end (t) != GNUNET_YES;
427 GNUNET_CONTAINER_slist_next (t), i += 1)
428 {
429 struct GNUNET_DISK_FileHandle *fh;
430 DWORD dwBytes;
431 fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL);
432 if (fh->type == GNUNET_PIPE)
433 {
434 if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL))
435 {
436 GNUNET_CONTAINER_slist_add (ml->handles_except,
437 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
438 fh, sizeof (struct GNUNET_DISK_FileHandle));
439 pre_ret += 1;
440 }
441 }
442 }
443 }
444 GNUNET_CONTAINER_slist_iter_destroy (t);
445
446 if (rfds != NULL && rfds->sds.fd_count > 0)
447 {
448#if DEBUG_NETWORK
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket read event to the array as %d\n", fd_counter);
450#endif
451 gfds[fd_counter].fd = (intptr_t) ml->hEventRead;
452 gfds[fd_counter].events = G_IO_IN;
453 for (i = 0; i < rfds->sds.fd_count; i++)
454 WSAEventSelect (rfds->sds.fd_array[i], ml->hEventRead, FD_ACCEPT | FD_READ | FD_CLOSE);
455 fd_counter += 1;
456 sock_read = rfds->sds.fd_count;
457 }
458 if (wfds != NULL && wfds->sds.fd_count > 0)
459 {
460 int wakeup = 0;
461#if DEBUG_NETWORK
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket write event to the array as %d\n", fd_counter);
463#endif
464 gfds[fd_counter].fd = (intptr_t) ml->hEventWrite;
465 gfds[fd_counter].events = G_IO_OUT;
466 for (i = 0; i < wfds->sds.fd_count; i++)
467 {
468 DWORD error;
469 int status;
470 status = send (wfds->sds.fd_array[i], NULL, 0, 0);
471 error = GetLastError ();
472#if DEBUG_NETWORK
473 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre-send to the socket %d returned %d (%u)\n", i, status, error);
474#endif
475 if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN))
476 wakeup = 1;
477 WSAEventSelect (wfds->sds.fd_array[i], ml->hEventWrite, FD_WRITE | FD_CONNECT | FD_CLOSE);
478 }
479 if (wakeup)
480 SetEvent (ml->hEventWrite);
481 fd_counter += 1;
482 sock_write = wfds->sds.fd_count;
483 }
484 if (efds != NULL && efds->sds.fd_count > 0)
485 {
486#if DEBUG_NETWORK
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket error event to the array as %d\n", fd_counter);
488#endif
489 gfds[fd_counter].fd = (intptr_t) ml->hEventException;
490 gfds[fd_counter].events = G_IO_ERR;
491 for (i = 0; i < efds->sds.fd_count; i++)
492 WSAEventSelect (efds->sds.fd_array[i], ml->hEventException, FD_OOB | FD_CLOSE);
493 fd_counter += 1;
494 sock_err = efds->sds.fd_count;
495 }
496 break;
497#endif
498 }
499 socks = sock_read + sock_write + sock_err;
500
501 g_main_context_prepare (ml->gmc, &ml->max_priority);
502 while (allocated_nfds < (need_gfds = g_main_context_query (ml->gmc,
503 ml->max_priority, &delay, &gfds[fd_counter], allocated_nfds - fd_counter)))
504 {
505 ml->cached_poll_array = gfds = g_renew (GPollFD, gfds, allocated_nfds - fd_counter + need_gfds);
506 ml->cached_poll_array_size = allocated_nfds = allocated_nfds - fd_counter + need_gfds;
507 }
508 ml->poll_array_active = fd_counter + need_gfds;
509
510 if (timeout.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
511 {
512 if (delay >= 0)
513 delay = MIN(timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value, delay);
514 else
515 delay = timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value;
516 }
517
518 if (pre_ret > 0)
519 {
520#if DEBUG_NETWORK
521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre_ret is %d, setting delay to 0\n", pre_ret);
522#endif
523 delay = 0;
524 }
525
526#if DEBUG_NETWORK
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "We have %d of our FDs and %d of GMC ones, going to wait %6dms\n", fd_counter, need_gfds, delay);
528#endif
529
530 poll_result = g_poll (gfds, fd_counter + need_gfds, delay);
531
532#if DEBUG_NETWORK
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "g_poll returned : %d\n", poll_result);
534#endif
535
536 /* Take care of GUI events.
537 * Dispatching the events here will eventually crash the scheduler, must do this
538 * from within a task (currently we're not in a task, but in a select() call, remember)
539 * Startup reason is used to pass the scheduler sanity check.
540 */
541 if (TRUE == g_main_context_check (ml->gmc, ml->max_priority, &gfds[fd_counter], need_gfds))
542 GNUNET_SCHEDULER_add_continuation (gnunet_gtk_dispatch_task, ml,
543 GNUNET_SCHEDULER_REASON_STARTUP);
544
545#if !WINDOWS
546 if (rfds)
547 GNUNET_NETWORK_fdset_zero (rfds);
548 if (wfds)
549 GNUNET_NETWORK_fdset_zero (wfds);
550 if (efds)
551 GNUNET_NETWORK_fdset_zero (efds);
552 for (i = 0; i < fd_counter; i++)
553 {
554 int set[3];
555 if ((set[0] = FD_ISSET (gfds[i].fd, &aread)))
556 FD_SET (gfds[i].fd, &rfds->sds);
557 if ((set[1] = FD_ISSET (gfds[i].fd, &awrite)))
558 FD_SET (gfds[i].fd, &wfds->sds);
559 if ((set[2] = FD_ISSET (gfds[i].fd, &aexcept)))
560 FD_SET (gfds[i].fd, &efds->sds);
561 if (set[0] || set[1] || set[2])
562 result += 1;
563 }
564#else
565 if (socks > 0)
566 {
567 struct timeval tvslice;
568 tvslice.tv_sec = 0;
569 tvslice.tv_usec = 0;
570 select_ret = select (max_nfds, &aread, &awrite, &aexcept, &tvslice);
571 if (select_ret == -1)
572 select_ret = 0;
573#if DEBUG_NETWORK
574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "select() returned %d\n", select_ret);
575#endif
576 }
577 if (always_ready_write_fd >= 0 && gfds[always_ready_write_fd].revents & G_IO_OUT)
578 {
579 GNUNET_CONTAINER_slist_append (ml->handles_write, wfds->handles);
580 result += GNUNET_CONTAINER_slist_count (ml->handles_write);
581#if DEBUG_NETWORK
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added write pipe\n");
583#endif
584 }
585 for (i = 0; i < read_handles; i++)
586 {
587 DWORD error;
588 BOOL bret;
589 if (!(gfds[i].revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)))
590 continue;
591 SetLastError (0);
592 waitstatus = 0;
593 bret = PeekNamedPipe (ml->read_array[i]->h, NULL, 0, NULL, &waitstatus, NULL);
594 error = GetLastError ();
595#if DEBUG_NETWORK
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peek at read pipe %d (0x%x) returned %d (%d bytes available) GLE %u\n", i, ml->read_array[i]->h, bret, waitstatus, error);
597#endif
598 if (bret == 0 || (gfds[i].revents & G_IO_ERR))
599 {
600 if (efds != NULL)
601 {
602 struct GNUNET_CONTAINER_SList_Iterator *t;
603 for (t = GNUNET_CONTAINER_slist_begin (efds->handles), i = 0;
604 GNUNET_CONTAINER_slist_end (t) != GNUNET_YES;
605 GNUNET_CONTAINER_slist_next (t), i += 1)
606 {
607 struct GNUNET_DISK_FileHandle *fh;
608 fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL);
609 if (fh == ml->read_array[i])
610 {
611 GNUNET_CONTAINER_slist_add (ml->handles_except,
612 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
613 fh, sizeof (struct GNUNET_DISK_FileHandle));
614 break;
615 }
616 }
617 GNUNET_CONTAINER_slist_iter_destroy (t);
618 }
619 }
620 else if (waitstatus <= 0)
621 continue;
622 GNUNET_CONTAINER_slist_add (ml->handles_read,
623 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
624 ml->read_array[i], sizeof (struct GNUNET_DISK_FileHandle));
625 result += 1;
626#if DEBUG_NETWORK
627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe 0x%x (0x%x)\n", ml->read_array[i], ml->read_array[i]->h);
628#endif
629 }
630 waitstatus = WaitForSingleObject (ml->hEventWrite, 0);
631#if DEBUG_NETWORK
632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wait for the write event returned %d\n", waitstatus);
633#endif
634 if (waitstatus == WAIT_OBJECT_0)
635 {
636 for (i = 0; i < wfds->sds.fd_count; i++)
637 {
638 DWORD error;
639 int status;
640 int so_error = 0;
641 int sizeof_so_error = sizeof (so_error);
642 int gso_result = getsockopt (wfds->sds.fd_array[i], SOL_SOCKET, SO_ERROR, (char *) &so_error, &sizeof_so_error);
643 status = send (wfds->sds.fd_array[i], NULL, 0, 0);
644 error = GetLastError ();
645#if DEBUG_NETWORK
646 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "send to the socket %d returned %d (%u)\n", i, status, error);
647#endif
648 if (status == 0
649 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN)
650 || (status == -1 && gso_result == 0 && error == WSAENOTCONN && so_error == WSAECONNREFUSED))
651 {
652 FD_SET (wfds->sds.fd_array[i], &awrite);
653 result += 1;
654 }
655 }
656 }
657 if (rfds)
658 {
659 struct GNUNET_CONTAINER_SList_Iterator *t;
660 for (i = 0; i < rfds->sds.fd_count; i++)
661 WSAEventSelect (rfds->sds.fd_array[i], ml->hEventRead, 0);
662 for (t = GNUNET_CONTAINER_slist_begin (rfds->handles);
663 GNUNET_CONTAINER_slist_end (t) != GNUNET_YES;
664 GNUNET_CONTAINER_slist_next (t))
665 {
666 struct GNUNET_DISK_FileHandle *fh;
667 fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL);
668 if (fh->type == GNUNET_PIPE)
669 CancelIo (fh->h);
670 }
671 GNUNET_CONTAINER_slist_iter_destroy (t);
672 GNUNET_NETWORK_fdset_zero (rfds);
673 if (select_ret != -1 && socks > 0)
674 GNUNET_NETWORK_fdset_copy_native (rfds, &aread, select_ret);
675 GNUNET_CONTAINER_slist_append (rfds->handles, ml->handles_read);
676 }
677 if (wfds)
678 {
679 for (i = 0; i < wfds->sds.fd_count; i++)
680 WSAEventSelect (wfds->sds.fd_array[i], ml->hEventWrite, 0);
681 GNUNET_NETWORK_fdset_zero (wfds);
682 if (select_ret != -1 && socks > 0)
683 GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, select_ret);
684 GNUNET_CONTAINER_slist_append (wfds->handles, ml->handles_write);
685 }
686 if (efds)
687 {
688 for (i = 0; i < efds->sds.fd_count; i++)
689 WSAEventSelect (efds->sds.fd_array[i], ml->hEventException, 0);
690 GNUNET_NETWORK_fdset_zero (efds);
691 if (select_ret != -1 && socks > 0)
692 GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, select_ret);
693 GNUNET_CONTAINER_slist_append (efds->handles, ml->handles_except);
694 result += GNUNET_CONTAINER_slist_count (ml->handles_except);
695 }
696 if (fd_counter > 0)
697 /* This is not accurate (select_ret counts write-ready sockets,
698 * and result does as well. Anything out there actually cares
699 * about this?
700 */
701 return select_ret + result;
702 else
703 return 0;
704#endif
705 return result;
706}
707
708
709/**
710 * Actual main function run right after GNUnet's scheduler
711 * is initialized. Initializes up GTK and Glade and starts the
712 * combined event loop.
713 *
714 * @param cls the 'struct GNUNET_GTK_MainLoop'
715 * @param args leftover command line arguments (go to gtk)
716 * @param cfgfile name of the configuration file
717 * @param cfg handle to the configuration
718 */
719static void
720run_main_loop (void *cls,
721 char *const *args,
722 const char *cfgfile,
723 const struct GNUNET_CONFIGURATION_Handle *cfg)
724{
725 struct GNUNET_GTK_MainLoop *ml = cls;
726 int argc;
727
728 /* command-line processing for Gtk arguments */
729 argc = 0;
730 while (args[argc] != NULL) argc++;
731 gtk_init (&argc, (char ***) &args);
732
733 /* setup main context */
734 ml->builder = GNUNET_GTK_get_new_builder (ml->main_window_file);
735 if (ml->builder == NULL)
736 return;
737 ml->rs = GNUNET_NETWORK_fdset_create ();
738 ml->ws = GNUNET_NETWORK_fdset_create ();
739 ml->gml = g_main_loop_new (NULL, TRUE);
740 ml->gmc = g_main_loop_get_context (ml->gml);
741 ml->cfg = cfg;
742#if WINDOWS
743 ml->hEventRead = CreateEvent (NULL, TRUE, FALSE, NULL);
744 ml->hEventReadReady = CreateEvent (NULL, TRUE, TRUE, NULL);
745 ml->hEventWrite = CreateEvent (NULL, TRUE, FALSE, NULL);
746 ml->hEventException = CreateEvent (NULL, TRUE, FALSE, NULL);
747 ml->hEventPipeWrite = CreateEvent (NULL, TRUE, TRUE, NULL);
748 ml->handles_read = GNUNET_CONTAINER_slist_create ();
749 ml->handles_write = GNUNET_CONTAINER_slist_create ();
750 ml->handles_except = GNUNET_CONTAINER_slist_create ();
751 ml->read_array = NULL;
752 ml->read_array_length = 0;
753#endif
754
755 /* run main task of the application */
756 GNUNET_SCHEDULER_add_continuation (ml->main_task, ml,
757 GNUNET_SCHEDULER_REASON_STARTUP);
758
759 /* start the Gtk event loop */
760 GNUNET_assert (TRUE == g_main_context_acquire (ml->gmc));
761 GNUNET_SCHEDULER_set_select (&gnunet_gtk_select, ml);
762}
763
764
765/**
766 * Initialize the main loop.
767 *
768 * @param binary_name binary name
769 * @param binary_help help text for the binary
770 * @param argc number of command line options
771 * @param argv command line options
772 * @param options allowed command line options
773 * @param main_window_file glade file for the main window
774 * @param main_task first task to run, closure will be set to the 'struct GNUNET_GTK_MainLoop'
775 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. bad command-line options, etc)
776 */
777int
778GNUNET_GTK_main_loop_start (const char *binary_name,
779 const char *binary_help,
780 int argc,
781 char *const*argv,
782 struct GNUNET_GETOPT_CommandLineOption *options,
783 const char *main_window_file,
784 GNUNET_SCHEDULER_Task main_task)
785{
786 struct GNUNET_GTK_MainLoop ml;
787 int ret;
788
789 ml.main_window_file = main_window_file;
790 ret = GNUNET_PROGRAM_run (argc, argv,
791 binary_name,
792 "GTK GUI for GNUnet",
793 options,
794 &run_main_loop, &ml);
795 if (NULL != ml.cached_poll_array)
796 g_free (ml.cached_poll_array);
797 if (NULL != ml.rs)
798 GNUNET_NETWORK_fdset_destroy (ml.rs);
799 if (NULL != ml.ws)
800 GNUNET_NETWORK_fdset_destroy (ml.ws);
801 if (NULL != ml.builder)
802 g_object_unref (G_OBJECT (ml.builder));
803 if (NULL != ml.gml)
804 g_main_loop_unref (ml.gml);
805 return ret;
806}
807
808
809/* end of eventloop.c */