/* Lightweight thread library for Objective Caml * http://www.ocsigen.org/lwt * Module Lwt_glib_stubs * Copyright (C) 2009-2011 Jérémie Dimino * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, with linking exceptions; * either version 2.1 of the License, or (at your option) any later * version. See COPYING file for details. * * This program 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "../unix/lwt_unix.h" GMainContext *gc; GPollFD *gpollfds = NULL; gint fds_count = 0; gint n_fds; gint max_priority; /* +-----------------------------------------------------------------+ | Polling | +-----------------------------------------------------------------+ */ CAMLprim value lwt_glib_poll(value val_fds, value val_count, value val_timeout) { gint timeout, lwt_timeout; long count; int i; GPollFD *gpollfd; gint events, revents; CAMLparam3(val_fds, val_count, val_timeout); CAMLlocal5(node, src, node_result, src_result, tmp); count = Long_val(val_count); g_main_context_dispatch(gc); g_main_context_prepare(gc, &max_priority); while (fds_count < count + (n_fds = g_main_context_query(gc, max_priority, &timeout, gpollfds, fds_count))) { free(gpollfds); fds_count = n_fds + count; gpollfds = lwt_unix_malloc(fds_count * sizeof (GPollFD)); } /* Clear all revents fields. */ for (i = 0; i < n_fds + count; i++) gpollfds[i].revents = 0; /* Add all Lwt fds. */ for (i = n_fds, node = val_fds; i < n_fds + count; i++, node = Field(node, 1)) { src = Field(node, 0); gpollfd = gpollfds + i; #if defined(LWT_ON_WINDOWS) gpollfd->fd = Handle_val(Field(src, 0)); #else gpollfd->fd = Int_val(Field(src, 0)); #endif events = 0; if (Bool_val(Field(src, 1))) events |= G_IO_IN; if (Bool_val(Field(src, 2))) events |= G_IO_OUT; gpollfd->events = events; } lwt_timeout = Int_val(val_timeout); if (timeout < 0 || (lwt_timeout >= 0 && lwt_timeout < timeout)) timeout = lwt_timeout; /* Do the blocking call. */ g_main_context_get_poll_func(gc)(gpollfds, n_fds + count, timeout); g_main_context_check(gc, max_priority, gpollfds, n_fds); /* Build the result. */ node_result = Val_int(0); for (i = n_fds, node = val_fds; i < n_fds + count; i++, node = Field(node, 1)) { src_result = caml_alloc_tuple(3); src = Field(node, 0); Field(src_result, 0) = Field(src, 0); revents = gpollfds[i].revents; Field(src_result, 1) = Val_bool(revents & G_IO_IN); Field(src_result, 2) = Val_bool(revents & G_IO_OUT); tmp = caml_alloc_tuple(2); Field(tmp, 0) = src_result; Field(tmp, 1) = node_result; node_result = tmp; } CAMLreturn(node_result); } /* +-----------------------------------------------------------------+ | Get sources | +-----------------------------------------------------------------+ */ #if defined(LWT_ON_WINDOWS) static value alloc_fd(HANDLE handle) { value res = win_alloc_handle(handle); int opt; int optlen = sizeof(opt); if (getsockopt((SOCKET)handle, SOL_SOCKET, SO_TYPE, (char *)&opt, &optlen) == 0) Descr_kind_val(res) = KIND_SOCKET; return res; } #endif CAMLprim value lwt_glib_get_sources() { gint timeout; int i; GPollFD *gpollfd; CAMLparam0(); CAMLlocal4(fd, fds, src, result); g_main_context_dispatch(gc); g_main_context_prepare(gc, &max_priority); while (fds_count < (n_fds = g_main_context_query(gc, max_priority, &timeout, gpollfds, fds_count))) { free(gpollfds); fds_count = n_fds; gpollfds = lwt_unix_malloc(fds_count * sizeof (GPollFD)); } fds = caml_alloc_tuple(n_fds); for (i = 0; i < n_fds; i++) { gpollfd = gpollfds + i; gpollfd->revents = 0; #if defined(LWT_ON_WINDOWS) /* On windows, glib file descriptors are handles */ fd = alloc_fd((HANDLE)gpollfd->fd); #else fd = Val_int(gpollfd->fd); #endif src = caml_alloc_tuple(3); Field(src, 0) = fd; Field(src, 1) = Val_bool(gpollfd->events & G_IO_IN); Field(src, 2) = Val_bool(gpollfd->events & G_IO_OUT); Field(fds, i) = src; } result = caml_alloc_tuple(2); Store_field(result, 0, fds); Store_field(result, 1, caml_copy_double(timeout * 1e-3)); CAMLreturn(result); } /* +-----------------------------------------------------------------+ | Marking | +-----------------------------------------------------------------+ */ CAMLprim value lwt_glib_mark_readable(value i) { gpollfds[Int_val(i)].revents |= G_IO_IN; return Val_unit; } CAMLprim value lwt_glib_mark_writable(value i) { gpollfds[Int_val(i)].revents |= G_IO_OUT; return Val_unit; } /* +-----------------------------------------------------------------+ | Check | +-----------------------------------------------------------------+ */ CAMLprim value lwt_glib_check() { g_main_context_check(gc, max_priority, gpollfds, n_fds); return Val_unit; } /* +-----------------------------------------------------------------+ | Initialization/stopping | +-----------------------------------------------------------------+ */ CAMLprim value lwt_glib_init() { gc = g_main_context_default(); g_main_context_ref(gc); return Val_unit; } CAMLprim value lwt_glib_stop() { g_main_context_unref(gc); return Val_unit; } /* +-----------------------------------------------------------------+ | Misc | +-----------------------------------------------------------------+ */ CAMLprim value lwt_glib_iter(value may_block) { GMainContext *gc; gint max_priority, timeout; GPollFD *pollfds = NULL; gint pollfds_size = 0; gint nfds; gint i; /* Get the main context. */ gc = g_main_context_default(); /* Try to acquire it. */ if (!g_main_context_acquire(gc)) caml_failwith("Lwt_glib.iter"); /* Dispatch pending events. */ g_main_context_dispatch(gc); /* Prepare the context for polling. */ g_main_context_prepare(gc, &max_priority); /* Get all file descriptors to poll. */ while (pollfds_size < (nfds = g_main_context_query(gc, max_priority, &timeout, pollfds, pollfds_size))) { free(pollfds); pollfds_size = nfds; pollfds = lwt_unix_malloc(pollfds_size * sizeof (GPollFD)); } /* Clear all revents fields. */ for (i = 0; i < nfds; i++) pollfds[i].revents = 0; /* Set the timeout to 0 if we do not want to block. */ if (!Bool_val(may_block)) timeout = 0; /* Do the blocking call. */ caml_enter_blocking_section(); g_main_context_get_poll_func(gc)(pollfds, nfds, timeout); caml_leave_blocking_section(); /* Let glib parse the result. */ g_main_context_check(gc, max_priority, pollfds, nfds); /* Release the context. */ g_main_context_release(gc); free(pollfds); return Val_unit; } CAMLprim value lwt_glib_wakeup() { g_main_context_wakeup(g_main_context_default()); return Val_unit; }