1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-15 23:35:34 +00:00

Introduced --with-libevent based on links code

I don't know how to deal with select's exception fds in libevent,
so some functions may not work properly.
This commit is contained in:
Witold Filipczyk 2017-11-12 13:41:31 +01:00
parent 050f304161
commit 3b6ff1d22f
6 changed files with 408 additions and 21 deletions

View File

@ -1213,6 +1213,36 @@ else
fi
fi
# ========
# libevent
# ========
AC_ARG_WITH(libevent, [ --with-libevent compile with libevent],
[if test "$withval" = yes; then enable_libevent=yes; else enable_libevent=no; fi])
cf_have_libevent=no
if test "$enable_libevent" = yes; then
AC_CHECK_HEADERS(event.h ev-event.h libev/event.h)
if test "$ac_cv_header_event_h" = yes; then
AC_CHECK_LIB(event, event_loop)
if test "$ac_cv_lib_event_event_loop" = yes; then
cf_have_libevent=libevent
fi
fi
if test "$cf_have_libevent" = no; then
if test "$ac_cv_header_event_h" = yes -o "$ac_cv_header_ev_event_h" = yes -o "$ac_cv_header_libev_event_h"; then
AC_CHECK_LIB(ev, event_loop)
if test "$ac_cv_lib_ev_event_loop" = yes; then
cf_have_libevent=libev
fi
fi
fi
fi
if test "$cf_have_libevent" != no; then
AC_HAVE_FUNCS(event_base_set event_get_version event_get_method event_base_free event_base_new event_reinit event_base_get_method event_config_set_flag event_get_struct_event_size)
fi
EL_LOG_CONFIG([CONFIG_LIBEVENT], [[libevent]], [[$cf_have_libevent]])
# Final SSL setup
EL_CONFIG_DEPENDS(CONFIG_SSL, [CONFIG_OPENSSL CONFIG_GNUTLS CONFIG_NSS_COMPAT_OSSL], [SSL])

View File

@ -302,6 +302,7 @@ terminate_all_subsystems(void)
unregister_modules_options(main_modules);
done_options();
done_event();
terminate_select();
terminate_osdep();
#ifdef CONFIG_COMBINE
free_combined();

View File

