/* This file is part of GNUnet. (C) 2010, 2011 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/lib/eventloop.c * @brief code for merging GNUnet scheduler and Gtk Main Loop event loops * @author Christian Grothoff */ #include "gnunet_gtk.h" /** * Initial size of our poll array cache. * * TODO: get some statistics, find the maximum number of fds ever * polled during normal gnunet-gtk operation, and set this to that number. */ #define INITIAL_POLL_ARRAY_SIZE 30 /** * Main context for our event loop. */ struct GNUNET_GTK_MainLoop { /** * Our configuration (includes defaults from gnunet-gtk/config.d/ and gnunet/config.d/) */ struct GNUNET_CONFIGURATION_Handle *cfg; /** * Name of the glade file for the main window */ const char *main_window_file; /** * Initial task to run to setup the system. */ GNUNET_SCHEDULER_Task main_task; /** * Builder for the main window. */ GtkBuilder *builder; /** * Gib's Main loop. */ GMainLoop *gml; /** * GTK's main context. */ GMainContext *gmc; /** * Read set. */ struct GNUNET_NETWORK_FDSet *rs; /** * Write set. */ struct GNUNET_NETWORK_FDSet *ws; /** * Recycled array of polling descriptors. */ GPollFD *cached_poll_array; /** * Name of the configuration file. */ char *cfgfile; /** * Size of the 'cached_poll_array'. */ guint cached_poll_array_size; /** * Task we keep around just to keep the event loop running. */ GNUNET_SCHEDULER_TaskIdentifier dummy_task; #if WINDOWS /** * Array to hold pipe handles during a select() call */ struct GNUNET_DISK_FileHandle **read_array; /** * Allocated length of read_array */ int read_array_length; /** * Event to fire when a socket is ready for reading */ HANDLE hEventRead; /** * Event to fire when a socket is ready for writing */ HANDLE hEventWrite; /** * Event to fire when a socket had an error */ HANDLE hEventException; /** * Event that is permanently enabled and is used to signal a pipe * that is ready for writing (asynchronous pipes are always writable) */ HANDLE hEventPipeWrite; /** * Event that is permanently enabled and is used to signal a pipe * that is ready for reading (used to wake up early on a pipe that * is known to be readable) */ HANDLE hEventReadReady; /** * A list to hold file handles that are ready for reading */ struct GNUNET_CONTAINER_SList *handles_read; /** * A list to hold file handles that are ready for writing */ struct GNUNET_CONTAINER_SList *handles_write; /** * A list to hold file handles that are broken */ struct GNUNET_CONTAINER_SList *handles_except; #endif }; /** * Get the configuration. * * @param ml handle to the main loop * @return handle to the configuration, never NULL */ const struct GNUNET_CONFIGURATION_Handle * GNUNET_GTK_main_loop_get_configuration (struct GNUNET_GTK_MainLoop *ml) { return ml->cfg; } /** * Trigger shutdown of the GUI and exit the main loop. * * @param ml handle to the main loop */ void GNUNET_GTK_main_loop_quit (struct GNUNET_GTK_MainLoop *ml) { g_main_loop_quit (ml->gml); ml->gml = NULL; if (GNUNET_SCHEDULER_NO_TASK != ml->dummy_task) { GNUNET_SCHEDULER_cancel (ml->dummy_task); ml->dummy_task = GNUNET_SCHEDULER_NO_TASK; } } /** * Get the builder from the main window. * * @param ml handle to the main loop * @return NULL on error, otherwise the builder */ GtkBuilder * GNUNET_GTK_main_loop_get_builder (struct GNUNET_GTK_MainLoop *ml) { return ml->builder; } int GNUNET_GTK_main_loop_build_window (struct GNUNET_GTK_MainLoop *ml, gpointer data) { ml->builder = GNUNET_GTK_get_new_builder (ml->main_window_file, data); if (ml->builder == NULL) { GNUNET_GTK_main_loop_quit (ml); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Obtain the name of the configuration file that is being used. * * @param ml handle to the main loop * @return name of configuration file */ const char * GNUNET_GTK_main_loop_get_configuration_file (struct GNUNET_GTK_MainLoop *ml) { return ml->cfgfile; } /** * Get an object from the main window. * * @param ml handle to the main loop * @param name name of the object * @return NULL on error, otherwise the object */ GObject * GNUNET_GTK_main_loop_get_object (struct GNUNET_GTK_MainLoop * ml, const char *name) { return gtk_builder_get_object (ml->builder, name); } /** * Task to run Gtk events (within a GNUnet scheduler task). * * @param cls the main loop handle * @param tc scheduler context */ static void dispatch_gtk_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_GTK_MainLoop *ml = cls; g_main_context_dispatch (ml->gmc); } /** * Change the size of the cached poll array to the given value. * * @param ml main loop context with the cached poll array * @param new_size desired size of the cached poll array */ static void resize_cached_poll_array (struct GNUNET_GTK_MainLoop *ml, guint new_size) { if (NULL == ml->cached_poll_array) ml->cached_poll_array = g_new (GPollFD, new_size); else ml->cached_poll_array = g_renew (GPollFD, ml->cached_poll_array, new_size); ml->cached_poll_array_size = new_size; } /** * Dummy task to keep our scheduler running. */ static void keepalive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_GTK_MainLoop *ml = cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dummy task was scheduled\n"); ml->dummy_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &keepalive_task, ml); } #ifndef FD_COPY #define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set))) #endif /** * Replacement for the GNUnet scheduler's "select" that integrates the * Gtk event loop. We merge Gtk's events with those from GNUnet's * scheduler and then use 'g_poll' on both. Then we process the Gtk * events (by adding a task to do so to the GNUnet scheduler), and, if * applicable, return the GNUnet-scheduler events back to GNUnet. * * @param cls the 'struct GNUNET_GTK_MainLoop' * @param rfds set of sockets to be checked for readability * @param wfds set of sockets to be checked for writability * @param efds set of sockets to be checked for exceptions * @param timeout relative value when to return * @return number of selected sockets, GNUNET_SYSERR on error */ static int gnunet_gtk_select (void *cls, struct GNUNET_NETWORK_FDSet *rfds, struct GNUNET_NETWORK_FDSet *wfds, struct GNUNET_NETWORK_FDSet *efds, const struct GNUNET_TIME_Relative timeout) { struct GNUNET_GTK_MainLoop *ml = cls; int max_nfds; gint poll_result; gint delay = INT_MAX; int i; guint ui; guint fd_counter; guint need_gfds = 0; fd_set aread; fd_set awrite; fd_set aexcept; int result = 0; gint max_priority; #if WINDOWS int pre_ret = 0; int sock_read = 0; int sock_write = 0; int sock_err = 0; int socks; int always_ready_write_fd = -1; int select_ret = 0; int read_handles = 0; DWORD waitstatus; #endif if (ml->gml == NULL || TRUE != g_main_loop_is_running (ml->gml)) return GNUNET_NETWORK_socket_select (rfds, wfds, efds, timeout); if (NULL != rfds) FD_COPY (&rfds->sds, &aread); else FD_ZERO (&aread); if (NULL != wfds) FD_COPY (&wfds->sds, &awrite); else FD_ZERO (&awrite); if (NULL != efds) FD_COPY (&efds->sds, &aexcept); else FD_ZERO (&aexcept); #if WINDOWS ResetEvent (ml->hEventRead); ResetEvent (ml->hEventWrite); ResetEvent (ml->hEventException); GNUNET_CONTAINER_slist_clear (ml->handles_read); GNUNET_CONTAINER_slist_clear (ml->handles_write); GNUNET_CONTAINER_slist_clear (ml->handles_except); #endif max_nfds = -1; if (rfds != NULL) max_nfds = GNUNET_MAX (max_nfds, rfds->nsds); if (wfds != NULL) max_nfds = GNUNET_MAX (max_nfds, wfds->nsds); if (efds != NULL) max_nfds = GNUNET_MAX (max_nfds, efds->nsds); if (ml->cached_poll_array_size == 0) resize_cached_poll_array (ml, INITIAL_POLL_ARRAY_SIZE); #if !WINDOWS fd_counter = 0; for (i = 0; i < max_nfds; i++) { int isset[3]; isset[0] = (rfds == NULL) ? 0 : FD_ISSET (i, &rfds->sds); isset[1] = (wfds == NULL) ? 0 : FD_ISSET (i, &wfds->sds); isset[2] = (efds == NULL) ? 0 : FD_ISSET (i, &efds->sds); if ((!isset[0]) && (!isset[1]) && (!isset[2])) continue; if (fd_counter >= ml->cached_poll_array_size) resize_cached_poll_array (ml, ml->cached_poll_array_size * 2); ml->cached_poll_array[fd_counter].fd = i; ml->cached_poll_array[fd_counter].events = (isset[0] ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0) | (isset[1] ? G_IO_OUT | G_IO_ERR : 0) | (isset[2] ? G_IO_ERR : 0); fd_counter++; } #else { struct GNUNET_CONTAINER_SList_Iterator t; fd_counter = 0; /* We might overshoot a little, but that won't hurt very much */ int need_nfds = (rfds->sds.fd_count + rfds->sds.fd_count + rfds->sds.fd_count > 0 ? 3 : 0) + (rfds == NULL ? 0 : GNUNET_CONTAINER_slist_count (rfds-> handles)) + (wfds == NULL ? 0 : 1) + 1; if (need_nfds >= ml->cached_poll_array_size) resize_cached_poll_array (ml, need_nfds * 2); /* Since there are also gmainloop's own fds, just need_nfds won't be * enough, so make it twice as long. */ if (ml->read_array_length < GNUNET_CONTAINER_slist_count (rfds->handles)) { ml->read_array = GNUNET_realloc (ml->read_array, GNUNET_CONTAINER_slist_count (rfds->handles) * sizeof (struct GNUNET_DISK_FileHandle *)); ml->read_array_length = GNUNET_CONTAINER_slist_count (rfds->handles); } if (rfds != NULL) { for (t = GNUNET_CONTAINER_slist_begin (rfds->handles), i = 0; GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES; GNUNET_CONTAINER_slist_next (&t), i += 1) { struct GNUNET_DISK_FileHandle *fh = GNUNET_CONTAINER_slist_get (&t, NULL); if (fh->type == GNUNET_PIPE) { if (!ReadFile (fh->h, NULL, 0, NULL, fh->oOverlapRead)) { DWORD error_code = GetLastError (); if (error_code == ERROR_IO_PENDING) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the pipe's 0x%x overlapped event to the array as %d\n", fh->h, nhandles); ml->cached_poll_array[fd_counter].fd = (intptr_t) fh->oOverlapRead->hEvent; /* On W32 .events makes no sense - g_poll will just OR its * contents into .revents when the .fd event fires. * So we'll use it in the way that suits us the best. */ ml->cached_poll_array[fd_counter].events = G_IO_IN; fd_counter += 1; ml->read_array[read_handles] = fh; read_handles += 1; } else { ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventReadReady; ml->cached_poll_array[fd_counter].events = G_IO_HUP; fd_counter += 1; ml->read_array[read_handles] = fh; read_handles += 1; } } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the read ready event to the array as %d\n", nhandles); ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventReadReady; ml->cached_poll_array[fd_counter].events = G_IO_IN; fd_counter += 1; ml->read_array[read_handles] = fh; read_handles += 1; } } else { GNUNET_CONTAINER_slist_add (ml->handles_read, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, fh, sizeof (struct GNUNET_DISK_FileHandle)); pre_ret++; } } } if ((wfds != NULL) && (GNUNET_CONTAINER_slist_count (wfds->handles) > 0)) { ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventPipeWrite; ml->cached_poll_array[fd_counter].events = G_IO_OUT; always_ready_write_fd = fd_counter; fd_counter += 1; } if (efds != NULL) { for (t = GNUNET_CONTAINER_slist_begin (efds->handles), i = 0; GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES; GNUNET_CONTAINER_slist_next (&t), i += 1) { struct GNUNET_DISK_FileHandle *fh = GNUNET_CONTAINER_slist_get (&t, NULL); DWORD dwBytes; if (fh->type == GNUNET_PIPE) { if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) { GNUNET_CONTAINER_slist_add (ml->handles_except, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, fh, sizeof (struct GNUNET_DISK_FileHandle)); pre_ret++; } } } } if ((rfds != NULL) && (rfds->sds.fd_count > 0)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket read event to the array as %d\n", fd_counter); ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventRead; ml->cached_poll_array[fd_counter].events = G_IO_IN; for (i = 0; i < rfds->sds.fd_count; i++) WSAEventSelect (rfds->sds.fd_array[i], ml->hEventRead, FD_ACCEPT | FD_READ | FD_CLOSE); fd_counter += 1; sock_read = rfds->sds.fd_count; } if ((wfds != NULL) && (wfds->sds.fd_count > 0)) { int wakeup = 0; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket write event to the array as %d\n", fd_counter); ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventWrite; ml->cached_poll_array[fd_counter].events = G_IO_OUT; for (ui = 0; ui < wfds->sds.fd_count; ui++) { DWORD error; int status; status = send (wfds->sds.fd_array[ui], NULL, 0, 0); error = GetLastError (); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre-send to the socket %u returned %d (%u)\n", ui, status, error); if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN)) wakeup = 1; WSAEventSelect (wfds->sds.fd_array[ui], ml->hEventWrite, FD_WRITE | FD_CONNECT | FD_CLOSE); } if (wakeup) SetEvent (ml->hEventWrite); fd_counter += 1; sock_write = wfds->sds.fd_count; } if ((efds != NULL) && (efds->sds.fd_count > 0)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket error event to the array as %d\n", fd_counter); ml->cached_poll_array[fd_counter].fd = (intptr_t) ml->hEventException; ml->cached_poll_array[fd_counter].events = G_IO_ERR; for (ui = 0; ui < efds->sds.fd_count; ui++) WSAEventSelect (efds->sds.fd_array[ui], ml->hEventException, FD_OOB | FD_CLOSE); fd_counter++; sock_err = efds->sds.fd_count; } } socks = sock_read + sock_write + sock_err; #endif /* combine with Gtk events */ if (NULL != ml->gmc) { g_main_context_prepare (ml->gmc, &max_priority); while (1) { need_gfds = g_main_context_query (ml->gmc, max_priority, &delay, &ml->cached_poll_array[fd_counter], ml->cached_poll_array_size - fd_counter); if (ml->cached_poll_array_size >= need_gfds + fd_counter) break; resize_cached_poll_array (ml, fd_counter + need_gfds); } } if (timeout.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value) { if (delay >= 0) delay = GNUNET_MIN (timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value, delay); else delay = timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value; } #if WINDOWS if (pre_ret > 0) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre_ret is %d, setting delay to 0\n", pre_ret); delay = 0; } #endif 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); poll_result = g_poll (ml->cached_poll_array, fd_counter + need_gfds, delay); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "g_poll returned : %d\n", poll_result); if (-1 == poll_result) return GNUNET_SYSERR; /* Take care of GUI events. * Dispatching the events here will eventually crash the scheduler, must do this * from within a task (currently we're not in a task, but in a select() call, remember) * Startup reason is used to pass the scheduler sanity check. */ if (NULL != ml->gmc) { if (TRUE == g_main_context_check (ml->gmc, max_priority, &ml->cached_poll_array[fd_counter], need_gfds)) GNUNET_SCHEDULER_add_continuation_with_priority (&dispatch_gtk_task, ml, GNUNET_SCHEDULER_REASON_STARTUP, GNUNET_SCHEDULER_PRIORITY_UI); } /* Now map back GNUnet scheduler events ... */ #if !WINDOWS if (NULL != rfds) GNUNET_NETWORK_fdset_zero (rfds); if (NULL != wfds) GNUNET_NETWORK_fdset_zero (wfds); if (NULL != efds) GNUNET_NETWORK_fdset_zero (efds); for (ui = 0; ui < fd_counter; ui++) { int set = 0; if ((NULL != rfds) && (set |= (FD_ISSET (ml->cached_poll_array[ui].fd, &aread) && (0 != (ml->cached_poll_array[ui]. revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)))))) GNUNET_NETWORK_fdset_set_native (rfds, ml->cached_poll_array[ui].fd); if ((NULL != wfds) && (set |= (FD_ISSET (ml->cached_poll_array[ui].fd, &awrite) && (0 != (ml->cached_poll_array[ui].revents & (G_IO_OUT | G_IO_ERR)))))) GNUNET_NETWORK_fdset_set_native (wfds, ml->cached_poll_array[ui].fd); if ((NULL != efds) && (set |= (FD_ISSET (ml->cached_poll_array[ui].fd, &aexcept) && (0 != (ml->cached_poll_array[ui].revents & G_IO_ERR))))) GNUNET_NETWORK_fdset_set_native (efds, ml->cached_poll_array[ui].fd); if (set) result++; } #else if (socks > 0) { struct timeval tvslice; tvslice.tv_sec = 0; tvslice.tv_usec = 0; select_ret = select (max_nfds, &aread, &awrite, &aexcept, &tvslice); if (select_ret == -1) select_ret = 0; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "select() returned %d\n", select_ret); } if (always_ready_write_fd >= 0 && ml->cached_poll_array[always_ready_write_fd].revents & G_IO_OUT) { GNUNET_CONTAINER_slist_append (ml->handles_write, wfds->handles); result += GNUNET_CONTAINER_slist_count (ml->handles_write); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added write pipe\n"); } for (i = 0; i < read_handles; i++) { DWORD error; BOOL bret; if (!(ml->cached_poll_array[i].revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) continue; SetLastError (0); waitstatus = 0; bret = PeekNamedPipe (ml->read_array[i]->h, NULL, 0, NULL, &waitstatus, NULL); error = GetLastError (); 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); if (bret == 0 || (ml->cached_poll_array[i].revents & G_IO_ERR)) { if (efds != NULL) { struct GNUNET_CONTAINER_SList_Iterator t; for (t = GNUNET_CONTAINER_slist_begin (efds->handles), i = 0; GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES; GNUNET_CONTAINER_slist_next (&t), i += 1) { struct GNUNET_DISK_FileHandle *fh = GNUNET_CONTAINER_slist_get (&t, NULL); if (fh == ml->read_array[i]) { GNUNET_CONTAINER_slist_add (ml->handles_except, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, fh, sizeof (struct GNUNET_DISK_FileHandle)); break; } } } } else if (waitstatus <= 0) continue; GNUNET_CONTAINER_slist_add (ml->handles_read, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, ml->read_array[i], sizeof (struct GNUNET_DISK_FileHandle)); result += 1; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe 0x%x (0x%x)\n", ml->read_array[i], ml->read_array[i]->h); } waitstatus = WaitForSingleObject (ml->hEventWrite, 0); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wait for the write event returned %d\n", waitstatus); if (waitstatus == WAIT_OBJECT_0) { for (ui = 0; ui < wfds->sds.fd_count; ui++) { DWORD error; int status; int so_error = 0; int sizeof_so_error = sizeof (so_error); int gso_result = getsockopt (wfds->sds.fd_array[ui], SOL_SOCKET, SO_ERROR, (char *) &so_error, &sizeof_so_error); status = send (wfds->sds.fd_array[ui], NULL, 0, 0); error = GetLastError (); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "send to the socket %u returned %d (%u)\n", ui, status, error); if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN) || (status == -1 && gso_result == 0 && error == WSAENOTCONN && so_error == WSAECONNREFUSED)) { FD_SET (wfds->sds.fd_array[ui], &awrite); result += 1; } } } if (rfds) { struct GNUNET_CONTAINER_SList_Iterator t; for (ui = 0; ui < rfds->sds.fd_count; ui++) WSAEventSelect (rfds->sds.fd_array[ui], ml->hEventRead, 0); for (t = GNUNET_CONTAINER_slist_begin (rfds->handles); GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES; GNUNET_CONTAINER_slist_next (&t)) { struct GNUNET_DISK_FileHandle *fh = GNUNET_CONTAINER_slist_get (&t, NULL); if (fh->type == GNUNET_PIPE) CancelIo (fh->h); } GNUNET_NETWORK_fdset_zero (rfds); if (select_ret != -1 && socks > 0) GNUNET_NETWORK_fdset_copy_native (rfds, &aread, select_ret); GNUNET_CONTAINER_slist_append (rfds->handles, ml->handles_read); } if (wfds) { for (ui = 0; ui < wfds->sds.fd_count; ui++) WSAEventSelect (wfds->sds.fd_array[ui], ml->hEventWrite, 0); GNUNET_NETWORK_fdset_zero (wfds); if (select_ret != -1 && socks > 0) GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, select_ret); GNUNET_CONTAINER_slist_append (wfds->handles, ml->handles_write); } if (efds) { for (ui = 0; ui < efds->sds.fd_count; ui++) WSAEventSelect (efds->sds.fd_array[ui], ml->hEventException, 0); GNUNET_NETWORK_fdset_zero (efds); if (select_ret != -1 && socks > 0) GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, select_ret); GNUNET_CONTAINER_slist_append (efds->handles, ml->handles_except); result += GNUNET_CONTAINER_slist_count (ml->handles_except); } if (fd_counter > 0) /* This is not accurate (select_ret counts write-ready sockets, * and result does as well. Anything out there actually cares * about this? */ result = select_ret + result; else result = 0; #endif return result; } /** * Actual main function run right after GNUnet's scheduler * is initialized. Initializes up GTK and Glade and starts the * combined event loop. * * @param cls the 'struct GNUNET_GTK_MainLoop' * @param args leftover command line arguments (go to gtk) * @param cfgfile name of the configuration file * @param cfg handle to the configuration */ static void run_main_loop (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_GTK_MainLoop *ml = cls; struct GNUNET_CONFIGURATION_Handle *gcfg; char *baseconfig; char *ipath; int argc; /* command-line processing for Gtk arguments */ argc = 0; while (args[argc] != NULL) argc++; gtk_init (&argc, (char ***) &args); gcfg = GNUNET_CONFIGURATION_create (); ipath = GNUNET_GTK_installation_get_path (GNUNET_OS_IPK_DATADIR); if (ipath == NULL) { GNUNET_break (0); return; } GNUNET_asprintf (&baseconfig, "%s%s", ipath, "config.d"); GNUNET_free (ipath); (void) GNUNET_CONFIGURATION_load_from (gcfg, baseconfig); GNUNET_free (baseconfig); (void) GNUNET_CONFIGURATION_load (gcfg, cfgfile); ml->rs = GNUNET_NETWORK_fdset_create (); ml->ws = GNUNET_NETWORK_fdset_create (); ml->gml = g_main_loop_new (NULL, TRUE); ml->gmc = g_main_loop_get_context (ml->gml); ml->cfg = gcfg; ml->cfgfile = GNUNET_strdup (cfgfile); #if WINDOWS ml->hEventRead = CreateEvent (NULL, TRUE, FALSE, NULL); ml->hEventReadReady = CreateEvent (NULL, TRUE, TRUE, NULL); ml->hEventWrite = CreateEvent (NULL, TRUE, FALSE, NULL); ml->hEventException = CreateEvent (NULL, TRUE, FALSE, NULL); ml->hEventPipeWrite = CreateEvent (NULL, TRUE, TRUE, NULL); ml->handles_read = GNUNET_CONTAINER_slist_create (); ml->handles_write = GNUNET_CONTAINER_slist_create (); ml->handles_except = GNUNET_CONTAINER_slist_create (); ml->read_array = NULL; ml->read_array_length = 0; #endif /* run main task of the application */ GNUNET_SCHEDULER_add_continuation (ml->main_task, ml, GNUNET_SCHEDULER_REASON_STARTUP); /* start the Gtk event loop */ GNUNET_assert (TRUE == g_main_context_acquire (ml->gmc)); GNUNET_SCHEDULER_set_select (&gnunet_gtk_select, ml); /* keep Gtk event loop running even if there are no GNUnet tasks */ ml->dummy_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &keepalive_task, ml); } /** * Initialize the main loop. * * @param binary_name binary name * @param binary_help help text for the binary * @param argc number of command line options * @param argv command line options * @param options allowed command line options * @param main_window_file glade file for the main window * @param main_task first task to run, closure will be set to the 'struct GNUNET_GTK_MainLoop' * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. bad command-line options, etc) */ int GNUNET_GTK_main_loop_start (const char *binary_name, const char *binary_help, int argc, char *const *argv, struct GNUNET_GETOPT_CommandLineOption *options, const char *main_window_file, GNUNET_SCHEDULER_Task main_task) { struct GNUNET_GTK_MainLoop ml; int ret; memset (&ml, 0, sizeof (ml)); ml.main_window_file = main_window_file; ml.main_task = main_task; ret = GNUNET_PROGRAM_run (argc, argv, binary_name, binary_help, options, &run_main_loop, &ml); if (NULL != ml.cached_poll_array) g_free (ml.cached_poll_array); if (NULL != ml.rs) GNUNET_NETWORK_fdset_destroy (ml.rs); if (NULL != ml.ws) GNUNET_NETWORK_fdset_destroy (ml.ws); if (NULL != ml.builder) g_object_unref (G_OBJECT (ml.builder)); if (NULL != ml.gml) g_main_loop_unref (ml.gml); GNUNET_CONFIGURATION_destroy (ml.cfg); ml.cfg = NULL; GNUNET_free_non_null (ml.cfgfile); return ret; } /* end of eventloop.c */