diff -ur gambc40b14/configure.ac gambc40b14.new/configure.ac --- gambc40b14/configure.ac 2005-07-14 08:28:50.000000000 +0100 +++ gambc40b14.new/configure.ac 2005-08-19 14:27:17.668415996 +0100 @@ -232,6 +232,21 @@ ############################################################################### # +# Check if libevent should be used + +AC_ARG_ENABLE(libevent, + AC_HELP_STRING([--enable-libevent], + [use libevent for faster IO on platforms which support it (default is NO)]), + ENABLE_LIBEVENT=$enableval, + ENABLE_LIBEVENT=no) + +if test "$ENABLE_LIBEVENT" = yes; then + AC_DEFINE(LIBEVENT) + AC_SEARCH_LIBS(event_abilities, event) +fi + +############################################################################### +# # Check if a shared library must be built. AC_ARG_ENABLE(shared, diff -ur gambc40b14/include/config.h.in gambc40b14.new/include/config.h.in --- gambc40b14/include/config.h.in 2005-01-04 16:47:42.000000000 +0000 +++ gambc40b14.new/include/config.h.in 2005-08-19 14:27:47.596514964 +0100 @@ -25,6 +25,9 @@ /* Define as 1 if you want to compile as a shared library. */ #undef ___SHARED +/* Define asd 1 if you want to use libevent */ +#undef LIBEVENT + /* Define appropriately if "void*" is not the same width as "long". */ #undef ___VOIDSTAR_WIDTH diff -ur gambc40b14/lib/os_io.c gambc40b14.new/lib/os_io.c --- gambc40b14/lib/os_io.c 2005-07-13 22:12:17.000000000 +0100 +++ gambc40b14.new/lib/os_io.c 2005-08-19 14:27:20.532851205 +0100 @@ -18,6 +18,10 @@ #include "setup.h" #include "c_intf.h" +#ifdef LIBEVENT +#include +#endif + /*---------------------------------------------------------------------------*/ @@ -694,6 +698,101 @@ /* Generic device operations. */ +/* Event handling in Gambit: + * + * When using select or Win32 APIs the following function (___device_select) + * is passed an array of device structs. The first nb_read_devs of these are + * waiting for read events and the rest are waiting for writes. This function + * performs a virtual call on each one (there's a table of function pointers + * in the struct) and those calls callback zero of more helper functions, + * below. These set events in the select bitmaps (or Win32 equiv) or update + * the timeout value. (___device_select_add_timeout, ___device_select_add_fd). + * + * A select is then performed on those values and then a second pass is made on + * each device. This uses the same virtual function call, but sets a flag to + * indicate that the device should check the select bitmaps for interesting + * events. If the device considers itself ready it sets it's pointer in the + * devs struct to NULL. + * + * These NULL values are picked up in ___os_condvar_select. See the comments + * above that function. + * + * Edge IO + * ------- + * + * Modern kernels allow edge triggered IO. See man epoll(4) (on Linux systems, + * otherwise google for it) for details on the difference between edge and + * level triggered IO. + * + * Edge io is performed using libevent[1], an abstraction layer which allows + * us to use epoll, kqueue (BSD) or (in the future) rt signals. At the time of + * writing edge support in libevent requires a small patch and only epoll is + * supported. + * [1] http://www.monkey.org/~provos + * + * In edge io mode this function does very little more than call the libevent + * dispatch handler. Rather than poll all the devices and ask them to set bits + * in a bitmap, the devices are responsible for making libevent calls in order + * to register persistant callbacks for fd and timeout events. + * + * This has several effects: + * With select several devices can be waiting on the same fd since setting + * the bit twice in the bitmap is a nop. However, with libevent the second + * callback will override the first. (however, separate callbacks for read + * and write events are possible). + */ + +#ifdef LIBEVENT +/* This is the callback function, called by libevent, whenever a device_stream + * has an event. */ +static void +device_stream_event_callback(int fd, short events, void *arg) { + ___device *d = ___CAST(___device *, arg); + + if (events & EV_READ) d->read_ready = 1; + if (events & EV_WRITE) d->write_ready = 1; + if (events & EV_TIMEOUT) { + d->read_ready = 1; + d->write_ready = 1; + } +} +#endif + +#ifdef LIBEVENT + +/* This callback is used for registering a timeout event in ___device_select + */ +static void +null_callback(int fd, short events, void *arg) {} + +___SCMOBJ ___device_select + ___P((___time timeout), + (timeout) +___time timeout;) +{ + if (timeout < 0) { + event_loop(EVLOOP_NONBLOCK); + } else if (timeout == ___time_mod.time_pos_infinity) { + event_loop(EVLOOP_ONCE); + } else { + ___time delta; + struct timeval tv, *p_tv = &tv; + struct event timeout_event; + + ___absolute_time_to_relative_time(timeout, &delta); + ___absolute_time_to_nonnegative_timeval(delta, &p_tv); + event_set(&timeout_event, -1, EV_TIMEOUT, null_callback, NULL); + event_add(&timeout_event, p_tv); + + event_loop(EVLOOP_ONCE); + event_del(&timeout_event); + } + + return ___FIX(___NO_ERR); +} + +#else /* not LIBEVENT */ + ___SCMOBJ ___device_select ___P((___device **devs, int nb_read_devs, @@ -1054,6 +1153,8 @@ #endif +#endif /* LIBEVENT */ + ___SCMOBJ ___device_flush_write ___P((___device *self), @@ -1182,7 +1283,11 @@ return e; devs[0] = self; + /* This appears to wait for the device to really close, but there + * seems to be very little point. We can't get any using error + * information from this can we? --agl e = ___device_select (devs, 1, 0, ___time_mod.time_pos_infinity); + */ if (e != ___FIX(___NO_ERR)) return e; } @@ -1196,7 +1301,9 @@ return e; devs[0] = self; + /* see above comments e = ___device_select (devs, 0, 1, ___time_mod.time_pos_infinity); + */ if (e != ___FIX(___NO_ERR)) return e; } @@ -1221,6 +1328,7 @@ return ___TIMER_KIND; } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ device_timer_select_virt ___P((___device *self, ___BOOL for_writing, @@ -1257,6 +1365,7 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ device_timer_release_virt ___P((___device *self), @@ -1282,6 +1391,9 @@ ___device *self; int direction;) { +#ifdef LIBEVENT + event_del(&self->ev); +#endif return ___FIX(___NO_ERR); } @@ -1289,7 +1401,9 @@ { { device_timer_kind, +#ifndef LIBEVENT device_timer_select_virt, +#endif device_timer_release_virt, device_timer_flush_write_virt, device_timer_close_virt @@ -1335,6 +1449,16 @@ ___device_timer *dev; ___time expiry;) { +#ifdef LIBEVENT + struct timeval tv, *p_tv = &tv; + + /* Note that this works because of the layout of this device structure + * means that it's a perfectly good device_stream as well --agl */ + ___absolute_time_to_nonnegative_timeval(expiry, &p_tv); + event_set(&dev->base.ev, -1, EV_TIMEOUT, device_stream_event_callback, dev); + event_add(&dev->base.ev, p_tv); +#endif + dev->expiry = expiry; } @@ -1479,6 +1603,7 @@ #endif +#ifndef LIBEVENT ___SCMOBJ ___device_stream_select_virt ___P((___device *self, ___BOOL for_writing, @@ -1547,6 +1672,7 @@ pass, state); } +#endif /* !LIBEVENT */ ___SCMOBJ ___device_stream_release_virt @@ -1916,6 +2042,11 @@ dev->base.direction = direction; dev->base.read_stage = ___STAGE_CLOSED; dev->base.write_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + /* By doing this we make sure that we always catch every event */ + dev->base.read_ready = 1; + dev->base.write_ready = 1; +#endif #ifdef USE_PUMPS dev->read_pump = NULL; @@ -1995,6 +2126,10 @@ pid_t pid; /* pid of the process */ int fd_stdin; /* file descriptor corresponding to process' stdin */ int fd_stdout; /* file descriptor corresponding to process' stdout */ +#ifdef LIBEVENT + /* The struct event in the ___device struct is used for stdin */ + struct event stdout_event; +#endif } ___device_process; typedef struct ___device_process_vtbl_struct @@ -2021,11 +2156,19 @@ ___device_process *d = ___CAST(___device_process*,self); int is_not_closed = 0; - if (d->base.base.read_stage != ___STAGE_CLOSED) + if (d->base.base.read_stage != ___STAGE_CLOSED) { +#ifdef LIBEVENT + d->base.base.read_ready = 0; +#endif is_not_closed |= ___DIRECTION_RD; + } - if (d->base.base.write_stage != ___STAGE_CLOSED) + if (d->base.base.write_stage != ___STAGE_CLOSED) { +#ifdef LIBEVENT + d->base.base.write_ready = 0; +#endif is_not_closed |= ___DIRECTION_WR; + } if (is_not_closed == 0) return ___FIX(___NO_ERR); @@ -2035,6 +2178,9 @@ /* Close process' stdout */ d->base.base.read_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.read_ready = 0; +#endif if (close (d->fd_stdout) < 0) return err_code_from_errno (); @@ -2045,6 +2191,9 @@ /* Close process' stdin */ d->base.base.write_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.write_ready = 0; +#endif if (close (d->fd_stdin) < 0) return err_code_from_errno (); @@ -2054,6 +2203,9 @@ d->base.base.write_stage == ___STAGE_CLOSED) { int status; +#ifdef LIBEVENT + event_del(&d->base.base.ev); +#endif if (waitpid (d->pid, &status, 0) < 0) return err_code_from_errno (); } @@ -2061,6 +2213,7 @@ return ___FIX(___NO_ERR); } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_process_select_raw_virt ___P((___device_stream *self, ___BOOL for_writing, @@ -2110,6 +2263,7 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ ___device_process_release_raw_virt ___P((___device_stream *self), @@ -2182,10 +2336,16 @@ #endif if (errno == EIO) errno = EAGAIN; +#ifdef LIBEVENT + if (errno == EAGAIN) d->base.base.read_ready = 0; +#endif return err_code_from_errno (); } *len_done = n; +#ifdef LIBEVENT + if (n < len) d->base.base.read_ready = 0; +#endif return ___FIX(___NO_ERR); } @@ -2219,11 +2379,17 @@ errno); #endif +#ifdef LIBEVENT + if (errno == EAGAIN) d->base.base.write_ready = 0; +#endif return err_code_from_errno (); } *len_done = n; +#ifdef LIBEVENT + if (n < len) d->base.base.write_ready = 0; +#endif return ___FIX(___NO_ERR); } @@ -2271,12 +2437,16 @@ { { ___device_process_kind, +#ifndef LIBEVENT ___device_stream_select_virt, +#endif ___device_stream_release_virt, ___device_stream_flush_write_virt, ___device_process_close_virt }, +#ifndef LIBEVENT ___device_process_select_raw_virt, +#endif ___device_process_release_raw_virt, ___device_process_flush_write_raw_virt, ___device_process_seek_raw_virt, @@ -2334,6 +2504,14 @@ d->pid = pid; d->fd_stdin = fd_stdin; d->fd_stdout = fd_stdout; +#ifdef LIBEVENT + event_set(&d->base.base.ev, d->fd_stdin, EV_WRITE | EV_EDGE | EV_PERSIST, + device_stream_event_callback, &d->base); + event_add(&d->base.base.ev, NULL); + event_set(&d->stdout_event, d->fd_stdout, EV_READ | EV_EDGE | EV_PERSIST, + device_stream_event_callback, &d->base.base); + event_add(&d->stdout_event, NULL); +#endif *dev = d; @@ -2479,6 +2657,11 @@ d->base.base.read_stage = ___STAGE_CLOSED; /* avoid multiple closes */ d->base.base.write_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.read_ready = 0; + d->base.base.write_ready = 0; + event_del(&d->base.base.ev); +#endif #ifdef USE_WIN32 @@ -2502,6 +2685,9 @@ } d->base.base.read_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.read_ready = 0; +#endif } else if (direction & ___DIRECTION_WR) { @@ -2515,11 +2701,15 @@ } d->base.base.write_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.write_ready = 0; +#endif } return ___FIX(___NO_ERR); } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_tcp_client_select_raw_virt ___P((___device_stream *self, ___BOOL for_writing, @@ -2636,6 +2826,20 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ + +#ifdef LIBEVENT +___HIDDEN void ___device_tcp_client_timeout + ___P((___device_stream *self), + (self) +___device_stream *self;) +{ + ___device_tcp_client *d = ___CAST(___device_tcp_client*,self); + if (d->try_connect_again) d->try_connect_again = 2; + d->base.base.read_ready = 1; + d->base.base.write_ready = 1; +} +#endif ___HIDDEN ___SCMOBJ ___device_tcp_client_release_raw_virt ___P((___device_stream *self), @@ -2698,15 +2902,31 @@ return ERR_CODE_FROM_SOCKET_CALL; } - if (SOCKET_CALL_ERROR(n = recv (d->s, ___CAST(char*,buf), len, 0))) +#ifdef LIBEVENT + if (!d->base.base.read_ready) { + /* If we know that the kernel buffer is almost certainly empty (because + * we just got a short read then we don't bother with another read call + * just to tell us EAGAIN */ + errno = EAGAIN; + return ERR_CODE_FROM_SOCKET_CALL; + } +#endif + + if (SOCKET_CALL_ERROR(n = recv (d->s, ___CAST(char*,buf), len, 0))) { +#ifdef LIBEVENT + if (errno == EAGAIN) d->base.base.read_ready = 0; +#endif return ERR_CODE_FROM_SOCKET_CALL; + } *len_done = n; +#ifdef LIBEVENT + if (n < len) d->base.base.read_ready = 0; +#endif return ___FIX(___NO_ERR); } - ___HIDDEN ___SCMOBJ ___device_tcp_client_write_raw_virt ___P((___device_stream *self, ___U8 *buf, @@ -2738,15 +2958,31 @@ return ERR_CODE_FROM_SOCKET_CALL; } - if (SOCKET_CALL_ERROR(n = send (d->s, ___CAST(char*,buf), len, 0))) +#ifdef LIBEVENT + if (!d->base.base.write_ready) { + /* If we know that the kernel buffer is almost certainly full (because + * we just got a short write then we don't bother with another write call + * just to tell us EAGAIN */ + errno = EAGAIN; + return ERR_CODE_FROM_SOCKET_CALL; + } +#endif + + if (SOCKET_CALL_ERROR(n = send (d->s, ___CAST(char*,buf), len, 0))) { +#ifdef LIBEVENT + if (errno == EAGAIN) d->base.base.read_ready = 0; +#endif return ERR_CODE_FROM_SOCKET_CALL; + } *len_done = n; +#ifdef LIBEVENT + if (n < len) d->base.base.write_ready = 0; +#endif return ___FIX(___NO_ERR); } - ___HIDDEN ___SCMOBJ ___device_tcp_client_width_virt ___P((___device_stream *self), (self) @@ -2791,12 +3027,16 @@ { { ___device_tcp_client_kind, +#ifndef LIBEVENT ___device_stream_select_virt, +#endif ___device_stream_release_virt, ___device_stream_flush_write_virt, ___device_tcp_client_close_virt }, +#ifndef LIBEVENT ___device_tcp_client_select_raw_virt, +#endif ___device_tcp_client_release_raw_virt, ___device_tcp_client_flush_write_raw_virt, ___device_tcp_client_seek_raw_virt, @@ -2886,6 +3126,12 @@ d->server_addrlen = server_addrlen; d->try_connect_again = try_connect_again; +#ifdef LIBEVENT + event_set(&d->base.base.ev, s, EV_READ | EV_WRITE | EV_EDGE | EV_PERSIST, + device_stream_event_callback, &d->base); + event_add(&d->base.base.ev, NULL); +#endif + #ifdef USE_WIN32 d->io_event = @@ -3046,9 +3292,16 @@ return ERR_CODE_FROM_SOCKET_CALL; } +#ifdef LIBEVENT + d->base.read_ready = 0; + d->base.write_ready = 0; + event_del(&d->base.ev); +#endif + return ___FIX(___NO_ERR); } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_tcp_server_select_virt ___P((___device *self, ___BOOL for_writing, @@ -3122,6 +3375,7 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ ___device_tcp_server_release_virt ___P((___device *self), @@ -3143,7 +3397,9 @@ { { ___device_tcp_server_kind, +#ifndef LIBEVENT ___device_tcp_server_select_virt, +#endif ___device_tcp_server_release_virt, ___device_tcp_server_flush_write_virt, ___device_tcp_server_close_virt @@ -3234,6 +3490,14 @@ *dev = d; device_add_to_group (dgroup, &d->base); + +#ifdef LIBEVENT + event_set(&d->base.ev, s, EV_READ | EV_WRITE | EV_EDGE | EV_PERSIST, + device_stream_event_callback, &d->base); + event_add(&d->base.ev, NULL); + d->base.read_ready = 1; + d->base.write_ready = 1; +#endif return ___FIX(___NO_ERR); } @@ -3261,8 +3525,12 @@ if (SOCKET_CALL_ERROR2(s = accept (dev->s, ___CAST(struct sockaddr*,&addr), - &addrlen))) + &addrlen))) { +#ifdef LIBEVENT + if (errno == EAGAIN) dev->base.read_ready = 0; +#endif return ERR_CODE_FROM_SOCKET_CALL; + } return ___device_tcp_client_setup_from_socket (client, @@ -3340,6 +3608,7 @@ return ___FIX(___NO_ERR); } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_directory_select_virt ___P((___device *self, ___BOOL for_writing, @@ -3369,6 +3638,7 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ ___device_directory_release_virt ___P((___device *self), @@ -3390,7 +3660,9 @@ { { ___device_directory_kind, +#ifndef LIBEVENT ___device_directory_select_virt, +#endif ___device_directory_release_virt, ___device_directory_flush_write_virt, ___device_directory_close_virt @@ -3667,6 +3939,7 @@ return ___FIX(___NO_ERR); } +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_file_select_raw_virt ___P((___device_stream *self, ___BOOL for_writing, @@ -3732,6 +4005,7 @@ return ___FIX(___NO_ERR); } +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ ___device_file_release_raw_virt ___P((___device_stream *self), @@ -3971,12 +4245,16 @@ { { ___device_file_kind, +#ifndef LIBEVENT ___device_stream_select_virt, +#endif ___device_stream_release_virt, ___device_stream_flush_write_virt, ___device_file_close_virt }, +#ifndef LIBEVENT ___device_file_select_raw_virt, +#endif ___device_file_release_raw_virt, ___device_file_flush_write_raw_virt, ___device_file_seek_raw_virt, @@ -4020,6 +4298,12 @@ d->fd = fd; *dev = d; + + /* Note that the Linux VFS (at least) is not nonblocking in + * normal operation, therefore waiting for events on those + * fds is pointless - they are `always' ready for reading and + * writing. The device_stream_setup call will set the read_ready + * and write_ready flags to 1 and they'll stay that way --agl */ return ___device_stream_setup (&d->base, @@ -5833,6 +6117,24 @@ /* Waiting for I/O to become possible on a set of devices. */ +/* This is the main entry function for event handling. It's + * called in thread-schedule! and is passed the run-queue. + * The run-queue is a circular linked-list of threads + * with the condvars which they are waiting on and the + * assoc devices. + * + * The ___FIX(1) of the condvar is set if the device is + * ready for the operation requested, which is determined by + * the ___FIX(2) bit. 1 == write. + * + * This calls ___device_select and notes which devices set + * their pointer to NULL during that operation and updates + * the condvars accordingly. (see the comments above + * ___device_select for details). + */ + +#ifndef LIBEVENT + ___SCMOBJ ___os_condvar_select ___P((___SCMOBJ run_queue, ___SCMOBJ timeout), @@ -5943,6 +6245,87 @@ return e; } +#else /* LIBEVENT */ + +/* If we are using libevent then each device keeps track of read_ready and + * write_ready flags itself using the callbacks from libevent. + * This we use these flags to set the condvar variables. Sadly this is still + * an O(1) pass of the thread run-queue. + */ + +___SCMOBJ ___os_condvar_select + ___P((___SCMOBJ run_queue, + ___SCMOBJ timeout), + (run_queue, + timeout) +___SCMOBJ run_queue; +___SCMOBJ timeout;) +{ + ///////////////////////////////////// +#define ___BTQ_DEQ_NEXT 1 +#define ___BTQ_DEQ_PREV 2 +#define ___BTQ_COLOR 3 +#define ___BTQ_PARENT 4 +#define ___BTQ_LEFT 5 +#define ___BTQ_RIGHT 6 +#define ___BTQ_LEFTMOST 6 +#define ___BTQ_OWNER 7 +#define ___CONDVAR_NAME 8 + + ___time to; + ___SCMOBJ condvar; + ___BOOL condition_triggered = 0; + ___SCMOBJ e = ___FIX(___NO_ERR); + + if (timeout == ___FAL) + to = ___time_mod.time_neg_infinity; + else if (timeout == ___TRU) + to = ___time_mod.time_pos_infinity; + else { + ___time_from_seconds (&to, ___F64VECTORREF(timeout,___FIX(0))); + } + + for (;;) { + condvar = ___FIELD(run_queue,___BTQ_DEQ_NEXT); + + while (condvar != run_queue) + { + ___device *dev = ___CAST(___device *, + ___FIELD(___FIELD(condvar,___CONDVAR_NAME), ___FOREIGN_PTR)); + ___SCMOBJ owner = ___FIELD(condvar,___BTQ_OWNER); + ___FIELD(condvar,___BTQ_OWNER) = owner & ~___FIX(1); + if (owner & ___FIX(2)) { + if (dev->write_ready) { + ___FIELD(condvar,___BTQ_OWNER) = owner |= ___FIX(1); + condition_triggered = 1; + } else { + ___FIELD(condvar,___BTQ_OWNER) = owner & ~___FIX(1); + } + } else { + if (dev->read_ready) { + ___FIELD(condvar,___BTQ_OWNER) = owner |= ___FIX(1); + condition_triggered = 1; + } else { + ___FIELD(condvar,___BTQ_OWNER) = owner & ~___FIX(1); + } + } + condvar = ___FIELD(condvar,___BTQ_DEQ_NEXT); + } + + if (!condition_triggered) { + e = ___device_select(to); + if (e != ___FIX(___NO_ERR)) return e; + condition_triggered = 1; + } else { + break; + } + } + + return e; +} + +#endif /* LIBEVENT */ + /* - - - - - - - - - - - - - - - - - - */ @@ -6764,6 +7147,14 @@ ___SCMOBJ e; ////printf ("io_module_setup\n");fflush(stdout);///////////////////////////// + +#ifdef LIBEVENT + event_init(); + if (!(event_abilities() & EV_ABILITY_EDGE)) { + fprintf(stderr, "Event interface does not support edge io.\n"); + abort(); + } +#endif if ((e = ___device_group_setup (&io_mod.dgroup)) == ___FIX(___NO_ERR)) { diff -ur gambc40b14/lib/os_io.h gambc40b14.new/lib/os_io.h --- gambc40b14/lib/os_io.h 2005-01-13 23:29:06.000000000 +0000 +++ gambc40b14.new/lib/os_io.h 2005-08-19 14:27:20.538850022 +0100 @@ -9,6 +9,10 @@ ////////////////////////////// #include "os_time.h" +#ifdef LIBEVENT +#include +#endif + /*---------------------------------------------------------------------------*/ @@ -77,8 +81,13 @@ int direction; /* ___DIRECTION_RD and/or ___DIRECTION_WR */ int read_stage; /* ___STAGE_OPEN ... ___STAGE_CLOSED */ int write_stage; /* ___STAGE_OPEN ... ___STAGE_CLOSED */ +#ifdef LIBEVENT + struct event ev; + int read_ready, write_ready; +#endif } ___device; +#ifndef LIBEVENT typedef struct ___device_select_state_struct { @@ -147,6 +156,7 @@ #endif +#endif /* !LIBEVENT */ typedef struct ___device_vtbl_struct { @@ -155,6 +165,8 @@ int (*kind) ___P((___device *self),()); +#ifndef LIBEVENT + #define ___device_select_virt(self,for_writing,i,pass,state) \ ___CAST(___device_vtbl*,(self)->vtbl)->select_virt(self,for_writing,i,pass,state) @@ -166,6 +178,8 @@ ___device_select_state *state), ()); +#endif /* !LIBEVENT */ + #define ___device_release_virt(self) \ ___CAST(___device_vtbl*,(self)->vtbl)->release_virt(self) @@ -295,6 +309,8 @@ { ___device_vtbl base; +#ifndef LIBEVENT + #define ___device_stream_select_raw_virt(self,for_writing,i,pass,state) \ ___CAST(___device_stream_vtbl*,(self)->base.vtbl)->select_raw_virt(self,for_writing,i,pass,state) @@ -306,6 +322,8 @@ ___device_select_state *state), ()); +#endif /* !LIBEVENT */ + #define ___device_stream_release_raw_virt(self) \ ___CAST(___device_stream_vtbl*,(self)->base.vtbl)->release_raw_virt(self) @@ -372,6 +390,8 @@ ()); } ___device_stream_vtbl; +#ifndef LIBEVENT + extern ___SCMOBJ ___device_stream_select_virt ___P((___device *self, ___BOOL for_writing, @@ -380,6 +400,8 @@ ___device_select_state *state), ()); +#endif /* !LIBEVENT */ + extern ___SCMOBJ ___device_stream_release_virt ___P((___device *self), ()); @@ -444,13 +466,18 @@ - +#ifdef LIBEVENT +extern ___SCMOBJ ___device_select + ___P((___time timeout), + ()); +#else /* LIBEVENT */ extern ___SCMOBJ ___device_select ___P((___device **devs, int nb_read_devs, int nb_write_devs, ___time timeout), ()); +#endif extern ___SCMOBJ ___device_flush_write ___P((___device *self), diff -ur gambc40b14/lib/os_tty.c gambc40b14.new/lib/os_tty.c --- gambc40b14/lib/os_tty.c 2005-07-13 11:35:05.000000000 +0100 +++ gambc40b14.new/lib/os_tty.c 2005-08-19 14:27:20.556846474 +0100 @@ -18,6 +18,9 @@ #include "setup.h" #include "c_intf.h" +#ifdef LIBEVENT +#include +#endif /*---------------------------------------------------------------------------*/ @@ -697,6 +700,21 @@ return 0; } +#ifdef LIBEVENT +/* This is the callback function, called by libevent, whenever a device_stream + * has an event. */ +static void +device_stream_event_callback(int fd, short events, void *arg) { + ___device *d = ___CAST(___device *, arg); + + if (events & EV_READ) d->read_ready = 1; + if (events & EV_WRITE) d->write_ready = 1; + if (events & EV_TIMEOUT) { + d->read_ready = 1; + d->write_ready = 1; + } +} +#endif ___HIDDEN ___SCMOBJ ___device_tty_force_open ___P((___device_tty *self), @@ -725,6 +743,11 @@ return fnf_or_err_code_from_errno (); d->fd = fd; +#ifdef LIBEVENT + event_set(&d->base.base.ev, d->fd, EV_READ | EV_WRITE | EV_EDGE | EV_PERSIST, + device_stream_event_callback, self); + event_add(&d->base.base.ev, NULL); +#endif #endif @@ -854,8 +877,16 @@ { int n; - if ((n = write (d->fd, buf, len)) < 0) + if ((n = write (d->fd, buf, len)) < 0) { +#ifdef LIBEVENT + if (errno == EAGAIN) self->base.base.write_ready = 0; +#endif return err_code_from_errno (); + } + +#ifdef LIBEVENT + if (n < len) self->base.base.write_ready = 0; +#endif *len_done = n; } @@ -939,8 +970,16 @@ len); #endif - if ((n = read (d->fd, buf, len)) < 0) + if ((n = read (d->fd, buf, len)) < 0) { +#ifdef LIBEVENT + if (errno == EAGAIN) self->base.base.read_ready = 0; +#endif return err_code_from_errno (); + } + +#ifdef LIBEVENT + if (n < len) self->base.base.read_ready = 0; +#endif #ifdef ___DEBUG @@ -6208,10 +6247,18 @@ */ ___time duration; +#ifdef LIBEVENT + struct timeval tv, *p_tv = &tv; +#endif ___time_get_current_time (&d->current.paren_balance_end); ___time_from_nsecs (&duration, 0, d->paren_balance_duration_nsecs); ___time_add (&d->current.paren_balance_end, duration); +#ifdef LIBEVENT + ___absolute_time_to_nonnegative_timeval(duration, &p_tv); + evtimer_set(&d->timeout_event, device_stream_event_callback, d); + evtimer_add(&d->timeout_event, p_tv); +#endif d->current.paren_balance_in_progress = 1; } @@ -6315,19 +6362,38 @@ d->base.base.read_stage = ___STAGE_CLOSED; /* avoid multiple closes */ d->base.base.write_stage = ___STAGE_CLOSED; +#ifdef LIBEVENT + d->base.base.read_ready = 0; + d->base.base.write_ready = 0; + event_del(&d->base.base.ev); +#endif if ((e = ___device_tty_cleanup (d)) != ___FIX(___NO_ERR)) return e; } - else if (direction & ___DIRECTION_RD) + else if (direction & ___DIRECTION_RD) { +#ifdef LIBEVENT + d->base.base.read_ready = 0; +#endif d->base.base.read_stage = ___STAGE_CLOSED; - else if (direction & ___DIRECTION_WR) + } else if (direction & ___DIRECTION_WR) { +#ifdef LIBEVENT + d->base.base.read_ready = 0; +#endif d->base.base.write_stage = ___STAGE_CLOSED; + } + +#ifdef LIBEVENT + if (d->base.base.write_stage == ___STAGE_CLOSED && + d->base.base.read_stage == ___STAGE_CLOSED) { + event_del(&d->base.base.ev); + } +#endif return ___FIX(___NO_ERR); } - +#ifndef LIBEVENT ___HIDDEN ___SCMOBJ ___device_tty_select_raw_virt ___P((___device_stream *self, ___BOOL for_writing, @@ -6377,7 +6443,6 @@ #endif #ifdef USE_LINEEDITOR - if (!for_writing) { if (lineeditor_read_ready (d)) @@ -6391,7 +6456,6 @@ i, d->current.paren_balance_end); } - #endif return ___FIX(___SELECT_SETUP_DONE); @@ -6444,7 +6508,7 @@ return ___FIX(___NO_ERR); } - +#endif /* !LIBEVENT */ ___HIDDEN ___SCMOBJ ___device_tty_release_raw_virt ___P((___device_stream *self), @@ -6804,12 +6868,16 @@ { { ___device_tty_kind, +#ifndef LIBEVENT ___device_stream_select_virt, +#endif ___device_stream_release_virt, ___device_stream_flush_write_virt, ___device_tty_close_virt }, +#ifndef LIBEVENT ___device_tty_select_raw_virt, +#endif ___device_tty_release_raw_virt, ___device_tty_flush_write_raw_virt, ___device_tty_seek_raw_virt, @@ -6836,6 +6904,14 @@ #endif +#ifdef LIBEVENT + if (self->stage != TTY_STAGE_NOT_OPENED) { + event_set(&self->base.base.ev, self->fd, EV_READ | EV_WRITE | EV_EDGE | EV_PERSIST, + device_stream_event_callback, self); + event_add(&self->base.base.ev, NULL); + } +#endif + return e; } diff -ur gambc40b14/lib/os_tty.h gambc40b14.new/lib/os_tty.h --- gambc40b14/lib/os_tty.h 2005-01-04 16:59:40.000000000 +0000 +++ gambc40b14.new/lib/os_tty.h 2005-08-19 14:27:20.560845686 +0100 @@ -374,6 +374,10 @@ struct termios initial_termios; int initial_flags; +#ifdef LIBEVENT + struct event timeout_event; +#endif + #endif #ifdef USE_WIN32