@ -31,6 +31,17 @@
#include <poll.h>
#endif
#if (defined(HAVE_EVENT_H) || defined(HAVE_EV_EVENT_H) || defined(HAVE_LIBEV_EVENT_H)) && (defined(HAVE_LIBEVENT) || defined(HAVE_LIBEV)) && !defined(OPENVMS) && !defined(DOS)
#if defined(HAVE_EVENT_H)
#include <event.h>
#elif defined(HAVE_EV_EVENT_H)
#include <ev-event.h>
#else
#include <libev/event.h>
#endif
#define USE_LIBEVENT
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
@ -59,11 +70,21 @@ do { \
#define FD_SETSIZE 1024
#endif
/*
#define DEBUG_CALLS
*/
static int n_threads = 0;
struct thread {
select_handler_T read_func;
select_handler_T write_func;
select_handler_T error_func;
void *data;
#ifdef USE_LIBEVENT
struct event *read_event;
struct event *write_event;
#endif
};
#ifdef CONFIG_OS_WIN32
@ -72,7 +93,7 @@ struct thread {
#define FD_SETSIZE 4096
#endif
static struct thread threads[FD_SETSIZE];
static struct thread *threads = NULL;
static fd_set w_read;
static fd_set w_write;
@ -138,6 +159,192 @@ check_bottom_halves(void)
}
}
static void
restrict_fds(void)
{
#if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
#define RLIMIT_NOFILE RLIMIT_OFILE
#endif
#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
struct rlimit limit;
int rs;
EINTRLOOP(rs, getrlimit(RLIMIT_NOFILE, &limit));
if (rs)
goto skip_limit;
if (limit.rlim_cur > FD_SETSIZE) {
limit.rlim_cur = FD_SETSIZE;
EINTRLOOP(rs, setrlimit(RLIMIT_NOFILE, &limit));
}
skip_limit:;
#endif
}
#ifdef USE_LIBEVENT
int event_enabled = 0;
#ifndef HAVE_EVENT_GET_STRUCT_EVENT_SIZE
#define sizeof_struct_event sizeof(struct event)
#else
#define sizeof_struct_event (event_get_struct_event_size())
#endif
static inline
struct event *timer_event(struct timer *tm)
{
return (struct event *)((unsigned char *)tm - sizeof_struct_event);
}
#ifdef HAVE_EVENT_BASE_SET
struct event_base *event_base;
#endif
static void
event_callback(int h, short ev, void *data)
{
#ifndef EV_PERSIST
if (event_add((struct event *)data, NULL) == -1)
elinks_internal("ERROR: event_add failed: %s", strerror(errno));
#endif
if (!(ev & EV_READ) == !(ev & EV_WRITE))
elinks_internal("event_callback: invalid flags %d on handle %d", (int)ev, h);
if (ev & EV_READ) {
#if defined(HAVE_LIBEV)
/* Old versions of libev badly interact with fork and fire
* events spuriously. */
if (ev_version_major() < 4 && !can_read(h)) return;
#endif
threads[h].read_func(threads[h].data);
} else if (ev & EV_WRITE) {
#if defined(HAVE_LIBEV)
/* Old versions of libev badly interact with fork and fire
* events spuriously. */
if (ev_version_major() < 4 && !can_write(h)) return;
#endif
threads[h].write_func(threads[h].data);
}
check_bottom_halves();
}
static void
set_event_for_action(int h, void (*func)(void *), struct event **evptr, short evtype)
{
if (func) {
if (!*evptr) {
#ifdef EV_PERSIST
evtype |= EV_PERSIST;
#endif
*evptr = mem_alloc(sizeof_struct_event);
event_set(*evptr, h, evtype, event_callback, *evptr);
#ifdef HAVE_EVENT_BASE_SET
if (event_base_set(event_base, *evptr) == -1)
elinks_internal("ERROR: event_base_set failed: %s, handle %d", strerror(errno), h);
#endif
}
if (event_add(*evptr, NULL) == -1)
elinks_internal("ERROR: event_add failed: %s, handle %d", strerror(errno), h);
} else {
if (*evptr) {
if (event_del(*evptr) == -1)
elinks_internal("ERROR: event_del failed: %s, handle %d", strerror(errno), h);
}
}
}
static void
set_events_for_handle(int h)
{
set_event_for_action(h, threads[h].read_func, &threads[h].read_event, EV_READ);
set_event_for_action(h, threads[h].write_func, &threads[h].write_event, EV_WRITE);
}
static void
enable_libevent(void)
{
int i;
if (0 /* disable_libevent */)
return;
#if !defined(NO_FORK_ON_EXIT) && defined(HAVE_KQUEUE) && !defined(HAVE_EVENT_REINIT)
/* kqueue doesn't work after fork */
if (!F)
return;
#endif
#if defined(HAVE_EVENT_CONFIG_SET_FLAG)
{
struct event_config *cfg;
cfg = event_config_new();
if (!cfg)
return;
if (event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK) == -1) {
event_config_free(cfg);
return;
}
event_base = event_base_new_with_config(cfg);
event_config_free(cfg);
if (!event_base)
return;
}
#elif defined(HAVE_EVENT_BASE_NEW)
event_base = event_base_new();
if (!event_base)
return;
#elif defined(HAVE_EVENT_BASE_SET)
event_base = event_init();
if (!event_base)
return;
#else
event_init();
#endif
event_enabled = 1;
for (i = 0; i < w_max; i++)
set_events_for_handle(i);
/*
foreach(tm, timers)
set_event_for_timer(tm);
*/
set_events_for_timer();
}
static void
terminate_libevent(void)
{
int i;
if (event_enabled) {
for (i = 0; i < n_threads; i++) {
set_event_for_action(i, NULL, &threads[i].read_event, EV_READ);
if (threads[i].read_event)
mem_free(threads[i].read_event);
set_event_for_action(i, NULL, &threads[i].write_event, EV_WRITE);
if (threads[i].write_event)
mem_free(threads[i].write_event);
}
#ifdef HAVE_EVENT_BASE_FREE
event_base_free(event_base);
#endif
event_enabled = 0;
}
}
static void
do_event_loop(int flags)
{
int e;
#ifdef HAVE_EVENT_BASE_SET
e = event_base_loop(event_base, flags);
#else
e = event_loop(flags);
#endif
if (e == -1)
elinks_internal("ERROR: event_base_loop failed: %s", strerror(errno));
}
#endif
select_handler_T
get_handler(int fd, enum select_handler_type tp)
{
@ -183,11 +390,44 @@ set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
error_func = NULL;
}
#endif /* __GNU__ */
#if defined(USE_POLL) && defined(USE_LIBEVENT)
if (!event_enabled)
#endif
if (fd >= (int)FD_SETSIZE) {
elinks_internal("too big handle %d", fd);
return;
}
if (fd >= n_threads) {
threads = mem_realloc(threads, (fd + 1) * sizeof(struct thread));
memset(threads + n_threads, 0, (fd + 1 - n_threads) * sizeof(struct thread));
n_threads = fd + 1;
}
threads[fd].read_func = read_func;
threads[fd].write_func = write_func;
threads[fd].error_func = error_func;
threads[fd].data = data;
if (read_func || write_func || error_func) {
if (fd >= w_max) w_max = fd + 1;
} else if (fd == w_max - 1) {
int i;
for (i = fd - 1; i >= 0; i--)
if (FD_ISSET(i, &w_read)
|| FD_ISSET(i, &w_write)
|| FD_ISSET(i, &w_error))
break;
w_max = i + 1;
}
#ifdef USE_LIBEVENT
if (event_enabled) {
set_events_for_handle(fd);
return;
}
#endif
if (read_func) {
FD_SET(fd, &w_read);
} else {
@ -208,19 +448,6 @@ set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
FD_CLR(fd, &w_error);
FD_CLR(fd, &x_error);
}
if (read_func || write_func || error_func) {
if (fd >= w_max) w_max = fd + 1;
} else if (fd == w_max - 1) {
int i;
for (i = fd - 1; i >= 0; i--)
if (FD_ISSET(i, &w_read)
|| FD_ISSET(i, &w_write)
|| FD_ISSET(i, &w_error))
break;
w_max = i + 1;
}
}
void
@ -241,6 +468,27 @@ select_loop(void (*init)(void))
init();
check_bottom_halves();
#ifdef USE_LIBEVENT
enable_libevent();
#if defined(USE_POLL)
if (!event_enabled) {
restrict_fds();
}
#endif
if (event_enabled) {
while (!program.terminate) {
check_signals();
if (1 /*(!F)*/) {
do_event_loop(EVLOOP_NONBLOCK);
check_signals();
redraw_all_terminals();
}
if (program.terminate) break;
do_event_loop(EVLOOP_ONCE);
}
} else
#endif
while (!program.terminate) {
struct timeval *timeout = NULL;
int n, i, has_timer;
@ -392,3 +640,12 @@ can_write(int fd)
{
return can_read_or_write(fd, 1);
}
void
terminate_select(void)
{
#ifdef USE_LIBEVENT
terminate_libevent();
#endif
mem_free(threads);
}

View File

@ -46,4 +46,5 @@ void set_handlers(int fd,
int can_read(int fd);
int can_write(int fd);
void terminate_select(void);
#endif

View File

@ -4,6 +4,19 @@
#include "config.h"
#endif
#include <errno.h>
#if (defined(HAVE_EVENT_H) || defined(HAVE_EV_EVENT_H) || defined(HAVE_LIBEV_EVENT_H)) && (defined(HAVE_LIBEVENT) || defined(HAVE_LIBEV)) && !defined(OPENVMS) && !defined(DOS)
#if defined(HAVE_EVENT_H)
#include <event.h>
#elif defined(HAVE_EV_EVENT_H)
#include <ev-event.h>
#else
#include <libev/event.h>
#endif
#define USE_LIBEVENT
#endif
#include "elinks.h"
#include "main/select.h"
@ -68,6 +81,59 @@ check_timers(timeval_T *last_time)
timeval_copy(last_time, &now);
}
#ifdef HAVE_EVENT_BASE_SET
extern struct event_base *event_base;
#endif
#ifdef USE_LIBEVENT
extern int event_enabled;
#ifndef HAVE_EVENT_GET_STRUCT_EVENT_SIZE
#define sizeof_struct_event sizeof(struct event)
#else
#define sizeof_struct_event (event_get_struct_event_size())
#endif
static void
timer_callback(int h, short ev, void *data)
{
struct timer *tm = data;
tm->func(tm->data);
kill_timer(&tm);
check_bottom_halves();
}
static inline
struct event *timer_event(struct timer *tm)
{
return (struct event *)((unsigned char *)tm - sizeof_struct_event);
}
#endif
static void
set_event_for_timer(timer_id_T tm)
{
#ifdef USE_LIBEVENT
struct timeval tv;
struct event *ev = timer_event(tm);
timeout_set(ev, timer_callback, tm);
#ifdef HAVE_EVENT_BASE_SET
if (event_base_set(event_base, ev) == -1)
elinks_internal("ERROR: event_base_set failed: %s", strerror(errno));
#endif
tv.tv_sec = tm->interval.sec;
tv.tv_usec = tm->interval.usec;
#if defined(HAVE_LIBEV)
if (!tm->interval.usec && ev_version_major() < 4) {
/* libev bug */
tv.tv_usec = 1;
}
#endif
if (timeout_add(ev, &tv) == -1)
elinks_internal("ERROR: timeout_add failed: %s", strerror(errno));
#endif
}
/* Install a timer that calls @func(@data) after @delay milliseconds.
* Store to *@id either the ID of the new timer, or TIMER_ID_UNDEF if
* the timer cannot be installed. (This function ignores the previous
@ -83,20 +149,34 @@ install_timer(timer_id_T *id, milliseconds_T delay, void (*func)(void *), void *
assert(id && delay > 0);
#ifdef USE_LIBEVENT
{
unsigned char *q = mem_alloc(sizeof_struct_event + sizeof(struct timer));
new_timer = (struct timer *)(q + sizeof_struct_event);
}
#else
new_timer = mem_alloc(sizeof(*new_timer));
#endif
*id = (timer_id_T) new_timer; /* TIMER_ID_UNDEF is NULL */
if (!new_timer) return;
timeval_from_milliseconds(&new_timer->interval, delay);
new_timer->func = func;
new_timer->data = data;
#ifdef USE_LIBEVENT
if (event_enabled) {
set_event_for_timer(new_timer);
add_to_list(timers, new_timer);
} else
#endif
{
foreach (timer, timers) {
if (timeval_cmp(&timer->interval, &new_timer->interval) >= 0)
break;
}
foreach (timer, timers) {
if (timeval_cmp(&timer->interval, &new_timer->interval) >= 0)
break;
add_at_pos(timer->prev, new_timer);
}
add_at_pos(timer->prev, new_timer);
}
void
@ -106,11 +186,16 @@ kill_timer(timer_id_T *id)
assert(id != NULL);
if (*id == TIMER_ID_UNDEF) return;
timer = *id;
del_from_list(timer);
mem_free(timer);
#ifdef USE_LIBEVENT
if (event_enabled)
timeout_del(timer_event(timer));
mem_free(timer_event(timer));
#else
mem_free(timer);
#endif
*id = TIMER_ID_UNDEF;
}
@ -124,3 +209,15 @@ get_next_timer_time(timeval_T *t)
return 0;
}
void
set_events_for_timer(void)
{
#ifdef USE_LIBEVENT
timer_id_T tm;
foreach(tm, timers)
set_event_for_timer(tm);
#endif
}

View File

@ -22,5 +22,6 @@ void check_timers(timeval_T *last_time);
void install_timer(timer_id_T *id, milliseconds_T delay, void (*)(void *), void *);
void kill_timer(timer_id_T *id);
int get_next_timer_time(timeval_T *t);
void set_events_for_timer(void);
#endif