diff options
Diffstat (limited to 'src/lib/eventloop.c')
-rw-r--r-- | src/lib/eventloop.c | 809 |
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 | */ | ||
32 | struct 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 | */ | ||
160 | const struct GNUNET_CONFIGURATION_Handle * | ||
161 | GNUNET_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 | */ | ||
172 | void | ||
173 | GNUNET_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 | */ | ||
186 | GObject * | ||
187 | GNUNET_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 | */ | ||
200 | static void | ||
201 | gnunet_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 | */ | ||
227 | static int | ||
228 | gnunet_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 | */ | ||
719 | static void | ||
720 | run_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 | */ | ||
777 | int | ||
778 | GNUNET_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 */ | ||