From e91bbca5fb080a8d988c156d78c7dc1b1daaad82 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 8 Dec 2011 19:17:49 +0100 Subject: [PATCH 01/51] Initial threadpool implementation for MariaDB 5.5 --- cmake/os/FreeBSD.cmake | 7 + include/thr_alarm.h | 6 +- include/violite.h | 3 + mysql-test/mysql-test-run.pl | 4 +- .../sys_vars/t/slow_launch_time_func.test | 2 +- mysql-test/t/wait_timeout.test | 2 +- mysys/thr_alarm.c | 88 +- sql/CMakeLists.txt | 13 +- sql/mysqld.cc | 11 +- sql/net_serv.cc | 4 +- sql/scheduler.cc | 21 +- sql/scheduler.h | 58 +- sql/sql_class.cc | 37 +- sql/sql_class.h | 4 + sql/sql_connect.cc | 3 +- sql/sql_parse.cc | 5 +- sql/sys_vars.cc | 76 +- sql/threadpool.h | 47 + sql/threadpool_common.cc | 246 ++++ sql/threadpool_unix.cc | 1238 +++++++++++++++++ sql/threadpool_win.cc | 756 ++++++++++ vio/vio.c | 25 +- vio/vio_priv.h | 4 + vio/viosocket.c | 83 +- 24 files changed, 2553 insertions(+), 190 deletions(-) create mode 100644 sql/threadpool.h create mode 100644 sql/threadpool_common.cc create mode 100644 sql/threadpool_unix.cc create mode 100644 sql/threadpool_win.cc diff --git a/cmake/os/FreeBSD.cmake b/cmake/os/FreeBSD.cmake index e09592942c1..9bc7d944da2 100644 --- a/cmake/os/FreeBSD.cmake +++ b/cmake/os/FreeBSD.cmake @@ -22,3 +22,10 @@ # The below was used for really old versions of FreeBSD, roughly: before 5.1.9 # ADD_DEFINITIONS(-DHAVE_BROKEN_REALPATH) + +# Use atomic builtins +IF(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "i386") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i686") + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=i686") +ENDIF() diff --git a/include/thr_alarm.h b/include/thr_alarm.h index f4823b618f7..66e344d10fd 100644 --- a/include/thr_alarm.h +++ b/include/thr_alarm.h @@ -40,7 +40,11 @@ typedef struct st_alarm_info } ALARM_INFO; void thr_alarm_info(ALARM_INFO *info); +extern my_bool my_disable_thr_alarm; +#ifdef _WIN32 +#define DONT_USE_THR_ALARM +#endif #if defined(DONT_USE_THR_ALARM) #define USE_ALARM_THREAD @@ -88,7 +92,7 @@ typedef struct st_alarm { extern uint thr_client_alarm; extern pthread_t alarm_thread; -extern my_bool my_disable_thr_alarm; + #define thr_alarm_init(A) (*(A))=0 #define thr_alarm_in_use(A) (*(A)!= 0) diff --git a/include/violite.h b/include/violite.h index ba057028ed2..05b20245c5a 100644 --- a/include/violite.h +++ b/include/violite.h @@ -168,6 +168,7 @@ void vio_end(void); #define vio_should_retry(vio) (vio)->should_retry(vio) #define vio_was_interrupted(vio) (vio)->was_interrupted(vio) #define vio_close(vio) ((vio)->vioclose)(vio) +#define vio_shutdown(vio,how) ((vio)->shutdown)(vio,how) #define vio_peer_addr(vio, buf, prt, buflen) (vio)->peer_addr(vio, buf, prt, buflen) #define vio_timeout(vio, which, seconds) (vio)->timeout(vio, which, seconds) #define vio_poll_read(vio, timeout) (vio)->poll_read(vio, timeout) @@ -219,6 +220,7 @@ struct st_vio void (*timeout)(Vio*, unsigned int which, unsigned int timeout); my_bool (*poll_read)(Vio *vio, uint timeout); my_bool (*is_connected)(Vio*); + int (*shutdown)(Vio *, int); my_bool (*has_data) (Vio*); #ifdef HAVE_OPENSSL void *ssl_arg; @@ -235,6 +237,7 @@ struct st_vio char *shared_memory_pos; #endif /* HAVE_SMEM */ #ifdef _WIN32 + DWORD thread_id; /* Used to XP only in vio_shutdown */ OVERLAPPED pipe_overlapped; DWORD read_timeout_ms; DWORD write_timeout_ms; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 25427b81eff..e4cd9e6ab89 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3461,7 +3461,9 @@ sub mysql_install_db { mtr_add_arg($args, "--loose-skip-ndbcluster"); mtr_add_arg($args, "--loose-skip-aria"); mtr_add_arg($args, "--disable-sync-frm"); - mtr_add_arg($args, "--tmpdir=%s", "$opt_vardir/tmp/"); + mtr_add_arg($args, "--tmpdir=."); + mtr_add_arg($args, "--max_allowed_packet=8M"); + mtr_add_arg($args, "--net_buffer_length=16K"); mtr_add_arg($args, "--core-file"); if ( $opt_debug ) diff --git a/mysql-test/suite/sys_vars/t/slow_launch_time_func.test b/mysql-test/suite/sys_vars/t/slow_launch_time_func.test index c9fc357b10f..a5b429f81cb 100644 --- a/mysql-test/suite/sys_vars/t/slow_launch_time_func.test +++ b/mysql-test/suite/sys_vars/t/slow_launch_time_func.test @@ -29,7 +29,7 @@ # # Setup # - +--source include/not_threadpool.inc --source include/not_embedded.inc --source include/not_threadpool.inc diff --git a/mysql-test/t/wait_timeout.test b/mysql-test/t/wait_timeout.test index eb86cf17ebb..68c0957347d 100644 --- a/mysql-test/t/wait_timeout.test +++ b/mysql-test/t/wait_timeout.test @@ -8,7 +8,7 @@ ############################################################################### # These tests cannot run with the embedded server -- source include/not_embedded.inc --- source include/one_thread_per_connection.inc +#-- source include/one_thread_per_connection.inc # Save the initial number of concurrent sessions --source include/count_sessions.inc diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index adce7407239..4b5144e90bf 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -597,93 +597,6 @@ static void *alarm_handler(void *arg __attribute__((unused))) return 0; /* Impossible */ } #endif /* USE_ALARM_THREAD */ - -/***************************************************************************** - thr_alarm for win95 -*****************************************************************************/ - -#else /* __WIN__ */ - -void thr_alarm_kill(my_thread_id thread_id) -{ - /* Can't do this yet */ -} - -sig_handler process_alarm(int sig __attribute__((unused))) -{ - /* Can't do this yet */ -} - - -my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm) -{ - (*alrm)= &alarm->alarmed; - if (alarm_aborted) - { - alarm->alarmed.crono=0; - return 1; - } - if (!(alarm->alarmed.crono=SetTimer((HWND) NULL,0, sec*1000, - (TIMERPROC) NULL))) - return 1; - return 0; -} - - -my_bool thr_got_alarm(thr_alarm_t *alrm_ptr) -{ - thr_alarm_t alrm= *alrm_ptr; - MSG msg; - if (alrm->crono) - { - PeekMessage(&msg,NULL,WM_TIMER,WM_TIMER,PM_REMOVE) ; - if (msg.message == WM_TIMER || alarm_aborted) - { - KillTimer(NULL, alrm->crono); - alrm->crono = 0; - } - } - return !alrm->crono || alarm_aborted; -} - - -void thr_end_alarm(thr_alarm_t *alrm_ptr) -{ - thr_alarm_t alrm= *alrm_ptr; - /* alrm may be zero if thr_alarm aborted with an error */ - if (alrm && alrm->crono) - - { - KillTimer(NULL, alrm->crono); - alrm->crono = 0; - } -} - -void end_thr_alarm(my_bool free_structures) -{ - DBUG_ENTER("end_thr_alarm"); - alarm_aborted=1; /* No more alarms */ - DBUG_VOID_RETURN; -} - -void init_thr_alarm(uint max_alarm) -{ - DBUG_ENTER("init_thr_alarm"); - alarm_aborted=0; /* Yes, Gimmie alarms */ - DBUG_VOID_RETURN; -} - -void thr_alarm_info(ALARM_INFO *info) -{ - bzero((char*) info, sizeof(*info)); -} - -void resize_thr_alarm(uint max_alarms) -{ -} - -#endif /* __WIN__ */ - #endif /**************************************************************************** @@ -954,4 +867,5 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) } #endif /* !defined(DONT_USE_ALARM_THREAD) */ +#endif /* WIN */ #endif /* MAIN */ diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index ce6c96e55f2..1772c9e12b2 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -31,7 +31,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1) -ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER) +ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER -DHAVE_POOL_OF_THREADS) IF(SSL_DEFINES) ADD_DEFINITIONS(${SSL_DEFINES}) ENDIF() @@ -82,9 +82,16 @@ SET (SQL_SOURCE opt_index_cond_pushdown.cc opt_subselect.cc opt_table_elimination.cc sql_expression_cache.cc gcalc_slicescan.cc gcalc_tools.cc - + threadpool_common.cc ${GEN_SOURCES} - ${MYSYS_LIBWRAP_SOURCE}) + ${MYSYS_LIBWRAP_SOURCE} + ) + +IF(WIN32) + SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc) +ELSE() + SET(SQL_SOURCE ${SQL_SOURCE} threadpool_unix.cc) +ENDIF() MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY RECOMPILE_FOR_EMBEDDED) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c43f516fc2e..303e7d00221 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -73,6 +73,7 @@ #include #include "debug_sync.h" #include "sql_callback.h" +#include "threadpool.h" #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE #include "../storage/perfschema/pfs_server.h" @@ -5236,6 +5237,8 @@ default_service_handling(char **argv, int mysqld_main(int argc, char **argv) { + my_progname= argv[0]; + /* When several instances are running on the same machine, we need to have an unique named hEventShudown through the @@ -7132,6 +7135,10 @@ SHOW_VAR status_vars[]= { {"Tc_log_max_pages_used", (char*) &tc_log_max_pages_used, SHOW_LONG}, {"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG}, {"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG}, +#endif +#ifndef EMBEDDED_LIBRARY + {"Threadpool_idle_threads", (char *) &tp_stats.num_waiting_threads, SHOW_INT}, + {"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT}, #endif {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH}, {"Threads_connected", (char*) &connection_count, SHOW_INT}, @@ -8018,7 +8025,9 @@ static int get_options(int *argc_ptr, char ***argv_ptr) else if (thread_handling == SCHEDULER_NO_THREADS) one_thread_scheduler(thread_scheduler); else - pool_of_threads_scheduler(thread_scheduler); /* purecov: tested */ + pool_of_threads_scheduler(thread_scheduler, &max_connections, + &connection_count); + one_thread_per_connection_scheduler(extra_thread_scheduler, &extra_max_connections, &extra_connection_count); diff --git a/sql/net_serv.cc b/sql/net_serv.cc index ccc67e64ea4..9011d570497 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -842,7 +842,7 @@ my_real_read(NET *net, size_t *complen) DBUG_PRINT("info",("vio_read returned %ld errno: %d", (long) length, vio_errno(net->vio))); -#if !defined(__WIN__) || defined(MYSQL_SERVER) +#if !defined(__WIN__) && defined(MYSQL_SERVER) /* We got an error that there was no data on the socket. We now set up an alarm to not 'read forever', change the socket to the blocking @@ -874,7 +874,7 @@ my_real_read(NET *net, size_t *complen) continue; } } -#endif /* (!defined(__WIN__) || defined(MYSQL_SERVER) */ +#endif /* (!defined(__WIN__) && defined(MYSQL_SERVER) */ if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && interrupted) { /* Probably in MIT threads */ diff --git a/sql/scheduler.cc b/sql/scheduler.cc index f04fdef39f9..c174d300d2e 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -79,7 +79,7 @@ static void scheduler_wait_sync_end(void) { one_thread_scheduler() or one_thread_per_connection_scheduler() in mysqld.cc, so this init function will always be called. */ -static void scheduler_init() { +void scheduler_init() { thr_set_lock_wait_callback(scheduler_wait_lock_begin, scheduler_wait_lock_end); thr_set_sync_wait_callback(scheduler_wait_sync_begin, @@ -124,25 +124,6 @@ void one_thread_scheduler(scheduler_functions *func) } -#ifdef HAVE_POOL_OF_THREADS - -/* - thd_scheduler keeps the link between THD and events. - It's embedded in the THD class. -*/ - -thd_scheduler::thd_scheduler() - : m_psi(NULL), logged_in(FALSE), io_event(NULL), thread_attached(FALSE) -{ -} - - -thd_scheduler::~thd_scheduler() -{ - my_free(io_event); -} - -#endif /* no pluggable schedulers in mariadb. diff --git a/sql/scheduler.h b/sql/scheduler.h index 03e1ad385c1..c91df8512ab 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -76,13 +76,11 @@ void one_thread_per_connection_scheduler(scheduler_functions *func, ulong *arg_max_connections, uint *arg_connection_count); void one_thread_scheduler(scheduler_functions *func); -#if defined(HAVE_LIBEVENT) && !defined(EMBEDDED_LIBRARY) -#define HAVE_POOL_OF_THREADS 1 - -struct event; - -class thd_scheduler +/* + To be used for pool-of-threads (implemeneted differently on various OSs) +*/ +struct thd_scheduler { public: /* @@ -96,29 +94,33 @@ public: differently. */ PSI_thread *m_psi; - - bool logged_in; - struct event* io_event; - LIST list; - bool thread_attached; /* Indicates if THD is attached to the OS thread */ - - thd_scheduler(); - ~thd_scheduler(); - bool init(THD* parent_thd); - bool thread_attach(); - void thread_detach(); + void *data; /* scheduler-specific data structure */ +#ifndef DBUG_OFF + bool set_explain; + char dbug_explain[512]; +#endif }; -void pool_of_threads_scheduler(scheduler_functions* func); +void *thd_get_scheduler_data(THD *thd); +void thd_set_scheduler_data(THD *thd, void *data); +PSI_thread* thd_get_psi(THD *thd); +void thd_set_psi(THD *thd, PSI_thread *psi); + +/* Common thread pool routines, suitable for different implementations */ +extern void threadpool_remove_connection(THD *thd); +extern int threadpool_process_request(THD *thd); +extern int threadpool_add_connection(THD *thd); + + +extern scheduler_functions *thread_scheduler; +#endif /* SCHEDULER_INCLUDED */ + +#if !defined(EMBEDDED_LIBRARY) +#define HAVE_POOL_OF_THREADS 1 +void pool_of_threads_scheduler(scheduler_functions* func, + ulong *arg_max_connections, + uint *arg_connection_count); #else - -#define pool_of_threads_scheduler(A) \ - one_thread_per_connection_scheduler(A, &max_connections, \ - &connection_count) - -class thd_scheduler -{}; - -#endif - +#define pool_of_threads_scheduler(A,B,C) \ + one_thread_per_connection_scheduler(A, B, C) #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index cc58a131f00..63e1b36073f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1536,35 +1536,10 @@ void THD::awake(killed_state state_to_set) if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED) { #ifdef SIGNAL_WITH_VIO_CLOSE - if (this != current_thd) + if (this != current_thd) { - /* - Before sending a signal, let's close the socket of the thread - that is being killed ("this", which is not the current thread). - This is to make sure it does not block if the signal is lost. - This needs to be done only on platforms where signals are not - a reliable interruption mechanism. - - Note that the downside of this mechanism is that we could close - the connection while "this" target thread is in the middle of - sending a result to the application, thus violating the client- - server protocol. - - On the other hand, without closing the socket we have a race - condition. If "this" target thread passes the check of - thd->killed, and then the current thread runs through - THD::awake(), sets the 'killed' flag and completes the - signaling, and then the target thread runs into read(), it will - block on the socket. As a result of the discussions around - Bug#37780, it has been decided that we accept the race - condition. A second KILL awakes the target from read(). - - If we are killing ourselves, we know that we are not blocked. - We also know that we will check thd->killed before we go for - reading the next statement. - */ - - close_active_vio(); + if(active_vio) + vio_shutdown(active_vio, SHUT_RDWR); } #endif @@ -1668,7 +1643,7 @@ void THD::disconnect() /* Disconnect even if a active vio is not associated. */ if (net.vio != vio) - vio_close(net.vio); + vio_close(net.vio); mysql_mutex_unlock(&LOCK_thd_data); } @@ -1740,6 +1715,10 @@ bool THD::store_globals() mysys_var->stack_ends_here= thread_stack + // for consistency, see libevent_thread_proc STACK_DIRECTION * (long)my_thread_stack_size; +#ifdef _WIN32 + if (net.vio) + net.vio->thread_id= real_id; /* Required to support IO cancelation on XP */ +#endif /* We have to call thr_lock_info_init() again here as THD may have been created in another thread diff --git a/sql/sql_class.h b/sql/sql_class.h index f271a6d5cd9..f87af8842d8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2339,6 +2339,10 @@ public: { mysql_mutex_lock(&LOCK_thd_data); active_vio = vio; +#ifdef _WIN32 + /* Required to support cancelation on XP */ + active_vio->thread_id = pthread_self(); +#endif mysql_mutex_unlock(&LOCK_thd_data); } inline void clear_active_vio() diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 3c88b7a054d..570f869c2a6 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -890,6 +890,7 @@ static int check_connection(THD *thd) DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); + #ifdef SIGNAL_WITH_VIO_CLOSE thd->set_active_vio(net->vio); #endif @@ -1175,7 +1176,7 @@ void do_handle_one_connection(THD *thd_arg) /* We need to set this because of time_out_user_resource_limits */ thd->start_utime= thd->thr_create_utime; - if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0)) + if (MYSQL_CALLBACK_ELSE(thd->scheduler, init_new_connection_thread, (), 0)) { close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 719fcad5883..7ee6c9fb74f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -678,6 +678,7 @@ void cleanup_items(Item *item) @retval 1 request of thread shutdown (see dispatch_command() description) */ +int skip_net_wait_timeout = 0; bool do_command(THD *thd) { @@ -700,7 +701,9 @@ bool do_command(THD *thd) the client, the connection is closed or "net_wait_timeout" number of seconds has passed. */ - my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + if(!skip_net_wait_timeout) + my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + /* XXX: this code is here only to clear possible errors of init_connect. diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index d01f1892d58..f547292e239 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -50,6 +50,7 @@ #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE #include "../storage/perfschema/pfs_server.h" #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ +#include "threadpool.h" /* The rule for this file: everything should be 'static'. When a sys_var @@ -1804,7 +1805,13 @@ static Sys_var_enum Sys_thread_handling( ", pool-of-threads" #endif , READ_ONLY GLOBAL_VAR(thread_handling), CMD_LINE(REQUIRED_ARG), - thread_handling_names, DEFAULT(0)); + thread_handling_names, +#ifdef HAVE_POOL_OF_THREADS + DEFAULT(2) +#else + DEFAULT(0) +#endif + ); #ifdef HAVE_QUERY_CACHE static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type) @@ -2173,14 +2180,67 @@ static Sys_var_ulong Sys_thread_cache_size( GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1)); -#ifdef HAVE_POOL_OF_THREADS -static Sys_var_ulong Sys_thread_pool_size( - "thread_pool_size", - "How many threads we should create to handle query requests in " - "case of 'thread_handling=pool-of-threads'", - GLOBAL_VAR(thread_pool_size), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 16384), DEFAULT(20), BLOCK_SIZE(0)); +#ifndef HAVE_POOL_OF_THREADS +static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type) +{ +#ifdef _WIN32 + tp_set_max_threads(threadpool_max_threads); #endif + return false; +} + +#ifdef _WIN32 +static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type) +{ + tp_set_min_threads(threadpool_min_threads); + return false; +} +#endif + +#ifdef _WIN32 +static Sys_var_uint Sys_threadpool_min_threads( + "thread_pool_min_threads", + "Minimuim number of threads in the thread pool.", + GLOBAL_VAR(threadpool_min_threads), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, 256), DEFAULT(1), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_tp_min_threads) + ); +#else +static Sys_var_uint Sys_threadpool_idle_thread_timeout( + "thread_pool_idle_timeout", + "Timeout in seconds for an idle thread in the thread pool." + "Worker thread will be shut down after timeout", + GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, UINT_MAX/100), DEFAULT(60000), BLOCK_SIZE(1) +); +static Sys_var_uint Sys_threadpool_size( + "thread_pool_size", + "Number of concurrently executing threads in the pool. " + "Leaving value default (0) sets it to the number of processors.", + GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 128), DEFAULT(0), BLOCK_SIZE(1) +); +static Sys_var_uint Sys_threadpool_stall_limit( + "thread_pool_stall_limit", + "Maximum query execution time before in milliseconds," + "before an executing non-yielding thread is considered stalled." + "If a worker thread is stalled, additional worker thread " + "may be created to handle remaining clients.", + GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(60, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1) +); +#endif /*! WIN32 */ +static Sys_var_uint Sys_threadpool_max_threads( + "thread_pool_max_threads", + "Maximum allowed number of worker threads in the thread pool", + GLOBAL_VAR(threadpool_max_threads), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, UINT_MAX), DEFAULT(3000), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_tp_max_threads) +); +#endif /* !HAVE_POOL_OF_THREADS */ + /** Can't change the 'next' tx_isolation if we are already in a diff --git a/sql/threadpool.h b/sql/threadpool.h new file mode 100644 index 00000000000..0694981d76f --- /dev/null +++ b/sql/threadpool.h @@ -0,0 +1,47 @@ + +/* Threadpool parameters */ +#ifdef _WIN32 +extern uint threadpool_min_threads; /* Minimum threads in pool */ +#else +extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this timeout */ +extern uint threadpool_size; /* Number of parallel executing threads */ +extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall checks*/ +#endif +extern uint threadpool_max_threads; /* Maximum threads in pool */ + +/* + Threadpool statistics +*/ +struct TP_STATISTICS +{ + /* Current number of worker thread. */ + volatile int num_worker_threads; + /* Current number of idle threads. */ + volatile int num_waiting_threads; + /* Number of login requests are queued but not yet processed. */ + volatile int pending_login_requests; + /* Number of threads that are starting. */ + volatile int pending_thread_starts; + /* Number of threads that are being shut down */ + volatile int pending_thread_shutdowns; + /* Time (in milliseconds) since pool is blocked (num_waiting_threads is 0) */ + ulonglong pool_block_duration; + /* Maximum duration of the pending login, im milliseconds. */ + ulonglong pending_login_duration; + /* Time since last thread was created */ + ulonglong time_since_last_thread_creation; + /* Number of requests processed since pool monitor run last time. */ + volatile int requests_dequeued; + volatile int requests_completed; +}; + +extern TP_STATISTICS tp_stats; + + +/* Functions to set threadpool parameters */ +extern void tp_set_min_threads(uint val); +extern void tp_set_max_threads(uint val); + +/* Activate threadpool scheduler */ +extern void tp_scheduler(void); + diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc new file mode 100644 index 00000000000..01546162377 --- /dev/null +++ b/sql/threadpool_common.cc @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern bool login_connection(THD *thd); +extern bool do_command(THD *thd); +extern void prepare_new_connection_state(THD* thd); +extern void end_connection(THD *thd); +extern void thd_cleanup(THD *thd); +extern void delete_thd(THD *thd); + +/* Threadpool parameters */ +#ifdef _WIN32 +uint threadpool_min_threads; +#else +uint threadpool_idle_timeout; +uint threadpool_size; +uint threadpool_stall_limit; +#endif +uint threadpool_max_threads; + + +/* + Attach/associate the connection with the OS thread, for command processing. +*/ +static inline bool thread_attach(THD* thd, char *stack_start, PSI_thread **save_psi_thread) +{ + DBUG_ENTER("thread_attach"); + + if (PSI_server) + { + *save_psi_thread= PSI_server->get_thread(); + PSI_server->set_thread(thd->event_scheduler.m_psi); + } + else + *save_psi_thread= NULL; + + /* + We need to know the start of the stack so that we could check for + stack overruns. + */ + thd->thread_stack= stack_start; + + + /* Calls close_connection() on failure */ + if (setup_connection_thread_globals(thd)) + { + DBUG_RETURN(TRUE); + } + + /* clear errors from processing the previous THD */ + my_errno= 0; + thd->mysys_var->abort= 0; + +#ifndef DBUG_OFF + if (thd->event_scheduler.set_explain) + DBUG_SET(thd->event_scheduler.dbug_explain); +#endif + + DBUG_RETURN(FALSE); +} + +/* + Detach/disassociate the connection with the OS thread. +*/ +static inline void thread_detach(THD* thd, PSI_thread *restore_psi_thread) +{ + DBUG_ENTER("thread_detach"); + thd->mysys_var = NULL; +#ifndef DBUG_OFF + /* + If during the session @@session.dbug was assigned, the + dbug options/state has been pushed. Check if this is the + case, to be able to restore the state when we attach this + logical connection to a physical thread. + */ + if (_db_is_pushed_()) + { + thd->event_scheduler.set_explain= TRUE; + if (DBUG_EXPLAIN(thd->event_scheduler.dbug_explain, sizeof(thd->event_scheduler.dbug_explain))) + sql_print_error("thd_scheduler: DBUG_EXPLAIN buffer is too small"); + } + /* DBUG_POP() is a no-op in case there is no session state */ + DBUG_POP(); +#endif + if (PSI_server) + PSI_server->set_thread(restore_psi_thread); + pthread_setspecific(THR_THD, NULL); + DBUG_VOID_RETURN; +} + + + +int threadpool_add_connection(THD *thd) +{ + int retval=1; + PSI_thread *psi_thread; +#ifndef DBUG_OFF + thd->event_scheduler.set_explain = 0; +#endif + thread_attach(thd, (char *)&thd, &psi_thread); + ulonglong now= microsecond_interval_timer(); + thd->prior_thr_create_utime= now; + thd->start_utime= now; + thd->thr_create_utime= now; + + if (PSI_server) + { + thd->event_scheduler.m_psi = + PSI_server->new_thread(key_thread_one_connection, thd, thd->thread_id); + PSI_server->set_thread(thd->event_scheduler.m_psi); + } + + if (setup_connection_thread_globals(thd) == 0) + { + if (login_connection(thd) == 0) + { + prepare_new_connection_state(thd); + retval = thd_is_connection_alive(thd)?0:-1; + thd->net.reading_or_writing= 1; + } + } + + thread_detach(thd, psi_thread); + return retval; +} + +void threadpool_remove_connection(THD *thd) +{ + PSI_thread *save_psi_thread; + + thread_attach(thd, (char *)&thd, &save_psi_thread); + thd->killed= KILL_CONNECTION; + + thd->net.reading_or_writing= 0; + + end_connection(thd); + close_connection(thd, 0); + + mysql_mutex_lock(&thd->LOCK_thd_data); + thd->event_scheduler.data= NULL; + mysql_mutex_unlock(&thd->LOCK_thd_data); + + unlink_thd(thd); + mysql_mutex_unlock(&LOCK_thread_count); + mysql_cond_broadcast(&COND_thread_count); + DBUG_POP(); + if (PSI_server) + PSI_server->delete_current_thread(); + pthread_setspecific(THR_THD, NULL); +} + +int threadpool_process_request(THD *thd) +{ + int retval= 0; + PSI_thread *psi_thread; + thread_attach(thd, (char *)&thd, &psi_thread); + + if (thd->killed == KILL_CONNECTION) + { + /* + kill flag can be set have been killed by + timeout handler or by a KILL command + */ + thread_detach(thd, psi_thread); + return 1; + } + + for(;;) + { + Vio *vio; + thd->net.reading_or_writing= 0; + mysql_audit_release(thd); + + if ((retval= do_command(thd)) != 0) + break ; + + if (!thd_is_connection_alive(thd)) + { + retval= 1; + break; + } + + vio= thd->net.vio; + if (!vio->has_data(vio)) + { + /* + More info on this debug sync is in sql_parse.cc + */ + DEBUG_SYNC(thd, "before_do_command_net_read"); + break; + } + } + thread_detach(thd, psi_thread); + if (!retval) + thd->net.reading_or_writing= 1; + return retval; +} + + +/* + Scheduler struct, individual functions are implemented + in threadpool_unix.cc or threadpool_win.cc +*/ + +extern bool tp_init(); +extern void tp_add_connection(THD*); +extern void tp_wait_begin(THD *, int); +extern void tp_wait_end(THD*); +extern void tp_post_kill_notification(THD *thd); +extern void tp_end(void); + +static scheduler_functions tp_scheduler_functions= +{ + 0, // max_threads + NULL, + NULL, + tp_init, // init + NULL, // init_new_connection_thread + tp_add_connection, // add_connection + tp_wait_begin, // thd_wait_begin + tp_wait_end, // thd_wait_end + tp_post_kill_notification, // post_kill_notification + NULL, // end_thread + tp_end // end +}; + +extern void scheduler_init(); + +void pool_of_threads_scheduler(struct scheduler_functions *func, + ulong *arg_max_connections, + uint *arg_connection_count) +{ + *func = tp_scheduler_functions; + func->max_threads= *arg_max_connections + 1; + func->max_connections= arg_max_connections; + func->connection_count= arg_connection_count; + scheduler_init(); +} diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc new file mode 100644 index 00000000000..9fa95f151a5 --- /dev/null +++ b/sql/threadpool_unix.cc @@ -0,0 +1,1238 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __linux__ +#include +typedef struct epoll_event native_event; +#endif +#if defined (__FreeBSD__) || defined (__APPLE__) +#include +typedef struct kevent native_event; +#endif +#if defined (__sun) +#include +typedef port_event_t native_event; +#endif + + +static PSI_mutex_key key_group_mutex; +static PSI_mutex_key key_timer_mutex; +static PSI_mutex_info mutex_list[]= +{ + { &key_group_mutex, "group_mutex", 0}, + { &key_timer_mutex, "timer_mutex", PSI_FLAG_GLOBAL} +}; + +static PSI_cond_key key_worker_cond; +static PSI_cond_key key_timer_cond; +static PSI_cond_info cond_list[]= +{ + { &key_worker_cond, "worker_cond", 0}, + { &key_timer_cond, "timer_cond", PSI_FLAG_GLOBAL} +}; + +static PSI_thread_key key_worker_thread; +static PSI_thread_key key_timer_thread; +static PSI_thread_info thread_list[] = +{ + {&key_worker_thread, "worker_thread", 0}, + {&key_timer_thread, "timer_thread", PSI_FLAG_GLOBAL} +}; + + +TP_STATISTICS tp_stats; + + +struct thread_group_t; + +/* Per-thread structure for workers */ +struct worker_thread_t +{ + mysql_cond_t cond; + bool woken; + thread_group_t* thread_group; + ulonglong event_count; /* Stats: number of executed requests */ + SLIST_ENTRY(worker_thread_t) ptr; +}; + +/* + Data associated with an io event (also can be sent with with explicit + post_event()) +*/ +struct pool_event_t +{ + STAILQ_ENTRY (pool_event_t) next; + void *data; +}; +static pool_event_t POOL_SHUTDOWN_EVENT; + +struct thread_group_t +{ + + mysql_mutex_t mutex; + STAILQ_HEAD(queue_listhead, pool_event_t) queue; + SLIST_HEAD(wait_listhead, worker_thread_t) waiting_threads; + int pollfd; + int thread_count; + int active_thread_count; + int pending_thread_start_count; + int connection_count; + bool shutdown; + bool stalled; + int shutdown_pipe[2]; + worker_thread_t *listener; + pthread_attr_t *pthread_attr; + ulonglong last_thread_creation_time; + /* Stats for the deadlock detection timer routine.*/ + ulonglong io_event_count; + ulonglong queue_event_count; +} MY_ALIGNED(512); + +static thread_group_t all_groups[128]; + +/* Global timer for all groups */ +struct pool_timer_t +{ + mysql_mutex_t mutex; + mysql_cond_t cond; + int tick_interval; + volatile ulonglong current_microtime; + volatile ulonglong next_timeout_check; + bool shutdown; +}; + +static pool_timer_t pool_timer; + +struct connection_t +{ + pool_event_t event; + THD *thd; + thread_group_t *thread_group; + ulonglong abs_wait_timeout; + bool logged_in; + bool waiting; +}; + +/* Externals functions and variables we use */ +extern uint thread_created; +extern void scheduler_init(); +extern pthread_attr_t *get_connection_attrib(void); +extern int skip_net_wait_timeout; + + +static void post_event(thread_group_t *thread_group, pool_event_t* ev); +static int wake_thread(thread_group_t *thread_group); +static void handle_event(pool_event_t *ev); +static int wake_or_create_thread(thread_group_t *thread_group); +static int create_worker(thread_group_t *thread_group); +static void *worker_main(void *param); +static void check_stall(thread_group_t *thread_group); +static void connection_abort(connection_t *connection); +void tp_post_kill_notification(THD *thd); +static void set_wait_timeout(connection_t *connection); +static void set_next_timeout_check(ulonglong abstime); + + +/** + Asynchronous network IO. + + We use native edge-triggered network IO multiplexing facility. + This maps to different APIs on different Unixes. + + Supported are currently Linux with epoll, Solaris with event ports, + OSX and BSD with kevent. All those API's are used with one-shot flags + (the event is signalled once client has written something into the socket, + then socket is removed from the "poll-set" until the command is finished, + and we need to re-arm/re-register socket) + + No implementation for poll/select/AIO is currently provided. + + The API closely resembles all of the above mentioned platform APIs + and consists of following functions. + + - io_poll_create() + Creates an io_poll descriptor + On Linux: epoll_create() + + - io_poll_associate_fd(int poll_fd, int fd, void *data) + Associate file descriptor with io poll descriptor + On Linux : epoll_ctl(..EPOLL_CTL_ADD)) + + - io_poll_disassociate_fd(int pollfd, int fd) + Associate file descriptor with io poll descriptor + On Linux: epoll_ctl(..EPOLL_CTL_DEL) + + + - io_poll_start_read(int poll_fd,int fd, void *data) + The same as io_poll_associate_fd(), but cannot be used before + io_poll_associate_fd() was called. + On Linux : epoll_ctl(..EPOLL_CTL_MOD) + + - io_poll_wait (int pollfd, native_event *native_events, int maxevents, + int timeout_ms) + + wait until one or more descriptors added with io_poll_associate_fd() + or io_poll_start_read() becomes readable. Data associated with + descriptors can be retrieved from native_events array, using + native_event_get_userdata() function. + + On Linux: epoll_wait() +*/ + +#if defined (__linux__) +static int io_poll_create() +{ + return epoll_create(1); +} + + +int io_poll_associate_fd(int pollfd, int fd, void *data) +{ + struct epoll_event ev; + ev.data.ptr= data; + ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT; + return epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &ev); +} + + + +int io_poll_start_read(int pollfd, int fd, void *data) +{ + struct epoll_event ev; + ev.data.ptr= data; + ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT; + return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev); +} + +void io_poll_disassociate_fd(int pollfd, int fd) +{ + struct epoll_event ev; + epoll_ctl(pollfd, EPOLL_CTL_DEL, fd, &ev); +} + + +int io_poll_wait(int pollfd, native_event *native_events, int maxevents, + int timeout_ms) +{ + int ret; + do + { + ret = epoll_wait(pollfd, native_events, maxevents, timeout_ms); + } + while(ret == -1 && errno == EINTR); + return ret; +} + +static void *native_event_get_userdata(native_event *event) +{ + return event->data.ptr; +} + +#elif defined (__FreeBSD__) || defined (__APPLE__) +int io_poll_create() +{ + return kqueue(); +} + +int io_poll_start_read(int pollfd, int fd, void *data) +{ + struct kevent ke; + EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ENABLE|EV_CLEAR, + 0, 0, data); + return kevent(pollfd, &ke, 1, 0, 0, 0); +} + + +int io_poll_associate_fd(int pollfd, int fd, void *data) +{ + return io_poll_start_read(poolfd,fd, data); +} + + +int io_poll_disassociate_fd(thread_group_t *thread_group, int fd) +{ + struct kevent ke; + EV_SET(&ke,fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + return kevent(thread_group->pollfd, &ke, 1, 0, 0, 0); +} + + +int io_poll_wait(int pollfd, struct kevent *events, int maxevents, int timeout_ms) +{ + struct timespec ts; + int ret; + if (timeout_ms >= 0) + { + ts.tv_sec= timeout_ms/1000; + ts.tv_nsec= (timeout_ms%1000)*1000000; + } + do + { + ret= kevent(pollfd, 0, 0, events, maxevents, + (timeout_ms >= 0)?&ts:NULL); + } + while (ret == -1 && errno == EINTR); + if (ret > 0) + { + /* Disable monitoring for the events we that we dequeued */ + for (int i=0; i < ret; i++) + { + struct kevent *ke = &events[i]; + EV_SET(ke, ke->ident, EVFILT_READ, EV_ADD|EV_DISABLE, + 0, 0, ke->udata); + } + kevent(pollfd, events, ret, 0, 0, 0); + } + return ret; +} + +static void* native_event_get_userdata(native_event *event) +{ + return event->udata; +} +#elif defined (__sun) +static int io_poll_create() +{ + return port_create(); +} + +int io_poll_start_read(int pollfd, int fd, void *data) +{ + return port_associate(pollfd, PORT_SOURCE_FD, fd, POLLIN, data); +} + +static int io_poll_associate_fd(int pollfd, int fd, void *data) +{ + return io_poll_start_read(pollfd, fd, data); +} + +int io_poll_wait(int pollfd, native_event *events, int maxevents, int timeout_ms) +{ + struct timespec ts; + int ret; + uint_t nget= 1; + if (timeout_ms >= 0) + { + ts.tv_sec= timeout_ms/1000; + ts.tv_nsec= (timeout_ms%1000)*1000000; + } + do + { + ret= port_getn(pollfd, events, maxevents, &nget, + (timeout_ms >= 0)?&ts:NULL); + } + while (ret == -1 && errno == EINTR); + return nget; +} +static void* native_event_get_userdata(native_event *event) +{ + return event->portev_user; +} +#else +#error not ported yet to this OS +#endif + + + + +/* Dequeue element from a workqueue */ +static pool_event_t *queue_get(thread_group_t *thread_group) +{ + DBUG_ENTER("queue_get"); + pool_event_t *ev= NULL; + thread_group->queue_event_count++; + ev= STAILQ_FIRST(&thread_group->queue); + if (ev) + { + STAILQ_REMOVE_HEAD(&thread_group->queue,next); + } + DBUG_RETURN(ev); +} + +/* Check if workqueue is empty. */ +static bool queue_is_empty(thread_group_t* thread_group) +{ + DBUG_ENTER("queue_is_empty"); + bool empty= (STAILQ_FIRST(&thread_group->queue) == NULL); + DBUG_RETURN(empty); +} + +static void queue_put(thread_group_t *thread_group, pool_event_t *event) +{ + DBUG_ENTER("queue_put"); + STAILQ_INSERT_TAIL(&thread_group->queue, event, next); + DBUG_VOID_RETURN; +} + +static void increment_active_threads(thread_group_t *thread_group) +{ + my_atomic_add32(&tp_stats.num_waiting_threads,-1); + thread_group->active_thread_count++; +} + +static void decrement_active_threads(thread_group_t *thread_group) +{ + my_atomic_add32(&tp_stats.num_waiting_threads,1); + thread_group->active_thread_count--; +} + + +/* + Handle wait timeout : + Find connections that have been idle for too long and kill them. + Also, recalculate time when next timeout check should run. +*/ +static void timeout_check(pool_timer_t *timer) +{ + DBUG_ENTER("timeout_check"); + + mysql_mutex_lock(&LOCK_thread_count); + I_List_iterator it(threads); + + /* Reset next timeout check, it will be recalculated in the loop below */ + my_atomic_fas64((volatile int64*)&timer->next_timeout_check, ULONGLONG_MAX); + + THD *thd; + while ((thd=it++)) + { + if (thd->net.reading_or_writing != 1) + continue; + + connection_t *connection= (connection_t *)thd->scheduler.data; + if (!connection) + continue; + + if(connection->abs_wait_timeout < timer->current_microtime) + { + /* Wait timeout exceeded, kill connection. */ + mysql_mutex_lock(&thd->LOCK_thd_data); + thd->killed = THD::KILL_CONNECTION; + tp_post_kill_notification(thd); + mysql_mutex_unlock(&thd->LOCK_thd_data); + } + else + { + set_next_timeout_check(connection->abs_wait_timeout); + } + } + mysql_mutex_unlock(&LOCK_thread_count); + DBUG_VOID_RETURN; +} + + +/* + Timer thread. + + Periodically, check if one of the thread groups is stalled. Stalls happen if + events are not being dequeued from the queue, or from the network, Primary + reason for stall can be a lengthy executing non-blocking request. It could + also happen that thread is waiting but wait_begin/wait_end is forgotten by + storage engine. Timer thread will create a new thread in group in case of + a stall. + + Besides checking for stalls, timer thread is also responsible for terminating + clients that have been idle for longer than wait_timeout seconds. +*/ +static void* timer_thread(void *param) +{ + uint i; + + pool_timer_t* timer=(pool_timer_t *)param; + timer->next_timeout_check= ULONGLONG_MAX; + timer->current_microtime= my_micro_time(); + + my_thread_init(); + DBUG_ENTER("timer_thread"); + + for(;;) + { + struct timespec ts; + set_timespec_nsec(ts,timer->tick_interval*1000000); + mysql_mutex_lock(&timer->mutex); + int err = mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts); + if (timer->shutdown) + break; + if (err == ETIMEDOUT) + { + timer->current_microtime= my_micro_time(); + + /* Check stallls in thread groups */ + for(i=0; i< threadpool_size;i++) + { + if(all_groups[i].connection_count) + check_stall(&all_groups[i]); + } + + /* Check if any client exceeded wait_timeout */ + if (timer->next_timeout_check <= timer->current_microtime) + timeout_check(timer); + } + mysql_mutex_unlock(&timer->mutex); + } + DBUG_POP(); + my_thread_end(); + return NULL; +} + + + +void check_stall(thread_group_t *thread_group) +{ + if (mysql_mutex_trylock(&thread_group->mutex) != 0) + { + /* Something happens. Don't disturb */ + return; + } + + /* + Check if listener is present. If not, check whether any IO + events were dequeued since last time. If not, this means + listener is either in tight loop or thd_wait_begin() + was forgotten. Create a new worker(it will make itself listener). + */ + if (!thread_group->listener && !thread_group->io_event_count) + { + wake_or_create_thread(thread_group); + mysql_mutex_unlock(&thread_group->mutex); + return; + } + + /* Reset io event count */ + thread_group->io_event_count= 0; + + /* + Check whether requests from the workqueue are being dequeued. + */ + if (!queue_is_empty(thread_group) && !thread_group->queue_event_count) + { + thread_group->stalled= true; + wake_or_create_thread(thread_group); + } + + /* Reset queue event count */ + thread_group->queue_event_count= 0; + + mysql_mutex_unlock(&thread_group->mutex); +} + + + +static void start_timer(pool_timer_t* timer) +{ + pthread_t thread_id; + DBUG_ENTER("start_timer"); + mysql_mutex_init(key_timer_mutex,&timer->mutex, NULL); + mysql_cond_init(key_timer_cond, &timer->cond, NULL); + timer->shutdown = false; + mysql_thread_create(key_timer_thread,&thread_id, NULL, timer_thread, timer); + DBUG_VOID_RETURN; +} + +static void stop_timer(pool_timer_t *timer) +{ + DBUG_ENTER("stop_timer"); + mysql_mutex_lock(&timer->mutex); + timer->shutdown = true; + mysql_cond_signal(&timer->cond); + mysql_mutex_unlock(&timer->mutex); + DBUG_VOID_RETURN; +} + +#define MAX_EVENTS 1024 + +/* + Poll for socket events and distribute them to worker threads. + In many case current thread will handle single event itself. +*/ +static pool_event_t * listener(worker_thread_t *current_thread, + thread_group_t *thread_group) +{ + DBUG_ENTER("listener"); + + for(;;) + { + native_event ev[MAX_EVENTS]; + int cnt; + + if (thread_group->shutdown) + { + DBUG_RETURN(&POOL_SHUTDOWN_EVENT); + } + do + { + cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1); + } + while(cnt <= 0 && errno == EINTR); + + if (cnt <=0) + { + DBUG_ASSERT(thread_group->shutdown); + DBUG_RETURN(&POOL_SHUTDOWN_EVENT); + } + + /* + Put events to queue, maybe wakeup workers. + If queue is currently empty, listener will return + so the current thread handles query itself, this avoids + wakeups and context switches. But if queue is not empty + this smells like a flood of queries, and the listener + stays. + */ + mysql_mutex_lock(&thread_group->mutex); + + if (thread_group->shutdown) + { + mysql_mutex_unlock(&thread_group->mutex); + DBUG_RETURN(&POOL_SHUTDOWN_EVENT); + } + + thread_group->io_event_count += cnt; + bool pick_event= queue_is_empty(thread_group); + + for(int i=(pick_event)?1:0; i < cnt ; i++) + { + pool_event_t *e= (pool_event_t *)native_event_get_userdata(&ev[i]); + queue_put(thread_group, e); + } + + /* Wake at most one worker thread */ + if(thread_group->active_thread_count==0 && + /*!queue_is_empty(thread_group)*/ !pick_event) + { + if(wake_thread(thread_group)) + { + if(thread_group->thread_count == 1) + create_worker(thread_group); + } + } + mysql_mutex_unlock(&thread_group->mutex); + + if (pick_event) + DBUG_RETURN((pool_event_t *)(native_event_get_userdata(&ev[0]))); + } +} + + + + +/* Creates a new worker thread. thread_mutex must be held when calling this function */ + +static int create_worker(thread_group_t *thread_group) +{ + pthread_t thread_id; + int err; + DBUG_ENTER("create_worker"); + if (tp_stats.num_worker_threads >= (int)threadpool_max_threads) + { + DBUG_PRINT("info", + ("Cannot create new thread (maximum allowed threads reached)")); + DBUG_RETURN(-1); + } + + err= pthread_create(&thread_id, thread_group->pthread_attr, worker_main, thread_group); + if (!err) + { + thread_group->pending_thread_start_count++; + thread_group->last_thread_creation_time=my_micro_time(); + } + DBUG_RETURN(err); +} + + +/* + Wakes a worker thread, or creates a new one. + + Worker creation is throttled, so we avoid too many threads + to be created during the short time. +*/ +static int wake_or_create_thread(thread_group_t *thread_group) +{ + ulonglong now; + ulonglong time_since_last_thread_created; + + DBUG_ENTER("wake_or_create_thread"); + + if (wake_thread(thread_group) == 0) + DBUG_RETURN(0); + + if (thread_group->pending_thread_start_count > 0) + DBUG_RETURN(-1); + + if (thread_group->thread_count < 4) + { + DBUG_RETURN(create_worker(thread_group)); + } + + now = my_micro_time(); + time_since_last_thread_created = + (now - thread_group->last_thread_creation_time)/1000; + + if (thread_group->active_thread_count == 0) + { + /* + We're better off creating a new thread here with no delay, as + others threads (at least 4) are all blocking and there was no sleeping + thread to wakeup. It smells like deadlock or very slowly executing + requests, e.g sleeps or user locks. + */ + DBUG_RETURN(create_worker(thread_group)); + } + + /* Throttle thread creation. */ + if ((thread_group->thread_count < 8 && time_since_last_thread_created > 50) + || (thread_group->thread_count < 16 && time_since_last_thread_created > 100) + || (time_since_last_thread_created > 200)) + { + DBUG_RETURN(create_worker(thread_group)); + } + + DBUG_RETURN(-1); +} + + + +/* Initialize thread group */ +int thread_group_init(thread_group_t *thread_group, pthread_attr_t* thread_attr) +{ + DBUG_ENTER("thread_group_init"); + + memset(thread_group, 0, sizeof(thread_group_t)); + thread_group->pthread_attr = thread_attr; + mysql_mutex_init(key_group_mutex, &thread_group->mutex, NULL); + STAILQ_INIT(&thread_group->queue); + SLIST_INIT(&thread_group->waiting_threads); + + thread_group->pending_thread_start_count= 0; + thread_group->pollfd= io_poll_create(); + thread_group->stalled= false; + if (thread_group->pollfd < 0) + { + DBUG_RETURN(-1); + } + if (pipe(thread_group->shutdown_pipe)) + { + DBUG_RETURN(-1); + } + if (io_poll_associate_fd(thread_group->pollfd, + thread_group->shutdown_pipe[0], &POOL_SHUTDOWN_EVENT)) + { + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + + +/* + Wake single sleeping thread in pool. Optionally, tell this thread + to listen to socket io notification. + */ +static int wake_thread(thread_group_t *thread_group) +{ + DBUG_ENTER("wake_thread"); + worker_thread_t *thread = SLIST_FIRST(&thread_group->waiting_threads); + if(thread) + { + thread->woken= true; + SLIST_REMOVE_HEAD(&thread_group->waiting_threads, ptr); + if (mysql_cond_signal(&thread->cond)) + abort(); + DBUG_RETURN(0); + } + DBUG_RETURN(-1); /* no thread- missed wakeup*/ +} + + +/* + Shutdown thread group. +*/ +static void thread_group_close(thread_group_t *thread_group) +{ + DBUG_ENTER("thread_group_close"); + + char c= 0; + + mysql_mutex_lock(&thread_group->mutex); + thread_group->shutdown= true; + thread_group->listener= NULL; + + /* Wake listener. */ + if (write(thread_group->shutdown_pipe[1], &c, 1) < 0) + DBUG_VOID_RETURN; + + /* Wake all workers. */ + while(wake_thread(thread_group) == 0) {}; + mysql_mutex_unlock(&thread_group->mutex); + +#if 0 + /* Wait until workers terminate */ + while(thread_group->thread_count) + usleep(1000); +#endif + + DBUG_VOID_RETURN; +} + + +/* + Post a task to the workqueue, maybe wake a worker so + it picks the task. +*/ +static void post_event(thread_group_t *thread_group, pool_event_t* ev) +{ + DBUG_ENTER("post_event"); + + mysql_mutex_lock(&thread_group->mutex); + STAILQ_INSERT_TAIL(&thread_group->queue, ev, next); + if (thread_group->active_thread_count == 0) + { + wake_or_create_thread(thread_group); + } + mysql_mutex_unlock(&thread_group->mutex); + DBUG_VOID_RETURN; +} + + +/* + Check if pool is already overcommited. + This is used to prevent too many threads executing at the same time, + if the workload is not CPU bound. +*/ +static bool too_many_threads(thread_group_t *thread_group) +{ + return (thread_group->active_thread_count > 4 && !thread_group->stalled); +} + + + +/* + Dequeue a work item. + + If it is not immediately available, thread will sleep until + work is available (it also can become IO listener for a while). +*/ +int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, + pool_event_t **ev, struct timespec *ts) +{ + DBUG_ENTER("get_event"); + + pool_event_t *first_event = NULL; + int err=0; + + mysql_mutex_lock(&thread_group->mutex); + + decrement_active_threads(thread_group); + DBUG_ASSERT(thread_group->active_thread_count >= 0); + + do + { + if (thread_group->shutdown) + break; + + /* Check if queue is not empty */ + if (!too_many_threads(thread_group)) + { + first_event= queue_get(thread_group); + if(first_event) + break; + } + + /* If there is currently no listener in the group, become one. */ + if(!thread_group->listener) + { + thread_group->listener= current_thread; + mysql_mutex_unlock(&thread_group->mutex); + + first_event= listener(current_thread, thread_group); + + mysql_mutex_lock(&thread_group->mutex); + /* There is no listener anymore, it just returned. */ + thread_group->listener= NULL; + break; + } + + /* + Last thing we try before going to sleep is to + pick a single event via epoll, without waiting (timeout 0) + */ + if (!too_many_threads(thread_group)) + { + native_event nev; + if (io_poll_wait(thread_group->pollfd,&nev,1, 0) == 1) + { + thread_group->io_event_count++; + first_event = (pool_event_t *)native_event_get_userdata(&nev); + break; + } + } + + + /* And now, finally sleep */ + current_thread->woken = false; /* wake() sets this to true */ + + /* + Add current thread to the head of the waiting list and wait. + It is important to add thread to the head rather than tail + as it ensures LIFO wakeup order (hot caches, working inactivity timeout) + */ + SLIST_INSERT_HEAD(&thread_group->waiting_threads, current_thread, ptr); + + if(ts) + err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, ts); + else + err = mysql_cond_wait(¤t_thread->cond, &thread_group->mutex); + + if (!current_thread->woken) + { + /* + Thread was not signalled by wake(), it might be a spurious wakeup or + a timeout. Anyhow, we need to remove ourselves from the list now. + If thread was explicitly woken, than caller removed us from the list. + */ + SLIST_REMOVE(&thread_group->waiting_threads, current_thread, worker_thread_t, ptr); + } + + if(err) + break; + + } + while(true); + + thread_group->stalled= false; + increment_active_threads(thread_group); + mysql_mutex_unlock(&thread_group->mutex); + + + if (first_event) + *ev = first_event; + else + *ev = &POOL_SHUTDOWN_EVENT; + + DBUG_RETURN(err); +} + + + +/* + Tells the pool that thread starts waiting on IO, lock, condition, + sleep() or similar. + + Will wake another worker, and if there is no listener will + promote a listener, +*/ +void wait_begin(thread_group_t *thread_group) +{ + DBUG_ENTER("wait_begin"); + mysql_mutex_lock(&thread_group->mutex); + decrement_active_threads(thread_group); + DBUG_ASSERT(thread_group->active_thread_count >=0); + DBUG_ASSERT(thread_group->connection_count > 0); + + if((thread_group->active_thread_count == 0) && + (!queue_is_empty(thread_group) || !thread_group->listener)) + { + wake_or_create_thread(thread_group); + } + + mysql_mutex_unlock(&thread_group->mutex); + DBUG_VOID_RETURN; +} + +/* + Tells the pool current thread finished waiting. +*/ +void wait_end(thread_group_t *thread_group) +{ + DBUG_ENTER("wait_end"); + mysql_mutex_lock(&thread_group->mutex); + increment_active_threads(thread_group); + mysql_mutex_unlock(&thread_group->mutex); + DBUG_VOID_RETURN; +} + + +/* Scheduler */ +connection_t *alloc_connection(THD *thd) +{ + DBUG_ENTER("alloc_connection"); + + connection_t* connection = (connection_t *)my_malloc(sizeof(connection_t),0); + if (connection) + { + connection->thd = thd; + connection->waiting= false; + connection->logged_in= false; + connection->abs_wait_timeout= ULONGLONG_MAX; + } + DBUG_RETURN(connection); +} + + + +/* + Add a new connection to thread pool.. +*/ +void tp_add_connection(THD *thd) +{ + DBUG_ENTER("tp_add_connection"); + + threads.append(thd); + mysql_mutex_unlock(&LOCK_thread_count); + connection_t *c= alloc_connection(thd); + if(c) + { + c->thread_group= &all_groups[c->thd->thread_id%threadpool_size]; + mysql_mutex_lock(&c->thread_group->mutex); + c->thread_group->connection_count++; + mysql_mutex_unlock(&c->thread_group->mutex); + c->thd->scheduler.data = c; + post_event(c->thread_group,&c->event); + } + + DBUG_VOID_RETURN; +} + + +static void connection_abort(connection_t *c) +{ + DBUG_ENTER("connection_abort"); + mysql_mutex_lock(&c->thread_group->mutex); + c->thread_group->connection_count--; + mysql_mutex_unlock(&c->thread_group->mutex); + + threadpool_remove_connection(c->thd); + my_free(c); + DBUG_VOID_RETURN; +} + +void tp_post_kill_notification(THD *thd) +{ + DBUG_ENTER("tp_post_kill_notification"); + if (current_thd == thd || thd->system_thread) + DBUG_VOID_RETURN; + + if (thd->net.vio) + vio_shutdown(thd->net.vio, SHUT_RD); + DBUG_VOID_RETURN; +} + +void tp_wait_begin(THD *thd, int type) +{ + DBUG_ENTER("tp_wait_begin"); + + if (!thd) + DBUG_VOID_RETURN; + + connection_t *connection = (connection_t *)thd->scheduler.data; + if(connection) + { + DBUG_ASSERT(!connection->waiting); + connection->waiting= true; + wait_begin(connection->thread_group); + } + DBUG_VOID_RETURN; +} + + +void tp_wait_end(THD *thd) +{ + DBUG_ENTER("tp_wait_end"); + if (!thd) + DBUG_VOID_RETURN; + + connection_t *connection = (connection_t *)thd->scheduler.data; + if(connection) + { + DBUG_ASSERT(connection->waiting); + connection->waiting = false; + wait_end(connection->thread_group); + } + DBUG_VOID_RETURN; +} + + +static void set_next_timeout_check(ulonglong abstime) +{ + DBUG_ENTER("set_next_timeout_check"); + while(abstime < pool_timer.next_timeout_check) + { + longlong old= (longlong)pool_timer.next_timeout_check; + my_atomic_cas64((volatile int64*)&pool_timer.next_timeout_check, + &old, abstime); + } + DBUG_VOID_RETURN; +} + +static void set_wait_timeout(connection_t *c) +{ + DBUG_ENTER("set_wait_timeout"); + /* + Calculate wait deadline for this connection. + Instead of using my_micro_time() which has a syscall + overhead, use pool_timer.current_microtime and take + into account that its value could be off by at most + one tick interval. + */ + + c->abs_wait_timeout= pool_timer.current_microtime + + 1000LL*pool_timer.tick_interval + + 1000000LL*c->thd->variables.net_wait_timeout; + + set_next_timeout_check(c->abs_wait_timeout); + DBUG_VOID_RETURN; +} + +static void handle_event(pool_event_t *ev) +{ + + DBUG_ENTER("handle_event"); + + /* Normal case, handle query on connection */ + connection_t *c = (connection_t*)(void *)ev; + bool do_login = (!c->logged_in); + int ret; + + if (do_login) + { + ret= threadpool_add_connection(c->thd); + c->logged_in= true; + } + else + { + ret= threadpool_process_request(c->thd); + } + + if(!ret) + { + set_wait_timeout(c); + int fd = c->thd->net.vio->sd; + if (do_login) + { + ret= io_poll_associate_fd(c->thread_group->pollfd, fd, c); + } + else + ret= io_poll_start_read(c->thread_group->pollfd, fd, c); + } + + if (ret) + { + connection_abort(c); + } + + DBUG_VOID_RETURN; +} + + +static void *worker_main(void *param) +{ + + worker_thread_t this_thread; + + thread_created++; + pthread_detach_this_thread(); + my_thread_init(); + DBUG_ENTER("worker_main"); + + thread_group_t *thread_group = (thread_group_t *)param; + + /* Init per-thread structure */ + mysql_cond_init(key_worker_cond, &this_thread.cond, NULL); + this_thread.thread_group= thread_group; + this_thread.event_count=0; + + mysql_mutex_lock(&thread_group->mutex); + tp_stats.num_worker_threads++; + thread_group->thread_count++; + thread_group->active_thread_count++; + thread_group->pending_thread_start_count--; + mysql_mutex_unlock(&thread_group->mutex); + + /* Run event loop */ + for(;;) + { + struct pool_event_t *ev; + struct timespec ts; + set_timespec(ts,threadpool_idle_timeout); + if (get_event(&this_thread, thread_group, &ev, &ts) + || ev == &POOL_SHUTDOWN_EVENT) + { + break; + } + this_thread.event_count++; + handle_event(ev); + } + + /* Thread shutdown: cleanup per-worker-thread structure. */ + mysql_cond_destroy(&this_thread.cond); + + mysql_mutex_lock(&thread_group->mutex); + thread_group->active_thread_count--; + thread_group->thread_count--; + tp_stats.num_worker_threads--; + mysql_mutex_unlock(&thread_group->mutex); + + /* If it is the last thread in pool and pool is terminating, destroy pool.*/ + if (thread_group->shutdown && (thread_group->thread_count == 0)) + { + /* last thread existing, cleanup the pool structure */ + mysql_mutex_destroy(&thread_group->mutex); + } + DBUG_POP(); + my_thread_end(); + return NULL; +} + + +static bool started=false; + +bool tp_init() +{ + DBUG_ENTER("tp_init"); + started = true; + scheduler_init(); + skip_net_wait_timeout= 1; + if (threadpool_size == 0) + { + threadpool_size= my_getncpus(); + } + + for(uint i=0; i < threadpool_size; i++) + { + thread_group_init(&all_groups[i], get_connection_attrib()); + } + + #define PSI_register(X) \ + if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) + + PSI_register(mutex); + PSI_register(cond); + PSI_register(thread); + + pool_timer.tick_interval= threadpool_stall_limit; + start_timer(&pool_timer); + DBUG_RETURN(0); +} + +void tp_end() +{ + DBUG_ENTER("tp_end"); + + if (!started) + DBUG_VOID_RETURN; + + stop_timer(&pool_timer); + for(uint i=0; i< threadpool_size; i++) + { + thread_group_close(&all_groups[i]); + } + DBUG_VOID_RETURN; +} diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc new file mode 100644 index 00000000000..4e46e393c24 --- /dev/null +++ b/sql/threadpool_win.cc @@ -0,0 +1,756 @@ +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0601 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +TP_STATISTICS tp_stats; + +#define WEAK_SYMBOL(return_type, function, ...) \ + typedef return_type (WINAPI *pFN_##function)(__VA_ARGS__); \ + static pFN_##function my_##function = (pFN_##function) \ + (GetProcAddress(GetModuleHandle("kernel32"),#function)) + +WEAK_SYMBOL(VOID, CancelThreadpoolIo, PTP_IO); +#define CancelThreadpoolIo my_CancelThreadpoolIo + +WEAK_SYMBOL(VOID, CloseThreadpool, PTP_POOL); +#define CloseThreadpool my_CloseThreadpool + +WEAK_SYMBOL(VOID, CloseThreadpoolIo, PTP_IO); +#define CloseThreadpoolIo my_CloseThreadpoolIo + +WEAK_SYMBOL(VOID, CloseThreadpoolTimer,PTP_TIMER); +#define CloseThreadpoolTimer my_CloseThreadpoolTimer + +WEAK_SYMBOL(VOID, CloseThreadpoolWait,PTP_WAIT); +#define CloseThreadpoolWait my_CloseThreadpoolWait + +WEAK_SYMBOL(PTP_POOL, CreateThreadpool,PVOID); +#define CreateThreadpool my_CreateThreadpool + +WEAK_SYMBOL(PTP_IO, CreateThreadpoolIo, HANDLE, PTP_WIN32_IO_CALLBACK, PVOID , + PTP_CALLBACK_ENVIRON); +#define CreateThreadpoolIo my_CreateThreadpoolIo + +WEAK_SYMBOL(PTP_TIMER, CreateThreadpoolTimer, PTP_TIMER_CALLBACK , + PVOID pv, PTP_CALLBACK_ENVIRON pcbe); +#define CreateThreadpoolTimer my_CreateThreadpoolTimer + +WEAK_SYMBOL(PTP_WAIT, CreateThreadpoolWait, PTP_WAIT_CALLBACK, PVOID, + PTP_CALLBACK_ENVIRON); +#define CreateThreadpoolWait my_CreateThreadpoolWait + +WEAK_SYMBOL(VOID, DisassociateCurrentThreadFromCallback, PTP_CALLBACK_INSTANCE); +#define DisassociateCurrentThreadFromCallback my_DisassociateCurrentThreadFromCallback + +WEAK_SYMBOL(DWORD, FlsAlloc, PFLS_CALLBACK_FUNCTION); +#define FlsAlloc my_FlsAlloc + +WEAK_SYMBOL(PVOID, FlsGetValue, DWORD); +#define FlsGetValue my_FlsGetValue + +WEAK_SYMBOL(BOOL, FlsSetValue, DWORD, PVOID); +#define FlsSetValue my_FlsSetValue + +WEAK_SYMBOL(VOID, SetThreadpoolThreadMaximum, PTP_POOL, DWORD); +#define SetThreadpoolThreadMaximum my_SetThreadpoolThreadMaximum + +WEAK_SYMBOL(BOOL, SetThreadpoolThreadMinimum, PTP_POOL, DWORD); +#define SetThreadpoolThreadMinimum my_SetThreadpoolThreadMinimum + +WEAK_SYMBOL(VOID, SetThreadpoolTimer, PTP_TIMER, PFILETIME,DWORD,DWORD); +#define SetThreadpoolTimer my_SetThreadpoolTimer + +WEAK_SYMBOL(VOID, SetThreadpoolWait, PTP_WAIT,HANDLE,PFILETIME); +#define SetThreadpoolWait my_SetThreadpoolWait + +WEAK_SYMBOL(VOID, StartThreadpoolIo, PTP_IO); +#define StartThreadpoolIo my_StartThreadpoolIo + +WEAK_SYMBOL(VOID, WaitForThreadpoolIoCallbacks,PTP_IO, BOOL); +#define WaitForThreadpoolIoCallbacks my_WaitForThreadpoolIoCallbacks + +WEAK_SYMBOL(VOID, WaitForThreadpoolTimerCallbacks, PTP_TIMER, BOOL); +#define WaitForThreadpoolTimerCallbacks my_WaitForThreadpoolTimerCallbacks + +WEAK_SYMBOL(VOID, WaitForThreadpoolWaitCallbacks, PTP_WAIT, BOOL); +#define WaitForThreadpoolWaitCallbacks my_WaitForThreadpoolWaitCallbacks + +WEAK_SYMBOL(BOOL, SetFileCompletionNotificationModes, HANDLE, UCHAR); +#define SetFileCompletionNotificationModes my_SetFileCompletionNotificationModes + +WEAK_SYMBOL(BOOL, TrySubmitThreadpoolCallback, PTP_SIMPLE_CALLBACK pfns, + PVOID pv,PTP_CALLBACK_ENVIRON pcbe); +#define TrySubmitThreadpoolCallback my_TrySubmitThreadpoolCallback + +WEAK_SYMBOL(PTP_WORK, CreateThreadpoolWork, PTP_WORK_CALLBACK pfnwk, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); +#define CreateThreadpoolWork my_CreateThreadpoolWork + +WEAK_SYMBOL(VOID, SubmitThreadpoolWork,PTP_WORK pwk); +#define SubmitThreadpoolWork my_SubmitThreadpoolWork + +WEAK_SYMBOL(VOID, CloseThreadpoolWork, PTP_WORK pwk); +#define CloseThreadpoolWork my_CloseThreadpoolWork + +#if _MSC_VER >= 1600 +/* Stack size manipulation available only on Win7+ /declarations in VS10 */ +WEAK_SYMBOL(BOOL, SetThreadpoolStackInformation, PTP_POOL, + PTP_POOL_STACK_INFORMATION); +#define SetThreadpoolStackInformation my_SetThreadpoolStackInformation +#endif + +#if _MSC_VER < 1600 +#define SetThreadpoolCallbackPriority(env,prio) +typedef enum _TP_CALLBACK_PRIORITY { + TP_CALLBACK_PRIORITY_HIGH, + TP_CALLBACK_PRIORITY_NORMAL, + TP_CALLBACK_PRIORITY_LOW, + TP_CALLBACK_PRIORITY_INVALID +} TP_CALLBACK_PRIORITY; +#endif + + +/* Log a warning */ +static void tp_log_warning(const char *msg, const char *fct) +{ + sql_print_warning("Threadpool: %s. %s failed (last error %d)",msg, fct, + GetLastError()); +} + + +PTP_POOL pool; +DWORD fls; +extern int skip_net_wait_timeout; + +static bool skip_completion_port_on_success = false; + +/* + Threadpool callbacks. + + io_completion_callback - handle client request + timer_callback - handle wait timeout (kill connection) + shm_read_callback, shm_close_callback - shared memory stuff + login_callback - user login (submitted as threadpool work) + +*/ + +static void CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PTP_TIMER timer); + +static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io); + +static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, + PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result); + +static void CALLBACK shm_close_callback(PTP_CALLBACK_INSTANCE instance, + PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result); + +#define CONNECTION_SIGNATURE 0xAFFEAFFE + +static void check_thread_init(); + +/* Get current time as Windows time */ +static ulonglong now() +{ + ulonglong current_time; + GetSystemTimeAsFileTime((PFILETIME)¤t_time); + return current_time; +} + +/* + Connection structure, encapsulates THD + structures for asynchronous + IO and pool. +*/ + +struct connection_t +{ + THD *thd; + bool logged_in; + HANDLE handle; + OVERLAPPED overlapped; + + /* absolute time for wait timeout (as Windows time) */ + volatile ulonglong timeout; + + PTP_CLEANUP_GROUP cleanup_group; + TP_CALLBACK_ENVIRON callback_environ; + + PTP_IO io; + PTP_TIMER timer; + PTP_WAIT shm_read; +}; + +void init_connection(connection_t *connection) +{ + connection->logged_in = false; + connection->handle= 0; + connection->io= 0; + connection->shm_read= 0; + connection->timer= 0; + connection->logged_in = false; + connection->timeout= ULONGLONG_MAX; + memset(&connection->overlapped, 0, sizeof(OVERLAPPED)); + InitializeThreadpoolEnvironment(&connection->callback_environ); + SetThreadpoolCallbackPool(&connection->callback_environ, pool); + connection->thd = 0; +} + +int init_io(connection_t *connection, THD *thd) +{ + connection->thd= thd; + Vio *vio = thd->net.vio; + switch(vio->type) + { + case VIO_TYPE_SSL: + case VIO_TYPE_TCPIP: + connection->handle= (HANDLE)vio->sd; + break; + case VIO_TYPE_NAMEDPIPE: + connection->handle= (HANDLE)vio->hPipe; + break; + case VIO_TYPE_SHARED_MEMORY: + connection->shm_read= CreateThreadpoolWait(shm_read_callback, connection, + &connection->callback_environ); + if (!connection->shm_read) + { + tp_log_warning("Allocation failed", "CreateThreadpoolWait"); + return -1; + } + break; + default: + abort(); + } + + if (connection->handle) + { + /* Performance tweaks (s. MSDN documentation)*/ + UCHAR flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + if (skip_completion_port_on_success) + { + flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + } + (void)SetFileCompletionNotificationModes(connection->handle, flags); + + /* Assign io completion callback */ + connection->io = CreateThreadpoolIo(connection->handle, + io_completion_callback, connection, &connection->callback_environ); + if(!connection->io) + { + tp_log_warning("Allocation failed", "CreateThreadpoolWait"); + return -1; + } + } + connection->timer = CreateThreadpoolTimer(timer_callback, connection, + &connection->callback_environ); + if (!connection->timer) + { + tp_log_warning("Allocation failed", "CreateThreadpoolWait"); + return -1; + } + + return 0; +} + + +/* + Start asynchronous read +*/ +int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance) +{ + /* Start async read */ + DWORD num_bytes = 0; + static char c; + WSABUF buf; + buf.buf= &c; + buf.len= 0; + DWORD flags=0; + DWORD last_error= 0; + + int retval; + Vio *vio= connection->thd->net.vio; + + if (vio->type == VIO_TYPE_SHARED_MEMORY) + { + SetThreadpoolWait(connection->shm_read, vio->event_server_wrote, NULL); + return 0; + } + if (vio->type == VIO_CLOSED) + { + return -1; + } + + DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP || + vio->type == VIO_TYPE_SSL || + vio->type == VIO_TYPE_NAMEDPIPE); + + OVERLAPPED *overlapped= &connection->overlapped; + PTP_IO io= connection->io; + StartThreadpoolIo(io); + + if (vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SSL) + { + /* Start async io (sockets). */ + if (WSARecv(vio->sd , &buf, 1, &num_bytes, &flags, + overlapped, NULL) == 0) + { + retval= last_error= 0; + } + else + { + retval= -1; + last_error= WSAGetLastError(); + } + } + else + { + /* Start async io (named pipe) */ + if (ReadFile(vio->hPipe, &c, 0, &num_bytes ,overlapped)) + { + retval= last_error= 0; + } + else + { + retval= -1; + last_error= GetLastError(); + } + } + + if (retval == 0 || last_error == ERROR_MORE_DATA) + { + /* + IO successfully finished (synchronously). + If skip_completion_port_on_success is set, we need to handle it right + here, because completion callback would not be executed by the pool. + */ + if(skip_completion_port_on_success) + { + CancelThreadpoolIo(io); + io_completion_callback(instance, connection, overlapped, last_error, + num_bytes, io); + } + return 0; + } + + if(last_error == ERROR_IO_PENDING) + { + return 0; + } + + /* Some error occured */ + CancelThreadpoolIo(io); + return -1; +} + +int login(connection_t *connection, PTP_CALLBACK_INSTANCE instance) +{ + if (threadpool_add_connection(connection->thd) == 0 + && init_io(connection, connection->thd) == 0 + && start_io(connection, instance) == 0) + { + return 0; + } + return -1; +} + +/* + Recalculate wait timeout, maybe reset timer. +*/ +void set_wait_timeout(connection_t *connection, ulonglong old_timeout) +{ + ulonglong new_timeout = now() + + 10000000LL*connection->thd->variables.net_wait_timeout; + + if (new_timeout < old_timeout) + { + SetThreadpoolTimer(connection->timer, (PFILETIME) &new_timeout, 0, 1000); + } + connection->timeout = new_timeout; +} + +/* + Terminates (idle) connection by closing the socket. + This will activate io_completion_callback() in a different thread +*/ +void post_kill_notification(connection_t *connection) +{ + check_thread_init(); + THD *thd=connection->thd; + mysql_mutex_lock(&thd->LOCK_thd_data); + thd->killed = KILL_CONNECTION; + vio_shutdown(thd->net.vio, SHUT_RDWR); + thd->mysys_var= NULL; + mysql_mutex_unlock(&thd->LOCK_thd_data); +} + + +/* Connection destructor */ +void destroy_connection(connection_t *connection) +{ + if (connection->thd) + { + threadpool_remove_connection(connection->thd); + } + + if (connection->io) + { + WaitForThreadpoolIoCallbacks(connection->io, TRUE); + CloseThreadpoolIo(connection->io); + } + + if(connection->shm_read) + { + WaitForThreadpoolWaitCallbacks(connection->shm_read, TRUE); + CloseThreadpoolWait(connection->shm_read); + } + + if(connection->timer) + { + SetThreadpoolTimer(connection->timer, 0, 0, 0); + WaitForThreadpoolTimerCallbacks(connection->timer, TRUE); + CloseThreadpoolTimer(connection->timer); + } + + DestroyThreadpoolEnvironment(&connection->callback_environ); +} + + + +/* + This function should be called first whenever a callback is invoked in the + threadpool, does my_thread_init() if not yet done +*/ +extern ulong thread_created; +static void check_thread_init() +{ + if (FlsGetValue(fls) == NULL) + { + FlsSetValue(fls, (void *)1); + my_thread_init(); + thread_created++; + InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads); + } +} + + +/* + Take care of proper cleanup when threadpool threads exit. + We do not control how threads are created, thus it is our responsibility to + check that my_thread_init() is called on thread initialization and + my_thread_end() on thread destruction. On Windows, FlsAlloc() provides the + thread destruction callbacks. +*/ +static VOID WINAPI thread_destructor(void *data) +{ + if(data) + { + if (InterlockedDecrement((volatile long *)&tp_stats.num_worker_threads) >= 0) + { + /* + The above check for number of thread >= 0 is due to shutdown code ( + see tp_end()) where we forcefully set num_worker_threads to 0, even + if not all threads have shut down yet to the point they would ran Fls + destructors, even after CloseThreadpool(). See also comment in tp_end(). + */ + mysql_mutex_lock(&LOCK_thread_count); + my_thread_end(); + mysql_mutex_unlock(&LOCK_thread_count); + } + } +} + + +/* Scheduler callback : init */ +bool tp_init(void) +{ + fls= FlsAlloc(thread_destructor); + pool= CreateThreadpool(NULL); + if(!pool) + { + sql_print_error("Can't create threadpool. " + "CreateThreadpool() failed with %d. Likely cause is memory pressure", + GetLastError()); + exit(1); + } + + if (threadpool_max_threads) + { + SetThreadpoolThreadMaximum(pool,threadpool_max_threads); + } + + if (threadpool_min_threads) + { + if (!SetThreadpoolThreadMinimum(pool, threadpool_min_threads)) + { + tp_log_warning( "Can't set threadpool minimum threads", + "SetThreadpoolThreadMinimum"); + } + } + + /* + Control stack size (OS must be Win7 or later, plus corresponding SDK) + */ +#if _MSC_VER >=1600 + if (SetThreadpoolStackInformation) + { + TP_POOL_STACK_INFORMATION stackinfo; + stackinfo.StackCommit = 0; + stackinfo.StackReserve = my_thread_stack_size; + if (!SetThreadpoolStackInformation(pool, &stackinfo)) + { + tp_log_warning("Can't set threadpool stack size", + "SetThreadpoolStackInformation"); + } + } +#endif + + skip_net_wait_timeout = 1; + return 0; +} + + +/* + Scheduler callback : Destroy the scheduler. +*/ + +extern "C" uint THR_thread_count; +extern "C" mysql_mutex_t THR_LOCK_threads; +extern "C" mysql_cond_t THR_COND_threads; + +void tp_end(void) +{ + if(pool) + { + SetThreadpoolThreadMaximum(pool, 0); + CloseThreadpool(pool); + + /* + Tell my_global_thread_end() we're complete. + + This would not be necessary if CloseThreadpool() would synchronously + release all threads and wait until they disappear and call all their FLS + destrructors . However, threads in the pool are released asynchronously + and might spend some time in the CRT shutdown code. Thus zero + num_worker_threads, to avoid thread destructor's my_thread_end()s after + this point. + */ + LONG remaining_threads= + InterlockedExchange( (volatile long *)&tp_stats.num_worker_threads, 0); + + if (remaining_threads) + { + mysql_mutex_lock(&THR_LOCK_threads); + THR_thread_count -= remaining_threads; + mysql_cond_signal(&THR_COND_threads); + mysql_mutex_unlock(&THR_LOCK_threads); + } + } + skip_net_wait_timeout= 0; +} + +/* + Notify pool about connection being killed. +*/ +void tp_post_kill_notification(THD *thd) +{ + if (current_thd == thd) + return; /* There is nothing to do.*/ + + if (thd->system_thread) + return; /* Will crash if we attempt to kill system thread. */ + + Vio *vio= thd->net.vio; + + vio_shutdown(vio, SD_BOTH); + +} + +/* + Handle read completion/notification. +*/ +static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io) +{ + if(instance) + { + check_thread_init(); + } + + connection_t *connection = (connection_t*)context; + THD *thd= connection->thd; + ulonglong old_timeout = connection->timeout; + connection->timeout = ULONGLONG_MAX; + + if (threadpool_process_request(connection->thd)) + goto error; + + set_wait_timeout(connection, old_timeout); + if(start_io(connection, instance)) + goto error; + + return; + +error: + /* Some error has occured. */ + if (instance) + DisassociateCurrentThreadFromCallback(instance); + + destroy_connection(connection); + my_free(connection); +} + + +/* Simple callback for login */ +static void CALLBACK login_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PTP_WORK work) +{ + if(instance) + { + check_thread_init(); + } + + connection_t *connection =(connection_t *)context; + if (login(connection, instance) != 0) + { + destroy_connection(connection); + my_free(connection); + } +} + +/* + Timer callback. + Invoked when connection times out (wait_timeout) +*/ +static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, + PVOID parameter, PTP_TIMER timer) +{ + check_thread_init(); + + connection_t *con= (connection_t*)parameter; + ulonglong timeout= con->timeout; + + if (timeout <= now()) + { + con->thd->killed = KILL_CONNECTION; + if(con->thd->net.vio) + vio_shutdown(con->thd->net.vio, SD_BOTH); + } + else if(timeout != ULONGLONG_MAX) + { + /* + Reset timer. + There is a tiny possibility of a race condition, since the value of timeout + could have changed to smaller value in the thread doing io callback. + + Given the relative unimportance of the wait timeout, we accept race + condition. + */ + SetThreadpoolTimer(timer, (PFILETIME)&timeout, 0, 1000); + } +} + + +/* + Shared memory read callback. + Invoked when read event is set on connection. +*/ +static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PTP_WAIT wait,TP_WAIT_RESULT wait_result) +{ + connection_t *con= (connection_t *)context; + /* Disarm wait. */ + SetThreadpoolWait(wait, NULL, NULL); + + /* + This is an autoreset event, and one wakeup is eaten already by threadpool, + and the current state is "not set". Thus we need to reset the event again, + or vio_read will hang. + */ + HANDLE h = con->thd->net.vio->event_server_wrote; + SetEvent(h); + io_completion_callback(instance, context, NULL, 0, 0 , 0); +} + + +/* + Notify the thread pool about a new connection. + NOTE: LOCK_thread_count is locked on entry. This function must unlock it. +*/ +void tp_add_connection(THD *thd) +{ + bool success = false; + connection_t *con = (connection_t *)my_malloc(sizeof(connection_t), 0); + + if (con) + threads.append(thd); + mysql_mutex_unlock(&LOCK_thread_count); + + if(!con) + { + tp_log_warning("Allocation failed", "tp_add_connection"); + return; + } + + init_connection(con); + con->thd= thd; + /* Try to login asynchronously, using threads in the pool */ + PTP_WORK wrk = CreateThreadpoolWork(login_callback,con, &con->callback_environ); + if (wrk) + { + SubmitThreadpoolWork(wrk); + CloseThreadpoolWork(wrk); + } + else + { + /* Likely memory pressure */ + login_callback(NULL, con, NULL); /* deletes connection if something goes wrong */ + } +} + + + +/* + Sets the number of idle threads the thread pool maintains in anticipation of new + requests. +*/ +void tp_set_min_threads(uint val) +{ + if (pool) + SetThreadpoolThreadMinimum(pool, val); +} + +void tp_set_max_threads(uint val) +{ + if (pool) + SetThreadpoolThreadMaximum(pool, val); +} + +void tp_wait_begin(THD *thd, int type) +{ + if (thd && thd->event_scheduler.data) + { + /* TODO: call CallbackMayRunLong() */ + } +} + +void tp_wait_end(THD *thd) +{ + /* Do we need to do anything ? */ +} + diff --git a/vio/vio.c b/vio/vio.c index b8bc7bdae08..aa0d2012afa 100644 --- a/vio/vio.c +++ b/vio/vio.c @@ -49,6 +49,25 @@ static my_bool has_no_data(Vio *vio __attribute__((unused))) return FALSE; } +#ifdef _WIN32 +my_bool vio_shared_memory_has_data(Vio *vio) +{ + return (vio->shared_memory_remain > 0); +} + +int vio_shared_memory_shutdown(Vio *vio, int how) +{ + SetEvent(vio->event_conn_closed); + SetEvent(vio->event_server_wrote); + return 0; +} + +int vio_pipe_shutdown(Vio *vio, int how) +{ + return vio_socket_shutdown(vio, how); /* cancels io */ +} +#endif + /* * Helper to fill most of the Vio* with defaults. */ @@ -89,6 +108,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->poll_read =no_poll_read; vio->is_connected =vio_is_connected_pipe; vio->has_data =has_no_data; + vio->shutdown =vio_pipe_shutdown; vio->timeout=vio_win32_timeout; /* Set default timeout */ @@ -116,7 +136,8 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->poll_read =no_poll_read; vio->is_connected =vio_is_connected_shared_memory; - vio->has_data =has_no_data; + vio->has_data =vio_shared_memory_has_data; + vio->shutdown =vio_shared_memory_shutdown; /* Currently, shared memory is on Windows only, hence the below is ok*/ vio->timeout= vio_win32_timeout; @@ -145,6 +166,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->poll_read =vio_poll_read; vio->is_connected =vio_is_connected; vio->has_data =vio_ssl_has_data; + vio->shutdown =vio_socket_shutdown; DBUG_VOID_RETURN; } #endif /* HAVE_OPENSSL */ @@ -163,6 +185,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->timeout =vio_timeout; vio->poll_read =vio_poll_read; vio->is_connected =vio_is_connected; + vio->shutdown =vio_socket_shutdown; vio->has_data= (flags & VIO_BUFFERED_READ) ? vio_buff_has_data : has_no_data; DBUG_VOID_RETURN; diff --git a/vio/vio_priv.h b/vio/vio_priv.h index 702ba4de38a..3f62c508375 100644 --- a/vio/vio_priv.h +++ b/vio/vio_priv.h @@ -39,6 +39,7 @@ size_t vio_read_pipe(Vio *vio, uchar * buf, size_t size); size_t vio_write_pipe(Vio *vio, const uchar * buf, size_t size); my_bool vio_is_connected_pipe(Vio *vio); int vio_close_pipe(Vio * vio); +int vio_shutdown_pipe(Vio *vio,int how); #endif #ifdef HAVE_SMEM @@ -46,8 +47,11 @@ size_t vio_read_shared_memory(Vio *vio, uchar * buf, size_t size); size_t vio_write_shared_memory(Vio *vio, const uchar * buf, size_t size); my_bool vio_is_connected_shared_memory(Vio *vio); int vio_close_shared_memory(Vio * vio); +my_bool vio_shared_memory_has_data(Vio *vio); +int vio_shutdown_shared_memory(Vio *vio, int how); #endif +int vio_socket_shutdown(Vio *vio, int how); void vio_timeout(Vio *vio,uint which, uint timeout); my_bool vio_buff_has_data(Vio *vio); diff --git a/vio/viosocket.c b/vio/viosocket.c index 4772847abd8..1f129cb8e55 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -131,6 +131,60 @@ size_t vio_write(Vio * vio, const uchar* buf, size_t size) DBUG_RETURN(r); } +#ifdef _WIN32 +static void CALLBACK cancel_io_apc(ULONG_PTR data) +{ + CancelIo((HANDLE)data); +} + +/* + Cancel IO on Windows. + + On XP, issue CancelIo as asynchronous procedure call to the thread that started + IO. On Vista+, simpler cancelation is done with CancelIoEx. +*/ + +static int cancel_io(HANDLE handle, DWORD thread_id) +{ + static BOOL (WINAPI *fp_CancelIoEx) (HANDLE, OVERLAPPED *); + static volatile int first_time= 1; + int rc; + HANDLE thread_handle; + + if (first_time) + { + /* Try to load CancelIoEx using GetProcAddress */ + InterlockedCompareExchangePointer((volatile void *)&fp_CancelIoEx, + GetProcAddress(GetModuleHandle("kernel32"), "CancelIoEx"), NULL); + first_time =0; + } + + if (fp_CancelIoEx) + { + return fp_CancelIoEx(handle, NULL)? 0 :-1; + } + + thread_handle= OpenThread(THREAD_SET_CONTEXT, FALSE, thread_id); + if (thread_handle) + { + rc= QueueUserAPC(cancel_io_apc, thread_handle, (ULONG_PTR)handle); + CloseHandle(thread_handle); + } + return rc; + +} +#endif + +int vio_socket_shutdown(Vio *vio, int how) +{ +#ifdef _WIN32 + return cancel_io((HANDLE)vio->sd, vio->thread_id); +#else + return shutdown(vio->sd, how); +#endif +} + + int vio_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode, my_bool *old_mode) { @@ -726,6 +780,22 @@ void vio_timeout(Vio *vio, uint which, uint timeout) #ifdef __WIN__ +/* + Disable posting IO completion event to the port. + In some cases (synchronous timed IO) we want to skip IOCP notifications. +*/ +static void disable_iocp_notification(OVERLAPPED *overlapped) +{ + HANDLE *handle = &(overlapped->hEvent); + *handle = ((HANDLE)((ULONG_PTR) *handle|1)); +} + +/* Enable posting IO completion event to the port */ +static void enable_iocp_notification(OVERLAPPED *overlapped) +{ + HANDLE *handle = &(overlapped->hEvent); + *handle = (HANDLE)((ULONG_PTR) *handle & ~1); +} /* Finish pending IO on pipe. Honor wait timeout @@ -737,7 +807,7 @@ static size_t pipe_complete_io(Vio* vio, char* buf, size_t size, DWORD timeout_m DBUG_ENTER("pipe_complete_io"); - ret= WaitForSingleObject(vio->pipe_overlapped.hEvent, timeout_ms); + ret= WaitForSingleObjectEx(vio->pipe_overlapped.hEvent, timeout_ms, TRUE); /* WaitForSingleObjects will normally return WAIT_OBJECT_O (success, IO completed) or WAIT_TIMEOUT. @@ -767,7 +837,8 @@ size_t vio_read_pipe(Vio * vio, uchar *buf, size_t size) DBUG_ENTER("vio_read_pipe"); DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf, (uint) size)); - + + disable_iocp_notification(&vio->pipe_overlapped); if (ReadFile(vio->hPipe, buf, (DWORD)size, &bytes_read, &(vio->pipe_overlapped))) { @@ -777,13 +848,14 @@ size_t vio_read_pipe(Vio * vio, uchar *buf, size_t size) { if (GetLastError() != ERROR_IO_PENDING) { + enable_iocp_notification(&vio->pipe_overlapped); DBUG_PRINT("error",("ReadFile() returned last error %d", GetLastError())); DBUG_RETURN((size_t)-1); } retval= pipe_complete_io(vio, buf, size,vio->read_timeout_ms); } - + enable_iocp_notification(&vio->pipe_overlapped); DBUG_PRINT("exit", ("%lld", (longlong)retval)); DBUG_RETURN(retval); } @@ -796,7 +868,7 @@ size_t vio_write_pipe(Vio * vio, const uchar* buf, size_t size) DBUG_ENTER("vio_write_pipe"); DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf, (uint) size)); - + disable_iocp_notification(&vio->pipe_overlapped); if (WriteFile(vio->hPipe, buf, (DWORD)size, &bytes_written, &(vio->pipe_overlapped))) { @@ -804,6 +876,7 @@ size_t vio_write_pipe(Vio * vio, const uchar* buf, size_t size) } else { + enable_iocp_notification(&vio->pipe_overlapped); if (GetLastError() != ERROR_IO_PENDING) { DBUG_PRINT("vio_error",("WriteFile() returned last error %d", @@ -812,7 +885,7 @@ size_t vio_write_pipe(Vio * vio, const uchar* buf, size_t size) } retval= pipe_complete_io(vio, (char *)buf, size, vio->write_timeout_ms); } - + enable_iocp_notification(&vio->pipe_overlapped); DBUG_PRINT("exit", ("%lld", (longlong)retval)); DBUG_RETURN(retval); } From c439494942f4c1f9b8f1017ac2a0056b8c564bf3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 10 Dec 2011 19:35:44 +0100 Subject: [PATCH 02/51] Fix Unix build --- sql/sql_class.cc | 2 ++ sql/sys_vars.cc | 4 ++-- sql/threadpool_unix.cc | 21 ++++++++++----------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 63e1b36073f..de2e49f31e9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -766,6 +766,8 @@ THD::THD() stmt_arena= this; thread_stack= 0; scheduler= thread_scheduler; // Will be fixed later + event_scheduler.data= 0; + event_scheduler.m_psi= 0; extra_port= 0; catalog= (char*)"std"; // the only catalog we have for now main_security_ctx.init(); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index f547292e239..00f4d82d501 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2180,7 +2180,7 @@ static Sys_var_ulong Sys_thread_cache_size( GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1)); -#ifndef HAVE_POOL_OF_THREADS + static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type) { #ifdef _WIN32 @@ -2197,6 +2197,7 @@ static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type) } #endif + #ifdef _WIN32 static Sys_var_uint Sys_threadpool_min_threads( "thread_pool_min_threads", @@ -2239,7 +2240,6 @@ static Sys_var_uint Sys_threadpool_max_threads( NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_tp_max_threads) ); -#endif /* !HAVE_POOL_OF_THREADS */ /** diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 9fa95f151a5..57bff93e951 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -124,7 +124,6 @@ struct connection_t }; /* Externals functions and variables we use */ -extern uint thread_created; extern void scheduler_init(); extern pthread_attr_t *get_connection_attrib(void); extern int skip_net_wait_timeout; @@ -408,7 +407,7 @@ static void timeout_check(pool_timer_t *timer) if (thd->net.reading_or_writing != 1) continue; - connection_t *connection= (connection_t *)thd->scheduler.data; + connection_t *connection= (connection_t *)thd->event_scheduler.data; if (!connection) continue; @@ -416,7 +415,7 @@ static void timeout_check(pool_timer_t *timer) { /* Wait timeout exceeded, kill connection. */ mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed = THD::KILL_CONNECTION; + thd->killed = KILL_CONNECTION; tp_post_kill_notification(thd); mysql_mutex_unlock(&thd->LOCK_thd_data); } @@ -449,7 +448,7 @@ static void* timer_thread(void *param) pool_timer_t* timer=(pool_timer_t *)param; timer->next_timeout_check= ULONGLONG_MAX; - timer->current_microtime= my_micro_time(); + timer->current_microtime= microsecond_interval_timer(); my_thread_init(); DBUG_ENTER("timer_thread"); @@ -464,7 +463,7 @@ static void* timer_thread(void *param) break; if (err == ETIMEDOUT) { - timer->current_microtime= my_micro_time(); + timer->current_microtime= microsecond_interval_timer(); /* Check stallls in thread groups */ for(i=0; i< threadpool_size;i++) @@ -643,7 +642,7 @@ static int create_worker(thread_group_t *thread_group) if (!err) { thread_group->pending_thread_start_count++; - thread_group->last_thread_creation_time=my_micro_time(); + thread_group->last_thread_creation_time=microsecond_interval_timer(); } DBUG_RETURN(err); } @@ -673,7 +672,7 @@ static int wake_or_create_thread(thread_group_t *thread_group) DBUG_RETURN(create_worker(thread_group)); } - now = my_micro_time(); + now = microsecond_interval_timer(); time_since_last_thread_created = (now - thread_group->last_thread_creation_time)/1000; @@ -994,7 +993,7 @@ void tp_add_connection(THD *thd) mysql_mutex_lock(&c->thread_group->mutex); c->thread_group->connection_count++; mysql_mutex_unlock(&c->thread_group->mutex); - c->thd->scheduler.data = c; + c->thd->event_scheduler.data = c; post_event(c->thread_group,&c->event); } @@ -1032,7 +1031,7 @@ void tp_wait_begin(THD *thd, int type) if (!thd) DBUG_VOID_RETURN; - connection_t *connection = (connection_t *)thd->scheduler.data; + connection_t *connection = (connection_t *)thd->event_scheduler.data; if(connection) { DBUG_ASSERT(!connection->waiting); @@ -1049,7 +1048,7 @@ void tp_wait_end(THD *thd) if (!thd) DBUG_VOID_RETURN; - connection_t *connection = (connection_t *)thd->scheduler.data; + connection_t *connection = (connection_t *)thd->event_scheduler.data; if(connection) { DBUG_ASSERT(connection->waiting); @@ -1077,7 +1076,7 @@ static void set_wait_timeout(connection_t *c) DBUG_ENTER("set_wait_timeout"); /* Calculate wait deadline for this connection. - Instead of using my_micro_time() which has a syscall + Instead of using microsecond_interval_timer() which has a syscall overhead, use pool_timer.current_microtime and take into account that its value could be off by at most one tick interval. From 468104567f6b0a1fe552f38717005f286c402125 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 14 Dec 2011 23:16:50 +0100 Subject: [PATCH 03/51] On Unix, correct default threadpool_idle_timeout to be 60 sec --- sql/sys_vars.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 00f4d82d501..0a142e99c67 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2213,7 +2213,7 @@ static Sys_var_uint Sys_threadpool_idle_thread_timeout( "Timeout in seconds for an idle thread in the thread pool." "Worker thread will be shut down after timeout", GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, UINT_MAX/100), DEFAULT(60000), BLOCK_SIZE(1) + VALID_RANGE(1, UINT_MAX), DEFAULT(60), BLOCK_SIZE(1) ); static Sys_var_uint Sys_threadpool_size( "thread_pool_size", From a5a22e9f641623886ec7c051e7ae40eadad0b023 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 18 Dec 2011 20:40:38 +0100 Subject: [PATCH 04/51] Small adjustements to threadpool --- sql/net_serv.cc | 4 ++++ sql/sql_class.cc | 1 + sql/sql_class.h | 3 +++ sql/sql_parse.cc | 2 +- sql/sys_vars.cc | 6 +++--- sql/threadpool.h | 3 --- sql/threadpool_common.cc | 6 ++---- sql/threadpool_unix.cc | 1 - sql/threadpool_win.cc | 1 - 9 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 9011d570497..4a35dc52fa3 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1159,6 +1159,8 @@ void my_net_set_read_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_read_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); + if(net->read_timeout == timeout) + DBUG_VOID_RETURN; net->read_timeout= timeout; #ifdef NO_ALARM if (net->vio) @@ -1172,6 +1174,8 @@ void my_net_set_write_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_write_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); + if(net->write_timeout == timeout) + DBUG_VOID_RETURN; net->write_timeout= timeout; #ifdef NO_ALARM if (net->vio) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index de2e49f31e9..7633a4078ff 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -749,6 +749,7 @@ THD::THD() derived_tables_processing(FALSE), spcont(NULL), m_parser_state(NULL), + skip_wait_timeout(false), #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index f87af8842d8..53e0d83f37a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1697,6 +1697,9 @@ public: /* True if we want to log all errors */ bool log_all_errors; + /* Do not set socket timeouts for wait_timeout (used with threadpool) */ + bool skip_wait_timeout; + /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7ee6c9fb74f..957d7965262 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -701,7 +701,7 @@ bool do_command(THD *thd) the client, the connection is closed or "net_wait_timeout" number of seconds has passed. */ - if(!skip_net_wait_timeout) + if(!thd->skip_wait_timeout) my_net_set_read_timeout(net, thd->variables.net_wait_timeout); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 0a142e99c67..4ba547614f5 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2220,7 +2220,7 @@ static Sys_var_uint Sys_threadpool_size( "Number of concurrently executing threads in the pool. " "Leaving value default (0) sets it to the number of processors.", GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(0, 128), DEFAULT(0), BLOCK_SIZE(1) + VALID_RANGE(1, 128), DEFAULT(my_getncpus()), BLOCK_SIZE(1) ); static Sys_var_uint Sys_threadpool_stall_limit( "thread_pool_stall_limit", @@ -2231,12 +2231,12 @@ static Sys_var_uint Sys_threadpool_stall_limit( GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG), VALID_RANGE(60, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1) ); -#endif /*! WIN32 */ +#endif /* !WIN32 */ static Sys_var_uint Sys_threadpool_max_threads( "thread_pool_max_threads", "Maximum allowed number of worker threads in the thread pool", GLOBAL_VAR(threadpool_max_threads), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, UINT_MAX), DEFAULT(3000), BLOCK_SIZE(1), + VALID_RANGE(1, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_tp_max_threads) ); diff --git a/sql/threadpool.h b/sql/threadpool.h index 0694981d76f..fa8f1c4fbea 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -1,12 +1,9 @@ /* Threadpool parameters */ -#ifdef _WIN32 extern uint threadpool_min_threads; /* Minimum threads in pool */ -#else extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this timeout */ extern uint threadpool_size; /* Number of parallel executing threads */ extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall checks*/ -#endif extern uint threadpool_max_threads; /* Maximum threads in pool */ /* diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 01546162377..229c913ab44 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -17,13 +17,11 @@ extern void thd_cleanup(THD *thd); extern void delete_thd(THD *thd); /* Threadpool parameters */ -#ifdef _WIN32 + uint threadpool_min_threads; -#else uint threadpool_idle_timeout; uint threadpool_size; uint threadpool_stall_limit; -#endif uint threadpool_max_threads; @@ -127,7 +125,7 @@ int threadpool_add_connection(THD *thd) thd->net.reading_or_writing= 1; } } - + thd->skip_wait_timeout= true; thread_detach(thd, psi_thread); return retval; } diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 57bff93e951..f0d7d5f33be 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1198,7 +1198,6 @@ bool tp_init() DBUG_ENTER("tp_init"); started = true; scheduler_init(); - skip_net_wait_timeout= 1; if (threadpool_size == 0) { threadpool_size= my_getncpus(); diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 4e46e393c24..0afb628a1ca 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -520,7 +520,6 @@ bool tp_init(void) } #endif - skip_net_wait_timeout = 1; return 0; } From 2e4bde4c0febf1282639ce2f13e24c707e1f45b0 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 18 Dec 2011 23:03:35 +0100 Subject: [PATCH 05/51] Fix pool_of_threads test case --- mysql-test/r/pool_of_threads.result | 10 +++++----- mysql-test/t/pool_of_threads.cnf | 3 ++- mysql-test/t/pool_of_threads.test | 6 +++--- sql/threadpool_unix.cc | 15 +++++++++++++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/pool_of_threads.result b/mysql-test/r/pool_of_threads.result index 74ea7ba12eb..64168b0eb3c 100644 --- a/mysql-test/r/pool_of_threads.result +++ b/mysql-test/r/pool_of_threads.result @@ -2084,10 +2084,10 @@ fld6 char(4) latin1_swedish_ci NO # show full columns from t2 from test like 's%'; Field Type Collation Null Key Default Extra Privileges Comment show keys from t2; -Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment -t2 0 PRIMARY 1 auto A 1199 NULL NULL BTREE -t2 0 fld1 1 fld1 A 1199 NULL NULL BTREE -t2 1 fld3 1 fld3 A NULL NULL NULL BTREE +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment +t2 0 PRIMARY 1 auto A 1199 NULL NULL BTREE +t2 0 fld1 1 fld1 A 1199 NULL NULL BTREE +t2 1 fld3 1 fld3 A NULL NULL NULL BTREE drop table t4, t3, t2, t1; CREATE TABLE t1 ( cont_nr int(11) NOT NULL auto_increment, @@ -2153,7 +2153,7 @@ Warning 1052 Column 'kundentyp' in group statement is ambiguous drop table t1; SELECT sleep(5); SELECT sleep(5); -# -- Success: more than --thread-pool-size normal connections not possible +# -- Success: more than --thread_pool_max_threads normal connections not possible sleep(5) 0 sleep(5) diff --git a/mysql-test/t/pool_of_threads.cnf b/mysql-test/t/pool_of_threads.cnf index d719cc22ad4..ed4ad501c9d 100644 --- a/mysql-test/t/pool_of_threads.cnf +++ b/mysql-test/t/pool_of_threads.cnf @@ -2,7 +2,8 @@ [mysqld.1] loose-thread-handling= pool-of-threads -loose-thread_pool_size= 2 +loose-thread_pool_size= 1 +loose-thread_pool_max_threads= 2 extra-port= @ENV.MASTER_EXTRA_PORT extra-max-connections=1 diff --git a/mysql-test/t/pool_of_threads.test b/mysql-test/t/pool_of_threads.test index 530038cee91..caedb56dd2b 100644 --- a/mysql-test/t/pool_of_threads.test +++ b/mysql-test/t/pool_of_threads.test @@ -13,7 +13,7 @@ # connections on the extra port. # First set two connections running, and check that extra connection -# on normal port fails due to--thread-pool-size=2 +# on normal port fails due to--thread-pool-max_threads=2 connection default; send SELECT sleep(5); @@ -32,11 +32,11 @@ connect(con3,localhost,root,,); let $error = $mysql_errno; if (!$error) { - --echo # -- Error: managed to establish more than --thread-pool-size connections + --echo # -- Error: managed to establish more than --thread_pool_max_threads connections } if ($error) { - --echo # -- Success: more than --thread-pool-size normal connections not possible + --echo # -- Success: more than --thread_pool_max_threads normal connections not possible } connection default; diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index f0d7d5f33be..a8ceb250e5c 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -624,14 +624,22 @@ static pool_event_t * listener(worker_thread_t *current_thread, -/* Creates a new worker thread. thread_mutex must be held when calling this function */ +/* + Creates a new worker thread. + thread_mutex must be held when calling this function + + NOTE: in rare cases, the number of threads can exceed + threadpool_max_threads, because we need at least 2 threads + per group to prevent deadlocks (one listener + one worker) +*/ static int create_worker(thread_group_t *thread_group) { pthread_t thread_id; int err; DBUG_ENTER("create_worker"); - if (tp_stats.num_worker_threads >= (int)threadpool_max_threads) + if (tp_stats.num_worker_threads >= (int)threadpool_max_threads + && thread_group->thread_count >= 2) { DBUG_PRINT("info", ("Cannot create new thread (maximum allowed threads reached)")); @@ -667,6 +675,9 @@ static int wake_or_create_thread(thread_group_t *thread_group) if (thread_group->pending_thread_start_count > 0) DBUG_RETURN(-1); + if (thread_group->thread_count > thread_group->connection_count) + DBUG_RETURN(-1); + if (thread_group->thread_count < 4) { DBUG_RETURN(create_worker(thread_group)); From df48c9bf20955c4e49009c95c5c7e1856c212e81 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 19 Dec 2011 13:28:30 +0100 Subject: [PATCH 06/51] allow changing thread_pool_size without server restart --- mysql-test/t/xa.test | 1 + sql/sys_vars.cc | 13 ++- sql/threadpool.h | 3 + sql/threadpool_unix.cc | 186 ++++++++++++++++++++++++++++++++--------- 4 files changed, 163 insertions(+), 40 deletions(-) diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index 26c2f0ce210..78f41763e20 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -358,6 +358,7 @@ let $wait_condition= AND info = "INSERT INTO t2 SELECT a FROM t1"; --echo # Waiting until INSERT ... is blocked --source include/wait_condition.inc +--sleep 0.1 DELETE FROM t1; COMMIT; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 4ba547614f5..4ec6cbe72dd 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2189,6 +2189,7 @@ static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type) return false; } + #ifdef _WIN32 static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type) { @@ -2198,6 +2199,14 @@ static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type) #endif +#ifndef _WIN32 +static bool fix_threadpool_size(sys_var*, THD*, enum_var_type) +{ + tp_set_threadpool_size(threadpool_size); + return false; +} +#endif + #ifdef _WIN32 static Sys_var_uint Sys_threadpool_min_threads( "thread_pool_min_threads", @@ -2220,7 +2229,9 @@ static Sys_var_uint Sys_threadpool_size( "Number of concurrently executing threads in the pool. " "Leaving value default (0) sets it to the number of processors.", GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 128), DEFAULT(my_getncpus()), BLOCK_SIZE(1) + VALID_RANGE(1, MAX_THREAD_GROUPS), DEFAULT(my_getncpus()), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_threadpool_size) ); static Sys_var_uint Sys_threadpool_stall_limit( "thread_pool_stall_limit", diff --git a/sql/threadpool.h b/sql/threadpool.h index fa8f1c4fbea..4272d9a11f1 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -1,4 +1,6 @@ +#define MAX_THREAD_GROUPS 128 + /* Threadpool parameters */ extern uint threadpool_min_threads; /* Minimum threads in pool */ extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this timeout */ @@ -38,6 +40,7 @@ extern TP_STATISTICS tp_stats; /* Functions to set threadpool parameters */ extern void tp_set_min_threads(uint val); extern void tp_set_max_threads(uint val); +extern int tp_set_threadpool_size(uint val); /* Activate threadpool scheduler */ extern void tp_scheduler(void); diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index a8ceb250e5c..0b5c151d93b 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -98,7 +98,8 @@ struct thread_group_t ulonglong queue_event_count; } MY_ALIGNED(512); -static thread_group_t all_groups[128]; +static thread_group_t all_groups[MAX_THREAD_GROUPS]; +static uint group_count; /* Global timer for all groups */ struct pool_timer_t @@ -213,10 +214,10 @@ int io_poll_start_read(int pollfd, int fd, void *data) return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev); } -void io_poll_disassociate_fd(int pollfd, int fd) +int io_poll_disassociate_fd(int pollfd, int fd) { struct epoll_event ev; - epoll_ctl(pollfd, EPOLL_CTL_DEL, fd, &ev); + return epoll_ctl(pollfd, EPOLL_CTL_DEL, fd, &ev); } @@ -258,11 +259,11 @@ int io_poll_associate_fd(int pollfd, int fd, void *data) } -int io_poll_disassociate_fd(thread_group_t *thread_group, int fd) +int io_poll_disassociate_fd(int pollfd, int fd) { struct kevent ke; EV_SET(&ke,fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - return kevent(thread_group->pollfd, &ke, 1, 0, 0, 0); + return kevent(pollfd, &ke, 1, 0, 0, 0); } @@ -315,6 +316,11 @@ static int io_poll_associate_fd(int pollfd, int fd, void *data) return io_poll_start_read(pollfd, fd, data); } +int io_poll_disassociate_fd(int pollfd, int fd) +{ + return 0; +} + int io_poll_wait(int pollfd, native_event *events, int maxevents, int timeout_ms) { struct timespec ts; @@ -466,7 +472,7 @@ static void* timer_thread(void *param) timer->current_microtime= microsecond_interval_timer(); /* Check stallls in thread groups */ - for(i=0; i< threadpool_size;i++) + for(i=0; i< array_elements(all_groups);i++) { if(all_groups[i].connection_count) check_stall(&all_groups[i]); @@ -723,21 +729,9 @@ int thread_group_init(thread_group_t *thread_group, pthread_attr_t* thread_attr) SLIST_INIT(&thread_group->waiting_threads); thread_group->pending_thread_start_count= 0; - thread_group->pollfd= io_poll_create(); thread_group->stalled= false; - if (thread_group->pollfd < 0) - { - DBUG_RETURN(-1); - } - if (pipe(thread_group->shutdown_pipe)) - { - DBUG_RETURN(-1); - } - if (io_poll_associate_fd(thread_group->pollfd, - thread_group->shutdown_pipe[0], &POOL_SHUTDOWN_EVENT)) - { - DBUG_RETURN(-1); - } + + thread_group->pollfd= -1; DBUG_RETURN(0); } @@ -772,9 +766,29 @@ static void thread_group_close(thread_group_t *thread_group) char c= 0; mysql_mutex_lock(&thread_group->mutex); + if (thread_group->thread_count == 0 && + thread_group->pending_thread_start_count == 0) + { + if (thread_group->pollfd >= 0) + close(thread_group->pollfd); + mysql_mutex_unlock(&thread_group->mutex); + mysql_mutex_destroy(&thread_group->mutex); + DBUG_VOID_RETURN; + } + thread_group->shutdown= true; thread_group->listener= NULL; + if (pipe(thread_group->shutdown_pipe)) + { + DBUG_VOID_RETURN; + } + if (io_poll_associate_fd(thread_group->pollfd, + thread_group->shutdown_pipe[0], &POOL_SHUTDOWN_EVENT)) + { + DBUG_VOID_RETURN; + } + /* Wake listener. */ if (write(thread_group->shutdown_pipe[1], &c, 1) < 0) DBUG_VOID_RETURN; @@ -1000,7 +1014,7 @@ void tp_add_connection(THD *thd) connection_t *c= alloc_connection(thd); if(c) { - c->thread_group= &all_groups[c->thd->thread_id%threadpool_size]; + c->thread_group= &all_groups[c->thd->thread_id%group_count]; mysql_mutex_lock(&c->thread_group->mutex); c->thread_group->connection_count++; mysql_mutex_unlock(&c->thread_group->mutex); @@ -1101,6 +1115,87 @@ static void set_wait_timeout(connection_t *c) DBUG_VOID_RETURN; } + + +/* + Handle a (rare) special case,where connection needs to + migrate to a different group because group_count has changed + as a result of thread_pool_size setting. +*/ +static int change_group(connection_t *c, + thread_group_t *old_group, + thread_group_t *new_group) +{ + int ret= 0; + int fd = c->thd->net.vio->sd; + + DBUG_ASSERT(c->thread_group == old_group); + + /* Remove connection from the old group. */ + mysql_mutex_lock(&old_group->mutex); + if (c->logged_in) + io_poll_disassociate_fd(old_group->pollfd,fd); + c->thread_group->connection_count--; + mysql_mutex_lock(&old_group->mutex); + + /* Add connection to the new group. */ + mysql_mutex_lock(&new_group->mutex); + + c->thread_group= new_group; + new_group->connection_count++; + + /* Ensure that there is a listener in the new group. */ + if(!new_group->thread_count && !new_group->pending_thread_start_count) + ret= create_worker(new_group); + + mysql_mutex_unlock(&new_group->mutex); + return ret; +} + + +static int start_io(connection_t *c) +{ + int fd = c->thd->net.vio->sd; + + /* + Usually, connection will stay in the same group for the entire + connection's life. However, we do allow group_count to + change at runtime, which means in rare cases when it changes is + connection should need to migrate to another group, this ensures + to ensure equal load between groups. + + So we recalculate in which group the connection should be, based + on thread_id and current group count, and migrate if necessary. + */ + thread_group_t *g = &all_groups[c->thd->thread_id%group_count]; + + if (g != c->thread_group) + { + if (!change_group(c, c->thread_group, g)) + { + c->logged_in= true; + return io_poll_associate_fd(c->thread_group->pollfd, fd, c); + } + else + return -1; + } + + + /* + Handle case where connection is not yet logged in, i.e + not associated with poll fd. + */ + if(!c->logged_in) + { + c->logged_in= true; + return io_poll_associate_fd(c->thread_group->pollfd, fd, c); + } + + return io_poll_start_read(c->thread_group->pollfd, fd, c); +} + + + static void handle_event(pool_event_t *ev) { @@ -1108,13 +1203,11 @@ static void handle_event(pool_event_t *ev) /* Normal case, handle query on connection */ connection_t *c = (connection_t*)(void *)ev; - bool do_login = (!c->logged_in); int ret; - if (do_login) + if (!c->logged_in) { ret= threadpool_add_connection(c->thd); - c->logged_in= true; } else { @@ -1124,13 +1217,7 @@ static void handle_event(pool_event_t *ev) if(!ret) { set_wait_timeout(c); - int fd = c->thd->net.vio->sd; - if (do_login) - { - ret= io_poll_associate_fd(c->thread_group->pollfd, fd, c); - } - else - ret= io_poll_start_read(c->thread_group->pollfd, fd, c); + ret= start_io(c); } if (ret) @@ -1159,8 +1246,8 @@ static void *worker_main(void *param) this_thread.thread_group= thread_group; this_thread.event_count=0; + my_atomic_add32(&tp_stats.num_worker_threads, 1); mysql_mutex_lock(&thread_group->mutex); - tp_stats.num_worker_threads++; thread_group->thread_count++; thread_group->active_thread_count++; thread_group->pending_thread_start_count--; @@ -1187,8 +1274,8 @@ static void *worker_main(void *param) mysql_mutex_lock(&thread_group->mutex); thread_group->active_thread_count--; thread_group->thread_count--; - tp_stats.num_worker_threads--; mysql_mutex_unlock(&thread_group->mutex); + my_atomic_add32(&tp_stats.num_worker_threads, -1); /* If it is the last thread in pool and pool is terminating, destroy pool.*/ if (thread_group->shutdown && (thread_group->thread_count == 0)) @@ -1209,15 +1296,12 @@ bool tp_init() DBUG_ENTER("tp_init"); started = true; scheduler_init(); - if (threadpool_size == 0) - { - threadpool_size= my_getncpus(); - } - for(uint i=0; i < threadpool_size; i++) + for(uint i=0; i < array_elements(all_groups); i++) { thread_group_init(&all_groups[i], get_connection_attrib()); } + tp_set_threadpool_size(threadpool_size); #define PSI_register(X) \ if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) @@ -1239,9 +1323,33 @@ void tp_end() DBUG_VOID_RETURN; stop_timer(&pool_timer); - for(uint i=0; i< threadpool_size; i++) + for(uint i=0; i< array_elements(all_groups); i++) { thread_group_close(&all_groups[i]); } DBUG_VOID_RETURN; } + +/* Ensure that poll descriptors are created when threadpool_size changes */ +int tp_set_threadpool_size(uint size) +{ + bool success= true; + for(uint i=0; i< size; i++) + { + thread_group_t *group= &all_groups[i]; + mysql_mutex_lock(&group->mutex); + if (group->pollfd == -1) + { + group->pollfd= io_poll_create(); + success= (group->pollfd >= 0); + } + mysql_mutex_unlock(&all_groups[i].mutex); + if (!success) + { + group_count= i-1; + return -1; + } + } + group_count= size; + return 0; +} From 96041f8e1d37756f84c8da9055e966576d368443 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 20 Dec 2011 22:49:24 +0100 Subject: [PATCH 07/51] make sys_vars suite pass --- mysql-test/include/mysqld--help.inc | 4 +- mysql-test/r/mysqld--help-notwin.result | 8 +--- .../r/thread_pool_idle_timeout_basic.result | 47 +++++++++++++++++++ .../r/thread_pool_max_threads_basic.result | 47 +++++++++++++++++++ .../sys_vars/r/thread_pool_size_basic.result | 41 ++++++++++++++++ .../r/thread_pool_stall_limit_basic.result | 47 +++++++++++++++++++ .../sys_vars/t/slow_launch_time_func.test | 5 +- .../sys_vars/t/thread_cache_size_func.test | 3 +- .../t/thread_pool_idle_timeout_basic.test | 42 +++++++++++++++++ .../t/thread_pool_max_threads_basic.test | 42 +++++++++++++++++ .../sys_vars/t/thread_pool_size_basic.test | 46 ++++++++++++++++++ .../t/thread_pool_stall_limit_basic.test | 42 +++++++++++++++++ sql/threadpool_unix.cc | 5 +- 13 files changed, 361 insertions(+), 18 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_idle_timeout_basic.result create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_size_basic.result create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_size_basic.test create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index 107f80ba125..a037f255a3e 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -18,11 +18,11 @@ perl; # their paths may vary: @skipvars=qw/basedir open-files-limit general-log-file log plugin-dir log-slow-queries pid-file slow-query-log-file log-basename - datadir slave-load-tmpdir tmpdir socket/; + datadir slave-load-tmpdir tmpdir socket /; # Plugins which may or may not be there: @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster feedback debug temp-pool ssl des-key-file - xtradb thread-concurrency super-large-pages mutex-deadlock-detector null-audit maria aria pbxt oqgraph sphinx/; + xtradb thread-concurrency super-large-pages mutex-deadlock-detector null-audit maria aria pbxt oqgraph sphinx thread-handling thread-pool/; # And substitute the content some environment variables with their # names: diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 8bd84ea7049..dfadcea7769 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -813,9 +813,6 @@ The following options may be given as the first argument: (Defaults to on; use --skip-thread-alarm to disable.) --thread-cache-size=# How many threads we should keep in a cache for reuse - --thread-handling=name - Define threads usage for handling queries, one of - one-thread-per-connection, no-threads --thread-stack=# The stack size for each thread --time-format=name The TIME format (ignored) --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are @@ -956,7 +953,7 @@ lower-case-table-names 1 master-info-file master.info master-retry-count 86400 master-verify-checksum FALSE -max-allowed-packet 1048576 +max-allowed-packet 8388608 max-binlog-cache-size 18446744073709547520 max-binlog-dump-events 0 max-binlog-size 1073741824 @@ -968,7 +965,7 @@ max-error-count 64 max-heap-table-size 16777216 max-join-size 18446744073709551615 max-length-for-sort-data 1024 -max-long-data-size 1048576 +max-long-data-size 8388608 max-prepared-stmt-count 16382 max-relay-log-size 0 max-seeks-for-key 18446744073709551615 @@ -1087,7 +1084,6 @@ table-open-cache 400 tc-heuristic-recover COMMIT thread-alarm TRUE thread-cache-size 0 -thread-handling one-thread-per-connection thread-stack 294912 time-format %H:%i:%s timed-mutexes FALSE diff --git a/mysql-test/suite/sys_vars/r/thread_pool_idle_timeout_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_idle_timeout_basic.result new file mode 100644 index 00000000000..8dedbd0f2d2 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_idle_timeout_basic.result @@ -0,0 +1,47 @@ +SET @start_global_value = @@global.thread_pool_idle_timeout; +select @@global.thread_pool_idle_timeout; +@@global.thread_pool_idle_timeout +60 +select @@session.thread_pool_idle_timeout; +ERROR HY000: Variable 'thread_pool_idle_timeout' is a GLOBAL variable +show global variables like 'thread_pool_idle_timeout'; +Variable_name Value +thread_pool_idle_timeout 60 +show session variables like 'thread_pool_idle_timeout'; +Variable_name Value +thread_pool_idle_timeout 60 +select * from information_schema.global_variables where variable_name='thread_pool_idle_timeout'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_IDLE_TIMEOUT 60 +select * from information_schema.session_variables where variable_name='thread_pool_idle_timeout'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_IDLE_TIMEOUT 60 +set global thread_pool_idle_timeout=60; +select @@global.thread_pool_idle_timeout; +@@global.thread_pool_idle_timeout +60 +set global thread_pool_idle_timeout=4294967295; +select @@global.thread_pool_idle_timeout; +@@global.thread_pool_idle_timeout +4294967295 +set session thread_pool_idle_timeout=1; +ERROR HY000: Variable 'thread_pool_idle_timeout' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_idle_timeout=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_idle_timeout' +set global thread_pool_idle_timeout=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_idle_timeout' +set global thread_pool_idle_timeout="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_idle_timeout' +set global thread_pool_idle_timeout=-1; +Warnings: +Warning 1292 Truncated incorrect thread_pool_idle_timeout value: '-1' +select @@global.thread_pool_idle_timeout; +@@global.thread_pool_idle_timeout +1 +set global thread_pool_idle_timeout=10000000000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_idle_timeout value: '10000000000' +select @@global.thread_pool_idle_timeout; +@@global.thread_pool_idle_timeout +4294967295 +SET @@global.thread_pool_idle_timeout = @start_global_value; diff --git a/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result new file mode 100644 index 00000000000..87dc4295c11 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result @@ -0,0 +1,47 @@ +SET @start_global_value = @@global.thread_pool_max_threads; +select @@global.thread_pool_max_threads; +@@global.thread_pool_max_threads +500 +select @@session.thread_pool_max_threads; +ERROR HY000: Variable 'thread_pool_max_threads' is a GLOBAL variable +show global variables like 'thread_pool_max_threads'; +Variable_name Value +thread_pool_max_threads 500 +show session variables like 'thread_pool_max_threads'; +Variable_name Value +thread_pool_max_threads 500 +select * from information_schema.global_variables where variable_name='thread_pool_max_threads'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_MAX_THREADS 500 +select * from information_schema.session_variables where variable_name='thread_pool_max_threads'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_MAX_THREADS 500 +set global thread_pool_max_threads=1; +select @@global.thread_pool_max_threads; +@@global.thread_pool_max_threads +1 +set global thread_pool_max_threads=4294967295; +select @@global.thread_pool_max_threads; +@@global.thread_pool_max_threads +4294967295 +set session thread_pool_max_threads=1; +ERROR HY000: Variable 'thread_pool_max_threads' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_max_threads=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_max_threads' +set global thread_pool_max_threads=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_max_threads' +set global thread_pool_max_threads="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_max_threads' +set global thread_pool_max_threads=0; +Warnings: +Warning 1292 Truncated incorrect thread_pool_max_threads value: '0' +select @@global.thread_pool_max_threads; +@@global.thread_pool_max_threads +1 +set global thread_pool_max_threads=10000000000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_max_threads value: '10000000000' +select @@global.thread_pool_max_threads; +@@global.thread_pool_max_threads +4294967295 +SET @@global.thread_pool_max_threads = @start_global_value; diff --git a/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result new file mode 100644 index 00000000000..f08f9625e9d --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result @@ -0,0 +1,41 @@ +SET @start_global_value = @@global.thread_pool_size; +select @@global.thread_pool_size; +@@global.thread_pool_size +1 +select @@session.thread_pool_size; +ERROR HY000: Variable 'thread_pool_size' is a GLOBAL variable +show global variables like 'thread_pool_size'; +Variable_name Value +thread_pool_size # +show session variables like 'thread_pool_size'; +Variable_name Value +thread_pool_size # +select * from information_schema.global_variables where variable_name='thread_pool_size'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_SIZE # +select * from information_schema.session_variables where variable_name='thread_pool_size'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_SIZE # +set global thread_pool_size=1; +select @@global.thread_pool_size; +@@global.thread_pool_size +1 +set global thread_pool_size=128; +select @@global.thread_pool_size; +@@global.thread_pool_size +128 +set session thread_pool_size=1; +ERROR HY000: Variable 'thread_pool_size' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_size=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_size' +set global thread_pool_size=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_size' +set global thread_pool_size="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_size' +set global thread_pool_size=-1; +Warnings: +Warning 1292 Truncated incorrect thread_pool_size value: '-1' +set global thread_pool_size=100000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_size value: '100000' +SET @@global.thread_pool_size = @start_global_value; diff --git a/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result new file mode 100644 index 00000000000..552ac5f54f8 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result @@ -0,0 +1,47 @@ +SET @start_global_value = @@global.thread_pool_stall_limit; +select @@global.thread_pool_stall_limit; +@@global.thread_pool_stall_limit +500 +select @@session.thread_pool_stall_limit; +ERROR HY000: Variable 'thread_pool_stall_limit' is a GLOBAL variable +show global variables like 'thread_pool_stall_limit'; +Variable_name Value +thread_pool_stall_limit 500 +show session variables like 'thread_pool_stall_limit'; +Variable_name Value +thread_pool_stall_limit 500 +select * from information_schema.global_variables where variable_name='thread_pool_stall_limit'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_STALL_LIMIT 500 +select * from information_schema.session_variables where variable_name='thread_pool_stall_limit'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_STALL_LIMIT 500 +set global thread_pool_stall_limit=60; +select @@global.thread_pool_stall_limit; +@@global.thread_pool_stall_limit +60 +set global thread_pool_stall_limit=4294967295; +select @@global.thread_pool_stall_limit; +@@global.thread_pool_stall_limit +4294967295 +set session thread_pool_stall_limit=1; +ERROR HY000: Variable 'thread_pool_stall_limit' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_stall_limit=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_stall_limit' +set global thread_pool_stall_limit=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_stall_limit' +set global thread_pool_stall_limit="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_stall_limit' +set global thread_pool_stall_limit=-1; +Warnings: +Warning 1292 Truncated incorrect thread_pool_stall_limit value: '-1' +select @@global.thread_pool_stall_limit; +@@global.thread_pool_stall_limit +60 +set global thread_pool_stall_limit=10000000000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_stall_limit value: '10000000000' +select @@global.thread_pool_stall_limit; +@@global.thread_pool_stall_limit +4294967295 +set @@global.thread_pool_stall_limit = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/slow_launch_time_func.test b/mysql-test/suite/sys_vars/t/slow_launch_time_func.test index a5b429f81cb..4a4951535ac 100644 --- a/mysql-test/suite/sys_vars/t/slow_launch_time_func.test +++ b/mysql-test/suite/sys_vars/t/slow_launch_time_func.test @@ -29,10 +29,7 @@ # # Setup # ---source include/not_threadpool.inc ---source include/not_embedded.inc ---source include/not_threadpool.inc - +--source include/one_thread_per_connection.inc SET @global_slow_launch_time = @@GLOBAL.slow_launch_time; --echo ** Connection default ** diff --git a/mysql-test/suite/sys_vars/t/thread_cache_size_func.test b/mysql-test/suite/sys_vars/t/thread_cache_size_func.test index 9bffa32ca2b..7382fd671a9 100644 --- a/mysql-test/suite/sys_vars/t/thread_cache_size_func.test +++ b/mysql-test/suite/sys_vars/t/thread_cache_size_func.test @@ -27,9 +27,8 @@ # Setup # ---source include/not_embedded.inc ---source include/not_threadpool.inc +--source include/one_thread_per_connection.inc SET @global_thread_cache_size = @@GLOBAL.thread_cache_size; FLUSH STATUS; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test new file mode 100644 index 00000000000..9268d45cb55 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test @@ -0,0 +1,42 @@ +# uint global + +SET @start_global_value = @@global.thread_pool_idle_timeout; + +# +# exists as global only +# +select @@global.thread_pool_idle_timeout; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_idle_timeout; +show global variables like 'thread_pool_idle_timeout'; +show session variables like 'thread_pool_idle_timeout'; +select * from information_schema.global_variables where variable_name='thread_pool_idle_timeout'; +select * from information_schema.session_variables where variable_name='thread_pool_idle_timeout'; + +# +# show that it's writable +# +set global thread_pool_idle_timeout=60; +select @@global.thread_pool_idle_timeout; +set global thread_pool_idle_timeout=4294967295; +select @@global.thread_pool_idle_timeout; +--error ER_GLOBAL_VARIABLE +set session thread_pool_idle_timeout=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_idle_timeout=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_idle_timeout=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_idle_timeout="foo"; + + +set global thread_pool_idle_timeout=-1; +select @@global.thread_pool_idle_timeout; +set global thread_pool_idle_timeout=10000000000; +select @@global.thread_pool_idle_timeout; + +SET @@global.thread_pool_idle_timeout = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test new file mode 100644 index 00000000000..0544986acd5 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test @@ -0,0 +1,42 @@ +# uint global + +SET @start_global_value = @@global.thread_pool_max_threads; + +# +# exists as global only +# +select @@global.thread_pool_max_threads; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_max_threads; +show global variables like 'thread_pool_max_threads'; +show session variables like 'thread_pool_max_threads'; +select * from information_schema.global_variables where variable_name='thread_pool_max_threads'; +select * from information_schema.session_variables where variable_name='thread_pool_max_threads'; + +# +# show that it's writable +# +set global thread_pool_max_threads=1; +select @@global.thread_pool_max_threads; +set global thread_pool_max_threads=4294967295; +select @@global.thread_pool_max_threads; +--error ER_GLOBAL_VARIABLE +set session thread_pool_max_threads=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_max_threads=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_max_threads=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_max_threads="foo"; + + +set global thread_pool_max_threads=0; +select @@global.thread_pool_max_threads; +set global thread_pool_max_threads=10000000000; +select @@global.thread_pool_max_threads; + +SET @@global.thread_pool_max_threads = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test new file mode 100644 index 00000000000..8b43fc52a58 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test @@ -0,0 +1,46 @@ +# uint global + +SET @start_global_value = @@global.thread_pool_size; + +# +# exists as global only +# +--replace_column 2 # +select @@global.thread_pool_size; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_size; +--replace_column 2 # +show global variables like 'thread_pool_size'; +--replace_column 2 # +show session variables like 'thread_pool_size'; +--replace_column 2 # +select * from information_schema.global_variables where variable_name='thread_pool_size'; +--replace_column 2 # +select * from information_schema.session_variables where variable_name='thread_pool_size'; +--replace_column 2 # + +# +# show that it's writable +# +set global thread_pool_size=1; +select @@global.thread_pool_size; +set global thread_pool_size=128; +select @@global.thread_pool_size; +--error ER_GLOBAL_VARIABLE +set session thread_pool_size=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_size=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_size=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_size="foo"; + +set global thread_pool_size=-1; + +set global thread_pool_size=100000; + +SET @@global.thread_pool_size = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test new file mode 100644 index 00000000000..0fcfcd1d663 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test @@ -0,0 +1,42 @@ +# uint global + +SET @start_global_value = @@global.thread_pool_stall_limit; + +# +# exists as global only +# +select @@global.thread_pool_stall_limit; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_stall_limit; +show global variables like 'thread_pool_stall_limit'; +show session variables like 'thread_pool_stall_limit'; +select * from information_schema.global_variables where variable_name='thread_pool_stall_limit'; +select * from information_schema.session_variables where variable_name='thread_pool_stall_limit'; + +# +# show that it's writable +# +set global thread_pool_stall_limit=60; +select @@global.thread_pool_stall_limit; +set global thread_pool_stall_limit=4294967295; +select @@global.thread_pool_stall_limit; +--error ER_GLOBAL_VARIABLE +set session thread_pool_stall_limit=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_stall_limit=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_stall_limit=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_stall_limit="foo"; + + +set global thread_pool_stall_limit=-1; +select @@global.thread_pool_stall_limit; +set global thread_pool_stall_limit=10000000000; +select @@global.thread_pool_stall_limit; + +set @@global.thread_pool_stall_limit = @start_global_value; diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 0b5c151d93b..93094f599aa 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1136,18 +1136,15 @@ static int change_group(connection_t *c, if (c->logged_in) io_poll_disassociate_fd(old_group->pollfd,fd); c->thread_group->connection_count--; - mysql_mutex_lock(&old_group->mutex); + mysql_mutex_unlock(&old_group->mutex); /* Add connection to the new group. */ mysql_mutex_lock(&new_group->mutex); - c->thread_group= new_group; new_group->connection_count++; - /* Ensure that there is a listener in the new group. */ if(!new_group->thread_count && !new_group->pending_thread_start_count) ret= create_worker(new_group); - mysql_mutex_unlock(&new_group->mutex); return ret; } From 711d7452da8690c4e775313b55c92b12595352bd Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 21 Dec 2011 00:56:34 +0100 Subject: [PATCH 08/51] Fix threadpool related test failures --- mysql-test/r/mysqld--help-win.result | 8 ++------ .../suite/sys_vars/r/thread_pool_max_threads_basic.result | 6 +++--- .../suite/sys_vars/t/thread_pool_idle_timeout_basic.test | 2 +- .../suite/sys_vars/t/thread_pool_max_threads_basic.test | 2 +- mysql-test/suite/sys_vars/t/thread_pool_size_basic.test | 2 +- .../suite/sys_vars/t/thread_pool_stall_limit_basic.test | 2 +- sql/sys_vars.cc | 2 +- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 3c2f1935506..7e9cfab4bbb 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -817,9 +817,6 @@ The following options may be given as the first argument: (Defaults to on; use --skip-thread-alarm to disable.) --thread-cache-size=# How many threads we should keep in a cache for reuse - --thread-handling=name - Define threads usage for handling queries, one of - one-thread-per-connection, no-threads --thread-stack=# The stack size for each thread --time-format=name The TIME format (ignored) --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are @@ -959,7 +956,7 @@ lower-case-table-names 1 master-info-file master.info master-retry-count 86400 master-verify-checksum FALSE -max-allowed-packet 1048576 +max-allowed-packet 8388608 max-binlog-cache-size 18446744073709547520 max-binlog-dump-events 0 max-binlog-size 1073741824 @@ -971,7 +968,7 @@ max-error-count 64 max-heap-table-size 16777216 max-join-size 18446744073709551615 max-length-for-sort-data 1024 -max-long-data-size 1048576 +max-long-data-size 8388608 max-prepared-stmt-count 16382 max-relay-log-size 0 max-seeks-for-key 18446744073709551615 @@ -1093,7 +1090,6 @@ table-open-cache 400 tc-heuristic-recover COMMIT thread-alarm TRUE thread-cache-size 0 -thread-handling one-thread-per-connection thread-stack 294912 time-format %H:%i:%s timed-mutexes FALSE diff --git a/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result index 87dc4295c11..4a748ca7629 100644 --- a/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_pool_max_threads_basic.result @@ -20,10 +20,10 @@ set global thread_pool_max_threads=1; select @@global.thread_pool_max_threads; @@global.thread_pool_max_threads 1 -set global thread_pool_max_threads=4294967295; +set global thread_pool_max_threads=65536; select @@global.thread_pool_max_threads; @@global.thread_pool_max_threads -4294967295 +65536 set session thread_pool_max_threads=1; ERROR HY000: Variable 'thread_pool_max_threads' is a GLOBAL variable and should be set with SET GLOBAL set global thread_pool_max_threads=1.1; @@ -43,5 +43,5 @@ Warnings: Warning 1292 Truncated incorrect thread_pool_max_threads value: '10000000000' select @@global.thread_pool_max_threads; @@global.thread_pool_max_threads -4294967295 +65536 SET @@global.thread_pool_max_threads = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test index 9268d45cb55..0f82213ddd6 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test @@ -1,5 +1,5 @@ # uint global - +--source include/not_windows.inc SET @start_global_value = @@global.thread_pool_idle_timeout; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test index 0544986acd5..df5e25a9acd 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test @@ -18,7 +18,7 @@ select * from information_schema.session_variables where variable_name='thread_p # set global thread_pool_max_threads=1; select @@global.thread_pool_max_threads; -set global thread_pool_max_threads=4294967295; +set global thread_pool_max_threads=65536; select @@global.thread_pool_max_threads; --error ER_GLOBAL_VARIABLE set session thread_pool_max_threads=1; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test index 8b43fc52a58..94f0d4e8f10 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test @@ -1,5 +1,5 @@ # uint global - +--source include/not_windows.inc SET @start_global_value = @@global.thread_pool_size; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test index 0fcfcd1d663..d67c595369b 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test @@ -1,5 +1,5 @@ # uint global - +--source include/not_windows.inc SET @start_global_value = @@global.thread_pool_stall_limit; # diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 4ec6cbe72dd..3c54c256697 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2247,7 +2247,7 @@ static Sys_var_uint Sys_threadpool_max_threads( "thread_pool_max_threads", "Maximum allowed number of worker threads in the thread pool", GLOBAL_VAR(threadpool_max_threads), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), + VALID_RANGE(1, 65536), DEFAULT(500), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_tp_max_threads) ); From 7da4229f2ef2bdf24b6196991d8e474fa42c1811 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 26 Dec 2011 01:08:46 +0100 Subject: [PATCH 09/51] Fix build on old 32 bit Centos (kernel 2.6.18) --- cmake/os/Linux.cmake | 7 +++++++ sql/threadpool_unix.cc | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/cmake/os/Linux.cmake b/cmake/os/Linux.cmake index ccdf7e8d931..108e06cf7b0 100644 --- a/cmake/os/Linux.cmake +++ b/cmake/os/Linux.cmake @@ -46,3 +46,10 @@ IF(HAVE_DECL_SHM_HUGETLB) SET(HUGETLB_USE_PROC_MEMINFO 1) SET(HAVE_LARGE_PAGE_OPTION 1) ENDIF() + +IF(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "86") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i686") + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=i686") +ENDIF() + diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 93094f599aa..e0310303020 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -190,6 +190,10 @@ static void set_next_timeout_check(ulonglong abstime); */ #if defined (__linux__) +#ifndef EPOLLRDHUP +/* Early 2.6 kernel did not have EPOLLRDHUP */ +#define EPOLLRDHUP 0 +#endif static int io_poll_create() { return epoll_create(1); From c01fc2abc7820ec372c2f678474912196f49d983 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 27 Dec 2011 12:20:06 +0100 Subject: [PATCH 10/51] fix embedded build and warning --- sql/sql_class.cc | 2 +- sql/sys_vars.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7633a4078ff..edb7fe6c71e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -749,7 +749,6 @@ THD::THD() derived_tables_processing(FALSE), spcont(NULL), m_parser_state(NULL), - skip_wait_timeout(false), #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ @@ -769,6 +768,7 @@ THD::THD() scheduler= thread_scheduler; // Will be fixed later event_scheduler.data= 0; event_scheduler.m_psi= 0; + skip_wait_timeout= false; extra_port= 0; catalog= (char*)"std"; // the only catalog we have for now main_security_ctx.init(); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index cf38929f632..4cfb41d5b75 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2186,7 +2186,7 @@ static Sys_var_ulong Sys_thread_cache_size( GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1)); - +#ifdef HAVE_POOL_OF_THREADS static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type) { #ifdef _WIN32 @@ -2257,7 +2257,7 @@ static Sys_var_uint Sys_threadpool_max_threads( NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_tp_max_threads) ); - +#endif /* HAVE_POOL_OF_THREADS */ /** Can't change the 'next' tx_isolation if we are already in a From 27ef2fda985a1cb55166d77d3d973704ba57e30f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 27 Dec 2011 16:10:34 +0100 Subject: [PATCH 11/51] fix test suite --- mysql-test/r/kill.result | 4 +--- mysql-test/t/flush_read_lock_kill.test | 2 +- mysql-test/t/kill.test | 5 ++++- sql/threadpool_common.cc | 4 +++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index e91db0c9036..0cc8caf8f42 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -60,9 +60,7 @@ SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; KILL @id; SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; Got one of the listed errors -SELECT 1; -1 -1 +Got one of the listed errors SET DEBUG_SYNC = 'RESET'; DROP TABLE t1, t2; SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; diff --git a/mysql-test/t/flush_read_lock_kill.test b/mysql-test/t/flush_read_lock_kill.test index a672fa5dfc5..e0a772d9fbb 100644 --- a/mysql-test/t/flush_read_lock_kill.test +++ b/mysql-test/t/flush_read_lock_kill.test @@ -65,7 +65,7 @@ KILL CONNECTION @id; connection con1; --echo # Try to reap FLUSH TABLES WITH READ LOCK, --echo # it fail due to killed statement and connection. ---error 1317,2013 +--error 1317,2013,1927 reap; --echo # Switching to 'con2'. diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 7c6c37ecfc6..d0a82ae3749 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -142,9 +142,12 @@ KILL @id; SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; connection con1; ---error 1317,1053,2006,2013 +--error 1317,1053,2006,2013,1927 reap; +--disable_query_log +--error 2013,0 SELECT 1; +--enable_query_log connection default; SET DEBUG_SYNC = 'RESET'; diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 229c913ab44..b6676576fb3 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -71,7 +71,9 @@ static inline bool thread_attach(THD* thd, char *stack_start, PSI_thread **save_ static inline void thread_detach(THD* thd, PSI_thread *restore_psi_thread) { DBUG_ENTER("thread_detach"); + mysql_mutex_lock(&thd->LOCK_thd_data); thd->mysys_var = NULL; + mysql_mutex_unlock(&thd->LOCK_thd_data); #ifndef DBUG_OFF /* If during the session @@session.dbug was assigned, the @@ -161,7 +163,7 @@ int threadpool_process_request(THD *thd) PSI_thread *psi_thread; thread_attach(thd, (char *)&thd, &psi_thread); - if (thd->killed == KILL_CONNECTION) + if (thd->killed >= KILL_CONNECTION) { /* kill flag can be set have been killed by From ede423083b32491b7d69fb5bc0cbe57e1916c71e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 27 Dec 2011 17:54:04 +0100 Subject: [PATCH 12/51] disable threadpool threads in sys_var suite, when the suite runs with embedded server --- .../suite/sys_vars/t/thread_pool_idle_timeout_basic.test | 1 + mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test | 2 +- mysql-test/suite/sys_vars/t/thread_pool_size_basic.test | 3 ++- mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test index 0f82213ddd6..4afcc0379f0 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test @@ -1,5 +1,6 @@ # uint global --source include/not_windows.inc +--source include/not_embedded.inc SET @start_global_value = @@global.thread_pool_idle_timeout; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test index df5e25a9acd..3d5cd5f5198 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test @@ -1,5 +1,5 @@ # uint global - +--source include/not_embedded.inc SET @start_global_value = @@global.thread_pool_max_threads; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test index 94f0d4e8f10..eeed58956a4 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_size_basic.test @@ -1,11 +1,12 @@ # uint global --source include/not_windows.inc +--source include/not_embedded.inc SET @start_global_value = @@global.thread_pool_size; # # exists as global only # ---replace_column 2 # +--replace_column 1 # select @@global.thread_pool_size; --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@session.thread_pool_size; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test index d67c595369b..9b4e1df7ab0 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test @@ -1,5 +1,6 @@ # uint global --source include/not_windows.inc +--source include/not_embedded.inc SET @start_global_value = @@global.thread_pool_stall_limit; # From 50e6ae1973732d3438805cdadd8859871fd4e38a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 28 Dec 2011 03:51:12 +0100 Subject: [PATCH 13/51] fix result file --- mysql-test/suite/sys_vars/r/thread_pool_size_basic.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result index f08f9625e9d..70d46cbe067 100644 --- a/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_pool_size_basic.result @@ -1,7 +1,7 @@ SET @start_global_value = @@global.thread_pool_size; select @@global.thread_pool_size; @@global.thread_pool_size -1 +# select @@session.thread_pool_size; ERROR HY000: Variable 'thread_pool_size' is a GLOBAL variable show global variables like 'thread_pool_size'; From ecad00bc4e0a9f95f4db9dcff1745d793a91e4c3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 28 Dec 2011 16:23:46 +0100 Subject: [PATCH 14/51] use performance-schema friendly mysql_thread_create() instead of pthread_create() --- sql/threadpool_unix.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index e0310303020..6d0ab80ad78 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -656,7 +656,8 @@ static int create_worker(thread_group_t *thread_group) DBUG_RETURN(-1); } - err= pthread_create(&thread_id, thread_group->pthread_attr, worker_main, thread_group); + err= mysql_thread_create(key_worker_thread, &thread_id, + thread_group->pthread_attr, worker_main, thread_group); if (!err) { thread_group->pending_thread_start_count++; From 54b61b8b44eb0ff5257bbac8b38578c4c5f2ed38 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 01:59:05 +0100 Subject: [PATCH 15/51] LP9091416: destroy timer mutex when threadpool scheduler shuts down. Fixes valgrind warning. --- sql/threadpool_unix.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 6d0ab80ad78..d421870fcab 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -470,7 +470,10 @@ static void* timer_thread(void *param) mysql_mutex_lock(&timer->mutex); int err = mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts); if (timer->shutdown) + { + mysql_mutex_unlock(&timer->mutex); break; + } if (err == ETIMEDOUT) { timer->current_microtime= microsecond_interval_timer(); @@ -488,6 +491,8 @@ static void* timer_thread(void *param) } mysql_mutex_unlock(&timer->mutex); } + + mysql_mutex_destroy(&timer->mutex); DBUG_POP(); my_thread_end(); return NULL; From b9f2fb84f6f6224290fa8d65286a1862448e93b8 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 12:17:30 +0100 Subject: [PATCH 16/51] Fix LP#909414: Valgrind warnings in threadpool code --- sql/net_serv.cc | 2 ++ sql/threadpool_unix.cc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 4a35dc52fa3..d54fe3e0ebf 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -138,6 +138,8 @@ my_bool my_net_init(NET *net, Vio* vio) net->net_skip_rest_factor= 0; net->last_errno=0; net->unused= 0; + net->read_timeout=0; + net->write_timeout=0; if (vio != 0) /* If real connection */ { diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index d421870fcab..c936479a00c 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -203,6 +203,7 @@ static int io_poll_create() int io_poll_associate_fd(int pollfd, int fd, void *data) { struct epoll_event ev; + ev.data.u64= 0; /* Keep valgrind happy */ ev.data.ptr= data; ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT; return epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &ev); @@ -213,6 +214,7 @@ int io_poll_associate_fd(int pollfd, int fd, void *data) int io_poll_start_read(int pollfd, int fd, void *data) { struct epoll_event ev; + ev.data.u64= 0; /* Keep valgrind happy */ ev.data.ptr= data; ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT; return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev); From bee1d88abfd7071a5d8c1a0c593d09cc65b26220 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 12:53:07 +0100 Subject: [PATCH 17/51] LP909512: Fix crash on tp_set_threadpool_size if threadpool is not used(thread_handling != pool-of-threads) --- sql/threadpool_unix.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index c936479a00c..b9cf4c61b7e 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1343,6 +1343,9 @@ void tp_end() int tp_set_threadpool_size(uint size) { bool success= true; + if (!started) + return 0; + for(uint i=0; i< size; i++) { thread_group_t *group= &all_groups[i]; From 8d90582a3b36ae8be764b43043fedd03c3db4686 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 13:37:37 +0100 Subject: [PATCH 18/51] Fix valgrind errors with network timeouts. --- sql/net_serv.cc | 2 -- sql/sql_client.cc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index d54fe3e0ebf..4a35dc52fa3 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -138,8 +138,6 @@ my_bool my_net_init(NET *net, Vio* vio) net->net_skip_rest_factor= 0; net->last_errno=0; net->unused= 0; - net->read_timeout=0; - net->write_timeout=0; if (vio != 0) /* If real connection */ { diff --git a/sql/sql_client.cc b/sql/sql_client.cc index 22a7867012a..9630b92a9fc 100644 --- a/sql/sql_client.cc +++ b/sql/sql_client.cc @@ -29,7 +29,7 @@ void my_net_local_init(NET *net) { #ifndef EMBEDDED_LIBRARY net->max_packet= (uint) global_system_variables.net_buffer_length; - + net->read_timeout= net->write_timeout= 0; my_net_set_read_timeout(net, (uint)global_system_variables.net_read_timeout); my_net_set_write_timeout(net, (uint)global_system_variables.net_write_timeout); From 539a7ebe1038560c8de1d521120058907f2604bf Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 19:37:26 +0100 Subject: [PATCH 19/51] LP909537: Ensure thd_wait_begin/thd_wait_end callbacks are called. --- sql/sql_class.cc | 26 +++++++++----------------- sql/threadpool_unix.cc | 2 +- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index edb7fe6c71e..cd29d706823 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3927,9 +3927,7 @@ extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd) return sqlcom_can_generate_row_events(thd); } -#ifdef NOT_USED /* we'll do the correctly instead */ -extern "C" void thd_pool_wait_begin(MYSQL_THD thd, int wait_type); -extern "C" void thd_pool_wait_end(MYSQL_THD thd); + /* Interface for MySQL Server, plugins and storage engines to report @@ -3954,7 +3952,10 @@ extern "C" void thd_pool_wait_end(MYSQL_THD thd); */ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) { - MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type)); + if(!thd) + thd= current_thd; + if (thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type)); } /** @@ -3965,21 +3966,12 @@ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) */ extern "C" void thd_wait_end(MYSQL_THD thd) { - MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); -} -#else -extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) -{ - /* do NOTHING for the embedded library */ - return; + if(!thd) + thd= current_thd; + if (thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); } -extern "C" void thd_wait_end(MYSQL_THD thd) -{ - /* do NOTHING for the embedded library */ - return; -} -#endif #endif // INNODB_COMPATIBILITY_HOOKS */ /**************************************************************************** diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index b9cf4c61b7e..32847e91a32 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -845,7 +845,7 @@ static void post_event(thread_group_t *thread_group, pool_event_t* ev) */ static bool too_many_threads(thread_group_t *thread_group) { - return (thread_group->active_thread_count > 4 && !thread_group->stalled); + return (thread_group->active_thread_count >= 4 && !thread_group->stalled); } From bb0a0c52a65ce3a0621fcfc133d724b0485bb5c3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 29 Dec 2011 21:11:06 +0100 Subject: [PATCH 20/51] Make threadpool_stall_limit variable really dynamic --- sql/sys_vars.cc | 12 ++++++++++-- sql/threadpool.h | 1 + sql/threadpool_unix.cc | 11 +++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 4cfb41d5b75..6feba50a00d 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2211,6 +2211,12 @@ static bool fix_threadpool_size(sys_var*, THD*, enum_var_type) tp_set_threadpool_size(threadpool_size); return false; } + + +static bool fix_threadpool_stall_limit(sys_var*, THD*, enum_var_type) +{ + tp_set_threadpool_stall_limit(threadpool_size); +} #endif #ifdef _WIN32 @@ -2241,12 +2247,14 @@ static Sys_var_uint Sys_threadpool_size( ); static Sys_var_uint Sys_threadpool_stall_limit( "thread_pool_stall_limit", - "Maximum query execution time before in milliseconds," + "Maximum query execution time in milliseconds," "before an executing non-yielding thread is considered stalled." "If a worker thread is stalled, additional worker thread " "may be created to handle remaining clients.", GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(60, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1) + VALID_RANGE(60, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_threadpool_stall_limit) ); #endif /* !WIN32 */ static Sys_var_uint Sys_threadpool_max_threads( diff --git a/sql/threadpool.h b/sql/threadpool.h index 4272d9a11f1..a8e7f9031b3 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -41,6 +41,7 @@ extern TP_STATISTICS tp_stats; extern void tp_set_min_threads(uint val); extern void tp_set_max_threads(uint val); extern int tp_set_threadpool_size(uint val); +extern void tp_set_threadpool_stall_limit(uint val); /* Activate threadpool scheduler */ extern void tp_scheduler(void); diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 32847e91a32..d9eb90532af 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1324,6 +1324,7 @@ bool tp_init() DBUG_RETURN(0); } + void tp_end() { DBUG_ENTER("tp_end"); @@ -1365,3 +1366,13 @@ int tp_set_threadpool_size(uint size) group_count= size; return 0; } + +void tp_set_threadpool_stall_limit(uint limit) +{ + if (!started) + return; + mysql_mutex_lock(&(pool_timer.mutex)); + pool_timer.tick_interval= limit; + mysql_cond_signal(&(pool_timer.cond)); + mysql_mutex_unlock(&(pool_timer.mutex)); +} From c216c9f0f039836f2b8c9edad0884511303101fd Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 31 Dec 2011 05:24:11 +0100 Subject: [PATCH 21/51] Allow for faster creation of threads in corner cases where pool would be overloaded with long non-yielding queries. To allow it, change minimum of thread_pool_stall_limit to be 10 milliseconds. Also introduce a new parameter to oversubscribe a group . Number of threads running in parallel would be higher than it normally should, leading to thrashing, but it may improving preemptiveness, which is useful for the described corner case. --- .../r/thread_pool_oversubscribe_basic.result | 47 +++++++++++++++++++ .../r/thread_pool_stall_limit_basic.result | 2 +- .../t/thread_pool_oversubscribe_basic.test | 43 +++++++++++++++++ sql/sys_vars.cc | 11 ++++- sql/threadpool.h | 1 + sql/threadpool_common.cc | 1 + sql/threadpool_unix.cc | 4 +- 7 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_oversubscribe_basic.result create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test diff --git a/mysql-test/suite/sys_vars/r/thread_pool_oversubscribe_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_oversubscribe_basic.result new file mode 100644 index 00000000000..a9f1afa9f62 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_oversubscribe_basic.result @@ -0,0 +1,47 @@ +SET @start_global_value = @@global.thread_pool_oversubscribe; +select @@global.thread_pool_oversubscribe; +@@global.thread_pool_oversubscribe +3 +select @@session.thread_pool_oversubscribe; +ERROR HY000: Variable 'thread_pool_oversubscribe' is a GLOBAL variable +show global variables like 'thread_pool_oversubscribe'; +Variable_name Value +thread_pool_oversubscribe 3 +show session variables like 'thread_pool_oversubscribe'; +Variable_name Value +thread_pool_oversubscribe 3 +select * from information_schema.global_variables where variable_name='thread_pool_oversubscribe'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_OVERSUBSCRIBE 3 +select * from information_schema.session_variables where variable_name='thread_pool_oversubscribe'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_OVERSUBSCRIBE 3 +set global thread_pool_oversubscribe=60; +select @@global.thread_pool_oversubscribe; +@@global.thread_pool_oversubscribe +60 +set global thread_pool_oversubscribe=1000; +select @@global.thread_pool_oversubscribe; +@@global.thread_pool_oversubscribe +1000 +set session thread_pool_oversubscribe=1; +ERROR HY000: Variable 'thread_pool_oversubscribe' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_oversubscribe=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_oversubscribe' +set global thread_pool_oversubscribe=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_oversubscribe' +set global thread_pool_oversubscribe="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_oversubscribe' +set global thread_pool_oversubscribe=-1; +Warnings: +Warning 1292 Truncated incorrect thread_pool_oversubscribe value: '-1' +select @@global.thread_pool_oversubscribe; +@@global.thread_pool_oversubscribe +1 +set global thread_pool_oversubscribe=10000000000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_oversubscribe value: '10000000000' +select @@global.thread_pool_oversubscribe; +@@global.thread_pool_oversubscribe +1000 +set @@global.thread_pool_oversubscribe = @start_global_value; diff --git a/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result index 552ac5f54f8..eda4e6baebe 100644 --- a/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_pool_stall_limit_basic.result @@ -37,7 +37,7 @@ Warnings: Warning 1292 Truncated incorrect thread_pool_stall_limit value: '-1' select @@global.thread_pool_stall_limit; @@global.thread_pool_stall_limit -60 +10 set global thread_pool_stall_limit=10000000000; Warnings: Warning 1292 Truncated incorrect thread_pool_stall_limit value: '10000000000' diff --git a/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test new file mode 100644 index 00000000000..74f0f5e6ea7 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test @@ -0,0 +1,43 @@ +# uint global +--source include/not_windows.inc +--source include/not_embedded.inc +SET @start_global_value = @@global.thread_pool_oversubscribe; + +# +# exists as global only +# +select @@global.thread_pool_oversubscribe; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_oversubscribe; +show global variables like 'thread_pool_oversubscribe'; +show session variables like 'thread_pool_oversubscribe'; +select * from information_schema.global_variables where variable_name='thread_pool_oversubscribe'; +select * from information_schema.session_variables where variable_name='thread_pool_oversubscribe'; + +# +# show that it's writable +# +set global thread_pool_oversubscribe=60; +select @@global.thread_pool_oversubscribe; +set global thread_pool_oversubscribe=1000; +select @@global.thread_pool_oversubscribe; +--error ER_GLOBAL_VARIABLE +set session thread_pool_oversubscribe=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_oversubscribe=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_oversubscribe=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_oversubscribe="foo"; + + +set global thread_pool_oversubscribe=-1; +select @@global.thread_pool_oversubscribe; +set global thread_pool_oversubscribe=10000000000; +select @@global.thread_pool_oversubscribe; + +set @@global.thread_pool_oversubscribe = @start_global_value; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6feba50a00d..a6485dcaef8 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2215,7 +2215,8 @@ static bool fix_threadpool_size(sys_var*, THD*, enum_var_type) static bool fix_threadpool_stall_limit(sys_var*, THD*, enum_var_type) { - tp_set_threadpool_stall_limit(threadpool_size); + tp_set_threadpool_stall_limit(threadpool_stall_limit); + return false; } #endif @@ -2236,6 +2237,12 @@ static Sys_var_uint Sys_threadpool_idle_thread_timeout( GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, UINT_MAX), DEFAULT(60), BLOCK_SIZE(1) ); +static Sys_var_uint Sys_threadpool_oversubscribe( + "thread_pool_oversubscribe", + "How many additional active worker threads in a group are allowed.", + GLOBAL_VAR(threadpool_oversubscribe), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, 1000), DEFAULT(3), BLOCK_SIZE(1) +); static Sys_var_uint Sys_threadpool_size( "thread_pool_size", "Number of concurrently executing threads in the pool. " @@ -2252,7 +2259,7 @@ static Sys_var_uint Sys_threadpool_stall_limit( "If a worker thread is stalled, additional worker thread " "may be created to handle remaining clients.", GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(60, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), + VALID_RANGE(10, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_threadpool_stall_limit) ); diff --git a/sql/threadpool.h b/sql/threadpool.h index a8e7f9031b3..966dcbc18e0 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -7,6 +7,7 @@ extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this extern uint threadpool_size; /* Number of parallel executing threads */ extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall checks*/ extern uint threadpool_max_threads; /* Maximum threads in pool */ +extern uint threadpool_oversubscribe; /* Maximum active threads in group */ /* Threadpool statistics diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index b6676576fb3..91ae41a058f 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -23,6 +23,7 @@ uint threadpool_idle_timeout; uint threadpool_size; uint threadpool_stall_limit; uint threadpool_max_threads; +uint threadpool_oversubscribe; /* diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index d9eb90532af..ec9f5a91d40 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -839,13 +839,13 @@ static void post_event(thread_group_t *thread_group, pool_event_t* ev) /* - Check if pool is already overcommited. This is used to prevent too many threads executing at the same time, if the workload is not CPU bound. */ static bool too_many_threads(thread_group_t *thread_group) { - return (thread_group->active_thread_count >= 4 && !thread_group->stalled); + return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe + && !thread_group->stalled); } From ed946a7e895dd4a741c4ff3a6108a3c3cd329c9b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 2 Jan 2012 10:13:53 +0100 Subject: [PATCH 22/51] fix test --- mysql-test/t/pool_of_threads.cnf | 6 +++--- mysql-test/t/pool_of_threads.test | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysql-test/t/pool_of_threads.cnf b/mysql-test/t/pool_of_threads.cnf index ed4ad501c9d..c03e1da6450 100644 --- a/mysql-test/t/pool_of_threads.cnf +++ b/mysql-test/t/pool_of_threads.cnf @@ -2,13 +2,13 @@ [mysqld.1] loose-thread-handling= pool-of-threads -loose-thread_pool_size= 1 -loose-thread_pool_max_threads= 2 +loose-thread_pool_size= 2 +loose-thread_pool_max_threads= 2 extra-port= @ENV.MASTER_EXTRA_PORT extra-max-connections=1 [client] -connect-timeout= 2 +connect-timeout= 2 [ENV] MASTER_EXTRA_PORT= @OPT.port diff --git a/mysql-test/t/pool_of_threads.test b/mysql-test/t/pool_of_threads.test index caedb56dd2b..23ac8e063e2 100644 --- a/mysql-test/t/pool_of_threads.test +++ b/mysql-test/t/pool_of_threads.test @@ -16,11 +16,12 @@ # on normal port fails due to--thread-pool-max_threads=2 connection default; send SELECT sleep(5); +--sleep 1 connect(con2,localhost,root,,); connection con2; send SELECT sleep(5); ---sleep 1 +--sleep 0.5 --disable_abort_on_error --disable_result_log From d76c80700da58407e10e426740a2a6705c8af336 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 2 Jan 2012 11:43:22 +0100 Subject: [PATCH 23/51] Fix crashes in windows-embedded --- sql/sql_class.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index cd29d706823..e9591b7a412 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -814,8 +814,8 @@ THD::THD() #endif #ifndef EMBEDDED_LIBRARY mysql_audit_init_thd(this); - net.vio=0; #endif + net.vio=0; client_capabilities= 0; // minimalistic client ull=0; system_thread= NON_SYSTEM_THREAD; From 67cfb8a5c564b2f1fb54e799bf331907f106ea4d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 10 Jan 2012 16:58:30 +0100 Subject: [PATCH 24/51] MDEV-82 : Fix place in thr_lock.c where wait-end callback was called without corresponding wait-begin --- mysys/thr_lock.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 61f18d9dddf..7387e35b062 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -482,6 +482,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; const char *old_proc_info; + my_bool use_wait_callbacks; DBUG_ENTER("wait_for_lock"); /* @@ -534,7 +535,12 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, and once after the thread has exited the wait loop. */ if ((!thread_var->abort || in_wait_list) && before_lock_wait) + { + use_wait_callbacks= TRUE; (*before_lock_wait)(); + } + else + use_wait_callbacks= FALSE; set_timespec(wait_timeout, lock_wait_timeout); while (!thread_var->abort || in_wait_list) @@ -572,7 +578,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, We call the after_lock_wait callback once the wait loop has finished. */ - if (after_lock_wait) + if (after_lock_wait && use_wait_callbacks) (*after_lock_wait)(); DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d", From 85fc94ec064d6398c5451e498a39463b037bacf3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 12 Jan 2012 13:40:09 +0100 Subject: [PATCH 25/51] fix kill test, again --- mysql-test/r/kill.result | 1 - mysql-test/t/kill.test | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index 0cc8caf8f42..e1a33faa7ee 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -60,7 +60,6 @@ SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; KILL @id; SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; Got one of the listed errors -Got one of the listed errors SET DEBUG_SYNC = 'RESET'; DROP TABLE t1, t2; SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index d0a82ae3749..9e7a67a725f 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -144,10 +144,8 @@ SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; connection con1; --error 1317,1053,2006,2013,1927 reap; ---disable_query_log ---error 2013,0 -SELECT 1; ---enable_query_log +--error 0,2013 +let $ignore= `SELECT 1`; connection default; SET DEBUG_SYNC = 'RESET'; From 2533633b5fdb17b2506a9d041f43669b198e13b4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 13 Jan 2012 15:53:17 +0100 Subject: [PATCH 26/51] Simplify thread attach/detach. Use connection specific mysys_var, rather than sharing worker thread's my_thread_var with THD. --- sql/threadpool_common.cc | 195 +++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 90 deletions(-) diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 91ae41a058f..2ed9e6f2ba6 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -26,99 +26,105 @@ uint threadpool_max_threads; uint threadpool_oversubscribe; -/* - Attach/associate the connection with the OS thread, for command processing. -*/ -static inline bool thread_attach(THD* thd, char *stack_start, PSI_thread **save_psi_thread) -{ - DBUG_ENTER("thread_attach"); +extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys); - if (PSI_server) +/* + Worker threads contexts, and THD contexts. + ===================================== + + Both worker threads and connections have their sets of thread local variables + At the moment it is mysys_var (which has e.g dbug my_error and similar + goodies inside), and PSI per-client structure. + + Whenever query is executed following needs to be done: + + 1. Save worker thread context. + 2. Change TLS variables to connection specific ones using thread_attach(THD*). + This function does some additional work , e.g setting up + thread_stack/thread_ends_here pointers. + 3. Process query + 4. Restore worker thread context. + + Connection login and termination follows similar schema w.r.t saving and + restoring contexts. + + For both worker thread, and for the connection, mysys variables are created + using my_thread_init() and freed with my_thread_end(). + +*/ +struct Worker_thread_context +{ + PSI_thread *psi_thread; + st_my_thread_var* mysys_var; + + void save() { - *save_psi_thread= PSI_server->get_thread(); + psi_thread= PSI_server?PSI_server->get_thread():0; + mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys); + } + + void restore() + { + if (PSI_server) + PSI_server->set_thread(psi_thread); + pthread_setspecific(THR_KEY_mysys,mysys_var); + pthread_setspecific(THR_THD, 0); + pthread_setspecific(THR_MALLOC, 0); + } +}; + + +/* + Attach/associate the connection with the OS thread, +*/ +static inline bool thread_attach(THD* thd) +{ + pthread_setspecific(THR_KEY_mysys,thd->mysys_var); + thd->thread_stack=(char*)&thd; + thd->store_globals(); + if (PSI_server) PSI_server->set_thread(thd->event_scheduler.m_psi); - } - else - *save_psi_thread= NULL; - - /* - We need to know the start of the stack so that we could check for - stack overruns. - */ - thd->thread_stack= stack_start; - - - /* Calls close_connection() on failure */ - if (setup_connection_thread_globals(thd)) - { - DBUG_RETURN(TRUE); - } - - /* clear errors from processing the previous THD */ - my_errno= 0; - thd->mysys_var->abort= 0; - -#ifndef DBUG_OFF - if (thd->event_scheduler.set_explain) - DBUG_SET(thd->event_scheduler.dbug_explain); -#endif - - DBUG_RETURN(FALSE); + return 0; } -/* - Detach/disassociate the connection with the OS thread. -*/ -static inline void thread_detach(THD* thd, PSI_thread *restore_psi_thread) -{ - DBUG_ENTER("thread_detach"); - mysql_mutex_lock(&thd->LOCK_thd_data); - thd->mysys_var = NULL; - mysql_mutex_unlock(&thd->LOCK_thd_data); -#ifndef DBUG_OFF - /* - If during the session @@session.dbug was assigned, the - dbug options/state has been pushed. Check if this is the - case, to be able to restore the state when we attach this - logical connection to a physical thread. - */ - if (_db_is_pushed_()) - { - thd->event_scheduler.set_explain= TRUE; - if (DBUG_EXPLAIN(thd->event_scheduler.dbug_explain, sizeof(thd->event_scheduler.dbug_explain))) - sql_print_error("thd_scheduler: DBUG_EXPLAIN buffer is too small"); - } - /* DBUG_POP() is a no-op in case there is no session state */ - DBUG_POP(); -#endif - if (PSI_server) - PSI_server->set_thread(restore_psi_thread); - pthread_setspecific(THR_THD, NULL); - DBUG_VOID_RETURN; -} - - int threadpool_add_connection(THD *thd) { int retval=1; - PSI_thread *psi_thread; -#ifndef DBUG_OFF - thd->event_scheduler.set_explain = 0; -#endif - thread_attach(thd, (char *)&thd, &psi_thread); + Worker_thread_context worker_context; + worker_context.save(); + + /* + Create a new connection context: mysys_thread_var and PSI thread + Store them in thd->mysys_var and thd->scheduler.m_psi. + */ + + /* Use my_thread_init() to create new mysys_thread_var. */ + pthread_setspecific(THR_KEY_mysys, 0); + my_thread_init(); + thd->mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys); + if (!thd->mysys_var) + { + /* Out of memory? */ + worker_context.restore(); + return 1; + } + + /* Create new PSI thread for use with the THD. */ + if (PSI_server) + { + thd->event_scheduler.m_psi = + PSI_server->new_thread(key_thread_one_connection, thd, thd->thread_id); + } + + + /* Login. */ + thread_attach(thd); ulonglong now= microsecond_interval_timer(); thd->prior_thr_create_utime= now; thd->start_utime= now; thd->thr_create_utime= now; - if (PSI_server) - { - thd->event_scheduler.m_psi = - PSI_server->new_thread(key_thread_one_connection, thd, thd->thread_id); - PSI_server->set_thread(thd->event_scheduler.m_psi); - } - if (setup_connection_thread_globals(thd) == 0) { if (login_connection(thd) == 0) @@ -129,15 +135,19 @@ int threadpool_add_connection(THD *thd) } } thd->skip_wait_timeout= true; - thread_detach(thd, psi_thread); + + worker_context.restore(); return retval; } void threadpool_remove_connection(THD *thd) { - PSI_thread *save_psi_thread; - thread_attach(thd, (char *)&thd, &save_psi_thread); + Worker_thread_context worker_context; + worker_context.save(); + + thread_attach(thd); + thd->killed= KILL_CONNECTION; thd->net.reading_or_writing= 0; @@ -152,17 +162,21 @@ void threadpool_remove_connection(THD *thd) unlink_thd(thd); mysql_mutex_unlock(&LOCK_thread_count); mysql_cond_broadcast(&COND_thread_count); - DBUG_POP(); - if (PSI_server) - PSI_server->delete_current_thread(); - pthread_setspecific(THR_THD, NULL); + + /* Free resources (thread_var and PSI connection specific struct)*/ + my_thread_end(); + + worker_context.restore(); + } int threadpool_process_request(THD *thd) { int retval= 0; - PSI_thread *psi_thread; - thread_attach(thd, (char *)&thd, &psi_thread); + Worker_thread_context worker_context; + worker_context.save(); + + thread_attach(thd); if (thd->killed >= KILL_CONNECTION) { @@ -170,7 +184,7 @@ int threadpool_process_request(THD *thd) kill flag can be set have been killed by timeout handler or by a KILL command */ - thread_detach(thd, psi_thread); + worker_context.restore(); return 1; } @@ -199,9 +213,10 @@ int threadpool_process_request(THD *thd) break; } } - thread_detach(thd, psi_thread); if (!retval) thd->net.reading_or_writing= 1; + + worker_context.restore(); return retval; } From 18c9b345b43b62b7c4dbac8ce0289c1c8103c2d1 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 15 Jan 2012 11:17:45 +0100 Subject: [PATCH 27/51] Threadpool -address review comments --- sql/threadpool.h | 32 +- sql/threadpool_common.cc | 79 ++--- sql/threadpool_unix.cc | 663 ++++++++++++++++++++++++--------------- 3 files changed, 454 insertions(+), 320 deletions(-) diff --git a/sql/threadpool.h b/sql/threadpool.h index 966dcbc18e0..8c991aab2cb 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -9,30 +9,28 @@ extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall c extern uint threadpool_max_threads; /* Maximum threads in pool */ extern uint threadpool_oversubscribe; /* Maximum active threads in group */ + +/* + Functions used by scheduler. + OS-specific implementations are in + threadpool_unix.cc or threadpool_win.cc +*/ +extern bool tp_init(); +extern void tp_add_connection(THD*); +extern void tp_wait_begin(THD *, int); +extern void tp_wait_end(THD*); +extern void tp_post_kill_notification(THD *thd); +extern void tp_end(void); + /* Threadpool statistics */ struct TP_STATISTICS { /* Current number of worker thread. */ - volatile int num_worker_threads; + volatile int32 num_worker_threads; /* Current number of idle threads. */ - volatile int num_waiting_threads; - /* Number of login requests are queued but not yet processed. */ - volatile int pending_login_requests; - /* Number of threads that are starting. */ - volatile int pending_thread_starts; - /* Number of threads that are being shut down */ - volatile int pending_thread_shutdowns; - /* Time (in milliseconds) since pool is blocked (num_waiting_threads is 0) */ - ulonglong pool_block_duration; - /* Maximum duration of the pending login, im milliseconds. */ - ulonglong pending_login_duration; - /* Time since last thread was created */ - ulonglong time_since_last_thread_creation; - /* Number of requests processed since pool monitor run last time. */ - volatile int requests_dequeued; - volatile int requests_completed; + volatile int32 num_waiting_threads; }; extern TP_STATISTICS tp_stats; diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 2ed9e6f2ba6..5152f62efe9 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -7,15 +7,9 @@ #include #include #include +#include -extern bool login_connection(THD *thd); -extern bool do_command(THD *thd); -extern void prepare_new_connection_state(THD* thd); -extern void end_connection(THD *thd); -extern void thd_cleanup(THD *thd); -extern void delete_thd(THD *thd); - /* Threadpool parameters */ uint threadpool_min_threads; @@ -27,14 +21,15 @@ uint threadpool_oversubscribe; extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys); +extern bool do_command(THD*); /* Worker threads contexts, and THD contexts. - ===================================== + ========================================= Both worker threads and connections have their sets of thread local variables - At the moment it is mysys_var (which has e.g dbug my_error and similar - goodies inside), and PSI per-client structure. + At the moment it is mysys_var (this has specific data for dbug, my_error and + similar goodies), and PSI per-client structure. Whenever query is executed following needs to be done: @@ -77,7 +72,7 @@ struct Worker_thread_context /* Attach/associate the connection with the OS thread, */ -static inline bool thread_attach(THD* thd) +static bool thread_attach(THD* thd) { pthread_setspecific(THR_KEY_mysys,thd->mysys_var); thd->thread_stack=(char*)&thd; @@ -95,11 +90,10 @@ int threadpool_add_connection(THD *thd) worker_context.save(); /* - Create a new connection context: mysys_thread_var and PSI thread - Store them in thd->mysys_var and thd->scheduler.m_psi. + Create a new connection context: mysys_thread_var and PSI thread + Store them in THD. */ - /* Use my_thread_init() to create new mysys_thread_var. */ pthread_setspecific(THR_KEY_mysys, 0); my_thread_init(); thd->mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys); @@ -125,21 +119,29 @@ int threadpool_add_connection(THD *thd) thd->start_utime= now; thd->thr_create_utime= now; - if (setup_connection_thread_globals(thd) == 0) + if (!setup_connection_thread_globals(thd)) { - if (login_connection(thd) == 0) + if (!login_connection(thd)) { - prepare_new_connection_state(thd); - retval = thd_is_connection_alive(thd)?0:-1; - thd->net.reading_or_writing= 1; + prepare_new_connection_state(thd); + + /* + Check if THD is ok, as prepare_new_connection_state() + can fail, for example if init command failed. + */ + if (thd_is_connection_alive(thd)) + { + retval= 0; + thd->net.reading_or_writing= 1; + thd->skip_wait_timeout= true; + } } } - thd->skip_wait_timeout= true; - worker_context.restore(); return retval; } + void threadpool_remove_connection(THD *thd) { @@ -147,9 +149,7 @@ void threadpool_remove_connection(THD *thd) worker_context.save(); thread_attach(thd); - thd->killed= KILL_CONNECTION; - thd->net.reading_or_writing= 0; end_connection(thd); @@ -163,11 +163,13 @@ void threadpool_remove_connection(THD *thd) mysql_mutex_unlock(&LOCK_thread_count); mysql_cond_broadcast(&COND_thread_count); - /* Free resources (thread_var and PSI connection specific struct)*/ + /* + Free resources associated with this connection: + mysys thread_var and PSI thread. + */ my_thread_end(); worker_context.restore(); - } int threadpool_process_request(THD *thd) @@ -181,8 +183,8 @@ int threadpool_process_request(THD *thd) if (thd->killed >= KILL_CONNECTION) { /* - kill flag can be set have been killed by - timeout handler or by a KILL command + killed flag was set by timeout handler + or KILL command. Return error. */ worker_context.restore(); return 1; @@ -206,33 +208,18 @@ int threadpool_process_request(THD *thd) vio= thd->net.vio; if (!vio->has_data(vio)) { - /* - More info on this debug sync is in sql_parse.cc - */ + /* More info on this debug sync is in sql_parse.cc*/ DEBUG_SYNC(thd, "before_do_command_net_read"); + thd->net.reading_or_writing= 1; break; } - } - if (!retval) - thd->net.reading_or_writing= 1; + } worker_context.restore(); return retval; } -/* - Scheduler struct, individual functions are implemented - in threadpool_unix.cc or threadpool_win.cc -*/ - -extern bool tp_init(); -extern void tp_add_connection(THD*); -extern void tp_wait_begin(THD *, int); -extern void tp_wait_end(THD*); -extern void tp_post_kill_notification(THD *thd); -extern void tp_end(void); - static scheduler_functions tp_scheduler_functions= { 0, // max_threads @@ -255,7 +242,7 @@ void pool_of_threads_scheduler(struct scheduler_functions *func, uint *arg_connection_count) { *func = tp_scheduler_functions; - func->max_threads= *arg_max_connections + 1; + func->max_threads= threadpool_max_threads; func->max_connections= arg_max_connections; func->connection_count= arg_connection_count; scheduler_init(); diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index ec9f5a91d40..b6eeb5bcffd 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -7,9 +7,8 @@ #include #include #include -#include #include - +#include #include #ifdef __linux__ #include @@ -25,6 +24,13 @@ typedef port_event_t native_event; #endif +/* + Define PSI Keys for performance schema. + We have a mutex per group, worker threads, condition per worker thread, + and timer thread with its own mutex and condition. +*/ + + static PSI_mutex_key key_group_mutex; static PSI_mutex_key key_timer_mutex; static PSI_mutex_info mutex_list[]= @@ -49,53 +55,73 @@ static PSI_thread_info thread_list[] = {&key_timer_thread, "timer_thread", PSI_FLAG_GLOBAL} }; +/* Macro to simplify performance schema registration */ +#define PSI_register(X) \ + if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) + TP_STATISTICS tp_stats; - struct thread_group_t; /* Per-thread structure for workers */ struct worker_thread_t { + ulonglong event_count; /* number of request handled by this thread */ + thread_group_t* thread_group; + worker_thread_t *next_in_list; + worker_thread_t **prev_in_list; + mysql_cond_t cond; - bool woken; - thread_group_t* thread_group; - ulonglong event_count; /* Stats: number of executed requests */ - SLIST_ENTRY(worker_thread_t) ptr; + bool woken; }; -/* - Data associated with an io event (also can be sent with with explicit - post_event()) -*/ -struct pool_event_t +typedef I_P_List + > +worker_list_t; + +struct connection_t { - STAILQ_ENTRY (pool_event_t) next; - void *data; + + THD *thd; + thread_group_t *thread_group; + connection_t *next_in_queue; + connection_t **prev_in_queue; + ulonglong abs_wait_timeout; + bool logged_in; + bool waiting; }; -static pool_event_t POOL_SHUTDOWN_EVENT; + +typedef I_P_List, + I_P_List_null_counter, + I_P_List_fast_push_back > +connection_queue_t; struct thread_group_t { - mysql_mutex_t mutex; - STAILQ_HEAD(queue_listhead, pool_event_t) queue; - SLIST_HEAD(wait_listhead, worker_thread_t) waiting_threads; + connection_queue_t queue; + worker_list_t waiting_threads; + worker_thread_t *listener; + pthread_attr_t *pthread_attr; int pollfd; int thread_count; int active_thread_count; int pending_thread_start_count; - int connection_count; + int connection_count; + /* Stats for the deadlock detection timer routine.*/ + int io_event_count; + int queue_event_count; + ulonglong last_thread_creation_time; + int shutdown_pipe[2]; bool shutdown; bool stalled; - int shutdown_pipe[2]; - worker_thread_t *listener; - pthread_attr_t *pthread_attr; - ulonglong last_thread_creation_time; - /* Stats for the deadlock detection timer routine.*/ - ulonglong io_event_count; - ulonglong queue_event_count; + } MY_ALIGNED(512); static thread_group_t all_groups[MAX_THREAD_GROUPS]; @@ -106,33 +132,21 @@ struct pool_timer_t { mysql_mutex_t mutex; mysql_cond_t cond; - int tick_interval; - volatile ulonglong current_microtime; - volatile ulonglong next_timeout_check; + volatile uint64 current_microtime; + volatile uint64 next_timeout_check; + int tick_interval; bool shutdown; }; static pool_timer_t pool_timer; -struct connection_t -{ - pool_event_t event; - THD *thd; - thread_group_t *thread_group; - ulonglong abs_wait_timeout; - bool logged_in; - bool waiting; -}; - /* Externals functions and variables we use */ extern void scheduler_init(); extern pthread_attr_t *get_connection_attrib(void); -extern int skip_net_wait_timeout; - -static void post_event(thread_group_t *thread_group, pool_event_t* ev); +static void queue_put(thread_group_t *thread_group, connection_t *connection); static int wake_thread(thread_group_t *thread_group); -static void handle_event(pool_event_t *ev); +static void handle_event(connection_t *connection); static int wake_or_create_thread(thread_group_t *thread_group); static int create_worker(thread_group_t *thread_group); static void *worker_main(void *param); @@ -357,33 +371,18 @@ static void* native_event_get_userdata(native_event *event) /* Dequeue element from a workqueue */ -static pool_event_t *queue_get(thread_group_t *thread_group) +static connection_t *queue_get(thread_group_t *thread_group) { DBUG_ENTER("queue_get"); - pool_event_t *ev= NULL; thread_group->queue_event_count++; - ev= STAILQ_FIRST(&thread_group->queue); - if (ev) + connection_t *c= thread_group->queue.front(); + if (c) { - STAILQ_REMOVE_HEAD(&thread_group->queue,next); + thread_group->queue.remove(c); } - DBUG_RETURN(ev); + DBUG_RETURN(c); } -/* Check if workqueue is empty. */ -static bool queue_is_empty(thread_group_t* thread_group) -{ - DBUG_ENTER("queue_is_empty"); - bool empty= (STAILQ_FIRST(&thread_group->queue) == NULL); - DBUG_RETURN(empty); -} - -static void queue_put(thread_group_t *thread_group, pool_event_t *event) -{ - DBUG_ENTER("queue_put"); - STAILQ_INSERT_TAIL(&thread_group->queue, event, next); - DBUG_VOID_RETURN; -} static void increment_active_threads(thread_group_t *thread_group) { @@ -418,10 +417,16 @@ static void timeout_check(pool_timer_t *timer) { if (thd->net.reading_or_writing != 1) continue; - + connection_t *connection= (connection_t *)thd->event_scheduler.data; if (!connection) - continue; + { + /* + Connection does not have scheduler data. This happens for example + if THD belongs to another scheduler, that is listening to extra_port. + */ + continue; + } if(connection->abs_wait_timeout < timer->current_microtime) { @@ -454,6 +459,7 @@ static void timeout_check(pool_timer_t *timer) Besides checking for stalls, timer thread is also responsible for terminating clients that have been idle for longer than wait_timeout seconds. */ + static void* timer_thread(void *param) { uint i; @@ -468,9 +474,10 @@ static void* timer_thread(void *param) for(;;) { struct timespec ts; + int err; set_timespec_nsec(ts,timer->tick_interval*1000000); mysql_mutex_lock(&timer->mutex); - int err = mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts); + err= mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts); if (timer->shutdown) { mysql_mutex_unlock(&timer->mutex); @@ -495,7 +502,6 @@ static void* timer_thread(void *param) } mysql_mutex_destroy(&timer->mutex); - DBUG_POP(); my_thread_end(); return NULL; } @@ -529,7 +535,7 @@ void check_stall(thread_group_t *thread_group) /* Check whether requests from the workqueue are being dequeued. */ - if (!queue_is_empty(thread_group) && !thread_group->queue_event_count) + if (!thread_group->queue.is_empty() && !thread_group->queue_event_count) { thread_group->stalled= true; wake_or_create_thread(thread_group); @@ -566,76 +572,141 @@ static void stop_timer(pool_timer_t *timer) #define MAX_EVENTS 1024 -/* - Poll for socket events and distribute them to worker threads. +/** + Poll for socket events and distribute them to worker threads In many case current thread will handle single event itself. + + @return a ready connection, or NULL on shutdown */ -static pool_event_t * listener(worker_thread_t *current_thread, +static connection_t * listener(worker_thread_t *current_thread, thread_group_t *thread_group) { DBUG_ENTER("listener"); + connection_t *retval= NULL; + + decrement_active_threads(thread_group); for(;;) { native_event ev[MAX_EVENTS]; int cnt; if (thread_group->shutdown) - { - DBUG_RETURN(&POOL_SHUTDOWN_EVENT); - } - do - { - cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1); - } - while(cnt <= 0 && errno == EINTR); - + break; + + cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1); + if (cnt <=0) { DBUG_ASSERT(thread_group->shutdown); - DBUG_RETURN(&POOL_SHUTDOWN_EVENT); + break; } - /* - Put events to queue, maybe wakeup workers. - If queue is currently empty, listener will return - so the current thread handles query itself, this avoids - wakeups and context switches. But if queue is not empty - this smells like a flood of queries, and the listener - stays. - */ mysql_mutex_lock(&thread_group->mutex); if (thread_group->shutdown) { mysql_mutex_unlock(&thread_group->mutex); - DBUG_RETURN(&POOL_SHUTDOWN_EVENT); + break; } - thread_group->io_event_count += cnt; - bool pick_event= queue_is_empty(thread_group); - - for(int i=(pick_event)?1:0; i < cnt ; i++) + thread_group->io_event_count += cnt; + + /* + We got some network events and need to make decisions : whether + listener hould handle events and whether or not any wake worker + threads so they can handle events. + + Q1 : Should listener handle an event itself, or put all events into + queue and let workers handle the events? + + Solution : + Generally, listener that handles events itself is preferable. We do not + want listener thread to change its state from waiting to running too + often, Since listener has just woken from poll, it better uses its time + slice and does some work. Besides, not handling events means they go to + the queue, and often to wake another worker must wake up to handle the + event. This is not good, as we want to avoid wakeups. + + The downside of listener that also handles queries is that we can + potentially leave thread group for long time not picking the new + network events. It is not a major problem, because this stall will be + detected sooner or later by the timer thread. Still, relying on timer + is not always good, because it may "tick" too slow (large timer_interval) + + We use following strategy to solve this problem - if queue was not empty + we suspect flood of network events and listener stays, Otherwise, it + handles a query. + + + Q2: If queue is not empty, how many workers to wake? + + Solution: + We generally try to keep one thread per group active (threads handling + queries are considered active, unless they stuck in inside some "wait") + Thus, we will wake only one worker, and only if there is not active + threads currently,and listener is not going to handle a query. When we + don't wake, we hope that currently active threads will finish fast and + handle the queue. If this does not happen, timer thread will detect stall + and wake a worker. + + NOTE: Currently nothing is done to detect or prevent long queuing times. + A solution (for the future) would be to give up "one active thread per group" + principle, if events stay in the queue for too long, and wake more workers. + + */ + + bool listener_picks_event= thread_group->queue.is_empty(); + + /* + If listener_picks_event is set, listener thread will handle first event, + and put the rest into the queue. If listener_pick_event is not set, all + events go to the queue. + */ + for(int i=(listener_picks_event)?1:0; i < cnt ; i++) { - pool_event_t *e= (pool_event_t *)native_event_get_userdata(&ev[i]); - queue_put(thread_group, e); + connection_t *c= (connection_t *)native_event_get_userdata(&ev[i]); + thread_group->queue.push_back(c); } - /* Wake at most one worker thread */ - if(thread_group->active_thread_count==0 && - /*!queue_is_empty(thread_group)*/ !pick_event) + + if(thread_group->active_thread_count==0 && !listener_picks_event) { + /* Wake one worker thread */ if(wake_thread(thread_group)) { - if(thread_group->thread_count == 1) + /* + Wake failed, groups has no idle threads. + Now check if the group has at least one worker. + */ + if(thread_group->thread_count == 1 && + thread_group->pending_thread_start_count == 0) + { + /* + Currently there is no worker thread in the group, as indicated by + thread_count == 1 (means listener is the only one thread in the + group). + + Rhe queue is not empty, and listener is not going to handle + events. In order to drain the queue, we create a worker here. + Alternatively, we could just rely on timer to detect stall, but + this would be an inefficient, pointless delay. + */ create_worker(thread_group); + } } } mysql_mutex_unlock(&thread_group->mutex); - if (pick_event) - DBUG_RETURN((pool_event_t *)(native_event_get_userdata(&ev[0]))); + if (listener_picks_event) + { + retval= (connection_t *)native_event_get_userdata(&ev[0]); + break; + } } + + increment_active_threads(thread_group); + DBUG_RETURN(retval); } @@ -674,7 +745,36 @@ static int create_worker(thread_group_t *thread_group) } -/* +/** + Calculate microseconds throttling delay for thread creation. + + The value depends on how many threads are already in the group: + small number of threads means no delay, the more threads the larger + the delay. + + The actual values were not calculated using any scientific methods. + They just look right, and behave well in practice. + + TODO: Should throttling depend on thread_pool_stall_limit? +*/ +static ulonglong microsecond_throttling_interval(thread_group_t *thread_group) +{ + int count= thread_group->thread_count; + + if (count < 4) + return 0; + + if (count < 8) + return 50*1000; + + if(count < 16) + return 100*1000; + + return 200*1000; +} + + +/** Wakes a worker thread, or creates a new one. Worker creation is throttled, so we avoid too many threads @@ -682,9 +782,6 @@ static int create_worker(thread_group_t *thread_group) */ static int wake_or_create_thread(thread_group_t *thread_group) { - ulonglong now; - ulonglong time_since_last_thread_created; - DBUG_ENTER("wake_or_create_thread"); if (wake_thread(thread_group) == 0) @@ -696,30 +793,25 @@ static int wake_or_create_thread(thread_group_t *thread_group) if (thread_group->thread_count > thread_group->connection_count) DBUG_RETURN(-1); - if (thread_group->thread_count < 4) - { - DBUG_RETURN(create_worker(thread_group)); - } - - now = microsecond_interval_timer(); - time_since_last_thread_created = - (now - thread_group->last_thread_creation_time)/1000; - + if (thread_group->active_thread_count == 0) { /* - We're better off creating a new thread here with no delay, as - others threads (at least 4) are all blocking and there was no sleeping - thread to wakeup. It smells like deadlock or very slowly executing - requests, e.g sleeps or user locks. + We're better off creating a new thread here with no delay, + either there is no workers at all, or they all are all blocking + and there was no sleeping thread to wakeup. It smells like deadlock + or very slowly executing requests, e.g sleeps or user locks. */ DBUG_RETURN(create_worker(thread_group)); } + ulonglong now = microsecond_interval_timer(); + ulonglong time_since_last_thread_created = + (now - thread_group->last_thread_creation_time); + /* Throttle thread creation. */ - if ((thread_group->thread_count < 8 && time_since_last_thread_created > 50) - || (thread_group->thread_count < 16 && time_since_last_thread_created > 100) - || (time_since_last_thread_created > 200)) + if (time_since_last_thread_created > + microsecond_throttling_interval(thread_group)) { DBUG_RETURN(create_worker(thread_group)); } @@ -729,62 +821,75 @@ static int wake_or_create_thread(thread_group_t *thread_group) -/* Initialize thread group */ int thread_group_init(thread_group_t *thread_group, pthread_attr_t* thread_attr) { DBUG_ENTER("thread_group_init"); - - memset(thread_group, 0, sizeof(thread_group_t)); thread_group->pthread_attr = thread_attr; mysql_mutex_init(key_group_mutex, &thread_group->mutex, NULL); - STAILQ_INIT(&thread_group->queue); - SLIST_INIT(&thread_group->waiting_threads); - - thread_group->pending_thread_start_count= 0; - thread_group->stalled= false; - - thread_group->pollfd= -1; + thread_group->pollfd=-1; + thread_group->shutdown_pipe[0]= -1; + thread_group->shutdown_pipe[1]= -1; DBUG_RETURN(0); } -/* - Wake single sleeping thread in pool. Optionally, tell this thread - to listen to socket io notification. +void thread_group_destroy(thread_group_t *thread_group) +{ + mysql_mutex_destroy(&thread_group->mutex); + if (thread_group->pollfd != -1) + { + close(thread_group->pollfd); + thread_group->pollfd= -1; + } + for(int i=0; i < 2; i++) + { + if(thread_group->shutdown_pipe[i] != -1) + { + close(thread_group->shutdown_pipe[i]); + thread_group->shutdown_pipe[i]= -1; + } + } +} + +/** + Wake sleeping thread from waiting list */ static int wake_thread(thread_group_t *thread_group) { DBUG_ENTER("wake_thread"); - worker_thread_t *thread = SLIST_FIRST(&thread_group->waiting_threads); + worker_thread_t *thread = thread_group->waiting_threads.front(); if(thread) { thread->woken= true; - SLIST_REMOVE_HEAD(&thread_group->waiting_threads, ptr); + thread_group->waiting_threads.remove(thread); if (mysql_cond_signal(&thread->cond)) - abort(); + abort(); DBUG_RETURN(0); } DBUG_RETURN(-1); /* no thread- missed wakeup*/ } -/* - Shutdown thread group. +/* + Initiate shutdown for thread group. + + The shutdown is asynchronous, we only care to wake all threads + in here, so they can finish. We do not wait here until threads + terminate, + + Final cleanup of the group (thread_group_destroy) will be done by + the last exiting threads. */ static void thread_group_close(thread_group_t *thread_group) { DBUG_ENTER("thread_group_close"); - - char c= 0; mysql_mutex_lock(&thread_group->mutex); if (thread_group->thread_count == 0 && thread_group->pending_thread_start_count == 0) { - if (thread_group->pollfd >= 0) - close(thread_group->pollfd); mysql_mutex_unlock(&thread_group->mutex); - mysql_mutex_destroy(&thread_group->mutex); + thread_group_destroy(thread_group); DBUG_VOID_RETURN; } @@ -795,40 +900,41 @@ static void thread_group_close(thread_group_t *thread_group) { DBUG_VOID_RETURN; } + + /* Wake listener */ if (io_poll_associate_fd(thread_group->pollfd, - thread_group->shutdown_pipe[0], &POOL_SHUTDOWN_EVENT)) + thread_group->shutdown_pipe[0], NULL)) { DBUG_VOID_RETURN; } - - /* Wake listener. */ + char c= 0; if (write(thread_group->shutdown_pipe[1], &c, 1) < 0) DBUG_VOID_RETURN; /* Wake all workers. */ - while(wake_thread(thread_group) == 0) {}; - mysql_mutex_unlock(&thread_group->mutex); + while(wake_thread(thread_group) == 0) + { + } -#if 0 - /* Wait until workers terminate */ - while(thread_group->thread_count) - usleep(1000); -#endif + mysql_mutex_unlock(&thread_group->mutex); DBUG_VOID_RETURN; } /* - Post a task to the workqueue, maybe wake a worker so - it picks the task. + Add work to the queue. Maybe wake a worker if they all sleep. + + Currently, this function is only used when new connections need to + perform login (this is done in worker threads). + */ -static void post_event(thread_group_t *thread_group, pool_event_t* ev) +static void queue_put(thread_group_t *thread_group, connection_t *connection) { - DBUG_ENTER("post_event"); + DBUG_ENTER("queue_put"); mysql_mutex_lock(&thread_group->mutex); - STAILQ_INSERT_TAIL(&thread_group->queue, ev, next); + thread_group->queue.push_back(connection); if (thread_group->active_thread_count == 0) { wake_or_create_thread(thread_group); @@ -850,23 +956,33 @@ static bool too_many_threads(thread_group_t *thread_group) -/* - Dequeue a work item. +/** + Retrieve a connection with pending event. + + Pending event in our case means that there is either a pending login request + (if connection is not yet logged in), or there are unread bytes on the socket. - If it is not immediately available, thread will sleep until - work is available (it also can become IO listener for a while). + If there are no pending events currently, thread will wait. If timeout specified + int abstime parameter passes, the function returns NULL. + + @param current_thread - current worker thread + @param thread_group - current thread group + @param abstime - absolute wait timeout + + @return + connection with pending event. NULL is returned if timeout has expired,or on shutdown. */ -int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, - pool_event_t **ev, struct timespec *ts) +connection_t *get_event(worker_thread_t *current_thread, + thread_group_t *thread_group, struct timespec *abstime) { DBUG_ENTER("get_event"); - pool_event_t *first_event = NULL; + connection_t *connection = NULL; int err=0; mysql_mutex_lock(&thread_group->mutex); - decrement_active_threads(thread_group); + DBUG_ASSERT(thread_group->active_thread_count >= 0); do @@ -877,8 +993,8 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, /* Check if queue is not empty */ if (!too_many_threads(thread_group)) { - first_event= queue_get(thread_group); - if(first_event) + connection = queue_get(thread_group); + if(connection) break; } @@ -888,7 +1004,7 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, thread_group->listener= current_thread; mysql_mutex_unlock(&thread_group->mutex); - first_event= listener(current_thread, thread_group); + connection = listener(current_thread, thread_group); mysql_mutex_lock(&thread_group->mutex); /* There is no listener anymore, it just returned. */ @@ -906,12 +1022,11 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, if (io_poll_wait(thread_group->pollfd,&nev,1, 0) == 1) { thread_group->io_event_count++; - first_event = (pool_event_t *)native_event_get_userdata(&nev); + connection = (connection_t *)native_event_get_userdata(&nev); break; } } - /* And now, finally sleep */ current_thread->woken = false; /* wake() sets this to true */ @@ -920,13 +1035,15 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, It is important to add thread to the head rather than tail as it ensures LIFO wakeup order (hot caches, working inactivity timeout) */ - SLIST_INSERT_HEAD(&thread_group->waiting_threads, current_thread, ptr); - - if(ts) - err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, ts); + thread_group->waiting_threads.push_front(current_thread); + + decrement_active_threads(thread_group); + if(abstime) + err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, abstime); else err = mysql_cond_wait(¤t_thread->cond, &thread_group->mutex); - + increment_active_threads(thread_group); + if (!current_thread->woken) { /* @@ -934,7 +1051,7 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, a timeout. Anyhow, we need to remove ourselves from the list now. If thread was explicitly woken, than caller removed us from the list. */ - SLIST_REMOVE(&thread_group->waiting_threads, current_thread, worker_thread_t, ptr); + thread_group->waiting_threads.remove(current_thread); } if(err) @@ -944,26 +1061,16 @@ int get_event(worker_thread_t *current_thread, thread_group_t *thread_group, while(true); thread_group->stalled= false; - increment_active_threads(thread_group); mysql_mutex_unlock(&thread_group->mutex); - - - if (first_event) - *ev = first_event; - else - *ev = &POOL_SHUTDOWN_EVENT; - DBUG_RETURN(err); + DBUG_RETURN(connection); } -/* - Tells the pool that thread starts waiting on IO, lock, condition, +/** + Tells the pool that worker starts waiting on IO, lock, condition, sleep() or similar. - - Will wake another worker, and if there is no listener will - promote a listener, */ void wait_begin(thread_group_t *thread_group) { @@ -974,8 +1081,12 @@ void wait_begin(thread_group_t *thread_group) DBUG_ASSERT(thread_group->connection_count > 0); if((thread_group->active_thread_count == 0) && - (!queue_is_empty(thread_group) || !thread_group->listener)) + (thread_group->queue.is_empty() || !thread_group->listener)) { + /* + Group might stall while this thread waits, thus wake + or create a worker to prevent stall. + */ wake_or_create_thread(thread_group); } @@ -983,9 +1094,10 @@ void wait_begin(thread_group_t *thread_group) DBUG_VOID_RETURN; } -/* - Tells the pool current thread finished waiting. +/** + Tells the pool has finished waiting. */ + void wait_end(thread_group_t *thread_group) { DBUG_ENTER("wait_end"); @@ -996,7 +1108,10 @@ void wait_end(thread_group_t *thread_group) } -/* Scheduler */ +/** + Allocate/initialize a new connection structure. +*/ + connection_t *alloc_connection(THD *thd) { DBUG_ENTER("alloc_connection"); @@ -1014,42 +1129,67 @@ connection_t *alloc_connection(THD *thd) -/* +/** Add a new connection to thread pool.. */ + void tp_add_connection(THD *thd) { DBUG_ENTER("tp_add_connection"); threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); - connection_t *c= alloc_connection(thd); - if(c) + connection_t *connection= alloc_connection(thd); + if(connection) { - c->thread_group= &all_groups[c->thd->thread_id%group_count]; - mysql_mutex_lock(&c->thread_group->mutex); - c->thread_group->connection_count++; - mysql_mutex_unlock(&c->thread_group->mutex); - c->thd->event_scheduler.data = c; - post_event(c->thread_group,&c->event); + mysql_mutex_lock(&thd->LOCK_thd_data); + thd->event_scheduler.data= connection; + mysql_mutex_unlock(&thd->LOCK_thd_data); + + /* Assign connection to a group. */ + thread_group_t *group= + &all_groups[connection->thd->thread_id%group_count]; + + connection->thread_group=group; + + mysql_mutex_lock(&group->mutex); + group->connection_count++; + mysql_mutex_unlock(&group->mutex); + + /* + Add connection to the work queue.Actual logon + will be done by a worker thread. + */ + queue_put(group, connection); } DBUG_VOID_RETURN; } -static void connection_abort(connection_t *c) +/** + Terminate connection. +*/ + +static void connection_abort(connection_t *connection) { DBUG_ENTER("connection_abort"); - mysql_mutex_lock(&c->thread_group->mutex); - c->thread_group->connection_count--; - mysql_mutex_unlock(&c->thread_group->mutex); + thread_group_t *group= connection->thread_group; + + mysql_mutex_lock(&group->mutex); + group->connection_count--; + mysql_mutex_unlock(&group->mutex); - threadpool_remove_connection(c->thd); - my_free(c); + threadpool_remove_connection(connection->thd); + my_free(connection); DBUG_VOID_RETURN; } + +/** + MySQL scheduler callback : kill connection +*/ + void tp_post_kill_notification(THD *thd) { DBUG_ENTER("tp_post_kill_notification"); @@ -1061,6 +1201,10 @@ void tp_post_kill_notification(THD *thd) DBUG_VOID_RETURN; } +/** + MySQL scheduler callback: wait begin +*/ + void tp_wait_begin(THD *thd, int type) { DBUG_ENTER("tp_wait_begin"); @@ -1079,6 +1223,10 @@ void tp_wait_begin(THD *thd, int type) } +/** + MySQL scheduler callback: wait end +*/ + void tp_wait_end(THD *thd) { DBUG_ENTER("tp_wait_end"); @@ -1095,7 +1243,7 @@ void tp_wait_end(THD *thd) DBUG_VOID_RETURN; } - + static void set_next_timeout_check(ulonglong abstime) { DBUG_ENTER("set_next_timeout_check"); @@ -1108,6 +1256,11 @@ static void set_next_timeout_check(ulonglong abstime) DBUG_VOID_RETURN; } + +/** + Set wait timeout for connection. +*/ + static void set_wait_timeout(connection_t *c) { DBUG_ENTER("set_wait_timeout"); @@ -1129,10 +1282,10 @@ static void set_wait_timeout(connection_t *c) -/* - Handle a (rare) special case,where connection needs to - migrate to a different group because group_count has changed - as a result of thread_pool_size setting. +/** + Handle a (rare) special case,where connection needs to + migrate to a different group because group_count has changed + after thread_pool_size setting. */ static int change_group(connection_t *c, thread_group_t *old_group, @@ -1162,9 +1315,9 @@ static int change_group(connection_t *c, } -static int start_io(connection_t *c) +static int start_io(connection_t *connection) { - int fd = c->thd->net.vio->sd; + int fd = connection->thd->net.vio->sd; /* Usually, connection will stay in the same group for the entire @@ -1176,78 +1329,79 @@ static int start_io(connection_t *c) So we recalculate in which group the connection should be, based on thread_id and current group count, and migrate if necessary. */ - thread_group_t *g = &all_groups[c->thd->thread_id%group_count]; + thread_group_t *group = + &all_groups[connection->thd->thread_id%group_count]; - if (g != c->thread_group) + if (group != connection->thread_group) { - if (!change_group(c, c->thread_group, g)) + if (!change_group(connection, connection->thread_group, group)) { - c->logged_in= true; - return io_poll_associate_fd(c->thread_group->pollfd, fd, c); + connection->logged_in= true; + return io_poll_associate_fd(group->pollfd, fd, connection); } else return -1; } - /* - Handle case where connection is not yet logged in, i.e - not associated with poll fd. + In case binding to a poll descriptor was not yet done, + (start_io called first time), do it now. */ - if(!c->logged_in) + if(!connection->logged_in) { - c->logged_in= true; - return io_poll_associate_fd(c->thread_group->pollfd, fd, c); + connection->logged_in= true; + return io_poll_associate_fd(group->pollfd, fd, connection); } - return io_poll_start_read(c->thread_group->pollfd, fd, c); + return io_poll_start_read(group->pollfd, fd, connection); } -static void handle_event(pool_event_t *ev) +static void handle_event(connection_t *connection) { DBUG_ENTER("handle_event"); - - /* Normal case, handle query on connection */ - connection_t *c = (connection_t*)(void *)ev; int ret; - if (!c->logged_in) + if (!connection->logged_in) { - ret= threadpool_add_connection(c->thd); + ret= threadpool_add_connection(connection->thd); } else { - ret= threadpool_process_request(c->thd); + ret= threadpool_process_request(connection->thd); } if(!ret) { - set_wait_timeout(c); - ret= start_io(c); + set_wait_timeout(connection); + ret= start_io(connection); } if (ret) { - connection_abort(c); + connection_abort(connection); } + DBUG_VOID_RETURN; } +/** + Worker thread's main +*/ static void *worker_main(void *param) { worker_thread_t this_thread; - - thread_created++; pthread_detach_this_thread(); my_thread_init(); + DBUG_ENTER("worker_main"); + thread_created++; thread_group_t *thread_group = (thread_group_t *)param; /* Init per-thread structure */ @@ -1265,16 +1419,16 @@ static void *worker_main(void *param) /* Run event loop */ for(;;) { - struct pool_event_t *ev; + connection_t *connection; struct timespec ts; set_timespec(ts,threadpool_idle_timeout); - if (get_event(&this_thread, thread_group, &ev, &ts) - || ev == &POOL_SHUTDOWN_EVENT) + connection = get_event(&this_thread, thread_group, &ts); + if (!connection) { break; } this_thread.event_count++; - handle_event(ev); + handle_event(connection); } /* Thread shutdown: cleanup per-worker-thread structure. */ @@ -1286,20 +1440,18 @@ static void *worker_main(void *param) mysql_mutex_unlock(&thread_group->mutex); my_atomic_add32(&tp_stats.num_worker_threads, -1); - /* If it is the last thread in pool and pool is terminating, destroy pool.*/ - if (thread_group->shutdown && (thread_group->thread_count == 0)) + /* If it is the last thread in group and pool is terminating, destroy group.*/ + if (thread_group->shutdown && thread_group->thread_count == 0 + && thread_group->pending_thread_start_count == 0) { - /* last thread existing, cleanup the pool structure */ - mysql_mutex_destroy(&thread_group->mutex); + thread_group_destroy(thread_group); } - DBUG_POP(); my_thread_end(); return NULL; } -static bool started=false; - +static bool started=false; bool tp_init() { DBUG_ENTER("tp_init"); @@ -1311,10 +1463,7 @@ bool tp_init() thread_group_init(&all_groups[i], get_connection_attrib()); } tp_set_threadpool_size(threadpool_size); - - #define PSI_register(X) \ - if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) - + PSI_register(mutex); PSI_register(cond); PSI_register(thread); From d212991e892c366f6df96f8b52c8306ae329770f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sun, 15 Jan 2012 15:41:25 +0100 Subject: [PATCH 28/51] Get rid of idle thread counter atomic variable. Instead, use function that loops over groups and calculates idle threads for "show status". --- sql/mysqld.cc | 13 ++++++++++-- sql/threadpool.h | 7 +++++-- sql/threadpool_unix.cc | 46 +++++++++++++++++++++++++----------------- sql/threadpool_win.cc | 10 +++++++++ 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index cc32411e567..78faf5cef76 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7008,6 +7008,15 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff) return 0; } +#ifdef HAVE_POOL_OF_THREADS +int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_INT; + var->value= buff; + *(int *)buff= tp_get_idle_thread_count(); + return 0; +} +#endif /* Variables shown by SHOW STATUS in alphabetical order @@ -7144,8 +7153,8 @@ SHOW_VAR status_vars[]= { {"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG}, {"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG}, #endif -#ifndef EMBEDDED_LIBRARY - {"Threadpool_idle_threads", (char *) &tp_stats.num_waiting_threads, SHOW_INT}, +#ifdef HAVE_POOL_OF_THREADS + {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_FUNC}, {"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT}, #endif {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH}, diff --git a/sql/threadpool.h b/sql/threadpool.h index 8c991aab2cb..78112b8b7bc 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -22,6 +22,9 @@ extern void tp_wait_end(THD*); extern void tp_post_kill_notification(THD *thd); extern void tp_end(void); +/* Used in SHOW for threadpool_idle_thread_count */ +extern int tp_get_idle_thread_count(); + /* Threadpool statistics */ @@ -29,8 +32,6 @@ struct TP_STATISTICS { /* Current number of worker thread. */ volatile int32 num_worker_threads; - /* Current number of idle threads. */ - volatile int32 num_waiting_threads; }; extern TP_STATISTICS tp_stats; @@ -45,3 +46,5 @@ extern void tp_set_threadpool_stall_limit(uint val); /* Activate threadpool scheduler */ extern void tp_scheduler(void); +extern int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff); + diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index b6eeb5bcffd..f66c862dd0f 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -384,18 +384,6 @@ static connection_t *queue_get(thread_group_t *thread_group) } -static void increment_active_threads(thread_group_t *thread_group) -{ - my_atomic_add32(&tp_stats.num_waiting_threads,-1); - thread_group->active_thread_count++; -} - -static void decrement_active_threads(thread_group_t *thread_group) -{ - my_atomic_add32(&tp_stats.num_waiting_threads,1); - thread_group->active_thread_count--; -} - /* Handle wait timeout : @@ -585,7 +573,7 @@ static connection_t * listener(worker_thread_t *current_thread, connection_t *retval= NULL; - decrement_active_threads(thread_group); + for(;;) { native_event ev[MAX_EVENTS]; @@ -593,8 +581,10 @@ static connection_t * listener(worker_thread_t *current_thread, if (thread_group->shutdown) break; - + + thread_group->active_thread_count--; cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1); + thread_group->active_thread_count++; if (cnt <=0) { @@ -705,7 +695,7 @@ static connection_t * listener(worker_thread_t *current_thread, } } - increment_active_threads(thread_group); + DBUG_RETURN(retval); } @@ -1037,12 +1027,12 @@ connection_t *get_event(worker_thread_t *current_thread, */ thread_group->waiting_threads.push_front(current_thread); - decrement_active_threads(thread_group); + thread_group->active_thread_count--; if(abstime) err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, abstime); else err = mysql_cond_wait(¤t_thread->cond, &thread_group->mutex); - increment_active_threads(thread_group); + thread_group->active_thread_count++; if (!current_thread->woken) { @@ -1076,7 +1066,8 @@ void wait_begin(thread_group_t *thread_group) { DBUG_ENTER("wait_begin"); mysql_mutex_lock(&thread_group->mutex); - decrement_active_threads(thread_group); + thread_group->active_thread_count--; + DBUG_ASSERT(thread_group->active_thread_count >=0); DBUG_ASSERT(thread_group->connection_count > 0); @@ -1102,7 +1093,7 @@ void wait_end(thread_group_t *thread_group) { DBUG_ENTER("wait_end"); mysql_mutex_lock(&thread_group->mutex); - increment_active_threads(thread_group); + thread_group->active_thread_count++; mysql_mutex_unlock(&thread_group->mutex); DBUG_VOID_RETURN; } @@ -1525,3 +1516,20 @@ void tp_set_threadpool_stall_limit(uint limit) mysql_cond_signal(&(pool_timer.cond)); mysql_mutex_unlock(&(pool_timer.mutex)); } + + +/** + Calculate number of idle/waiting threads in the pool. + + Sum idle threads over all groups. + Don't do any locking, it is not required for stats. +*/ +int tp_get_idle_thread_count() +{ + int sum=0; + for(uint i= 0; i< array_elements(all_groups) && (all_groups[i].pollfd >= 0); i++) + { + sum+= (all_groups[i].thread_count - all_groups[i].active_thread_count); + } + return sum; +} \ No newline at end of file diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 0afb628a1ca..1a560e62301 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -753,3 +753,13 @@ void tp_wait_end(THD *thd) /* Do we need to do anything ? */ } + +/** + Number of idle threads in pool. + This info is not available in Windows implementation, + thus function always returns 0. +*/ +int tp_get_idle_thread_count() +{ + return 0; +} \ No newline at end of file From 1f8cbf168c2283d59bff0ea9f73b9f3d334dfe4f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 16 Jan 2012 02:18:24 +0100 Subject: [PATCH 29/51] Fix threadpool on BSD and Solaris --- sql/threadpool_unix.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index f66c862dd0f..7f7d52eef1c 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -275,7 +275,7 @@ int io_poll_start_read(int pollfd, int fd, void *data) int io_poll_associate_fd(int pollfd, int fd, void *data) { - return io_poll_start_read(poolfd,fd, data); + return io_poll_start_read(pollfd,fd, data); } @@ -338,7 +338,7 @@ static int io_poll_associate_fd(int pollfd, int fd, void *data) int io_poll_disassociate_fd(int pollfd, int fd) { - return 0; + return port_dissociate(pollfd, PORT_SOURCE_FD, fd); } int io_poll_wait(int pollfd, native_event *events, int maxevents, int timeout_ms) @@ -1532,4 +1532,4 @@ int tp_get_idle_thread_count() sum+= (all_groups[i].thread_count - all_groups[i].active_thread_count); } return sum; -} \ No newline at end of file +} From 690931102858d7cd90e912364e0bf934a8e7436b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 17 Jan 2012 18:50:40 +0100 Subject: [PATCH 30/51] Threadpool : address some of the monty's review points Also, print message when pool blocks. --- sql/threadpool_unix.cc | 141 +++++++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 33 deletions(-) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 7f7d52eef1c..d59d33c6589 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef __linux__ #include typedef struct epoll_event native_event; @@ -91,6 +92,7 @@ struct connection_t connection_t **prev_in_queue; ulonglong abs_wait_timeout; bool logged_in; + bool bound_to_poll_descriptor; bool waiting; }; @@ -127,6 +129,12 @@ struct thread_group_t static thread_group_t all_groups[MAX_THREAD_GROUPS]; static uint group_count; +/** + Used for printing "pool blocked" message, see + print_pool_blocked_message(); +*/ +static time_t pool_block_start; + /* Global timer for all groups */ struct pool_timer_t { @@ -140,6 +148,7 @@ struct pool_timer_t static pool_timer_t pool_timer; + /* Externals functions and variables we use */ extern void scheduler_init(); extern pthread_attr_t *get_connection_attrib(void); @@ -155,7 +164,7 @@ static void connection_abort(connection_t *connection); void tp_post_kill_notification(THD *thd); static void set_wait_timeout(connection_t *connection); static void set_next_timeout_check(ulonglong abstime); - +static void print_pool_blocked_message(bool); /** Asynchronous network IO. @@ -641,9 +650,9 @@ static connection_t * listener(worker_thread_t *current_thread, and wake a worker. NOTE: Currently nothing is done to detect or prevent long queuing times. - A solution (for the future) would be to give up "one active thread per group" - principle, if events stay in the queue for too long, and wake more workers. - + A solutionc for the future would be to give up "one active thread per + group" principle, if events stay in the queue for too long, and just wake + more workers. */ bool listener_picks_event= thread_group->queue.is_empty(); @@ -714,16 +723,18 @@ static connection_t * listener(worker_thread_t *current_thread, static int create_worker(thread_group_t *thread_group) { pthread_t thread_id; + bool max_threads_reached= false; int err; + DBUG_ENTER("create_worker"); if (tp_stats.num_worker_threads >= (int)threadpool_max_threads && thread_group->thread_count >= 2) { - DBUG_PRINT("info", - ("Cannot create new thread (maximum allowed threads reached)")); - DBUG_RETURN(-1); + err= 1; + max_threads_reached= true; + goto end; } - + err= mysql_thread_create(key_worker_thread, &thread_id, thread_group->pthread_attr, worker_main, thread_group); if (!err) @@ -731,6 +742,18 @@ static int create_worker(thread_group_t *thread_group) thread_group->pending_thread_start_count++; thread_group->last_thread_creation_time=microsecond_interval_timer(); } + else + { + my_errno= errno; + } + + +end: + if (err) + print_pool_blocked_message(max_threads_reached); + else + pool_block_start= 0; /* Reset pool blocked timer, if it was set */ + DBUG_RETURN(err); } @@ -816,7 +839,7 @@ int thread_group_init(thread_group_t *thread_group, pthread_attr_t* thread_attr) DBUG_ENTER("thread_group_init"); thread_group->pthread_attr = thread_attr; mysql_mutex_init(key_group_mutex, &thread_group->mutex, NULL); - thread_group->pollfd=-1; + thread_group->pollfd= -1; thread_group->shutdown_pipe[0]= -1; thread_group->shutdown_pipe[1]= -1; DBUG_RETURN(0); @@ -1113,6 +1136,7 @@ connection_t *alloc_connection(THD *thd) connection->thd = thd; connection->waiting= false; connection->logged_in= false; + connection->bound_to_poll_descriptor= false; connection->abs_wait_timeout= ULONGLONG_MAX; } DBUG_RETURN(connection); @@ -1289,8 +1313,11 @@ static int change_group(connection_t *c, /* Remove connection from the old group. */ mysql_mutex_lock(&old_group->mutex); - if (c->logged_in) + if (c->bound_to_poll_descriptor) + { io_poll_disassociate_fd(old_group->pollfd,fd); + c->bound_to_poll_descriptor= false; + } c->thread_group->connection_count--; mysql_mutex_unlock(&old_group->mutex); @@ -1325,22 +1352,18 @@ static int start_io(connection_t *connection) if (group != connection->thread_group) { - if (!change_group(connection, connection->thread_group, group)) + if (change_group(connection, connection->thread_group, group)) { - connection->logged_in= true; - return io_poll_associate_fd(group->pollfd, fd, connection); - } - else return -1; + } } /* - In case binding to a poll descriptor was not yet done, - (start_io called first time), do it now. + Bind to poll descriptor if not yet done. */ - if(!connection->logged_in) + if(!connection->bound_to_poll_descriptor) { - connection->logged_in= true; + connection->bound_to_poll_descriptor= true; return io_poll_associate_fd(group->pollfd, fd, connection); } @@ -1353,36 +1376,34 @@ static void handle_event(connection_t *connection) { DBUG_ENTER("handle_event"); - int ret; + int err; if (!connection->logged_in) { - ret= threadpool_add_connection(connection->thd); + err = threadpool_add_connection(connection->thd); + connection->logged_in= true; } else { - ret= threadpool_process_request(connection->thd); + err = threadpool_process_request(connection->thd); } - if(!ret) + if(!err) { set_wait_timeout(connection); - ret= start_io(connection); + err= start_io(connection); } - if (ret) + if (err) { connection_abort(connection); } - - DBUG_VOID_RETURN; } /** Worker thread's main */ - static void *worker_main(void *param) { @@ -1432,8 +1453,9 @@ static void *worker_main(void *param) my_atomic_add32(&tp_stats.num_worker_threads, -1); /* If it is the last thread in group and pool is terminating, destroy group.*/ - if (thread_group->shutdown && thread_group->thread_count == 0 - && thread_group->pending_thread_start_count == 0) + if (thread_group->shutdown + && thread_group->thread_count == 0 + && thread_group->pending_thread_start_count == 0) { thread_group_destroy(thread_group); } @@ -1480,7 +1502,8 @@ void tp_end() DBUG_VOID_RETURN; } -/* Ensure that poll descriptors are created when threadpool_size changes */ + +/** Ensure that poll descriptors are created when threadpool_size changes */ int tp_set_threadpool_size(uint size) { bool success= true; @@ -1513,8 +1536,8 @@ void tp_set_threadpool_stall_limit(uint limit) return; mysql_mutex_lock(&(pool_timer.mutex)); pool_timer.tick_interval= limit; - mysql_cond_signal(&(pool_timer.cond)); mysql_mutex_unlock(&(pool_timer.mutex)); + mysql_cond_signal(&(pool_timer.cond)); } @@ -1527,9 +1550,61 @@ void tp_set_threadpool_stall_limit(uint limit) int tp_get_idle_thread_count() { int sum=0; - for(uint i= 0; i< array_elements(all_groups) && (all_groups[i].pollfd >= 0); i++) + for(uint i= 0; + i< array_elements(all_groups) && (all_groups[i].pollfd >= 0); + i++) { sum+= (all_groups[i].thread_count - all_groups[i].active_thread_count); } return sum; } + + +/* Report threadpool problems */ + +#define BLOCK_MSG_DELAY 30 + +static const char *max_threads_reached_msg= +"Threadpool could not create additional thread to handle queries, because the " +"number of allowed threads was reached. Increasing 'thread_pool_max_threads' " +"parameter can help in this situation.\n" +"If 'extra_port' parameter is set, you can still connect to the database with " +"superuser account (it must be TCP connection using extra_port as TCP port) " +"and troubleshoot the situation. " +"A likely cause of pool blocks are clients that lock resources for long time. " +"'show processlist' or 'show engine innodb status' can give additional hints."; + +static const char *create_thread_error_msg= +"Can't create threads in threadpool (errno=%d)."; + +/** + Write a message when blocking situation in threadpool occurs. + The message is written only when pool blocks for BLOCK_MSG_DELAY (30) seconds. + It will be just a single message for each blocking situation (to prevent + log flood). +*/ +static void print_pool_blocked_message(bool max_threads_reached) +{ + time_t now; + static bool msg_written; + + now= time(NULL); + if (pool_block_start == 0) + { + pool_block_start= now; + msg_written = false; + return; + } + + if(now > pool_block_start + BLOCK_MSG_DELAY && !msg_written) + { + if(max_threads_reached) + sql_print_error(max_threads_reached_msg); + else + sql_print_error(create_thread_error_msg, my_errno); + + sql_print_information("Threadpool has been blocked for %u seconds\n",(uint)(now- pool_block_start)); + /* avoid reperated messages for the same blocking situation */ + msg_written= true; + } +} From c492296127b55f35f4dd78ba8829b65d3a5c0740 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 18 Jan 2012 21:12:04 +0100 Subject: [PATCH 31/51] ensure that lock is held, whenever active thread counter changes. It was not the case inside listener routine. --- sql/threadpool_unix.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index d59d33c6589..d0a6b0d8a08 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -591,9 +591,7 @@ static connection_t * listener(worker_thread_t *current_thread, if (thread_group->shutdown) break; - thread_group->active_thread_count--; cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1); - thread_group->active_thread_count++; if (cnt <=0) { @@ -1015,11 +1013,13 @@ connection_t *get_event(worker_thread_t *current_thread, if(!thread_group->listener) { thread_group->listener= current_thread; + thread_group->active_thread_count--; mysql_mutex_unlock(&thread_group->mutex); connection = listener(current_thread, thread_group); mysql_mutex_lock(&thread_group->mutex); + thread_group->active_thread_count++; /* There is no listener anymore, it just returned. */ thread_group->listener= NULL; break; From b1422b8f1b2e93de2ec548c81df5609dd5996515 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 24 Jan 2012 02:26:29 +0100 Subject: [PATCH 32/51] reduce diffs to the 5.5 version, remove random change in mysql-test-run.pl --- mysql-test/mysql-test-run.pl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 0b088391236..1bef01e3bd5 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3495,9 +3495,7 @@ sub mysql_install_db { mtr_add_arg($args, "--loose-skip-ndbcluster"); mtr_add_arg($args, "--loose-skip-aria"); mtr_add_arg($args, "--disable-sync-frm"); - mtr_add_arg($args, "--tmpdir=."); - mtr_add_arg($args, "--max_allowed_packet=8M"); - mtr_add_arg($args, "--net_buffer_length=16K"); + mtr_add_arg($args, "--tmpdir=%s", "$opt_vardir/tmp/"); mtr_add_arg($args, "--core-file"); if ( $opt_debug ) From d50649ecf787d4adf80544e892a00a709db37f32 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 24 Jan 2012 03:23:14 +0100 Subject: [PATCH 33/51] small cleanups --- sql/scheduler.h | 20 ++------------------ sql/sql_parse.cc | 1 - sql/threadpool.h | 6 ++++++ sql/threadpool_common.cc | 3 +++ sql/threadpool_unix.cc | 2 -- sql/threadpool_win.cc | 7 +------ 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/sql/scheduler.h b/sql/scheduler.h index c91df8512ab..302ccd7ab11 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -95,26 +95,8 @@ public: */ PSI_thread *m_psi; void *data; /* scheduler-specific data structure */ -#ifndef DBUG_OFF - bool set_explain; - char dbug_explain[512]; -#endif }; -void *thd_get_scheduler_data(THD *thd); -void thd_set_scheduler_data(THD *thd, void *data); -PSI_thread* thd_get_psi(THD *thd); -void thd_set_psi(THD *thd, PSI_thread *psi); - -/* Common thread pool routines, suitable for different implementations */ -extern void threadpool_remove_connection(THD *thd); -extern int threadpool_process_request(THD *thd); -extern int threadpool_add_connection(THD *thd); - - -extern scheduler_functions *thread_scheduler; -#endif /* SCHEDULER_INCLUDED */ - #if !defined(EMBEDDED_LIBRARY) #define HAVE_POOL_OF_THREADS 1 void pool_of_threads_scheduler(scheduler_functions* func, @@ -124,3 +106,5 @@ void pool_of_threads_scheduler(scheduler_functions* func, #define pool_of_threads_scheduler(A,B,C) \ one_thread_per_connection_scheduler(A, B, C) #endif + +#endif /* SCHEDULER_INCLUDED */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2ea94e55eda..c0792b2c342 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -683,7 +683,6 @@ void cleanup_items(Item *item) @retval 1 request of thread shutdown (see dispatch_command() description) */ -int skip_net_wait_timeout = 0; bool do_command(THD *thd) { diff --git a/sql/threadpool.h b/sql/threadpool.h index 78112b8b7bc..2b0d09c0d6d 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -10,6 +10,12 @@ extern uint threadpool_max_threads; /* Maximum threads in pool */ extern uint threadpool_oversubscribe; /* Maximum active threads in group */ + +/* Common thread pool routines, suitable for different implementations */ +extern void threadpool_remove_connection(THD *thd); +extern int threadpool_process_request(THD *thd); +extern int threadpool_add_connection(THD *thd); + /* Functions used by scheduler. OS-specific implementations are in diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 5152f62efe9..dc8ef9f6cfd 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -19,6 +19,9 @@ uint threadpool_stall_limit; uint threadpool_max_threads; uint threadpool_oversubscribe; +/* Stats */ +TP_STATISTICS tp_stats; + extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys); extern bool do_command(THD*); diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index d0a6b0d8a08..5dcc9d4420c 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -60,8 +60,6 @@ static PSI_thread_info thread_list[] = #define PSI_register(X) \ if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) - -TP_STATISTICS tp_stats; struct thread_group_t; diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 1a560e62301..7a2c8c0c6cf 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -17,9 +17,6 @@ #include - -TP_STATISTICS tp_stats; - #define WEAK_SYMBOL(return_type, function, ...) \ typedef return_type (WINAPI *pFN_##function)(__VA_ARGS__); \ static pFN_##function my_##function = (pFN_##function) \ @@ -136,7 +133,6 @@ static void tp_log_warning(const char *msg, const char *fct) PTP_POOL pool; DWORD fls; -extern int skip_net_wait_timeout; static bool skip_completion_port_on_success = false; @@ -544,7 +540,7 @@ void tp_end(void) This would not be necessary if CloseThreadpool() would synchronously release all threads and wait until they disappear and call all their FLS - destrructors . However, threads in the pool are released asynchronously + destructors . However, threads in the pool are released asynchronously and might spend some time in the CRT shutdown code. Thus zero num_worker_threads, to avoid thread destructor's my_thread_end()s after this point. @@ -560,7 +556,6 @@ void tp_end(void) mysql_mutex_unlock(&THR_LOCK_threads); } } - skip_net_wait_timeout= 0; } /* From 398c935db30f934cd231fb5566a2bce7917a621c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 24 Jan 2012 19:18:22 +0100 Subject: [PATCH 34/51] further reduce diffs to 5.5, monty review --- include/thr_alarm.h | 1 - include/violite.h | 13 ++++++++++++- mysql-test/include/mysqld--help.inc | 2 +- mysql-test/r/kill.result | 3 +++ mysql-test/r/mysqld--help-win.result | 4 ++-- mysql-test/t/flush_read_lock_kill.test | 2 +- mysql-test/t/kill.test | 5 ++--- sql/sql_class.cc | 6 +----- sql/sql_class.h | 5 +---- vio/vio.c | 2 +- vio/vio_priv.h | 1 + vio/viosocket.c | 11 ++++++----- 12 files changed, 31 insertions(+), 24 deletions(-) diff --git a/include/thr_alarm.h b/include/thr_alarm.h index 66e344d10fd..8de70d4cdb8 100644 --- a/include/thr_alarm.h +++ b/include/thr_alarm.h @@ -93,7 +93,6 @@ typedef struct st_alarm { extern uint thr_client_alarm; extern pthread_t alarm_thread; - #define thr_alarm_init(A) (*(A))=0 #define thr_alarm_in_use(A) (*(A)!= 0) void init_thr_alarm(uint max_alarm); diff --git a/include/violite.h b/include/violite.h index 05b20245c5a..f28f72662ff 100644 --- a/include/violite.h +++ b/include/violite.h @@ -175,6 +175,17 @@ void vio_end(void); #define vio_is_connected(vio) (vio)->is_connected(vio) #endif /* !defined(DONT_MAP_VIO) */ +#ifdef _WIN32 +/* + Set thread id for io cancellation (required on Windows XP only, + and should to be removed if XP is no more supported) +*/ + +#define vio_set_thread_id(vio, tid) if(vio) vio->thread_id= tid +#else +#define vio_set_thread_id(vio, tid) +#endif + /* This enumerator is used in parser - should be always visible */ enum SSL_type { @@ -237,7 +248,7 @@ struct st_vio char *shared_memory_pos; #endif /* HAVE_SMEM */ #ifdef _WIN32 - DWORD thread_id; /* Used to XP only in vio_shutdown */ + DWORD thread_id; /* Used on XP only by vio_shutdown() */ OVERLAPPED pipe_overlapped; DWORD read_timeout_ms; DWORD write_timeout_ms; diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index a037f255a3e..85f420f3c7b 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -18,7 +18,7 @@ perl; # their paths may vary: @skipvars=qw/basedir open-files-limit general-log-file log plugin-dir log-slow-queries pid-file slow-query-log-file log-basename - datadir slave-load-tmpdir tmpdir socket /; + datadir slave-load-tmpdir tmpdir socket/; # Plugins which may or may not be there: @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster feedback debug temp-pool ssl des-key-file diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index e1a33faa7ee..e91db0c9036 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -60,6 +60,9 @@ SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; KILL @id; SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; Got one of the listed errors +SELECT 1; +1 +1 SET DEBUG_SYNC = 'RESET'; DROP TABLE t1, t2; SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 2cfa2221d1a..05cbcda7129 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -941,7 +941,7 @@ lower-case-table-names 1 master-info-file master.info master-retry-count 86400 master-verify-checksum FALSE -max-allowed-packet 8388608 +max-allowed-packet 1048576 max-binlog-cache-size 18446744073709547520 max-binlog-size 1073741824 max-binlog-stmt-cache-size 18446744073709547520 @@ -952,7 +952,7 @@ max-error-count 64 max-heap-table-size 16777216 max-join-size 18446744073709551615 max-length-for-sort-data 1024 -max-long-data-size 8388608 +max-long-data-size 1048576 max-prepared-stmt-count 16382 max-relay-log-size 0 max-seeks-for-key 18446744073709551615 diff --git a/mysql-test/t/flush_read_lock_kill.test b/mysql-test/t/flush_read_lock_kill.test index e0a772d9fbb..a672fa5dfc5 100644 --- a/mysql-test/t/flush_read_lock_kill.test +++ b/mysql-test/t/flush_read_lock_kill.test @@ -65,7 +65,7 @@ KILL CONNECTION @id; connection con1; --echo # Try to reap FLUSH TABLES WITH READ LOCK, --echo # it fail due to killed statement and connection. ---error 1317,2013,1927 +--error 1317,2013 reap; --echo # Switching to 'con2'. diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 9e7a67a725f..7c6c37ecfc6 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -142,10 +142,9 @@ KILL @id; SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; connection con1; ---error 1317,1053,2006,2013,1927 +--error 1317,1053,2006,2013 reap; ---error 0,2013 -let $ignore= `SELECT 1`; +SELECT 1; connection default; SET DEBUG_SYNC = 'RESET'; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0eb5c784859..fc1c05c236f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1701,11 +1701,7 @@ bool THD::store_globals() real_id= pthread_self(); // For debugging mysys_var->stack_ends_here= thread_stack + // for consistency, see libevent_thread_proc STACK_DIRECTION * (long)my_thread_stack_size; - -#ifdef _WIN32 - if (net.vio) - net.vio->thread_id= real_id; /* Required to support IO cancelation on XP */ -#endif + vio_set_thread_id(net.vio, real_id); /* We have to call thr_lock_info_init() again here as THD may have been created in another thread diff --git a/sql/sql_class.h b/sql/sql_class.h index c98ee4d6180..7c7ae1eb2af 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2341,10 +2341,7 @@ public: { mysql_mutex_lock(&LOCK_thd_data); active_vio = vio; -#ifdef _WIN32 - /* Required to support cancelation on XP */ - active_vio->thread_id = pthread_self(); -#endif + vio_set_thread_id(vio, pthread_self()); mysql_mutex_unlock(&LOCK_thd_data); } inline void clear_active_vio() diff --git a/vio/vio.c b/vio/vio.c index aa0d2012afa..415e3e23768 100644 --- a/vio/vio.c +++ b/vio/vio.c @@ -64,7 +64,7 @@ int vio_shared_memory_shutdown(Vio *vio, int how) int vio_pipe_shutdown(Vio *vio, int how) { - return vio_socket_shutdown(vio, how); /* cancels io */ + return cancel_io(vio->hPipe, vio->thread_id); } #endif diff --git a/vio/vio_priv.h b/vio/vio_priv.h index 3f62c508375..c390f8ea42e 100644 --- a/vio/vio_priv.h +++ b/vio/vio_priv.h @@ -39,6 +39,7 @@ size_t vio_read_pipe(Vio *vio, uchar * buf, size_t size); size_t vio_write_pipe(Vio *vio, const uchar * buf, size_t size); my_bool vio_is_connected_pipe(Vio *vio); int vio_close_pipe(Vio * vio); +int cancel_io(HANDLE handle, DWORD thread_id); int vio_shutdown_pipe(Vio *vio,int how); #endif diff --git a/vio/viosocket.c b/vio/viosocket.c index b554eec7d4f..e656d1809e4 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -144,7 +144,7 @@ static void CALLBACK cancel_io_apc(ULONG_PTR data) IO. On Vista+, simpler cancelation is done with CancelIoEx. */ -static int cancel_io(HANDLE handle, DWORD thread_id) +int cancel_io(HANDLE handle, DWORD thread_id) { static BOOL (WINAPI *fp_CancelIoEx) (HANDLE, OVERLAPPED *); static volatile int first_time= 1; @@ -177,11 +177,12 @@ static int cancel_io(HANDLE handle, DWORD thread_id) int vio_socket_shutdown(Vio *vio, int how) { -#ifdef _WIN32 - return cancel_io((HANDLE)vio->sd, vio->thread_id); -#else - return shutdown(vio->sd, how); + int ret= shutdown(vio->sd, how); +#ifdef _WIN32 + /* Cancel possible IO in progress (shutdown does not do that on Windows). */ + (void) cancel_io((HANDLE)vio->sd, vio->thread_id); #endif + return ret; } From 7ed6530a066ab1e1659c39ee614e503462d8d403 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 25 Jan 2012 03:59:09 +0100 Subject: [PATCH 35/51] add test thread_pool_min_basic --- .../r/thread_pool_min_threads_basic.result | 49 +++++++++++++++++++ .../t/thread_pool_min_threads_basic.test | 43 ++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 mysql-test/suite/sys_vars/r/thread_pool_min_threads_basic.result create mode 100644 mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test diff --git a/mysql-test/suite/sys_vars/r/thread_pool_min_threads_basic.result b/mysql-test/suite/sys_vars/r/thread_pool_min_threads_basic.result new file mode 100644 index 00000000000..bfa05d81609 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/thread_pool_min_threads_basic.result @@ -0,0 +1,49 @@ +SET @start_global_value = @@global.thread_pool_min_threads; +select @@global.thread_pool_min_threads; +@@global.thread_pool_min_threads +1 +select @@session.thread_pool_min_threads; +ERROR HY000: Variable 'thread_pool_min_threads' is a GLOBAL variable +show global variables like 'thread_pool_min_threads'; +Variable_name Value +thread_pool_min_threads 1 +show session variables like 'thread_pool_min_threads'; +Variable_name Value +thread_pool_min_threads 1 +select * from information_schema.global_variables where variable_name='thread_pool_min_threads'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_MIN_THREADS 1 +select * from information_schema.session_variables where variable_name='thread_pool_min_threads'; +VARIABLE_NAME VARIABLE_VALUE +THREAD_POOL_MIN_THREADS 1 +set global thread_pool_min_threads=1; +select @@global.thread_pool_min_threads; +@@global.thread_pool_min_threads +1 +set global thread_pool_min_threads=65536; +Warnings: +Warning 1292 Truncated incorrect thread_pool_min_threads value: '65536' +select @@global.thread_pool_min_threads; +@@global.thread_pool_min_threads +256 +set session thread_pool_min_threads=1; +ERROR HY000: Variable 'thread_pool_min_threads' is a GLOBAL variable and should be set with SET GLOBAL +set global thread_pool_min_threads=1.1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_min_threads' +set global thread_pool_min_threads=1e1; +ERROR 42000: Incorrect argument type to variable 'thread_pool_min_threads' +set global thread_pool_min_threads="foo"; +ERROR 42000: Incorrect argument type to variable 'thread_pool_min_threads' +set global thread_pool_min_threads=0; +Warnings: +Warning 1292 Truncated incorrect thread_pool_min_threads value: '0' +select @@global.thread_pool_min_threads; +@@global.thread_pool_min_threads +1 +set global thread_pool_min_threads=10000000000; +Warnings: +Warning 1292 Truncated incorrect thread_pool_min_threads value: '10000000000' +select @@global.thread_pool_min_threads; +@@global.thread_pool_min_threads +256 +SET @@global.thread_pool_min_threads = @start_global_value; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test new file mode 100644 index 00000000000..131fbe98502 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test @@ -0,0 +1,43 @@ +# uint global +--source include/not_embedded.inc +--source include/windows.inc +SET @start_global_value = @@global.thread_pool_min_threads; + +# +# exists as global only +# +select @@global.thread_pool_min_threads; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.thread_pool_min_threads; +show global variables like 'thread_pool_min_threads'; +show session variables like 'thread_pool_min_threads'; +select * from information_schema.global_variables where variable_name='thread_pool_min_threads'; +select * from information_schema.session_variables where variable_name='thread_pool_min_threads'; + +# +# show that it's writable +# +set global thread_pool_min_threads=1; +select @@global.thread_pool_min_threads; +set global thread_pool_min_threads=65536; +select @@global.thread_pool_min_threads; +--error ER_GLOBAL_VARIABLE +set session thread_pool_min_threads=1; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_min_threads=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_min_threads=1e1; +--error ER_WRONG_TYPE_FOR_VAR +set global thread_pool_min_threads="foo"; + + +set global thread_pool_min_threads=0; +select @@global.thread_pool_min_threads; +set global thread_pool_min_threads=10000000000; +select @@global.thread_pool_min_threads; + +SET @@global.thread_pool_min_threads = @start_global_value; From 57b6cb39aa268a49ad05f86025386ddde6516670 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 26 Jan 2012 04:35:54 +0100 Subject: [PATCH 36/51] Further review points and simplify Windows implementation --- mysql-test/r/mysqld--help-notwin.result | 4 +- sql/sql_class.cc | 2 + sql/sys_vars.cc | 7 +- sql/threadpool_unix.cc | 132 +++++++++++++----------- sql/threadpool_win.cc | 125 ++++++++-------------- 5 files changed, 123 insertions(+), 147 deletions(-) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 2badf5e5d09..94d7465d39b 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -934,7 +934,7 @@ lower-case-table-names 1 master-info-file master.info master-retry-count 86400 master-verify-checksum FALSE -max-allowed-packet 8388608 +max-allowed-packet 1048576 max-binlog-cache-size 18446744073709547520 max-binlog-size 1073741824 max-binlog-stmt-cache-size 18446744073709547520 @@ -945,7 +945,7 @@ max-error-count 64 max-heap-table-size 16777216 max-join-size 18446744073709551615 max-length-for-sort-data 1024 -max-long-data-size 8388608 +max-long-data-size 1048576 max-prepared-stmt-count 16382 max-relay-log-size 0 max-seeks-for-key 18446744073709551615 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index fc1c05c236f..8b8d07a47eb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3918,6 +3918,7 @@ extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd) SYNOPSIS thd_wait_begin() thd Thread object + Can be NULL, in this case current THD is used. wait_type Type of wait 1 -- short wait (e.g. for mutex) 2 -- medium wait (e.g. for disk io) @@ -3945,6 +3946,7 @@ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) when they waking up from a sleep/stall. @param thd Thread handle + Can be NULL, in this case current THD is used. */ extern "C" void thd_wait_end(MYSQL_THD thd) { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 65c74ffbeca..19f0b55f1fa 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2245,7 +2245,7 @@ static bool fix_threadpool_stall_limit(sys_var*, THD*, enum_var_type) #ifdef _WIN32 static Sys_var_uint Sys_threadpool_min_threads( "thread_pool_min_threads", - "Minimuim number of threads in the thread pool.", + "Minimum number of threads in the thread pool.", GLOBAL_VAR(threadpool_min_threads), CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, 256), DEFAULT(1), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), @@ -2267,8 +2267,9 @@ static Sys_var_uint Sys_threadpool_oversubscribe( ); static Sys_var_uint Sys_threadpool_size( "thread_pool_size", - "Number of concurrently executing threads in the pool. " - "Leaving value default (0) sets it to the number of processors.", + "Number of thread groups in the pool. " + "This parameter is roughly equivalent to maximum number of concurrently " + "executing threads (threads in a waiting state do not count as executing).", GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, MAX_THREAD_GROUPS), DEFAULT(my_getncpus()), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 5dcc9d4420c..9dc3739dfb4 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -206,6 +206,7 @@ static void print_pool_blocked_message(bool); or io_poll_start_read() becomes readable. Data associated with descriptors can be retrieved from native_events array, using native_event_get_userdata() function. + On Linux: epoll_wait() */ @@ -248,6 +249,11 @@ int io_poll_disassociate_fd(int pollfd, int fd) } +/* + Wrapper around epoll_wait. + NOTE - in case of EINTR, it restarts with original timeout. Since we use + either infinite or 0 timeouts, this is not critical +*/ int io_poll_wait(int pollfd, native_event *native_events, int maxevents, int timeout_ms) { @@ -260,6 +266,7 @@ int io_poll_wait(int pollfd, native_event *native_events, int maxevents, return ret; } + static void *native_event_get_userdata(native_event *event) { return event->data.ptr; @@ -364,8 +371,10 @@ int io_poll_wait(int pollfd, native_event *events, int maxevents, int timeout_ms (timeout_ms >= 0)?&ts:NULL); } while (ret == -1 && errno == EINTR); - return nget; + DBUG_ASSERT(nget < INT_MAX); + return (int)nget; } + static void* native_event_get_userdata(native_event *event) { return event->portev_user; @@ -375,9 +384,8 @@ static void* native_event_get_userdata(native_event *event) #endif - - /* Dequeue element from a workqueue */ + static connection_t *queue_get(thread_group_t *thread_group) { DBUG_ENTER("queue_get"); @@ -391,12 +399,12 @@ static connection_t *queue_get(thread_group_t *thread_group) } - /* Handle wait timeout : Find connections that have been idle for too long and kill them. Also, recalculate time when next timeout check should run. */ + static void timeout_check(pool_timer_t *timer) { DBUG_ENTER("timeout_check"); @@ -418,7 +426,7 @@ static void timeout_check(pool_timer_t *timer) { /* Connection does not have scheduler data. This happens for example - if THD belongs to another scheduler, that is listening to extra_port. + if THD belongs to a different scheduler, that is listening to extra_port. */ continue; } @@ -458,18 +466,18 @@ static void timeout_check(pool_timer_t *timer) static void* timer_thread(void *param) { uint i; - pool_timer_t* timer=(pool_timer_t *)param; - timer->next_timeout_check= ULONGLONG_MAX; - timer->current_microtime= microsecond_interval_timer(); - + my_thread_init(); DBUG_ENTER("timer_thread"); - + timer->next_timeout_check= ULONGLONG_MAX; + timer->current_microtime= microsecond_interval_timer(); + for(;;) { struct timespec ts; int err; + set_timespec_nsec(ts,timer->tick_interval*1000000); mysql_mutex_lock(&timer->mutex); err= mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts); @@ -543,7 +551,6 @@ void check_stall(thread_group_t *thread_group) } - static void start_timer(pool_timer_t* timer) { pthread_t thread_id; @@ -555,6 +562,7 @@ static void start_timer(pool_timer_t* timer) DBUG_VOID_RETURN; } + static void stop_timer(pool_timer_t *timer) { DBUG_ENTER("stop_timer"); @@ -664,50 +672,50 @@ static connection_t * listener(worker_thread_t *current_thread, thread_group->queue.push_back(c); } - - if(thread_group->active_thread_count==0 && !listener_picks_event) + if (listener_picks_event) { - /* Wake one worker thread */ + /* Handle the first event. */ + retval= (connection_t *)native_event_get_userdata(&ev[0]); + mysql_mutex_unlock(&thread_group->mutex); + break; + } + + if(thread_group->active_thread_count==0) + { + /* We added some work items to queue, now wake a worker. */ if(wake_thread(thread_group)) { /* - Wake failed, groups has no idle threads. - Now check if the group has at least one worker. + Wake failed, hence groups has no idle threads. Now check if there are + any threads in the group except listener. */ if(thread_group->thread_count == 1 && thread_group->pending_thread_start_count == 0) { - /* + /* Currently there is no worker thread in the group, as indicated by - thread_count == 1 (means listener is the only one thread in the - group). - - Rhe queue is not empty, and listener is not going to handle - events. In order to drain the queue, we create a worker here. - Alternatively, we could just rely on timer to detect stall, but - this would be an inefficient, pointless delay. + thread_count == 1 (this means listener is the only one thread in + the group). + The queue is not empty, and listener is not going to handle + events. In order to drain the queue, we create a worker here. + Alternatively, we could just rely on timer to detect stall, and + create thread, but waiting for timer would be an inefficient and + pointless delay. */ create_worker(thread_group); } } } mysql_mutex_unlock(&thread_group->mutex); - - if (listener_picks_event) - { - retval= (connection_t *)native_event_get_userdata(&ev[0]); - break; - } } - - + DBUG_RETURN(retval); } -/* +/** Creates a new worker thread. thread_mutex must be held when calling this function @@ -806,10 +814,10 @@ static int wake_or_create_thread(thread_group_t *thread_group) if (thread_group->active_thread_count == 0) { /* - We're better off creating a new thread here with no delay, - either there is no workers at all, or they all are all blocking - and there was no sleeping thread to wakeup. It smells like deadlock - or very slowly executing requests, e.g sleeps or user locks. + We're better off creating a new thread here with no delay, either there + are no workers at all, or they all are all blocking and there was no + idle thread to wakeup. Smells like a potential deadlock or very slowly + executing requests, e.g sleeps or user locks. */ DBUG_RETURN(create_worker(thread_group)); } @@ -862,7 +870,8 @@ void thread_group_destroy(thread_group_t *thread_group) /** Wake sleeping thread from waiting list - */ +*/ + static int wake_thread(thread_group_t *thread_group) { DBUG_ENTER("wake_thread"); @@ -879,16 +888,14 @@ static int wake_thread(thread_group_t *thread_group) } -/* +/** Initiate shutdown for thread group. - - The shutdown is asynchronous, we only care to wake all threads - in here, so they can finish. We do not wait here until threads - terminate, - - Final cleanup of the group (thread_group_destroy) will be done by - the last exiting threads. + + The shutdown is asynchronous, we only care to wake all threads in here, so + they can finish. We do not wait here until threads terminate. Final cleanup + of the group (thread_group_destroy) will be done by the last exiting threads. */ + static void thread_group_close(thread_group_t *thread_group) { DBUG_ENTER("thread_group_close"); @@ -938,10 +945,11 @@ static void thread_group_close(thread_group_t *thread_group) perform login (this is done in worker threads). */ + static void queue_put(thread_group_t *thread_group, connection_t *connection) { DBUG_ENTER("queue_put"); - + mysql_mutex_lock(&thread_group->mutex); thread_group->queue.push_back(connection); if (thread_group->active_thread_count == 0) @@ -949,14 +957,16 @@ static void queue_put(thread_group_t *thread_group, connection_t *connection) wake_or_create_thread(thread_group); } mysql_mutex_unlock(&thread_group->mutex); + DBUG_VOID_RETURN; } /* - This is used to prevent too many threads executing at the same time, - if the workload is not CPU bound. + Prevent too many threads executing at the same time,if the workload is + not CPU bound. */ + static bool too_many_threads(thread_group_t *thread_group) { return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe @@ -964,7 +974,6 @@ static bool too_many_threads(thread_group_t *thread_group) } - /** Retrieve a connection with pending event. @@ -981,17 +990,15 @@ static bool too_many_threads(thread_group_t *thread_group) @return connection with pending event. NULL is returned if timeout has expired,or on shutdown. */ + connection_t *get_event(worker_thread_t *current_thread, thread_group_t *thread_group, struct timespec *abstime) { DBUG_ENTER("get_event"); - connection_t *connection = NULL; int err=0; mysql_mutex_lock(&thread_group->mutex); - - DBUG_ASSERT(thread_group->active_thread_count >= 0); do @@ -1083,6 +1090,7 @@ connection_t *get_event(worker_thread_t *current_thread, Tells the pool that worker starts waiting on IO, lock, condition, sleep() or similar. */ + void wait_begin(thread_group_t *thread_group) { DBUG_ENTER("wait_begin"); @@ -1200,7 +1208,7 @@ static void connection_abort(connection_t *connection) /** - MySQL scheduler callback : kill connection + MySQL scheduler callback : kill connection */ void tp_post_kill_notification(THD *thd) @@ -1215,7 +1223,7 @@ void tp_post_kill_notification(THD *thd) } /** - MySQL scheduler callback: wait begin + MySQL scheduler callback: wait begin */ void tp_wait_begin(THD *thd, int type) @@ -1237,7 +1245,7 @@ void tp_wait_begin(THD *thd, int type) /** - MySQL scheduler callback: wait end + MySQL scheduler callback: wait end */ void tp_wait_end(THD *thd) @@ -1256,7 +1264,7 @@ void tp_wait_end(THD *thd) DBUG_VOID_RETURN; } - + static void set_next_timeout_check(ulonglong abstime) { DBUG_ENTER("set_next_timeout_check"); @@ -1273,7 +1281,6 @@ static void set_next_timeout_check(ulonglong abstime) /** Set wait timeout for connection. */ - static void set_wait_timeout(connection_t *c) { DBUG_ENTER("set_wait_timeout"); @@ -1400,8 +1407,9 @@ static void handle_event(connection_t *connection) } /** - Worker thread's main + Worker thread's main */ + static void *worker_main(void *param) { @@ -1543,7 +1551,8 @@ void tp_set_threadpool_stall_limit(uint limit) Calculate number of idle/waiting threads in the pool. Sum idle threads over all groups. - Don't do any locking, it is not required for stats. + D + on't do any locking, it is not required for stats. */ int tp_get_idle_thread_count() { @@ -1601,7 +1610,8 @@ static void print_pool_blocked_message(bool max_threads_reached) else sql_print_error(create_thread_error_msg, my_errno); - sql_print_information("Threadpool has been blocked for %u seconds\n",(uint)(now- pool_block_start)); + sql_print_information("Threadpool has been blocked for %u seconds\n", + (uint)(now- pool_block_start)); /* avoid reperated messages for the same blocking situation */ msg_written= true; } diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 7a2c8c0c6cf..ae186a614ee 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -17,6 +17,30 @@ #include +/* + Threadpool API is not available on XP. We still want to compile a single + version on Windows, but use the latest functionality if available. + We cannot use threadpool functionality directly, since executable won't + start on XP and loader will complain about missing symbols. + + We solve using the usual way it is done on Windows, i.e with dynamic loading. + We'll need to load a lot of function, and make this less painful with the + WEAK_SYMBOL macro below +*/ + +/* + WEAK_SYMBOL(return_type, function_name, argument_type1,..,argument_typeN) + + Declare and load function pointer from kernel32. The name of the static + variable that holds the function pointer is my_ + This should be combined with + #define my_ + so that one could use Widows APIs transparently, without worrying whether + they are present in a particular version or not. + + Of course, prior to use of any function there should be a check for correct + Windows version, or check whether function pointer is not NULL. +*/ #define WEAK_SYMBOL(return_type, function, ...) \ typedef return_type (WINAPI *pFN_##function)(__VA_ARGS__); \ static pFN_##function my_##function = (pFN_##function) \ @@ -110,9 +134,7 @@ WEAK_SYMBOL(VOID, CloseThreadpoolWork, PTP_WORK pwk); WEAK_SYMBOL(BOOL, SetThreadpoolStackInformation, PTP_POOL, PTP_POOL_STACK_INFORMATION); #define SetThreadpoolStackInformation my_SetThreadpoolStackInformation -#endif - -#if _MSC_VER < 1600 +#else /* _MSC_VER < 1600 */ #define SetThreadpoolCallbackPriority(env,prio) typedef enum _TP_CALLBACK_PRIORITY { TP_CALLBACK_PRIORITY_HIGH, @@ -158,8 +180,6 @@ static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, static void CALLBACK shm_close_callback(PTP_CALLBACK_INSTANCE instance, PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result); -#define CONNECTION_SIGNATURE 0xAFFEAFFE - static void check_thread_init(); /* Get current time as Windows time */ @@ -178,21 +198,19 @@ static ulonglong now() struct connection_t { THD *thd; - bool logged_in; HANDLE handle; OVERLAPPED overlapped; - /* absolute time for wait timeout (as Windows time) */ volatile ulonglong timeout; - PTP_CLEANUP_GROUP cleanup_group; TP_CALLBACK_ENVIRON callback_environ; - PTP_IO io; PTP_TIMER timer; PTP_WAIT shm_read; + bool logged_in; }; + void init_connection(connection_t *connection) { connection->logged_in = false; @@ -208,6 +226,7 @@ void init_connection(connection_t *connection) connection->thd = 0; } + int init_io(connection_t *connection, THD *thd) { connection->thd= thd; @@ -237,7 +256,7 @@ int init_io(connection_t *connection, THD *thd) if (connection->handle) { /* Performance tweaks (s. MSDN documentation)*/ - UCHAR flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE; if (skip_completion_port_on_success) { flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; @@ -245,7 +264,7 @@ int init_io(connection_t *connection, THD *thd) (void)SetFileCompletionNotificationModes(connection->handle, flags); /* Assign io completion callback */ - connection->io = CreateThreadpoolIo(connection->handle, + connection->io= CreateThreadpoolIo(connection->handle, io_completion_callback, connection, &connection->callback_environ); if(!connection->io) { @@ -253,7 +272,7 @@ int init_io(connection_t *connection, THD *thd) return -1; } } - connection->timer = CreateThreadpoolTimer(timer_callback, connection, + connection->timer= CreateThreadpoolTimer(timer_callback, connection, &connection->callback_environ); if (!connection->timer) { @@ -354,6 +373,7 @@ int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance) return -1; } + int login(connection_t *connection, PTP_CALLBACK_INSTANCE instance) { if (threadpool_add_connection(connection->thd) == 0 @@ -380,21 +400,6 @@ void set_wait_timeout(connection_t *connection, ulonglong old_timeout) connection->timeout = new_timeout; } -/* - Terminates (idle) connection by closing the socket. - This will activate io_completion_callback() in a different thread -*/ -void post_kill_notification(connection_t *connection) -{ - check_thread_init(); - THD *thd=connection->thd; - mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed = KILL_CONNECTION; - vio_shutdown(thd->net.vio, SHUT_RDWR); - thd->mysys_var= NULL; - mysql_mutex_unlock(&thd->LOCK_thd_data); -} - /* Connection destructor */ void destroy_connection(connection_t *connection) @@ -438,7 +443,6 @@ static void check_thread_init() if (FlsGetValue(fls) == NULL) { FlsSetValue(fls, (void *)1); - my_thread_init(); thread_created++; InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads); } @@ -446,28 +450,14 @@ static void check_thread_init() /* - Take care of proper cleanup when threadpool threads exit. - We do not control how threads are created, thus it is our responsibility to - check that my_thread_init() is called on thread initialization and - my_thread_end() on thread destruction. On Windows, FlsAlloc() provides the - thread destruction callbacks. + Decrement number of threads when a thread exits . + On Windows, FlsAlloc() provides the thread destruction callbacks. */ static VOID WINAPI thread_destructor(void *data) { if(data) { - if (InterlockedDecrement((volatile long *)&tp_stats.num_worker_threads) >= 0) - { - /* - The above check for number of thread >= 0 is due to shutdown code ( - see tp_end()) where we forcefully set num_worker_threads to 0, even - if not all threads have shut down yet to the point they would ran Fls - destructors, even after CloseThreadpool(). See also comment in tp_end(). - */ - mysql_mutex_lock(&LOCK_thread_count); - my_thread_end(); - mysql_mutex_unlock(&LOCK_thread_count); - } + InterlockedDecrement((volatile long *)&tp_stats.num_worker_threads); } } @@ -507,7 +497,7 @@ bool tp_init(void) { TP_POOL_STACK_INFORMATION stackinfo; stackinfo.StackCommit = 0; - stackinfo.StackReserve = my_thread_stack_size; + stackinfo.StackReserve = (SIZE_T)my_thread_stack_size; if (!SetThreadpoolStackInformation(pool, &stackinfo)) { tp_log_warning("Can't set threadpool stack size", @@ -520,45 +510,19 @@ bool tp_init(void) } -/* +/** Scheduler callback : Destroy the scheduler. */ - -extern "C" uint THR_thread_count; -extern "C" mysql_mutex_t THR_LOCK_threads; -extern "C" mysql_cond_t THR_COND_threads; - void tp_end(void) { if(pool) { SetThreadpoolThreadMaximum(pool, 0); CloseThreadpool(pool); - - /* - Tell my_global_thread_end() we're complete. - - This would not be necessary if CloseThreadpool() would synchronously - release all threads and wait until they disappear and call all their FLS - destructors . However, threads in the pool are released asynchronously - and might spend some time in the CRT shutdown code. Thus zero - num_worker_threads, to avoid thread destructor's my_thread_end()s after - this point. - */ - LONG remaining_threads= - InterlockedExchange( (volatile long *)&tp_stats.num_worker_threads, 0); - - if (remaining_threads) - { - mysql_mutex_lock(&THR_LOCK_threads); - THR_thread_count -= remaining_threads; - mysql_cond_signal(&THR_COND_threads); - mysql_mutex_unlock(&THR_LOCK_threads); - } } } -/* +/** Notify pool about connection being killed. */ void tp_post_kill_notification(THD *thd) @@ -606,7 +570,7 @@ error: DisassociateCurrentThreadFromCallback(instance); destroy_connection(connection); - my_free(connection); + free(connection); } @@ -623,7 +587,7 @@ static void CALLBACK login_callback(PTP_CALLBACK_INSTANCE instance, if (login(connection, instance) != 0) { destroy_connection(connection); - my_free(connection); + free(connection); } } @@ -688,9 +652,8 @@ static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, */ void tp_add_connection(THD *thd) { - bool success = false; - connection_t *con = (connection_t *)my_malloc(sizeof(connection_t), 0); - + connection_t *con = (connection_t *)malloc(sizeof(connection_t)); + if (con) threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); @@ -698,6 +661,7 @@ void tp_add_connection(THD *thd) if(!con) { tp_log_warning("Allocation failed", "tp_add_connection"); + threadpool_remove_connection(thd); return; } @@ -718,8 +682,7 @@ void tp_add_connection(THD *thd) } - -/* +/** Sets the number of idle threads the thread pool maintains in anticipation of new requests. */ From 777c9156000f062da26a237056c4f2ea90e17feb Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 26 Jan 2012 19:25:22 +0100 Subject: [PATCH 37/51] Disable perfschema/all_instances for the threadpool (because of new mutexes and conditions) --- mysql-test/suite/perfschema/t/all_instances.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/perfschema/t/all_instances.test b/mysql-test/suite/perfschema/t/all_instances.test index 18e1f0c179f..42942725c2b 100644 --- a/mysql-test/suite/perfschema/t/all_instances.test +++ b/mysql-test/suite/perfschema/t/all_instances.test @@ -5,6 +5,7 @@ --source include/have_blackhole.inc --source include/have_ssl.inc --source include/not_windows.inc +--source include/one_thread_per_connection.inc use performance_schema; From 8b945a14193825f98b6591ae34e5a6681f5ba04c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 27 Jan 2012 00:39:23 +0100 Subject: [PATCH 38/51] close callbacks prior to closing connection to avoid potential race when e.g timer callback and connection_destroy run in parallel --- sql/threadpool_win.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index ae186a614ee..b4c73e6be63 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -404,11 +404,6 @@ void set_wait_timeout(connection_t *connection, ulonglong old_timeout) /* Connection destructor */ void destroy_connection(connection_t *connection) { - if (connection->thd) - { - threadpool_remove_connection(connection->thd); - } - if (connection->io) { WaitForThreadpoolIoCallbacks(connection->io, TRUE); @@ -427,6 +422,11 @@ void destroy_connection(connection_t *connection) WaitForThreadpoolTimerCallbacks(connection->timer, TRUE); CloseThreadpoolTimer(connection->timer); } + + if (connection->thd) + { + threadpool_remove_connection(connection->thd); + } DestroyThreadpoolEnvironment(&connection->callback_environ); } From 804f3bfeafd922eceda856d9539e6ebc79e72b16 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 27 Jan 2012 19:52:53 +0100 Subject: [PATCH 39/51] Threadpool : Rest of monty's review --- sql/net_serv.cc | 4 ++-- sql/sql_class.cc | 2 +- sql/sys_vars.cc | 4 ---- sql/threadpool_common.cc | 4 ---- sql/threadpool_unix.cc | 45 ++++++++++++++++++++++++---------------- sql/threadpool_win.cc | 11 +++++----- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 4a35dc52fa3..1f4d5c44f73 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1159,7 +1159,7 @@ void my_net_set_read_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_read_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); - if(net->read_timeout == timeout) + if (net->read_timeout == timeout) DBUG_VOID_RETURN; net->read_timeout= timeout; #ifdef NO_ALARM @@ -1174,7 +1174,7 @@ void my_net_set_write_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_write_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); - if(net->write_timeout == timeout) + if (net->write_timeout == timeout) DBUG_VOID_RETURN; net->write_timeout= timeout; #ifdef NO_ALARM diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 8b8d07a47eb..d99b24b33f3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1630,7 +1630,7 @@ void THD::disconnect() /* Disconnect even if a active vio is not associated. */ if (net.vio != vio) - vio_close(net.vio); + vio_close(net.vio); mysql_mutex_unlock(&LOCK_thd_data); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 19f0b55f1fa..6e500737c1f 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1823,11 +1823,7 @@ static Sys_var_enum Sys_thread_handling( #endif , READ_ONLY GLOBAL_VAR(thread_handling), CMD_LINE(REQUIRED_ARG), thread_handling_names, -#ifdef HAVE_POOL_OF_THREADS - DEFAULT(2) -#else DEFAULT(0) -#endif ); #ifdef HAVE_QUERY_CACHE diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index dc8ef9f6cfd..fd641b45faa 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -158,10 +158,6 @@ void threadpool_remove_connection(THD *thd) end_connection(thd); close_connection(thd, 0); - mysql_mutex_lock(&thd->LOCK_thd_data); - thd->event_scheduler.data= NULL; - mysql_mutex_unlock(&thd->LOCK_thd_data); - unlink_thd(thd); mysql_mutex_unlock(&LOCK_thread_count); mysql_cond_broadcast(&COND_thread_count); diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 9dc3739dfb4..bdb9b2fc8a7 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -334,7 +334,9 @@ static void* native_event_get_userdata(native_event *event) { return event->udata; } + #elif defined (__sun) + static int io_poll_create() { return port_create(); @@ -980,15 +982,16 @@ static bool too_many_threads(thread_group_t *thread_group) Pending event in our case means that there is either a pending login request (if connection is not yet logged in), or there are unread bytes on the socket. - If there are no pending events currently, thread will wait. If timeout specified - int abstime parameter passes, the function returns NULL. + If there are no pending events currently, thread will wait. + If timeout specified in abstime parameter passes, the function returns NULL. @param current_thread - current worker thread @param thread_group - current thread group @param abstime - absolute wait timeout @return - connection with pending event. NULL is returned if timeout has expired,or on shutdown. + connection with pending event. + NULL is returned if timeout has expired,or on shutdown. */ connection_t *get_event(worker_thread_t *current_thread, @@ -1001,13 +1004,14 @@ connection_t *get_event(worker_thread_t *current_thread, mysql_mutex_lock(&thread_group->mutex); DBUG_ASSERT(thread_group->active_thread_count >= 0); - do + for(;;) { + bool oversubscribed = too_many_threads(thread_group); if (thread_group->shutdown) break; /* Check if queue is not empty */ - if (!too_many_threads(thread_group)) + if (!oversubscribed) { connection = queue_get(thread_group); if(connection) @@ -1034,7 +1038,7 @@ connection_t *get_event(worker_thread_t *current_thread, Last thing we try before going to sleep is to pick a single event via epoll, without waiting (timeout 0) */ - if (!too_many_threads(thread_group)) + if (!oversubscribed) { native_event nev; if (io_poll_wait(thread_group->pollfd,&nev,1, 0) == 1) @@ -1057,9 +1061,14 @@ connection_t *get_event(worker_thread_t *current_thread, thread_group->active_thread_count--; if(abstime) - err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, abstime); + { + err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, + abstime); + } else + { err = mysql_cond_wait(¤t_thread->cond, &thread_group->mutex); + } thread_group->active_thread_count++; if (!current_thread->woken) @@ -1074,9 +1083,7 @@ connection_t *get_event(worker_thread_t *current_thread, if(err) break; - } - while(true); thread_group->stalled= false; mysql_mutex_unlock(&thread_group->mutex); @@ -1163,9 +1170,7 @@ void tp_add_connection(THD *thd) connection_t *connection= alloc_connection(thd); if(connection) { - mysql_mutex_lock(&thd->LOCK_thd_data); thd->event_scheduler.data= connection; - mysql_mutex_unlock(&thd->LOCK_thd_data); /* Assign connection to a group. */ thread_group_t *group= @@ -1183,7 +1188,11 @@ void tp_add_connection(THD *thd) */ queue_put(group, connection); } - + else + { + /* Allocation failed */ + threadpool_remove_connection(thd); + } DBUG_VOID_RETURN; } @@ -1358,9 +1367,7 @@ static int start_io(connection_t *connection) if (group != connection->thread_group) { if (change_group(connection, connection->thread_group, group)) - { return -1; - } } /* @@ -1385,12 +1392,12 @@ static void handle_event(connection_t *connection) if (!connection->logged_in) { - err = threadpool_add_connection(connection->thd); + err= threadpool_add_connection(connection->thd); connection->logged_in= true; } else { - err = threadpool_process_request(connection->thd); + err= threadpool_process_request(connection->thd); } if(!err) @@ -1400,12 +1407,13 @@ static void handle_event(connection_t *connection) } if (err) - { connection_abort(connection); - } + DBUG_VOID_RETURN; } + + /** Worker thread's main */ @@ -1505,6 +1513,7 @@ void tp_end() { thread_group_close(&all_groups[i]); } + started= false; DBUG_VOID_RETURN; } diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index b4c73e6be63..e70d3c7edf0 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -652,12 +652,10 @@ static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, */ void tp_add_connection(THD *thd) { - connection_t *con = (connection_t *)malloc(sizeof(connection_t)); - - if (con) - threads.append(thd); + threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); + connection_t *con = (connection_t *)malloc(sizeof(connection_t)); if(!con) { tp_log_warning("Allocation failed", "tp_add_connection"); @@ -667,6 +665,8 @@ void tp_add_connection(THD *thd) init_connection(con); con->thd= thd; + thd->event_scheduler.data= con; + /* Try to login asynchronously, using threads in the pool */ PTP_WORK wrk = CreateThreadpoolWork(login_callback,con, &con->callback_environ); if (wrk) @@ -720,4 +720,5 @@ void tp_wait_end(THD *thd) int tp_get_idle_thread_count() { return 0; -} \ No newline at end of file +} + From d782e098a7dea389ab79d313c9467129a9aac3f4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 27 Jan 2012 21:24:17 +0100 Subject: [PATCH 40/51] Fix test case - result file needs one-thread-per-connection --- mysql-test/t/no-threads.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/no-threads.test b/mysql-test/t/no-threads.test index c2b326897f8..a2936abf784 100644 --- a/mysql-test/t/no-threads.test +++ b/mysql-test/t/no-threads.test @@ -1,4 +1,4 @@ ---source include/not_threadpool.inc +--source include/one_thread_per_connection.inc # # Test the --thread-handler=no-threads option # From 4d1c7b794730b101619c5dac33af71e7abcd54d2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 28 Jan 2012 01:09:28 +0100 Subject: [PATCH 41/51] some more whitespace, remove pending_thread_start_count. increment counters (thread_group->count, thread_group->active_thread_count) whenever mysql_create_thread returns success. --- sql/threadpool_unix.cc | 93 ++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index bdb9b2fc8a7..0a1e3ceb8a7 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -58,7 +58,7 @@ static PSI_thread_info thread_list[] = /* Macro to simplify performance schema registration */ #define PSI_register(X) \ - if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) + if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list)) struct thread_group_t; @@ -112,7 +112,6 @@ struct thread_group_t int pollfd; int thread_count; int active_thread_count; - int pending_thread_start_count; int connection_count; /* Stats for the deadlock detection timer routine.*/ int io_event_count; @@ -691,8 +690,7 @@ static connection_t * listener(worker_thread_t *current_thread, Wake failed, hence groups has no idle threads. Now check if there are any threads in the group except listener. */ - if(thread_group->thread_count == 1 && - thread_group->pending_thread_start_count == 0) + if(thread_group->thread_count == 1) { /* Currently there is no worker thread in the group, as indicated by @@ -714,7 +712,22 @@ static connection_t * listener(worker_thread_t *current_thread, DBUG_RETURN(retval); } +/** + Adjust thread counters in group or global + whenever thread is created or is about to exit + @param thread_group + @param count - 1, when new thread is created + -1, when thread is about to exit +*/ + +static void add_thread_count(thread_group_t *thread_group, int32 count) +{ + thread_group->thread_count += count; + /* worker starts out and end in "active" state */ + thread_group->active_thread_count += count; + my_atomic_add32(&tp_stats.num_worker_threads, count); +} /** @@ -740,13 +753,15 @@ static int create_worker(thread_group_t *thread_group) max_threads_reached= true; goto end; } + err= mysql_thread_create(key_worker_thread, &thread_id, thread_group->pthread_attr, worker_main, thread_group); if (!err) { - thread_group->pending_thread_start_count++; thread_group->last_thread_creation_time=microsecond_interval_timer(); + thread_created++; + add_thread_count(thread_group, 1); } else { @@ -803,12 +818,12 @@ static int wake_or_create_thread(thread_group_t *thread_group) { DBUG_ENTER("wake_or_create_thread"); + if (thread_group->shutdown) + DBUG_RETURN(0); + if (wake_thread(thread_group) == 0) DBUG_RETURN(0); - if (thread_group->pending_thread_start_count > 0) - DBUG_RETURN(-1); - if (thread_group->thread_count > thread_group->connection_count) DBUG_RETURN(-1); @@ -903,8 +918,7 @@ static void thread_group_close(thread_group_t *thread_group) DBUG_ENTER("thread_group_close"); mysql_mutex_lock(&thread_group->mutex); - if (thread_group->thread_count == 0 && - thread_group->pending_thread_start_count == 0) + if (thread_group->thread_count == 0) { mysql_mutex_unlock(&thread_group->mutex); thread_group_destroy(thread_group); @@ -954,10 +968,10 @@ static void queue_put(thread_group_t *thread_group, connection_t *connection) mysql_mutex_lock(&thread_group->mutex); thread_group->queue.push_back(connection); + if (thread_group->active_thread_count == 0) - { wake_or_create_thread(thread_group); - } + mysql_mutex_unlock(&thread_group->mutex); DBUG_VOID_RETURN; @@ -1060,7 +1074,7 @@ connection_t *get_event(worker_thread_t *current_thread, thread_group->waiting_threads.push_front(current_thread); thread_group->active_thread_count--; - if(abstime) + if (abstime) { err = mysql_cond_timedwait(¤t_thread->cond, &thread_group->mutex, abstime); @@ -1081,7 +1095,7 @@ connection_t *get_event(worker_thread_t *current_thread, thread_group->waiting_threads.remove(current_thread); } - if(err) + if (err) break; } @@ -1107,7 +1121,7 @@ void wait_begin(thread_group_t *thread_group) DBUG_ASSERT(thread_group->active_thread_count >=0); DBUG_ASSERT(thread_group->connection_count > 0); - if((thread_group->active_thread_count == 0) && + if ((thread_group->active_thread_count == 0) && (thread_group->queue.is_empty() || !thread_group->listener)) { /* @@ -1168,7 +1182,7 @@ void tp_add_connection(THD *thd) threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); connection_t *connection= alloc_connection(thd); - if(connection) + if (connection) { thd->event_scheduler.data= connection; @@ -1243,7 +1257,7 @@ void tp_wait_begin(THD *thd, int type) DBUG_VOID_RETURN; connection_t *connection = (connection_t *)thd->event_scheduler.data; - if(connection) + if (connection) { DBUG_ASSERT(!connection->waiting); connection->waiting= true; @@ -1264,7 +1278,7 @@ void tp_wait_end(THD *thd) DBUG_VOID_RETURN; connection_t *connection = (connection_t *)thd->event_scheduler.data; - if(connection) + if (connection) { DBUG_ASSERT(connection->waiting); connection->waiting = false; @@ -1290,6 +1304,7 @@ static void set_next_timeout_check(ulonglong abstime) /** Set wait timeout for connection. */ + static void set_wait_timeout(connection_t *c) { DBUG_ENTER("set_wait_timeout"); @@ -1316,6 +1331,7 @@ static void set_wait_timeout(connection_t *c) migrate to a different group because group_count has changed after thread_pool_size setting. */ + static int change_group(connection_t *c, thread_group_t *old_group, thread_group_t *new_group) @@ -1340,7 +1356,7 @@ static int change_group(connection_t *c, c->thread_group= new_group; new_group->connection_count++; /* Ensure that there is a listener in the new group. */ - if(!new_group->thread_count && !new_group->pending_thread_start_count) + if (!new_group->thread_count) ret= create_worker(new_group); mysql_mutex_unlock(&new_group->mutex); return ret; @@ -1373,7 +1389,7 @@ static int start_io(connection_t *connection) /* Bind to poll descriptor if not yet done. */ - if(!connection->bound_to_poll_descriptor) + if (!connection->bound_to_poll_descriptor) { connection->bound_to_poll_descriptor= true; return io_poll_associate_fd(group->pollfd, fd, connection); @@ -1400,7 +1416,7 @@ static void handle_event(connection_t *connection) err= threadpool_process_request(connection->thd); } - if(!err) + if (!err) { set_wait_timeout(connection); err= start_io(connection); @@ -1427,7 +1443,6 @@ static void *worker_main(void *param) DBUG_ENTER("worker_main"); - thread_created++; thread_group_t *thread_group = (thread_group_t *)param; /* Init per-thread structure */ @@ -1435,13 +1450,6 @@ static void *worker_main(void *param) this_thread.thread_group= thread_group; this_thread.event_count=0; - my_atomic_add32(&tp_stats.num_worker_threads, 1); - mysql_mutex_lock(&thread_group->mutex); - thread_group->thread_count++; - thread_group->active_thread_count++; - thread_group->pending_thread_start_count--; - mysql_mutex_unlock(&thread_group->mutex); - /* Run event loop */ for(;;) { @@ -1450,9 +1458,7 @@ static void *worker_main(void *param) set_timespec(ts,threadpool_idle_timeout); connection = get_event(&this_thread, thread_group, &ts); if (!connection) - { break; - } this_thread.event_count++; handle_event(connection); } @@ -1460,19 +1466,16 @@ static void *worker_main(void *param) /* Thread shutdown: cleanup per-worker-thread structure. */ mysql_cond_destroy(&this_thread.cond); + bool last_thread; /* last thread in group exits */ mysql_mutex_lock(&thread_group->mutex); - thread_group->active_thread_count--; - thread_group->thread_count--; + add_thread_count(thread_group, -1); + last_thread= ((thread_group->thread_count == 0) && thread_group->shutdown); mysql_mutex_unlock(&thread_group->mutex); - my_atomic_add32(&tp_stats.num_worker_threads, -1); - /* If it is the last thread in group and pool is terminating, destroy group.*/ - if (thread_group->shutdown - && thread_group->thread_count == 0 - && thread_group->pending_thread_start_count == 0) - { + /* Last thread in group exits and pool is terminating, destroy group.*/ + if (last_thread) thread_group_destroy(thread_group); - } + my_thread_end(); return NULL; } @@ -1519,6 +1522,7 @@ void tp_end() /** Ensure that poll descriptors are created when threadpool_size changes */ + int tp_set_threadpool_size(uint size) { bool success= true; @@ -1560,9 +1564,9 @@ void tp_set_threadpool_stall_limit(uint limit) Calculate number of idle/waiting threads in the pool. Sum idle threads over all groups. - D - on't do any locking, it is not required for stats. + Don't do any locking, it is not required for stats. */ + int tp_get_idle_thread_count() { int sum=0; @@ -1599,6 +1603,7 @@ static const char *create_thread_error_msg= It will be just a single message for each blocking situation (to prevent log flood). */ + static void print_pool_blocked_message(bool max_threads_reached) { time_t now; @@ -1612,9 +1617,9 @@ static void print_pool_blocked_message(bool max_threads_reached) return; } - if(now > pool_block_start + BLOCK_MSG_DELAY && !msg_written) + if (now > pool_block_start + BLOCK_MSG_DELAY && !msg_written) { - if(max_threads_reached) + if (max_threads_reached) sql_print_error(max_threads_reached_msg); else sql_print_error(create_thread_error_msg, my_errno); From cfa56f900acefcf228c26dfa39c1b9142ef3552b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 16 Feb 2012 16:59:04 +0100 Subject: [PATCH 42/51] address second round review comments --- mysys/thr_lock.c | 4 +- sql/scheduler.cc | 16 +++--- sql/scheduler.h | 1 + sql/sql_class.cc | 30 +++++++---- sql/sql_class.h | 2 + sql/threadpool.h | 3 +- sql/threadpool_common.cc | 18 +++---- sql/threadpool_unix.cc | 110 +++++++++++++++++++++------------------ 8 files changed, 103 insertions(+), 81 deletions(-) diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 926951b7443..c3a99d78d25 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -483,7 +483,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; const char *old_proc_info; - my_bool use_wait_callbacks; + my_bool use_wait_callbacks= FALSE; DBUG_ENTER("wait_for_lock"); /* @@ -540,8 +540,6 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, use_wait_callbacks= TRUE; (*before_lock_wait)(); } - else - use_wait_callbacks= FALSE; set_timespec(wait_timeout, lock_wait_timeout); while (!thread_var->abort || in_wait_list) diff --git a/sql/scheduler.cc b/sql/scheduler.cc index c174d300d2e..3f779337345 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -48,26 +48,26 @@ extern "C" { static void scheduler_wait_lock_begin(void) { THD *thd=current_thd; - scheduler_functions *func= thd->scheduler; - MYSQL_CALLBACK(func, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); + if(thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); } static void scheduler_wait_lock_end(void) { THD *thd=current_thd; - scheduler_functions *func= thd->scheduler; - MYSQL_CALLBACK(func, thd_wait_end, (thd)); + if(thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); } static void scheduler_wait_sync_begin(void) { THD *thd=current_thd; - scheduler_functions *func= thd ? thd->scheduler : thread_scheduler; - MYSQL_CALLBACK(func, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); + if(thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); } static void scheduler_wait_sync_end(void) { THD *thd=current_thd; - scheduler_functions *func= thd ? thd->scheduler : thread_scheduler; - MYSQL_CALLBACK(func, thd_wait_end, (thd)); + if(thd) + MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); } }; /**@}*/ diff --git a/sql/scheduler.h b/sql/scheduler.h index 302ccd7ab11..41e7b085a48 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -76,6 +76,7 @@ void one_thread_per_connection_scheduler(scheduler_functions *func, ulong *arg_max_connections, uint *arg_connection_count); void one_thread_scheduler(scheduler_functions *func); +extern void scheduler_init(); /* To be used for pool-of-threads (implemeneted differently on various OSs) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d99b24b33f3..af3cdf7b34f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1523,7 +1523,7 @@ void THD::awake(killed_state state_to_set) if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED) { #ifdef SIGNAL_WITH_VIO_CLOSE - if (this != current_thd) + if (this != current_thd) { if(active_vio) vio_shutdown(active_vio, SHUT_RDWR); @@ -3935,10 +3935,16 @@ extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd) */ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) { - if(!thd) - thd= current_thd; - if (thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type)); + + if (unlikely(!thread_scheduler) || !thread_scheduler->thd_wait_begin) + return; + if (thd == NULL) + { + thd=current_thd; + if(unlikely(thd == NULL)) + return; + } + MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type)); } /** @@ -3950,10 +3956,16 @@ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) */ extern "C" void thd_wait_end(MYSQL_THD thd) { - if(!thd) - thd= current_thd; - if (thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); + if (unlikely(!thread_scheduler) || ! thread_scheduler->thd_wait_begin) + return; + if (thd == NULL) + { + thd=current_thd; + if(unlikely(thd == NULL)) + return; + } + if(likely(thd->scheduler == thread_scheduler)) + thread_scheduler->thd_wait_end(thd); } #endif // INNODB_COMPATIBILITY_HOOKS */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 7c7ae1eb2af..ce000adc785 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4278,6 +4278,8 @@ inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data) return error; } +extern pthread_attr_t *get_connection_attrib(void); + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/threadpool.h b/sql/threadpool.h index 2b0d09c0d6d..a3ecca3302b 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -46,11 +46,10 @@ extern TP_STATISTICS tp_stats; /* Functions to set threadpool parameters */ extern void tp_set_min_threads(uint val); extern void tp_set_max_threads(uint val); -extern int tp_set_threadpool_size(uint val); +extern void tp_set_threadpool_size(uint val); extern void tp_set_threadpool_stall_limit(uint val); /* Activate threadpool scheduler */ extern void tp_scheduler(void); extern int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff); - diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index fd641b45faa..d003bae6727 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -136,7 +136,7 @@ int threadpool_add_connection(THD *thd) { retval= 0; thd->net.reading_or_writing= 1; - thd->skip_wait_timeout= true; + thd->skip_wait_timeout= true; } } } @@ -185,10 +185,11 @@ int threadpool_process_request(THD *thd) killed flag was set by timeout handler or KILL command. Return error. */ - worker_context.restore(); - return 1; + retval= 1; + goto end; } + for(;;) { Vio *vio; @@ -196,12 +197,12 @@ int threadpool_process_request(THD *thd) mysql_audit_release(thd); if ((retval= do_command(thd)) != 0) - break ; + goto end; if (!thd_is_connection_alive(thd)) { retval= 1; - break; + goto end; } vio= thd->net.vio; @@ -210,10 +211,11 @@ int threadpool_process_request(THD *thd) /* More info on this debug sync is in sql_parse.cc*/ DEBUG_SYNC(thd, "before_do_command_net_read"); thd->net.reading_or_writing= 1; - break; + goto end; } - } + } +end: worker_context.restore(); return retval; } @@ -234,8 +236,6 @@ static scheduler_functions tp_scheduler_functions= tp_end // end }; -extern void scheduler_init(); - void pool_of_threads_scheduler(struct scheduler_functions *func, ulong *arg_max_connections, uint *arg_connection_count) diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 0a1e3ceb8a7..ad44e237262 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -24,6 +24,11 @@ typedef struct kevent native_event; typedef port_event_t native_event; #endif +/** Maximum number of native events a listener can read in one go */ +#define MAX_EVENTS 1024 + +/** Indicates that threadpool was initialized*/ +static bool threadpool_started= false; /* Define PSI Keys for performance schema. @@ -130,7 +135,7 @@ static uint group_count; Used for printing "pool blocked" message, see print_pool_blocked_message(); */ -static time_t pool_block_start; +static ulonglong pool_block_start; /* Global timer for all groups */ struct pool_timer_t @@ -145,11 +150,6 @@ struct pool_timer_t static pool_timer_t pool_timer; - -/* Externals functions and variables we use */ -extern void scheduler_init(); -extern pthread_attr_t *get_connection_attrib(void); - static void queue_put(thread_group_t *thread_group, connection_t *connection); static int wake_thread(thread_group_t *thread_group); static void handle_event(connection_t *connection); @@ -462,6 +462,9 @@ static void timeout_check(pool_timer_t *timer) Besides checking for stalls, timer thread is also responsible for terminating clients that have been idle for longer than wait_timeout seconds. + + TODO: Let the timer sleep for long time if there is no work to be done. + Currently it wakes up rather often on and idle server. */ static void* timer_thread(void *param) @@ -491,7 +494,7 @@ static void* timer_thread(void *param) { timer->current_microtime= microsecond_interval_timer(); - /* Check stallls in thread groups */ + /* Check stalls in thread groups */ for(i=0; i< array_elements(all_groups);i++) { if(all_groups[i].connection_count) @@ -574,7 +577,6 @@ static void stop_timer(pool_timer_t *timer) DBUG_VOID_RETURN; } -#define MAX_EVENTS 1024 /** Poll for socket events and distribute them to worker threads @@ -586,10 +588,8 @@ static connection_t * listener(worker_thread_t *current_thread, thread_group_t *thread_group) { DBUG_ENTER("listener"); - connection_t *retval= NULL; - - + for(;;) { native_event ev[MAX_EVENTS]; @@ -767,7 +767,6 @@ static int create_worker(thread_group_t *thread_group) { my_errno= errno; } - end: if (err) @@ -897,11 +896,10 @@ static int wake_thread(thread_group_t *thread_group) { thread->woken= true; thread_group->waiting_threads.remove(thread); - if (mysql_cond_signal(&thread->cond)) - abort(); + mysql_cond_signal(&thread->cond); DBUG_RETURN(0); } - DBUG_RETURN(-1); /* no thread- missed wakeup*/ + DBUG_RETURN(1); /* no thread in waiter list => missed wakeup */ } @@ -1188,7 +1186,7 @@ void tp_add_connection(THD *thd) /* Assign connection to a group. */ thread_group_t *group= - &all_groups[connection->thd->thread_id%group_count]; + &all_groups[thd->thread_id%group_count]; connection->thread_group=group; @@ -1416,12 +1414,13 @@ static void handle_event(connection_t *connection) err= threadpool_process_request(connection->thd); } - if (!err) - { - set_wait_timeout(connection); - err= start_io(connection); - } + if(err) + goto end; + set_wait_timeout(connection); + err= start_io(connection); + +end: if (err) connection_abort(connection); @@ -1481,11 +1480,10 @@ static void *worker_main(void *param) } -static bool started=false; bool tp_init() { DBUG_ENTER("tp_init"); - started = true; + threadpool_started= true; scheduler_init(); for(uint i=0; i < array_elements(all_groups); i++) @@ -1493,7 +1491,12 @@ bool tp_init() thread_group_init(&all_groups[i], get_connection_attrib()); } tp_set_threadpool_size(threadpool_size); - + if(group_count == 0) + { + /* Something went wrong */ + sql_print_error("Can't set threadpool size to %d",threadpool_size); + DBUG_RETURN(1); + } PSI_register(mutex); PSI_register(cond); PSI_register(thread); @@ -1508,7 +1511,7 @@ void tp_end() { DBUG_ENTER("tp_end"); - if (!started) + if (!threadpool_started) DBUG_VOID_RETURN; stop_timer(&pool_timer); @@ -1516,18 +1519,18 @@ void tp_end() { thread_group_close(&all_groups[i]); } - started= false; + threadpool_started= false; DBUG_VOID_RETURN; } /** Ensure that poll descriptors are created when threadpool_size changes */ -int tp_set_threadpool_size(uint size) +void tp_set_threadpool_size(uint size) { bool success= true; - if (!started) - return 0; + if (!threadpool_started) + return; for(uint i=0; i< size; i++) { @@ -1537,21 +1540,25 @@ int tp_set_threadpool_size(uint size) { group->pollfd= io_poll_create(); success= (group->pollfd >= 0); + if(!success) + { + sql_print_error("io_poll_create() failed, errno=%d\n", errno); + break; + } } mysql_mutex_unlock(&all_groups[i].mutex); if (!success) { - group_count= i-1; - return -1; + group_count= i; + return; } } group_count= size; - return 0; } void tp_set_threadpool_stall_limit(uint limit) { - if (!started) + if (!threadpool_started) return; mysql_mutex_lock(&(pool_timer.mutex)); pool_timer.tick_interval= limit; @@ -1582,20 +1589,23 @@ int tp_get_idle_thread_count() /* Report threadpool problems */ -#define BLOCK_MSG_DELAY 30 +/** + Delay in microseconds, after which "pool blocked" message is printed. + (30 sec == 30 Mio usec) +*/ +#define BLOCK_MSG_DELAY 30*1000000 -static const char *max_threads_reached_msg= -"Threadpool could not create additional thread to handle queries, because the " -"number of allowed threads was reached. Increasing 'thread_pool_max_threads' " -"parameter can help in this situation.\n" -"If 'extra_port' parameter is set, you can still connect to the database with " -"superuser account (it must be TCP connection using extra_port as TCP port) " -"and troubleshoot the situation. " -"A likely cause of pool blocks are clients that lock resources for long time. " -"'show processlist' or 'show engine innodb status' can give additional hints."; +#define MAX_THREADS_REACHED_MSG \ +"Threadpool could not create additional thread to handle queries, because the \ +number of allowed threads was reached. Increasing 'thread_pool_max_threads' \ +parameter can help in this situation.\n \ +If 'extra_port' parameter is set, you can still connect to the database with \ +superuser account (it must be TCP connection using extra_port as TCP port) \ +and troubleshoot the situation. \ +A likely cause of pool blocks are clients that lock resources for long time. \ +'show processlist' or 'show engine innodb status' can give additional hints." -static const char *create_thread_error_msg= -"Can't create threads in threadpool (errno=%d)."; +#define CREATE_THREAD_ERROR_MSG "Can't create threads in threadpool (errno=%d)." /** Write a message when blocking situation in threadpool occurs. @@ -1606,10 +1616,10 @@ static const char *create_thread_error_msg= static void print_pool_blocked_message(bool max_threads_reached) { - time_t now; + ulonglong now; static bool msg_written; - now= time(NULL); + now= microsecond_interval_timer(); if (pool_block_start == 0) { pool_block_start= now; @@ -1620,12 +1630,12 @@ static void print_pool_blocked_message(bool max_threads_reached) if (now > pool_block_start + BLOCK_MSG_DELAY && !msg_written) { if (max_threads_reached) - sql_print_error(max_threads_reached_msg); + sql_print_error(MAX_THREADS_REACHED_MSG); else - sql_print_error(create_thread_error_msg, my_errno); + sql_print_error(CREATE_THREAD_ERROR_MSG, my_errno); sql_print_information("Threadpool has been blocked for %u seconds\n", - (uint)(now- pool_block_start)); + (uint)((now- pool_block_start)/1000000)); /* avoid reperated messages for the same blocking situation */ msg_written= true; } From 3fde590805dbd4955ea91de5a7a319c58c25cffc Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 16 Feb 2012 18:12:40 +0100 Subject: [PATCH 43/51] Only synchronous disk reads should use thd_wait_begin with THD_WAIT_DISKIO --- storage/xtradb/buf/buf0rea.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/xtradb/buf/buf0rea.c b/storage/xtradb/buf/buf0rea.c index 645d5fdc2f3..4ba0d2cf577 100644 --- a/storage/xtradb/buf/buf0rea.c +++ b/storage/xtradb/buf/buf0rea.c @@ -182,7 +182,10 @@ not_to_recover: ut_ad(buf_page_in_file(bpage)); - thd_wait_begin(NULL, THD_WAIT_DISKIO); + if(sync) { + thd_wait_begin(NULL, THD_WAIT_DISKIO); + } + if (zip_size) { *err = _fil_io(OS_FILE_READ | wake_later, sync, space, zip_size, offset, 0, zip_size, @@ -194,7 +197,6 @@ not_to_recover: sync, space, 0, offset, 0, UNIV_PAGE_SIZE, ((buf_block_t*) bpage)->frame, bpage, trx); } - thd_wait_end(NULL); if (srv_pass_corrupt_table) { if (*err != DB_SUCCESS) { @@ -205,6 +207,7 @@ not_to_recover: } if (sync) { + thd_wait_end(NULL); /* The i/o is already completed when we arrive from fil_read */ buf_page_io_complete(bpage); From dabb963934cc1ca5abeb5a9c49f2574fdfedb2d2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 16 Feb 2012 21:07:22 +0100 Subject: [PATCH 44/51] use poof-of-threads as default for thread_handling on Windows --- sql/sys_vars.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6e500737c1f..89f95ccf1ed 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1814,6 +1814,14 @@ static const char *thread_handling_names[]= #endif 0 }; + +#ifdef _WIN32 +/* Windows is using OS threadpool, so we're pretty sure it works well */ +#define DEFAULT_THREAD_HANDLING 2 +#else +#define DEFAULT_THREAD_HANDLING 0 +#endif + static Sys_var_enum Sys_thread_handling( "thread_handling", "Define threads usage for handling queries, one of " @@ -1823,7 +1831,7 @@ static Sys_var_enum Sys_thread_handling( #endif , READ_ONLY GLOBAL_VAR(thread_handling), CMD_LINE(REQUIRED_ARG), thread_handling_names, - DEFAULT(0) + DEFAULT(DEFAULT_THREAD_HANDLING) ); #ifdef HAVE_QUERY_CACHE From e0a500eba3193b2b2211fda3300914d7430846ce Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 17 Feb 2012 03:34:33 +0100 Subject: [PATCH 45/51] fix windows embedded (default thread handling ==pool-of-threads does not work in embedded) --- sql/sys_vars.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 89f95ccf1ed..231aa651603 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1815,7 +1815,7 @@ static const char *thread_handling_names[]= 0 }; -#ifdef _WIN32 +#if defined (_WIN32) && defined (HAVE_POOL_OF_THREADS) /* Windows is using OS threadpool, so we're pretty sure it works well */ #define DEFAULT_THREAD_HANDLING 2 #else From b932e57b27e413872a76deb082ccc425949a67ca Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 17 Feb 2012 23:23:54 +0100 Subject: [PATCH 46/51] Store callback instance in the connection structure, to call CallbackMayRunLong on long waits (currently binlog only) Also add copyright notice. --- sql/threadpool_win.cc | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index e70d3c7edf0..bf1d4740a13 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -1,3 +1,18 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program 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; version 2 of the License. + + 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 General Public License for more details. + + You should have received a copy of the GNU 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 */ + #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif @@ -202,11 +217,12 @@ struct connection_t OVERLAPPED overlapped; /* absolute time for wait timeout (as Windows time) */ volatile ulonglong timeout; - PTP_CLEANUP_GROUP cleanup_group; TP_CALLBACK_ENVIRON callback_environ; PTP_IO io; PTP_TIMER timer; PTP_WAIT shm_read; + /* Callback instance, used to inform treadpool about long callbacks */ + PTP_CALLBACK_INSTANCE callback_instance; bool logged_in; }; @@ -220,6 +236,7 @@ void init_connection(connection_t *connection) connection->timer= 0; connection->logged_in = false; connection->timeout= ULONGLONG_MAX; + connection->callback_instance= 0; memset(&connection->overlapped, 0, sizeof(OVERLAPPED)); InitializeThreadpoolEnvironment(&connection->callback_environ); SetThreadpoolCallbackPool(&connection->callback_environ, pool); @@ -554,7 +571,7 @@ static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance, THD *thd= connection->thd; ulonglong old_timeout = connection->timeout; connection->timeout = ULONGLONG_MAX; - + connection->callback_instance= instance; if (threadpool_process_request(connection->thd)) goto error; @@ -700,9 +717,24 @@ void tp_set_max_threads(uint val) void tp_wait_begin(THD *thd, int type) { - if (thd && thd->event_scheduler.data) + DBUG_ASSERT(thd); + + /* + Signal to the threadpool whenever callback can run long. Currently, binlog + waits are a good candidate, its waits are really long + */ + if (type == THD_WAIT_BINLOG) { - /* TODO: call CallbackMayRunLong() */ + connection_t *connection= (connection_t *)thd->event_scheduler.data; + if(connection && connection->callback_instance) + { + CallbackMayRunLong(connection->callback_instance); + /* + Reset instance, to avoid calling CallbackMayRunLong twice within + the same callback (it is an error according to docs). + */ + connection->callback_instance= 0; + } } } From 2a5d8ea9a201a1bf33fe309b8293e31ebd961e60 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 17 Feb 2012 23:27:15 +0100 Subject: [PATCH 47/51] Added copiright, some more comments --- sql/threadpool.h | 14 ++++++++++++ sql/threadpool_common.cc | 27 +++++++++++++++++++++++ sql/threadpool_unix.cc | 46 ++++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/sql/threadpool.h b/sql/threadpool.h index a3ecca3302b..919836e5a57 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -1,3 +1,17 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program 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; version 2 of the License. + + 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 General Public License for more details. + + You should have received a copy of the GNU 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 */ #define MAX_THREAD_GROUPS 128 diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index d003bae6727..03ccb3fa861 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -1,3 +1,18 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program 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; version 2 of the License. + + 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 General Public License for more details. + + You should have received a copy of the GNU 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 @@ -171,6 +186,9 @@ void threadpool_remove_connection(THD *thd) worker_context.restore(); } +/** + Process a single client request or a single batch. +*/ int threadpool_process_request(THD *thd) { int retval= 0; @@ -190,6 +208,15 @@ int threadpool_process_request(THD *thd) } + /* + In the loop below, the flow is essentially the copy of thead-per-connections + logic, see do_handle_one_connection() in sql_connect.c + + The goal is to execute a single query, thus the loop is normally executed + only once. However for SSL connections, it can be executed multiple times + (SSL can preread and cache incoming data, and vio->has_data() checks if it + was the case). + */ for(;;) { Vio *vio; diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index ad44e237262..407905fd5f6 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -1,3 +1,18 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program 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; version 2 of the License. + + 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 General Public License for more details. + + You should have received a copy of the GNU 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 @@ -541,6 +556,29 @@ void check_stall(thread_group_t *thread_group) /* Check whether requests from the workqueue are being dequeued. + + The stall detection and resolution works as follows: + + 1. There is a counter thread_group->queue_event_count for the number of + events removed from the queue. Timer resets the counter to 0 on each run. + 2. Timer determines stall if this counter remains 0 since last check + and the queue is not empty. + 3. Once timer determined a stall it sets thread_group->stalled flag and + wakes and idle worker (or creates a new one, subject to throttling). + 4. The stalled flag is reset, when an event is dequeued. + + Q : Will this handling lead to an unbound growth of threads, if queue + stalls permanently? + A : No. If queue stalls permanently, it is an indication for many very long + simultaneous queries. The maximum number of simultanoues queries is + max_connections, further we have threadpool_max_threads limit, upon which no + worker threads are created. So in case there is a flood of very long + queries, threadpool would slowly approach thread-per-connection behavior. + NOTE: + If long queries never wait, creation of the new threads is done by timer, + so it is slower than in real thread-per-connection. However if long queries + do wait and indicate that via thd_wait_begin/end callbacks, thread creation + will be faster. */ if (!thread_group->queue.is_empty() && !thread_group->queue_event_count) { @@ -1250,10 +1288,7 @@ void tp_post_kill_notification(THD *thd) void tp_wait_begin(THD *thd, int type) { DBUG_ENTER("tp_wait_begin"); - - if (!thd) - DBUG_VOID_RETURN; - + DBUG_ASSERT(thd); connection_t *connection = (connection_t *)thd->event_scheduler.data; if (connection) { @@ -1272,8 +1307,7 @@ void tp_wait_begin(THD *thd, int type) void tp_wait_end(THD *thd) { DBUG_ENTER("tp_wait_end"); - if (!thd) - DBUG_VOID_RETURN; + DBUG_ASSERT(thd); connection_t *connection = (connection_t *)thd->event_scheduler.data; if (connection) From 58c3e32dbdd43e8635c0320fcf5cc744e354fc65 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 17 Feb 2012 23:33:18 +0100 Subject: [PATCH 48/51] Simplify thd_wait_begin. given how seldom they are called, calling current_thd one more time is not going to be anything performance relevant. Also use thd_wait_begin/end for thr_lock and sync callbacks. --- sql/scheduler.cc | 18 +++++------------- sql/sql_class.cc | 20 +++++++------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 3f779337345..720b0d812cc 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -46,28 +46,20 @@ static bool no_threads_end(THD *thd, bool put_in_cache) /**@{*/ extern "C" { -static void scheduler_wait_lock_begin(void) { - THD *thd=current_thd; - if(thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); +static void scheduler_wait_lock_begin(void) { + thd_wait_begin(NULL, THD_WAIT_TABLE_LOCK); } static void scheduler_wait_lock_end(void) { - THD *thd=current_thd; - if(thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); + thd_wait_end(NULL); } static void scheduler_wait_sync_begin(void) { - THD *thd=current_thd; - if(thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, THD_WAIT_TABLE_LOCK)); + thd_wait_begin(NULL, THD_WAIT_SYNC); } static void scheduler_wait_sync_end(void) { - THD *thd=current_thd; - if(thd) - MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); + thd_wait_end(NULL); } }; /**@}*/ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index af3cdf7b34f..b174c3b4b55 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3935,13 +3935,10 @@ extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd) */ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) { - - if (unlikely(!thread_scheduler) || !thread_scheduler->thd_wait_begin) - return; - if (thd == NULL) + if (!thd) { - thd=current_thd; - if(unlikely(thd == NULL)) + thd= current_thd; + if (unlikely(!thd)) return; } MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type)); @@ -3956,16 +3953,13 @@ extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type) */ extern "C" void thd_wait_end(MYSQL_THD thd) { - if (unlikely(!thread_scheduler) || ! thread_scheduler->thd_wait_begin) - return; - if (thd == NULL) + if (!thd) { - thd=current_thd; - if(unlikely(thd == NULL)) + thd= current_thd; + if (unlikely(!thd)) return; } - if(likely(thd->scheduler == thread_scheduler)) - thread_scheduler->thd_wait_end(thd); + MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd)); } #endif // INNODB_COMPATIBILITY_HOOKS */ From 3ebb4b883370da9528b7482b8ba4ed9521f07553 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 27 Feb 2012 19:20:18 +0100 Subject: [PATCH 49/51] because of the high cost associated with fake symdir resolution, disable symbolic-links on Windows by default. Real symlinks (Vista+) as well as NTFS junctions (prior to Vista) do not require this parameter --- cmake/ssl.cmake | 2 ++ mysys/my_access.c | 16 +++++----------- sql/mysqld.cc | 4 +++- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cmake/ssl.cmake b/cmake/ssl.cmake index 001be69e62f..b5b94310d29 100644 --- a/cmake/ssl.cmake +++ b/cmake/ssl.cmake @@ -69,8 +69,10 @@ MACRO (MYSQL_CHECK_SSL) FIND_LIBRARY(CRYPTO_LIBRARY crypto) MARK_AS_ADVANCED(CRYPTO_LIBRARY) INCLUDE(CheckSymbolExists) + SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) CHECK_SYMBOL_EXISTS(SHA512_DIGEST_LENGTH "openssl/sha.h" HAVE_SHA512_DIGEST_LENGTH) + SET(CMAKE_REQUIRED_INCLUDES) IF(OPENSSL_FOUND AND CRYPTO_LIBRARY AND HAVE_SHA512_DIGEST_LENGTH) SET(SSL_SOURCES "") SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARY}) diff --git a/mysys/my_access.c b/mysys/my_access.c index 210946d50a8..d7d06f814d6 100644 --- a/mysys/my_access.c +++ b/mysys/my_access.c @@ -26,11 +26,6 @@ path Path to file amode Access method - DESCRIPTION - This function wraps the normal access method because the access - available in MSVCRT> +reports that filenames such as LPT1 and - COM1 are valid (they are but should not be so for us). - RETURN VALUES 0 ok -1 error (We use -1 as my_access is mapped to access on other platforms) @@ -38,12 +33,11 @@ int my_access(const char *path, int amode) { - WIN32_FILE_ATTRIBUTE_DATA fileinfo; - BOOL result; - - result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo); - if (! result || - (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK)) + DWORD attributes; + + attributes = GetFileAttributes(path); + if (attributes == INVALID_FILE_ATTRIBUTES || + (attributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK)) { my_errno= errno= EACCES; return -1; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6a5de0b82cb..a5b0c462a63 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6334,8 +6334,10 @@ struct my_option my_long_options[]= The system call realpath() produces warnings under valgrind and purify. These are not suppressed: instead we disable symlinks option if compiled with valgrind support. + Also disable by default on Windows, due to high overhead for checking .sym + files. */ - IF_VALGRIND(0,1), 0, 0, 0, 0, 0}, + IF_VALGRIND(0,IF_WIN(0,1)), 0, 0, 0, 0, 0}, {"debug-no-sync", 0, "Disables system sync calls. Only for running tests or debugging!", &my_disable_sync, &my_disable_sync, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, From 0d168ce514ea506a292c12d8624bfbc67fb3530f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 27 Feb 2012 19:53:49 +0100 Subject: [PATCH 50/51] Remove libevent from sources - not needed now --- extra/libevent/CMakeLists.txt | 43 - extra/libevent/README | 57 - extra/libevent/WIN32-Code/config.h | 247 --- extra/libevent/WIN32-Code/misc.c | 38 - extra/libevent/WIN32-Code/misc.h | 11 - extra/libevent/WIN32-Code/tree.h | 1354 ------------ extra/libevent/WIN32-Code/win32.c | 471 ----- extra/libevent/buffer.c | 455 ---- extra/libevent/compat/sys/_time.h | 163 -- extra/libevent/compat/sys/queue.h | 488 ----- extra/libevent/compat/sys/tree.h | 677 ------ extra/libevent/devpoll.c | 421 ---- extra/libevent/epoll.c | 360 ---- extra/libevent/epoll_sub.c | 60 - extra/libevent/evbuffer.c | 420 ---- extra/libevent/evdns.c | 3150 ---------------------------- extra/libevent/evdns.h | 528 ----- extra/libevent/event-internal.h | 103 - extra/libevent/event.c | 989 --------- extra/libevent/event.h | 1129 ---------- extra/libevent/event_tagging.c | 443 ---- extra/libevent/evhttp.h | 340 --- extra/libevent/evport.c | 517 ----- extra/libevent/evrpc-internal.h | 87 - extra/libevent/evrpc.c | 658 ------ extra/libevent/evrpc.h | 477 ----- extra/libevent/evsignal.h | 52 - extra/libevent/evutil.c | 198 -- extra/libevent/evutil.h | 174 -- extra/libevent/http-internal.h | 133 -- extra/libevent/http.c | 2512 ---------------------- extra/libevent/kqueue.c | 426 ---- extra/libevent/log.c | 218 -- extra/libevent/log.h | 51 - extra/libevent/min_heap.h | 138 -- extra/libevent/poll.c | 378 ---- extra/libevent/select.c | 356 ---- extra/libevent/signal.c | 305 --- extra/libevent/strlcpy-internal.h | 23 - extra/libevent/strlcpy.c | 76 - 40 files changed, 18726 deletions(-) delete mode 100644 extra/libevent/CMakeLists.txt delete mode 100644 extra/libevent/README delete mode 100644 extra/libevent/WIN32-Code/config.h delete mode 100644 extra/libevent/WIN32-Code/misc.c delete mode 100644 extra/libevent/WIN32-Code/misc.h delete mode 100644 extra/libevent/WIN32-Code/tree.h delete mode 100644 extra/libevent/WIN32-Code/win32.c delete mode 100644 extra/libevent/buffer.c delete mode 100644 extra/libevent/compat/sys/_time.h delete mode 100644 extra/libevent/compat/sys/queue.h delete mode 100644 extra/libevent/compat/sys/tree.h delete mode 100644 extra/libevent/devpoll.c delete mode 100644 extra/libevent/epoll.c delete mode 100644 extra/libevent/epoll_sub.c delete mode 100644 extra/libevent/evbuffer.c delete mode 100644 extra/libevent/evdns.c delete mode 100644 extra/libevent/evdns.h delete mode 100644 extra/libevent/event-internal.h delete mode 100644 extra/libevent/event.c delete mode 100644 extra/libevent/event.h delete mode 100644 extra/libevent/event_tagging.c delete mode 100644 extra/libevent/evhttp.h delete mode 100644 extra/libevent/evport.c delete mode 100644 extra/libevent/evrpc-internal.h delete mode 100644 extra/libevent/evrpc.c delete mode 100644 extra/libevent/evrpc.h delete mode 100644 extra/libevent/evsignal.h delete mode 100644 extra/libevent/evutil.c delete mode 100644 extra/libevent/evutil.h delete mode 100644 extra/libevent/http-internal.h delete mode 100644 extra/libevent/http.c delete mode 100644 extra/libevent/kqueue.c delete mode 100644 extra/libevent/log.c delete mode 100644 extra/libevent/log.h delete mode 100644 extra/libevent/min_heap.h delete mode 100644 extra/libevent/poll.c delete mode 100644 extra/libevent/select.c delete mode 100644 extra/libevent/signal.c delete mode 100644 extra/libevent/strlcpy-internal.h delete mode 100644 extra/libevent/strlcpy.c diff --git a/extra/libevent/CMakeLists.txt b/extra/libevent/CMakeLists.txt deleted file mode 100644 index 6be15641d3e..00000000000 --- a/extra/libevent/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -INCLUDE_DIRECTORIES( - ${CMAKE_SOURCE_DIR}/extra/libevent - ${CMAKE_SOURCE_DIR}/extra/libevent/compat - ${CMAKE_SOURCE_DIR}/extra/libevent/WIN32-Code - ${CMAKE_BINARY_DIR}/extra/libevent - ${CMAKE_SOURCE_DIR}/include -) - -IF(MSVC) - ADD_DEFINITIONS("-DWIN32 -DHAVE_CONFIG_H") -ENDIF(MSVC) - -SET(LIBEVENT_SOURCES - buffer.c - evbuffer.c - event.c - evutil.c - log.c - signal.c - strlcpy.c - WIN32-Code/win32.c - WIN32-Code/config.h - WIN32-Code/misc.c - WIN32-Code/misc.h - event-internal.h - event.h - evsignal.h - evutil.h - log.h - min_heap.h - strlcpy-internal.h - ) -IF(WIN32) - # Workaround source distribution bug, remove preconfigured event-config - IF(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) - FILE(REMOVE ${CMAKE_SOURCE_DIR}/extra/libevent/event-config.h) - ENDIF() - CONFIGURE_FILE(WIN32-Code/config.h ${CMAKE_BINARY_DIR}/extra/libevent/event-config.h COPYONLY) -ENDIF() - -IF(NOT SOURCE_SUBLIBS) - ADD_LIBRARY(libevent ${LIBEVENT_SOURCES}) -ENDIF(NOT SOURCE_SUBLIBS) diff --git a/extra/libevent/README b/extra/libevent/README deleted file mode 100644 index b0650392ed4..00000000000 --- a/extra/libevent/README +++ /dev/null @@ -1,57 +0,0 @@ -To build libevent, type - -$ ./configure && make - - (If you got libevent from the subversion repository, you will - first need to run the included "autogen.sh" script in order to - generate the configure script.) - -Install as root via - -# make install - -You can run the regression tests by - -$ make verify - -Before, reporting any problems, please run the regression tests. - -To enable the low-level tracing build the library as: - -CFLAGS=-DUSE_DEBUG ./configure [...] - -Acknowledgements: ------------------ - -The following people have helped with suggestions, ideas, code or -fixing bugs: - - Alejo - Weston Andros Adamson - William Ahern - Stas Bekman - Andrew Danforth - Mike Davis - Shie Erlich - Alexander von Gernler - Artur Grabowski - Aaron Hopkins - Claudio Jeker - Scott Lamb - Adam Langley - Philip Lewis - David Libenzi - Nick Mathewson - Andrey Matveev - Richard Nyberg - Jon Oberheide - Phil Oleson - Dave Pacheco - Tassilo von Parseval - Pierre Phaneuf - Jon Poland - Bert JW Regeer - Dug Song - Taral - -If I have forgotten your name, please contact me. diff --git a/extra/libevent/WIN32-Code/config.h b/extra/libevent/WIN32-Code/config.h deleted file mode 100644 index 99365edd96a..00000000000 --- a/extra/libevent/WIN32-Code/config.h +++ /dev/null @@ -1,247 +0,0 @@ -/* config.h. Generated by configure. */ -/* config.h.in. Generated from configure.in by autoheader. */ - -/* Define if clock_gettime is available in libc */ -/* #undef DNS_USE_CPU_CLOCK_FOR_ID */ - -/* Define if no secure id variant is available */ -#define DNS_USE_FTIME_FOR_ID 1 - -/* Define if no secure id variant is available */ -/* #define DNS_USE_GETTIMEOFDAY_FOR_ID 1 */ - -/* Define to 1 if you have the `clock_gettime' function. */ -/* #undef HAVE_CLOCK_GETTIME */ - -/* Define if /dev/poll is available */ -/* #undef HAVE_DEVPOLL */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Define if your system supports the epoll system calls */ -/* #undef HAVE_EPOLL */ - -/* Define to 1 if you have the `epoll_ctl' function. */ -/* #undef HAVE_EPOLL_CTL */ - -/* Define if your system supports event ports */ -/* #undef HAVE_EVENT_PORTS */ - -/* Define to 1 if you have the `fcntl' function. */ -/* #undef HAVE_FCNTL */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* Define to 1 if you have the `getaddrinfo' function. */ -/* #undef HAVE_GETADDRINFO */ - -/* Define to 1 if you have the `getnameinfo' function. */ -/* #undef HAVE_GETNAMEINFO */ - -/* Define to 1 if you have the `gettimeofday' function. */ -/* #define HAVE_GETTIMEOFDAY 1 */ - -/* Define to 1 if you have the `inet_ntop' function. */ -/* #undef HAVE_INET_NTOP */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_INTTYPES_H 1 */ - -/* Define to 1 if you have the `kqueue' function. */ -/* #undef HAVE_KQUEUE */ - -/* Define to 1 if you have the `nsl' library (-lnsl). */ -/* #undef HAVE_LIBNSL */ - -/* Define to 1 if you have the `resolv' library (-lresolv). */ -/* #undef HAVE_LIBRESOLV */ - -/* Define to 1 if you have the `rt' library (-lrt). */ -/* #undef HAVE_LIBRT */ - -/* Define to 1 if you have the `socket' library (-lsocket). */ -/* #undef HAVE_LIBSOCKET */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETINET_IN6_H */ - -/* Define to 1 if you have the `poll' function. */ -/* #undef HAVE_POLL */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_POLL_H */ - -/* Define to 1 if you have the `port_create' function. */ -/* #undef HAVE_PORT_CREATE */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_PORT_H */ - -/* Define to 1 if you have the `select' function. */ -/* #undef HAVE_SELECT */ - -/* Define if F_SETFD is defined in */ -/* #undef HAVE_SETFD */ - -/* Define to 1 if you have the `sigaction' function. */ -/* #undef HAVE_SIGACTION */ - -/* Define to 1 if you have the `signal' function. */ -#define HAVE_SIGNAL 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SIGNAL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if you have the header file. */ -/* #define HAVE_STDINT_H 1 */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strlcpy' function. */ -/* #undef HAVE_STRLCPY */ - -/* Define to 1 if you have the `strsep' function. */ -/* #undef HAVE_STRSEP */ - -/* Define to 1 if you have the `strtok_r' function. */ -/* #undef HAVE_STRTOK_R */ - -/* Define to 1 if the system has the type `struct in6_addr'. */ -#define HAVE_STRUCT_IN6_ADDR 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_DEVPOLL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_EPOLL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_EVENT_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_QUEUE_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SELECT_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ - -/* Define to 1 if you have the header file. */ -/* #define HAVE_SYS_STAT_H 1 */ - -/* Define to 1 if you have the header file. */ -/* #define HAVE_SYS_TIME_H 1 */ - -/* Define to 1 if you have the header file. */ -/* #define HAVE_SYS_TYPES_H 1 */ - -/* Define if TAILQ_FOREACH is defined in */ -/* #undef HAVE_TAILQFOREACH */ - -/* Define if timeradd is defined in */ -/* #undef HAVE_TIMERADD */ - -/* Define if timerclear is defined in */ -/* #define HAVE_TIMERCLEAR 1 */ - -/* Define if timercmp is defined in */ -#define HAVE_TIMERCMP 1 - -/* Define if timerisset is defined in */ -#define HAVE_TIMERISSET 1 - -/* Define to 1 if you have the header file. */ -/* #define HAVE_UNISTD_H 1 */ - -/* Define to 1 if you have the `vasprintf' function. */ -/* #undef HAVE_VASPRINTF */ - -/* Define if kqueue works correctly with pipes */ -/* #undef HAVE_WORKING_KQUEUE */ - -/* Name of package */ -#ifndef PACKAGE -#define PACKAGE "libevent" -#endif - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if you can safely include both and . */ -#define TIME_WITH_SYS_TIME 1 - -/* Version number of package */ -#define VERSION "1.3.99-trunk" - -#ifndef __func__ -/* Define to appropriate substitue if compiler doesnt have __func__ */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define __func__ "??" -#else -#define __func__ __FUNCTION__ -#endif -#endif - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#define inline __inline -#endif - -/* Define to `int' if does not define. */ -/* #undef pid_t */ - -/* Define to `unsigned' if does not define. */ -/* #undef size_t */ - -/* Define to unsigned int if you dont have it */ -#define socklen_t unsigned int - -/* Define to `unsigned short' if does not define. */ -#define uint16_t unsigned short - -/* Define to `unsigned int' if does not define. */ -#define uint32_t unsigned int - -/* Define to `unsigned long long' if does not define. */ -#define uint64_t __uint64_t - -/* Define to `unsigned char' if does not define. */ -#define uint8_t unsigned char diff --git a/extra/libevent/WIN32-Code/misc.c b/extra/libevent/WIN32-Code/misc.c deleted file mode 100644 index 271c9bb399e..00000000000 --- a/extra/libevent/WIN32-Code/misc.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -/*our prototypes for timeval and timezone are in here, just in case the above - headers don't have them*/ -#include "misc.h" -#endif - -/**************************************************************************** - * - * Function: gettimeofday(struct timeval *, struct timezone *) - * - * Purpose: Get current time of day. - * - * Arguments: tv => Place to store the curent time of day. - * tz => Ignored. - * - * Returns: 0 => Success. - * - ****************************************************************************/ - -#ifndef HAVE_GETTIMEOFDAY -int gettimeofday(struct timeval *tv, struct timezone *tz) { - struct _timeb tb; - - if(tv == NULL) - return -1; - - _ftime(&tb); - tv->tv_sec = (long) tb.time; - tv->tv_usec = ((int) tb.millitm) * 1000; - return 0; -} -#endif diff --git a/extra/libevent/WIN32-Code/misc.h b/extra/libevent/WIN32-Code/misc.h deleted file mode 100644 index aced574687c..00000000000 --- a/extra/libevent/WIN32-Code/misc.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef MISC_H -#define MISC_H - -struct timezone; -struct timeval; - -#ifndef HAVE_GETTIMEOFDAY -int gettimeofday(struct timeval *,struct timezone *); -#endif - -#endif diff --git a/extra/libevent/WIN32-Code/tree.h b/extra/libevent/WIN32-Code/tree.h deleted file mode 100644 index 79e8d91f0eb..00000000000 --- a/extra/libevent/WIN32-Code/tree.h +++ /dev/null @@ -1,1354 +0,0 @@ -/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ -/* - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _SYS_TREE_H_ -#define _SYS_TREE_H_ - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while ((__comp = (cmp)(elm, (head)->sph_root))) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-back tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ -} while (0) - -#define RB_SET_BLACKRED(black, red, field) do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ -} while (0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ -void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -struct type *name##_RB_REMOVE(struct name *, struct type *); \ -struct type *name##_RB_INSERT(struct name *, struct type *); \ -struct type *name##_RB_FIND(struct name *, struct type *); \ -struct type *name##_RB_NEXT(struct type *); \ -struct type *name##_RB_MINMAX(struct name *, int); \ - \ - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ -void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) && \ - RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ -} \ - \ -void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)))\ - RB_COLOR(oleft, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)))\ - RB_COLOR(oright, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ -} \ - \ -struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field))) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old)\ - RB_LEFT(RB_PARENT(old, field), field) = elm;\ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm;\ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field))); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#endif /* _SYS_TREE_H_ */ -/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ -/* - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _SYS_TREE_H_ -#define _SYS_TREE_H_ - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while ((__comp = (cmp)(elm, (head)->sph_root))) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-back tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ -} while (0) - -#define RB_SET_BLACKRED(black, red, field) do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ -} while (0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ -void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -struct type *name##_RB_REMOVE(struct name *, struct type *); \ -struct type *name##_RB_INSERT(struct name *, struct type *); \ -struct type *name##_RB_FIND(struct name *, struct type *); \ -struct type *name##_RB_NEXT(struct type *); \ -struct type *name##_RB_MINMAX(struct name *, int); \ - \ - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ -void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) && \ - RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ -} \ - \ -void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)))\ - RB_COLOR(oleft, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)))\ - RB_COLOR(oright, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ -} \ - \ -struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field))) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old)\ - RB_LEFT(RB_PARENT(old, field), field) = elm;\ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm;\ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field))); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#endif /* _SYS_TREE_H_ */ diff --git a/extra/libevent/WIN32-Code/win32.c b/extra/libevent/WIN32-Code/win32.c deleted file mode 100644 index 0d7dcfb276f..00000000000 --- a/extra/libevent/WIN32-Code/win32.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2000-2002 Niels Provos - * Copyright 2003 Michael A. Davis - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef _MSC_VER -#include "./config.h" -#else -/* Avoid the windows/msvc thing. */ -#include "../config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RB_AUGMENT(x) (void)(x) -#include "./tree.h" -#include "log.h" -#include "event.h" -#include "event-internal.h" - -#define XFREE(ptr) do { if (ptr) free(ptr); } while(0) - -extern struct event_list timequeue; -extern struct event_list addqueue; -#if 0 -extern struct event_list signalqueue; -#endif - -struct win_fd_set { - u_int fd_count; - SOCKET fd_array[1]; -}; - -int evsigcaught[NSIG]; -volatile sig_atomic_t signal_caught = 0; -/* MSDN says this is required to handle SIGFPE */ -volatile double SIGFPE_REQ = 0.0f; - -#if 0 -static void signal_handler(int sig); - -void signal_process(void); -int signal_recalc(void); -#endif - -struct event_entry { - RB_ENTRY(event_entry) node; - SOCKET sock; - int read_pos; - int write_pos; - struct event *read_event; - struct event *write_event; -}; - -static int -compare(struct event_entry *a, struct event_entry *b) -{ - if (a->sock < b->sock) - return -1; - else if (a->sock > b->sock) - return 1; - else - return 0; -} - -struct win32op { - int fd_setsz; - struct win_fd_set *readset_in; - struct win_fd_set *writeset_in; - struct win_fd_set *readset_out; - struct win_fd_set *writeset_out; - struct win_fd_set *exset_out; - RB_HEAD(event_map, event_entry) event_root; -}; - -RB_PROTOTYPE(event_map, event_entry, node, compare); -RB_GENERATE(event_map, event_entry, node, compare); - -void *win32_init (struct event_base *); -int win32_insert (void *, struct event *); -int win32_del (void *, struct event *); -int win32_dispatch (struct event_base *base, void *, struct timeval *); -void win32_dealloc (struct event_base *, void *); - -struct eventop win32ops = { - "win32", - win32_init, - win32_insert, - win32_del, - win32_dispatch, - win32_dealloc, - 0 -}; - -#define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET))) - -static int -realloc_fd_sets(struct win32op *op, size_t new_size) -{ - size_t size; - - assert(new_size >= op->readset_in->fd_count && - new_size >= op->writeset_in->fd_count); - assert(new_size >= 1); - - size = FD_SET_ALLOC_SIZE(new_size); - if (!(op->readset_in = realloc(op->readset_in, size))) - return (-1); - if (!(op->writeset_in = realloc(op->writeset_in, size))) - return (-1); - if (!(op->readset_out = realloc(op->readset_out, size))) - return (-1); - if (!(op->exset_out = realloc(op->exset_out, size))) - return (-1); - if (!(op->writeset_out = realloc(op->writeset_out, size))) - return (-1); - op->fd_setsz = (int)new_size; - return (0); -} - -static int -timeval_to_ms(struct timeval *tv) -{ - return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000)); -} - -static struct event_entry* -get_event_entry(struct win32op *op, SOCKET s, int create) -{ - struct event_entry key, *val; - key.sock = s; - val = RB_FIND(event_map, &op->event_root, &key); - if (val || !create) - return val; - if (!(val = calloc(1, sizeof(struct event_entry)))) { - event_warn("%s: calloc", __func__); - return NULL; - } - val->sock = s; - val->read_pos = val->write_pos = -1; - RB_INSERT(event_map, &op->event_root, val); - return val; -} - -static int -do_fd_set(struct win32op *op, struct event_entry *ent, int read) -{ - SOCKET s = ent->sock; - struct win_fd_set *set = read ? op->readset_in : op->writeset_in; - if (read) { - if (ent->read_pos >= 0) - return (0); - } else { - if (ent->write_pos >= 0) - return (0); - } - if (set->fd_count == op->fd_setsz) { - if (realloc_fd_sets(op, op->fd_setsz*2)) - return (-1); - /* set pointer will have changed and needs reiniting! */ - set = read ? op->readset_in : op->writeset_in; - } - set->fd_array[set->fd_count] = s; - if (read) - ent->read_pos = set->fd_count; - else - ent->write_pos = set->fd_count; - return (set->fd_count++); -} - -static int -do_fd_clear(struct win32op *op, struct event_entry *ent, int read) -{ - int i; - struct win_fd_set *set = read ? op->readset_in : op->writeset_in; - if (read) { - i = ent->read_pos; - ent->read_pos = -1; - } else { - i = ent->write_pos; - ent->write_pos = -1; - } - if (i < 0) - return (0); - if (--set->fd_count != i) { - struct event_entry *ent2; - SOCKET s2; - s2 = set->fd_array[i] = set->fd_array[set->fd_count]; - ent2 = get_event_entry(op, s2, 0); - if (!ent) /* This indicates a bug. */ - return (0); - if (read) - ent2->read_pos = i; - else - ent2->write_pos = i; - } - return (0); -} - -#define NEVENT 64 -void * -win32_init(struct event_base *_base) -{ - struct win32op *winop; - size_t size; - if (!(winop = calloc(1, sizeof(struct win32op)))) - return NULL; - winop->fd_setsz = NEVENT; - size = FD_SET_ALLOC_SIZE(NEVENT); - if (!(winop->readset_in = malloc(size))) - goto err; - if (!(winop->writeset_in = malloc(size))) - goto err; - if (!(winop->readset_out = malloc(size))) - goto err; - if (!(winop->writeset_out = malloc(size))) - goto err; - if (!(winop->exset_out = malloc(size))) - goto err; - RB_INIT(&winop->event_root); - winop->readset_in->fd_count = winop->writeset_in->fd_count = 0; - winop->readset_out->fd_count = winop->writeset_out->fd_count - = winop->exset_out->fd_count = 0; - - evsignal_init(_base); - - return (winop); - err: - XFREE(winop->readset_in); - XFREE(winop->writeset_in); - XFREE(winop->readset_out); - XFREE(winop->writeset_out); - XFREE(winop->exset_out); - XFREE(winop); - return (NULL); -} - -int -win32_insert(void *op, struct event *ev) -{ - struct win32op *win32op = op; - struct event_entry *ent; - - if (ev->ev_events & EV_SIGNAL) { - return (evsignal_add(ev)); - } - if (!(ev->ev_events & (EV_READ|EV_WRITE))) - return (0); - ent = get_event_entry(win32op, ev->ev_fd, 1); - if (!ent) - return (-1); /* out of memory */ - - event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd)); - if (ev->ev_events & EV_READ) { - if (do_fd_set(win32op, ent, 1)<0) - return (-1); - ent->read_event = ev; - } - if (ev->ev_events & EV_WRITE) { - if (do_fd_set(win32op, ent, 0)<0) - return (-1); - ent->write_event = ev; - } - return (0); -} - -int -win32_del(void *op, struct event *ev) -{ - struct win32op *win32op = op; - struct event_entry *ent; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_del(ev)); - - if (!(ent = get_event_entry(win32op, ev->ev_fd, 0))) - return (-1); - event_debug(("%s: Removing event for %d", __func__, ev->ev_fd)); - if (ev == ent->read_event) { - do_fd_clear(win32op, ent, 1); - ent->read_event = NULL; - } - if (ev == ent->write_event) { - do_fd_clear(win32op, ent, 0); - ent->write_event = NULL; - } - if (!ent->read_event && !ent->write_event) { - RB_REMOVE(event_map, &win32op->event_root, ent); - free(ent); - } - - return 0; -} - -static void -fd_set_copy(struct win_fd_set *out, const struct win_fd_set *in) -{ - out->fd_count = in->fd_count; - memcpy(out->fd_array, in->fd_array, in->fd_count * (sizeof(SOCKET))); -} - -/* - static void dump_fd_set(struct win_fd_set *s) - { - unsigned int i; - printf("[ "); - for(i=0;ifd_count;++i) - printf("%d ",(int)s->fd_array[i]); - printf("]\n"); - } -*/ - -int -win32_dispatch(struct event_base *base, void *op, - struct timeval *tv) -{ - struct win32op *win32op = op; - int res = 0; - u_int i; - int fd_count; - - fd_set_copy(win32op->readset_out, win32op->readset_in); - fd_set_copy(win32op->exset_out, win32op->readset_in); - fd_set_copy(win32op->writeset_out, win32op->writeset_in); - - fd_count = - (win32op->readset_out->fd_count > win32op->writeset_out->fd_count) ? - win32op->readset_out->fd_count : win32op->writeset_out->fd_count; - - if (!fd_count) { - /* Windows doesn't like you to call select() with no sockets */ - Sleep(timeval_to_ms(tv)); - evsignal_process(base); - return (0); - } - - res = select(fd_count, - (struct fd_set*)win32op->readset_out, - (struct fd_set*)win32op->writeset_out, - (struct fd_set*)win32op->exset_out, tv); - - event_debug(("%s: select returned %d", __func__, res)); - - if(res <= 0) { - evsignal_process(base); - return res; - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - for (i=0; ireadset_out->fd_count; ++i) { - struct event_entry *ent; - SOCKET s = win32op->readset_out->fd_array[i]; - if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event) - event_active(ent->read_event, EV_READ, 1); - } - for (i=0; iexset_out->fd_count; ++i) { - struct event_entry *ent; - SOCKET s = win32op->exset_out->fd_array[i]; - if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event) - event_active(ent->read_event, EV_READ, 1); - } - for (i=0; iwriteset_out->fd_count; ++i) { - struct event_entry *ent; - SOCKET s = win32op->writeset_out->fd_array[i]; - if ((ent = get_event_entry(win32op, s, 0)) && ent->write_event) - event_active(ent->write_event, EV_WRITE, 1); - } - -#if 0 - if (signal_recalc() == -1) - return (-1); -#endif - - return (0); -} - -void -win32_dealloc(struct event_base *_base, void *arg) -{ - struct win32op *win32op = arg; - - evsignal_dealloc(_base); - if (win32op->readset_in) - free(win32op->readset_in); - if (win32op->writeset_in) - free(win32op->writeset_in); - if (win32op->readset_out) - free(win32op->readset_out); - if (win32op->writeset_out) - free(win32op->writeset_out); - if (win32op->exset_out) - free(win32op->exset_out); - /* XXXXX free the tree. */ - - memset(win32op, 0, sizeof(win32op)); - free(win32op); -} - -#if 0 -static void -signal_handler(int sig) -{ - evsigcaught[sig]++; - signal_caught = 1; -} - -int -signal_recalc(void) -{ - struct event *ev; - - /* Reinstall our signal handler. */ - TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { - if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1) - return (-1); - } - return (0); -} - -void -signal_process(void) -{ - struct event *ev; - short ncalls; - - TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { - ncalls = evsigcaught[EVENT_SIGNAL(ev)]; - if (ncalls) { - if (!(ev->ev_events & EV_PERSIST)) - event_del(ev); - event_active(ev, EV_SIGNAL, ncalls); - } - } - - memset(evsigcaught, 0, sizeof(evsigcaught)); - signal_caught = 0; -} -#endif - diff --git a/extra/libevent/buffer.c b/extra/libevent/buffer.c deleted file mode 100644 index d3394dae470..00000000000 --- a/extra/libevent/buffer.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright (c) 2002, 2003 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#include -#include -#endif - -#ifdef HAVE_VASPRINTF -/* If we have vasprintf, we need to define this before we include stdio.h. */ -#define _GNU_SOURCE -#endif - -#include - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#include -#include -#include -#include -#include -#ifdef HAVE_STDARG_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "event.h" -#include "config.h" - -struct evbuffer * -evbuffer_new(void) -{ - struct evbuffer *buffer; - - buffer = calloc(1, sizeof(struct evbuffer)); - - return (buffer); -} - -void -evbuffer_free(struct evbuffer *buffer) -{ - if (buffer->orig_buffer != NULL) - free(buffer->orig_buffer); - free(buffer); -} - -/* - * This is a destructive add. The data from one buffer moves into - * the other buffer. - */ - -#define SWAP(x,y) do { \ - (x)->buffer = (y)->buffer; \ - (x)->orig_buffer = (y)->orig_buffer; \ - (x)->misalign = (y)->misalign; \ - (x)->totallen = (y)->totallen; \ - (x)->off = (y)->off; \ -} while (0) - -int -evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) -{ - int res; - - /* Short cut for better performance */ - if (outbuf->off == 0) { - struct evbuffer tmp; - size_t oldoff = inbuf->off; - - /* Swap them directly */ - SWAP(&tmp, outbuf); - SWAP(outbuf, inbuf); - SWAP(inbuf, &tmp); - - /* - * Optimization comes with a price; we need to notify the - * buffer if necessary of the changes. oldoff is the amount - * of data that we transfered from inbuf to outbuf - */ - if (inbuf->off != oldoff && inbuf->cb != NULL) - (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg); - if (oldoff && outbuf->cb != NULL) - (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg); - - return (0); - } - - res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off); - if (res == 0) { - /* We drain the input buffer on success */ - evbuffer_drain(inbuf, inbuf->off); - } - - return (res); -} - -int -evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) -{ - char *buffer; - size_t space; - size_t oldoff = buf->off; - int sz; - va_list aq; - - /* make sure that at least some space is available */ - evbuffer_expand(buf, 64); - for (;;) { - size_t used = buf->misalign + buf->off; - buffer = (char *)buf->buffer + buf->off; - assert(buf->totallen >= used); - space = buf->totallen - used; - -#ifndef va_copy -#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) -#endif - va_copy(aq, ap); - -#ifdef WIN32 - sz = _vsnprintf(buffer, space - 1, fmt, aq); - buffer[space - 1] = '\0'; -#else - sz = vsnprintf(buffer, space, fmt, aq); -#endif - - va_end(aq); - - if (sz < 0) - return (-1); - if ((size_t)sz < space) { - buf->off += sz; - if (buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); - return (sz); - } - if (evbuffer_expand(buf, sz + 1) == -1) - return (-1); - - } - /* NOTREACHED */ -} - -int -evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) -{ - int res = -1; - va_list ap; - - va_start(ap, fmt); - res = evbuffer_add_vprintf(buf, fmt, ap); - va_end(ap); - - return (res); -} - -/* Reads data from an event buffer and drains the bytes read */ - -int -evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) -{ - size_t nread = datlen; - if (nread >= buf->off) - nread = buf->off; - - memcpy(data, buf->buffer, nread); - evbuffer_drain(buf, nread); - - return (int)(nread); -} - -/* - * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. - * The returned buffer needs to be freed by the called. - */ - -char * -evbuffer_readline(struct evbuffer *buffer) -{ - u_char *data = EVBUFFER_DATA(buffer); - size_t len = EVBUFFER_LENGTH(buffer); - char *line; - unsigned int i; - - for (i = 0; i < len; i++) { - if (data[i] == '\r' || data[i] == '\n') - break; - } - - if (i == len) - return (NULL); - - if ((line = malloc(i + 1)) == NULL) { - fprintf(stderr, "%s: out of memory\n", __func__); - evbuffer_drain(buffer, i); - return (NULL); - } - - memcpy(line, data, i); - line[i] = '\0'; - - /* - * Some protocols terminate a line with '\r\n', so check for - * that, too. - */ - if ( i < len - 1 ) { - char fch = data[i], sch = data[i+1]; - - /* Drain one more character if needed */ - if ( (sch == '\r' || sch == '\n') && sch != fch ) - i += 1; - } - - evbuffer_drain(buffer, i + 1); - - return (line); -} - -/* Adds data to an event buffer */ - -static void -evbuffer_align(struct evbuffer *buf) -{ - memmove(buf->orig_buffer, buf->buffer, buf->off); - buf->buffer = buf->orig_buffer; - buf->misalign = 0; -} - -/* Expands the available space in the event buffer to at least datlen */ - -int -evbuffer_expand(struct evbuffer *buf, size_t datlen) -{ - size_t need = buf->misalign + buf->off + datlen; - - /* If we can fit all the data, then we don't have to do anything */ - if (buf->totallen >= need) - return (0); - - /* - * If the misalignment fulfills our data needs, we just force an - * alignment to happen. Afterwards, we have enough space. - */ - if (buf->misalign >= datlen) { - evbuffer_align(buf); - } else { - void *newbuf; - size_t length = buf->totallen; - - if (length < 256) - length = 256; - while (length < need) - length <<= 1; - - if (buf->orig_buffer != buf->buffer) - evbuffer_align(buf); - if ((newbuf = realloc(buf->buffer, length)) == NULL) - return (-1); - - buf->orig_buffer = buf->buffer = newbuf; - buf->totallen = length; - } - - return (0); -} - -int -evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) -{ - size_t need = buf->misalign + buf->off + datlen; - size_t oldoff = buf->off; - - if (buf->totallen < need) { - if (evbuffer_expand(buf, datlen) == -1) - return (-1); - } - - memcpy(buf->buffer + buf->off, data, datlen); - buf->off += datlen; - - if (datlen && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); - - return (0); -} - -void -evbuffer_drain(struct evbuffer *buf, size_t len) -{ - size_t oldoff = buf->off; - - if (len >= buf->off) { - buf->off = 0; - buf->buffer = buf->orig_buffer; - buf->misalign = 0; - goto done; - } - - buf->buffer += len; - buf->misalign += len; - - buf->off -= len; - - done: - /* Tell someone about changes in this buffer */ - if (buf->off != oldoff && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); - -} - -/* - * Reads data from a file descriptor into a buffer. - */ - -#define EVBUFFER_MAX_READ 4096 - -int -evbuffer_read(struct evbuffer *buf, int fd, int howmuch) -{ - u_char *p; - size_t oldoff = buf->off; - int n = EVBUFFER_MAX_READ; - -#if defined(FIONREAD) -#ifdef WIN32 - long lng = (long)n; - if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) { -#else - if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) { -#endif - n = EVBUFFER_MAX_READ; - } else if (n > EVBUFFER_MAX_READ && n > howmuch) { - /* - * It's possible that a lot of data is available for - * reading. We do not want to exhaust resources - * before the reader has a chance to do something - * about it. If the reader does not tell us how much - * data we should read, we artifically limit it. - */ - if ((size_t)n > (buf->totallen << 2)) - n = (int)(buf->totallen << 2); - if (n < EVBUFFER_MAX_READ) - n = EVBUFFER_MAX_READ; - } -#endif - if (howmuch < 0 || howmuch > n) - howmuch = n; - - /* If we don't have FIONREAD, we might waste some space here */ - if (evbuffer_expand(buf, howmuch) == -1) - return (-1); - - /* We can append new data at this point */ - p = buf->buffer + buf->off; - -#ifndef WIN32 - n = read(fd, p, howmuch); -#else - n = recv(fd, p, howmuch, 0); -#endif - if (n == -1) - return (-1); - if (n == 0) - return (0); - - buf->off += n; - - /* Tell someone about changes in this buffer */ - if (buf->off != oldoff && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); - - return (n); -} - -int -evbuffer_write(struct evbuffer *buffer, int fd) -{ - int n; - -#ifndef WIN32 - n = write(fd, buffer->buffer, buffer->off); -#else - n = send(fd, buffer->buffer, (int)buffer->off, 0); -#endif - if (n == -1) - return (-1); - if (n == 0) - return (0); - evbuffer_drain(buffer, n); - - return (n); -} - -u_char * -evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len) -{ - u_char *search = buffer->buffer, *end = search + buffer->off; - u_char *p; - - while (search < end && - (p = memchr(search, *what, end - search)) != NULL) { - if (p + len > end) - break; - if (memcmp(p, what, len) == 0) - return (p); - search = p + 1; - } - - return (NULL); -} - -void evbuffer_setcb(struct evbuffer *buffer, - void (*cb)(struct evbuffer *, size_t, size_t, void *), - void *cbarg) -{ - buffer->cb = cb; - buffer->cbarg = cbarg; -} diff --git a/extra/libevent/compat/sys/_time.h b/extra/libevent/compat/sys/_time.h deleted file mode 100644 index 8cabb0d55e7..00000000000 --- a/extra/libevent/compat/sys/_time.h +++ /dev/null @@ -1,163 +0,0 @@ -/* $OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $ */ -/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */ - -/* - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)time.h 8.2 (Berkeley) 7/10/94 - */ - -#ifndef _SYS_TIME_H_ -#define _SYS_TIME_H_ - -#include - -/* - * Structure returned by gettimeofday(2) system call, - * and used in other calls. - */ -struct timeval { - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ -}; - -/* - * Structure defined by POSIX.1b to be like a timeval. - */ -struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* and nanoseconds */ -}; - -#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ - (ts)->tv_sec = (tv)->tv_sec; \ - (ts)->tv_nsec = (tv)->tv_usec * 1000; \ -} -#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (ts)->tv_sec; \ - (tv)->tv_usec = (ts)->tv_nsec / 1000; \ -} - -struct timezone { - int tz_minuteswest; /* minutes west of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; -#define DST_NONE 0 /* not on dst */ -#define DST_USA 1 /* USA style dst */ -#define DST_AUST 2 /* Australian style dst */ -#define DST_WET 3 /* Western European dst */ -#define DST_MET 4 /* Middle European dst */ -#define DST_EET 5 /* Eastern European dst */ -#define DST_CAN 6 /* Canada */ - -/* Operations on timevals. */ -#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 -#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) -#define timercmp(tvp, uvp, cmp) \ - (((tvp)->tv_sec == (uvp)->tv_sec) ? \ - ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ - ((tvp)->tv_sec cmp (uvp)->tv_sec)) -#define timeradd(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ - if ((vvp)->tv_usec >= 1000000) { \ - (vvp)->tv_sec++; \ - (vvp)->tv_usec -= 1000000; \ - } \ - } while (0) -#define timersub(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ - if ((vvp)->tv_usec < 0) { \ - (vvp)->tv_sec--; \ - (vvp)->tv_usec += 1000000; \ - } \ - } while (0) - -/* Operations on timespecs. */ -#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0 -#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) -#define timespeccmp(tsp, usp, cmp) \ - (((tsp)->tv_sec == (usp)->tv_sec) ? \ - ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ - ((tsp)->tv_sec cmp (usp)->tv_sec)) -#define timespecadd(tsp, usp, vsp) \ - do { \ - (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ - (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ - if ((vsp)->tv_nsec >= 1000000000L) { \ - (vsp)->tv_sec++; \ - (vsp)->tv_nsec -= 1000000000L; \ - } \ - } while (0) -#define timespecsub(tsp, usp, vsp) \ - do { \ - (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ - (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ - if ((vsp)->tv_nsec < 0) { \ - (vsp)->tv_sec--; \ - (vsp)->tv_nsec += 1000000000L; \ - } \ - } while (0) - -/* - * Names of the interval timers, and structure - * defining a timer setting. - */ -#define ITIMER_REAL 0 -#define ITIMER_VIRTUAL 1 -#define ITIMER_PROF 2 - -struct itimerval { - struct timeval it_interval; /* timer interval */ - struct timeval it_value; /* current value */ -}; - -/* - * Getkerninfo clock information structure - */ -struct clockinfo { - int hz; /* clock frequency */ - int tick; /* micro-seconds per hz tick */ - int tickadj; /* clock skew rate for adjtime() */ - int stathz; /* statistics clock frequency */ - int profhz; /* profiling clock frequency */ -}; - -#define CLOCK_REALTIME 0 -#define CLOCK_VIRTUAL 1 -#define CLOCK_PROF 2 - -#define TIMER_RELTIME 0x0 /* relative timer */ -#define TIMER_ABSTIME 0x1 /* absolute timer */ - -/* --- stuff got cut here - niels --- */ - -#endif /* !_SYS_TIME_H_ */ diff --git a/extra/libevent/compat/sys/queue.h b/extra/libevent/compat/sys/queue.h deleted file mode 100644 index c0956ddce43..00000000000 --- a/extra/libevent/compat/sys/queue.h +++ /dev/null @@ -1,488 +0,0 @@ -/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#ifndef WIN32 -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} -#endif - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ - if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head).cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head).cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/extra/libevent/compat/sys/tree.h b/extra/libevent/compat/sys/tree.h deleted file mode 100644 index 6a3381caee4..00000000000 --- a/extra/libevent/compat/sys/tree.h +++ /dev/null @@ -1,677 +0,0 @@ -/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ -/* - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _SYS_TREE_H_ -#define _SYS_TREE_H_ - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while ((__comp = (cmp)(elm, (head)->sph_root))) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-back tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ -} while (0) - -#define RB_SET_BLACKRED(black, red, field) do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ -} while (0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ -void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -struct type *name##_RB_REMOVE(struct name *, struct type *); \ -struct type *name##_RB_INSERT(struct name *, struct type *); \ -struct type *name##_RB_FIND(struct name *, struct type *); \ -struct type *name##_RB_NEXT(struct type *); \ -struct type *name##_RB_MINMAX(struct name *, int); \ - \ - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ -void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) && \ - RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ -} \ - \ -void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)))\ - RB_COLOR(oleft, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)))\ - RB_COLOR(oright, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ -} \ - \ -struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field))) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old)\ - RB_LEFT(RB_PARENT(old, field), field) = elm;\ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm;\ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field))); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#endif /* _SYS_TREE_H_ */ diff --git a/extra/libevent/devpoll.c b/extra/libevent/devpoll.c deleted file mode 100644 index 3a3ec7e64a8..00000000000 --- a/extra/libevent/devpoll.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_DEVPOLL - -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "event.h" -#include "event-internal.h" -#include "evsignal.h" -#include "log.h" - -/* due to limitations in the devpoll interface, we need to keep track of - * all file descriptors outself. - */ -struct evdevpoll { - struct event *evread; - struct event *evwrite; -}; - -struct devpollop { - struct evdevpoll *fds; - int nfds; - struct pollfd *events; - int nevents; - int dpfd; - struct pollfd *changes; - int nchanges; -}; - -static void *devpoll_init (struct event_base *); -static int devpoll_add (void *, struct event *); -static int devpoll_del (void *, struct event *); -static int devpoll_dispatch (struct event_base *, void *, struct timeval *); -static void devpoll_dealloc (struct event_base *, void *); - -struct eventop devpollops = { - "devpoll", - devpoll_init, - devpoll_add, - devpoll_del, - devpoll_dispatch, - devpoll_dealloc, - 1 /* need reinit */ -}; - -#define NEVENT 32000 - -static int -devpoll_commit(struct devpollop *devpollop) -{ - /* - * Due to a bug in Solaris, we have to use pwrite with an offset of 0. - * Write is limited to 2GB of data, until it will fail. - */ - if (pwrite(devpollop->dpfd, devpollop->changes, - sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) - return(-1); - - devpollop->nchanges = 0; - return(0); -} - -static int -devpoll_queue(struct devpollop *devpollop, int fd, int events) { - struct pollfd *pfd; - - if (devpollop->nchanges >= devpollop->nevents) { - /* - * Change buffer is full, must commit it to /dev/poll before - * adding more - */ - if (devpoll_commit(devpollop) != 0) - return(-1); - } - - pfd = &devpollop->changes[devpollop->nchanges++]; - pfd->fd = fd; - pfd->events = events; - pfd->revents = 0; - - return(0); -} - -static void * -devpoll_init(struct event_base *base) -{ - int dpfd, nfiles = NEVENT; - struct rlimit rl; - struct devpollop *devpollop; - - /* Disable devpoll when this environment variable is set */ - if (getenv("EVENT_NODEVPOLL")) - return (NULL); - - if (!(devpollop = calloc(1, sizeof(struct devpollop)))) - return (NULL); - - if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && - (unsigned long long) rl.rlim_cur != (unsigned long long) RLIM_INFINITY) - nfiles = rl.rlim_cur - 1; - - /* Initialize the kernel queue */ - if ((dpfd = open("/dev/poll", O_RDWR)) == -1) { - event_warn("open: /dev/poll"); - free(devpollop); - return (NULL); - } - - devpollop->dpfd = dpfd; - - /* Initialize fields */ - devpollop->events = calloc(nfiles, sizeof(struct pollfd)); - if (devpollop->events == NULL) { - free(devpollop); - close(dpfd); - return (NULL); - } - devpollop->nevents = nfiles; - - devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll)); - if (devpollop->fds == NULL) { - free(devpollop->events); - free(devpollop); - close(dpfd); - return (NULL); - } - devpollop->nfds = nfiles; - - devpollop->changes = calloc(nfiles, sizeof(struct pollfd)); - if (devpollop->changes == NULL) { - free(devpollop->fds); - free(devpollop->events); - free(devpollop); - close(dpfd); - return (NULL); - } - - evsignal_init(base); - - return (devpollop); -} - -static int -devpoll_recalc(struct event_base *base __attribute__((unused)), void *arg, int max) -{ - struct devpollop *devpollop = arg; - - if (max > devpollop->nfds) { - struct evdevpoll *fds; - int nfds; - - nfds = devpollop->nfds; - while (nfds < max) - nfds <<= 1; - - fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll)); - if (fds == NULL) { - event_warn("realloc"); - return (-1); - } - devpollop->fds = fds; - memset(fds + devpollop->nfds, 0, - (nfds - devpollop->nfds) * sizeof(struct evdevpoll)); - devpollop->nfds = nfds; - } - - return (0); -} - -static int -devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) -{ - struct devpollop *devpollop = arg; - struct pollfd *events = devpollop->events; - struct dvpoll dvp; - struct evdevpoll *evdp; - int i, res, timeout = -1; - - if (devpollop->nchanges) - devpoll_commit(devpollop); - - if (tv != NULL) - timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; - - dvp.dp_fds = devpollop->events; - dvp.dp_nfds = devpollop->nevents; - dvp.dp_timeout = timeout; - - res = ioctl(devpollop->dpfd, DP_POLL, &dvp); - - if (res == -1) { - if (errno != EINTR) { - event_warn("ioctl: DP_POLL"); - return (-1); - } - - evsignal_process(base); - return (0); - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - event_debug(("%s: devpoll_wait reports %d", __func__, res)); - - for (i = 0; i < res; i++) { - int which = 0; - int what = events[i].revents; - struct event *evread = NULL, *evwrite = NULL; - - assert(events[i].fd < devpollop->nfds); - evdp = &devpollop->fds[events[i].fd]; - - if (what & POLLHUP) - what |= POLLIN | POLLOUT; - else if (what & POLLERR) - what |= POLLIN | POLLOUT; - - if (what & POLLIN) { - evread = evdp->evread; - which |= EV_READ; - } - - if (what & POLLOUT) { - evwrite = evdp->evwrite; - which |= EV_WRITE; - } - - if (!which) - continue; - - if (evread != NULL && !(evread->ev_events & EV_PERSIST)) - event_del(evread); - if (evwrite != NULL && evwrite != evread && - !(evwrite->ev_events & EV_PERSIST)) - event_del(evwrite); - - if (evread != NULL) - event_active(evread, EV_READ, 1); - if (evwrite != NULL) - event_active(evwrite, EV_WRITE, 1); - } - - return (0); -} - - -static int -devpoll_add(void *arg, struct event *ev) -{ - struct devpollop *devpollop = arg; - struct evdevpoll *evdp; - int fd, events; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_add(ev)); - - fd = ev->ev_fd; - if (fd >= devpollop->nfds) { - /* Extend the file descriptor array as necessary */ - if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1) - return (-1); - } - evdp = &devpollop->fds[fd]; - - /* - * It's not necessary to OR the existing read/write events that we - * are currently interested in with the new event we are adding. - * The /dev/poll driver ORs any new events with the existing events - * that it has cached for the fd. - */ - - events = 0; - if (ev->ev_events & EV_READ) { - if (evdp->evread && evdp->evread != ev) { - /* There is already a different read event registered */ - return(-1); - } - events |= POLLIN; - } - - if (ev->ev_events & EV_WRITE) { - if (evdp->evwrite && evdp->evwrite != ev) { - /* There is already a different write event registered */ - return(-1); - } - events |= POLLOUT; - } - - if (devpoll_queue(devpollop, fd, events) != 0) - return(-1); - - /* Update events responsible */ - if (ev->ev_events & EV_READ) - evdp->evread = ev; - if (ev->ev_events & EV_WRITE) - evdp->evwrite = ev; - - return (0); -} - -static int -devpoll_del(void *arg, struct event *ev) -{ - struct devpollop *devpollop = arg; - struct evdevpoll *evdp; - int fd, events; - int needwritedelete = 1, needreaddelete = 1; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_del(ev)); - - fd = ev->ev_fd; - if (fd >= devpollop->nfds) - return (0); - evdp = &devpollop->fds[fd]; - - events = 0; - if (ev->ev_events & EV_READ) - events |= POLLIN; - if (ev->ev_events & EV_WRITE) - events |= POLLOUT; - - /* - * The only way to remove an fd from the /dev/poll monitored set is - * to use POLLREMOVE by itself. This removes ALL events for the fd - * provided so if we care about two events and are only removing one - * we must re-add the other event after POLLREMOVE. - */ - - if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) - return(-1); - - if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { - /* - * We're not deleting all events, so we must resubmit the - * event that we are still interested in if one exists. - */ - - if ((events & POLLIN) && evdp->evwrite != NULL) { - /* Deleting read, still care about write */ - devpoll_queue(devpollop, fd, POLLOUT); - needwritedelete = 0; - } else if ((events & POLLOUT) && evdp->evread != NULL) { - /* Deleting write, still care about read */ - devpoll_queue(devpollop, fd, POLLIN); - needreaddelete = 0; - } - } - - if (needreaddelete) - evdp->evread = NULL; - if (needwritedelete) - evdp->evwrite = NULL; - - return (0); -} - -static void -devpoll_dealloc(struct event_base *base, void *arg) -{ - struct devpollop *devpollop = arg; - - evsignal_dealloc(base); - if (devpollop->fds) - free(devpollop->fds); - if (devpollop->events) - free(devpollop->events); - if (devpollop->changes) - free(devpollop->changes); - if (devpollop->dpfd >= 0) - close(devpollop->dpfd); - - memset(devpollop, 0, sizeof(struct devpollop)); - free(devpollop); -} - -#endif /* HAVE_DEVPOLL */ diff --git a/extra/libevent/epoll.c b/extra/libevent/epoll.c deleted file mode 100644 index 93ed5b83aa4..00000000000 --- a/extra/libevent/epoll.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2000-2003 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_EPOLL - -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_FCNTL_H -#include -#endif - -#include "event.h" -#include "event-internal.h" -#include "evsignal.h" -#include "log.h" - -/* due to limitations in the epoll interface, we need to keep track of - * all file descriptors outself. - */ -struct evepoll { - struct event *evread; - struct event *evwrite; -}; - -struct epollop { - struct evepoll *fds; - int nfds; - struct epoll_event *events; - int nevents; - int epfd; -}; - -static void *epoll_init (struct event_base *); -static int epoll_add (void *, struct event *); -static int epoll_del (void *, struct event *); -static int epoll_dispatch (struct event_base *, void *, struct timeval *); -static void epoll_dealloc (struct event_base *, void *); - -struct eventop epollops = { - "epoll", - epoll_init, - epoll_add, - epoll_del, - epoll_dispatch, - epoll_dealloc, - 1 /* need reinit */ -}; - -#ifdef HAVE_SETFD -#define FD_CLOSEONEXEC(x) do { \ - if (fcntl(x, F_SETFD, 1) == -1) \ - event_warn("fcntl(%d, F_SETFD)", x); \ -} while (0) -#else -#define FD_CLOSEONEXEC(x) -#endif - -#define NEVENT 32000 - -static void * -epoll_init(struct event_base *base) -{ - int epfd, nfiles = NEVENT; - struct rlimit rl; - struct epollop *epollop; - - /* Disable epollueue when this environment variable is set */ - if (getenv("EVENT_NOEPOLL")) - return (NULL); - - if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && - rl.rlim_cur != RLIM_INFINITY) { - /* - * Solaris is somewhat retarded - it's important to drop - * backwards compatibility when making changes. So, don't - * dare to put rl.rlim_cur here. - */ - nfiles = rl.rlim_cur - 1; - } - - /* Initalize the kernel queue */ - - if ((epfd = epoll_create(nfiles)) == -1) { - event_warn("epoll_create"); - return (NULL); - } - - FD_CLOSEONEXEC(epfd); - - if (!(epollop = calloc(1, sizeof(struct epollop)))) - return (NULL); - - epollop->epfd = epfd; - - /* Initalize fields */ - epollop->events = malloc(nfiles * sizeof(struct epoll_event)); - if (epollop->events == NULL) { - free(epollop); - return (NULL); - } - epollop->nevents = nfiles; - - epollop->fds = calloc(nfiles, sizeof(struct evepoll)); - if (epollop->fds == NULL) { - free(epollop->events); - free(epollop); - return (NULL); - } - epollop->nfds = nfiles; - - evsignal_init(base); - - return (epollop); -} - -static int -epoll_recalc(struct event_base *base __attribute__((unused)), void *arg, - int max) -{ - struct epollop *epollop = arg; - - if (max > epollop->nfds) { - struct evepoll *fds; - int nfds; - - nfds = epollop->nfds; - while (nfds < max) - nfds <<= 1; - - fds = realloc(epollop->fds, nfds * sizeof(struct evepoll)); - if (fds == NULL) { - event_warn("realloc"); - return (-1); - } - epollop->fds = fds; - memset(fds + epollop->nfds, 0, - (nfds - epollop->nfds) * sizeof(struct evepoll)); - epollop->nfds = nfds; - } - - return (0); -} - -static int -epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) -{ - struct epollop *epollop = arg; - struct epoll_event *events = epollop->events; - struct evepoll *evep; - int i, res, timeout = -1; - - if (tv != NULL) - timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; - - res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); - - if (res == -1) { - if (errno != EINTR) { - event_warn("epoll_wait"); - return (-1); - } - - evsignal_process(base); - return (0); - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - event_debug(("%s: epoll_wait reports %d", __func__, res)); - - for (i = 0; i < res; i++) { - int what = events[i].events; - struct event *evread = NULL, *evwrite = NULL; - - evep = (struct evepoll *)events[i].data.ptr; - - if (what & (EPOLLHUP|EPOLLERR)) { - evread = evep->evread; - evwrite = evep->evwrite; - } else { - if (what & EPOLLIN) { - evread = evep->evread; - } - - if (what & EPOLLOUT) { - evwrite = evep->evwrite; - } - } - - if (!(evread||evwrite)) - continue; - - if (evread != NULL) - event_active(evread, EV_READ, 1); - if (evwrite != NULL) - event_active(evwrite, EV_WRITE, 1); - } - - return (0); -} - - -static int -epoll_add(void *arg, struct event *ev) -{ - struct epollop *epollop = arg; - struct epoll_event epev = {0, {0}}; - struct evepoll *evep; - int fd, op, events; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_add(ev)); - - fd = ev->ev_fd; - if (fd >= epollop->nfds) { - /* Extent the file descriptor array as necessary */ - if (epoll_recalc(ev->ev_base, epollop, fd) == -1) - return (-1); - } - evep = &epollop->fds[fd]; - op = EPOLL_CTL_ADD; - events = 0; - if (evep->evread != NULL) { - events |= EPOLLIN; - op = EPOLL_CTL_MOD; - } - if (evep->evwrite != NULL) { - events |= EPOLLOUT; - op = EPOLL_CTL_MOD; - } - - if (ev->ev_events & EV_READ) - events |= EPOLLIN; - if (ev->ev_events & EV_WRITE) - events |= EPOLLOUT; - - epev.data.ptr = evep; - epev.events = events; - if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1) - return (-1); - - /* Update events responsible */ - if (ev->ev_events & EV_READ) - evep->evread = ev; - if (ev->ev_events & EV_WRITE) - evep->evwrite = ev; - - return (0); -} - -static int -epoll_del(void *arg, struct event *ev) -{ - struct epollop *epollop = arg; - struct epoll_event epev = {0, {0}}; - struct evepoll *evep; - int fd, events, op; - int needwritedelete = 1, needreaddelete = 1; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_del(ev)); - - fd = ev->ev_fd; - if (fd >= epollop->nfds) - return (0); - evep = &epollop->fds[fd]; - - op = EPOLL_CTL_DEL; - events = 0; - - if (ev->ev_events & EV_READ) - events |= EPOLLIN; - if (ev->ev_events & EV_WRITE) - events |= EPOLLOUT; - - if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) { - if ((events & EPOLLIN) && evep->evwrite != NULL) { - needwritedelete = 0; - events = EPOLLOUT; - op = EPOLL_CTL_MOD; - } else if ((events & EPOLLOUT) && evep->evread != NULL) { - needreaddelete = 0; - events = EPOLLIN; - op = EPOLL_CTL_MOD; - } - } - - epev.events = events; - epev.data.ptr = evep; - - if (needreaddelete) - evep->evread = NULL; - if (needwritedelete) - evep->evwrite = NULL; - - if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1) - return (-1); - - return (0); -} - -static void -epoll_dealloc(struct event_base *base, void *arg) -{ - struct epollop *epollop = arg; - - evsignal_dealloc(base); - if (epollop->fds) - free(epollop->fds); - if (epollop->events) - free(epollop->events); - if (epollop->epfd >= 0) - close(epollop->epfd); - - memset(epollop, 0, sizeof(struct epollop)); - free(epollop); -} - -#endif /* HAVE_EPOLL */ diff --git a/extra/libevent/epoll_sub.c b/extra/libevent/epoll_sub.c deleted file mode 100644 index 95daac3b276..00000000000 --- a/extra/libevent/epoll_sub.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2003 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_EPOLL - -#include - -#include -#include -#include -#include -#include - -int -epoll_create(int size) -{ - return (syscall(__NR_epoll_create, size)); -} - -int -epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) -{ - - return (syscall(__NR_epoll_ctl, epfd, op, fd, event)); -} - -int -epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) -{ - return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout)); -} - -#endif /* HAVE_EPOLL */ diff --git a/extra/libevent/evbuffer.c b/extra/libevent/evbuffer.c deleted file mode 100644 index 97dc40c0e41..00000000000 --- a/extra/libevent/evbuffer.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (c) 2002-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#include -#include -#include -#include -#ifdef HAVE_STDARG_H -#include -#endif - -#ifdef WIN32 -#include -#endif - -#include "evutil.h" -#include "event.h" - -/* prototypes */ - -void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t); -void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); - -static int -bufferevent_add(struct event *ev, int timeout) -{ - struct timeval tv, *ptv = NULL; - - if (timeout) { - evutil_timerclear(&tv); - tv.tv_sec = timeout; - ptv = &tv; - } - - return (event_add(ev, ptv)); -} - -/* - * This callback is executed when the size of the input buffer changes. - * We use it to apply back pressure on the reading side. - */ - -void -bufferevent_read_pressure_cb(struct evbuffer *buf, - size_t old __attribute__((unused)), size_t now, - void *arg) -{ - struct bufferevent *bufev = arg; - /* - * If we are below the watermark then reschedule reading if it's - * still enabled. - */ - if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) { - evbuffer_setcb(buf, NULL, NULL); - - if (bufev->enabled & EV_READ) - bufferevent_add(&bufev->ev_read, bufev->timeout_read); - } -} - -static void -bufferevent_readcb(int fd, short event, void *arg) -{ - struct bufferevent *bufev = arg; - int res = 0; - short what = EVBUFFER_READ; - size_t len; - int howmuch = -1; - - if (event == EV_TIMEOUT) { - what |= EVBUFFER_TIMEOUT; - goto error; - } - - /* - * If we have a high watermark configured then we don't want to - * read more data than would make us reach the watermark. - */ - if (bufev->wm_read.high != 0) - howmuch = (int)bufev->wm_read.high; - - res = evbuffer_read(bufev->input, fd, howmuch); - if (res == -1) { - if (errno == EAGAIN || errno == EINTR) - goto reschedule; - /* error case */ - what |= EVBUFFER_ERROR; - } else if (res == 0) { - /* eof case */ - what |= EVBUFFER_EOF; - } - - if (res <= 0) - goto error; - - bufferevent_add(&bufev->ev_read, bufev->timeout_read); - - /* See if this callbacks meets the water marks */ - len = EVBUFFER_LENGTH(bufev->input); - if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) - return; - if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) { - struct evbuffer *buf = bufev->input; - event_del(&bufev->ev_read); - - /* Now schedule a callback for us */ - evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); - return; - } - - /* Invoke the user callback - must always be called last */ - if (bufev->readcb != NULL) - (*bufev->readcb)(bufev, bufev->cbarg); - return; - - reschedule: - bufferevent_add(&bufev->ev_read, bufev->timeout_read); - return; - - error: - (*bufev->errorcb)(bufev, what, bufev->cbarg); -} - -static void -bufferevent_writecb(int fd, short event, void *arg) -{ - struct bufferevent *bufev = arg; - int res = 0; - short what = EVBUFFER_WRITE; - - if (event == EV_TIMEOUT) { - what |= EVBUFFER_TIMEOUT; - goto error; - } - - if (EVBUFFER_LENGTH(bufev->output)) { - res = evbuffer_write(bufev->output, fd); - if (res == -1) { -#ifndef WIN32 -/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not - *set errno. thus this error checking is not portable*/ - if (errno == EAGAIN || - errno == EINTR || - errno == EINPROGRESS) - goto reschedule; - /* error case */ - what |= EVBUFFER_ERROR; - -#else - goto reschedule; -#endif - - } else if (res == 0) { - /* eof case */ - what |= EVBUFFER_EOF; - } - if (res <= 0) - goto error; - } - - if (EVBUFFER_LENGTH(bufev->output) != 0) - bufferevent_add(&bufev->ev_write, bufev->timeout_write); - - /* - * Invoke the user callback if our buffer is drained or below the - * low watermark. - */ - if (bufev->writecb != NULL && - EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) - (*bufev->writecb)(bufev, bufev->cbarg); - - return; - - reschedule: - if (EVBUFFER_LENGTH(bufev->output) != 0) - bufferevent_add(&bufev->ev_write, bufev->timeout_write); - return; - - error: - (*bufev->errorcb)(bufev, what, bufev->cbarg); -} - -/* - * Create a new buffered event object. - * - * The read callback is invoked whenever we read new data. - * The write callback is invoked whenever the output buffer is drained. - * The error callback is invoked on a write/read error or on EOF. - * - * Both read and write callbacks maybe NULL. The error callback is not - * allowed to be NULL and have to be provided always. - */ - -struct bufferevent * -bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, - everrorcb errorcb, void *cbarg) -{ - struct bufferevent *bufev; - - if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL) - return (NULL); - - if ((bufev->input = evbuffer_new()) == NULL) { - free(bufev); - return (NULL); - } - - if ((bufev->output = evbuffer_new()) == NULL) { - evbuffer_free(bufev->input); - free(bufev); - return (NULL); - } - - event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev); - event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev); - - bufev->readcb = readcb; - bufev->writecb = writecb; - bufev->errorcb = errorcb; - - bufev->cbarg = cbarg; - - /* - * Set to EV_WRITE so that using bufferevent_write is going to - * trigger a callback. Reading needs to be explicitly enabled - * because otherwise no data will be available. - */ - bufev->enabled = EV_WRITE; - - return (bufev); -} - -int -bufferevent_priority_set(struct bufferevent *bufev, int priority) -{ - if (event_priority_set(&bufev->ev_read, priority) == -1) - return (-1); - if (event_priority_set(&bufev->ev_write, priority) == -1) - return (-1); - - return (0); -} - -/* Closing the file descriptor is the responsibility of the caller */ - -void -bufferevent_free(struct bufferevent *bufev) -{ - event_del(&bufev->ev_read); - event_del(&bufev->ev_write); - - evbuffer_free(bufev->input); - evbuffer_free(bufev->output); - - free(bufev); -} - -/* - * Returns 0 on success; - * -1 on failure. - */ - -int -bufferevent_write(struct bufferevent *bufev, const void *data, size_t size) -{ - int res; - - res = evbuffer_add(bufev->output, data, size); - - if (res == -1) - return (res); - - /* If everything is okay, we need to schedule a write */ - if (size > 0 && (bufev->enabled & EV_WRITE)) - bufferevent_add(&bufev->ev_write, bufev->timeout_write); - - return (res); -} - -int -bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf) -{ - int res; - - res = bufferevent_write(bufev, buf->buffer, buf->off); - if (res != -1) - evbuffer_drain(buf, buf->off); - - return (res); -} - -size_t -bufferevent_read(struct bufferevent *bufev, void *data, size_t size) -{ - struct evbuffer *buf = bufev->input; - - if (buf->off < size) - size = buf->off; - - /* Copy the available data to the user buffer */ - memcpy(data, buf->buffer, size); - - if (size) - evbuffer_drain(buf, size); - - return (size); -} - -int -bufferevent_enable(struct bufferevent *bufev, short event) -{ - if (event & EV_READ) { - if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1) - return (-1); - } - if (event & EV_WRITE) { - if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1) - return (-1); - } - - bufev->enabled |= event; - return (0); -} - -int -bufferevent_disable(struct bufferevent *bufev, short event) -{ - if (event & EV_READ) { - if (event_del(&bufev->ev_read) == -1) - return (-1); - } - if (event & EV_WRITE) { - if (event_del(&bufev->ev_write) == -1) - return (-1); - } - - bufev->enabled &= ~event; - return (0); -} - -/* - * Sets the read and write timeout for a buffered event. - */ - -void -bufferevent_settimeout(struct bufferevent *bufev, - int timeout_read, int timeout_write) { - bufev->timeout_read = timeout_read; - bufev->timeout_write = timeout_write; -} - -/* - * Sets the water marks - */ - -void -bufferevent_setwatermark(struct bufferevent *bufev, short events, - size_t lowmark, size_t highmark) -{ - if (events & EV_READ) { - bufev->wm_read.low = lowmark; - bufev->wm_read.high = highmark; - } - - if (events & EV_WRITE) { - bufev->wm_write.low = lowmark; - bufev->wm_write.high = highmark; - } - - /* If the watermarks changed then see if we should call read again */ - bufferevent_read_pressure_cb(bufev->input, - 0, EVBUFFER_LENGTH(bufev->input), bufev); -} - -int -bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) -{ - int res; - - res = event_base_set(base, &bufev->ev_read); - if (res == -1) - return (res); - - res = event_base_set(base, &bufev->ev_write); - return (res); -} diff --git a/extra/libevent/evdns.c b/extra/libevent/evdns.c deleted file mode 100644 index de08a18ca19..00000000000 --- a/extra/libevent/evdns.c +++ /dev/null @@ -1,3150 +0,0 @@ -/* $Id: evdns.c 6979 2006-08-04 18:31:13Z nickm $ */ - -/* The original version of this module was written by Adam Langley; for - * a history of modifications, check out the subversion logs. - * - * When editing this module, try to keep it re-mergeable by Adam. Don't - * reformat the whitespace, add Tor dependencies, or so on. - * - * TODO: - * - Support IPv6 and PTR records. - * - Replace all externally visible magic numbers with #defined constants. - * - Write doccumentation for APIs of all external functions. - */ - -/* Async DNS Library - * Adam Langley - * http://www.imperialviolet.org/eventdns.html - * Public Domain code - * - * This software is Public Domain. To view a copy of the public domain dedication, - * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to - * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. - * - * I ask and expect, but do not require, that all derivative works contain an - * attribution similar to: - * Parts developed by Adam Langley - * - * You may wish to replace the word "Parts" with something else depending on - * the amount of original code. - * - * (Derivative works does not include programs which link against, run or include - * the source verbatim in their source distributions) - * - * Version: 0.1b - */ - -#include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#include "misc.h" -#endif - -#ifdef DNS_USE_FTIME_FOR_ID -#include -#endif - -#ifndef DNS_USE_CPU_CLOCK_FOR_ID -#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID -#ifndef DNS_USE_OPENSSL_FOR_ID -#ifndef DNS_USE_FTIME_FOR_ID -#error Must configure at least one id generation method. -#error Please see the documentation. -#endif -#endif -#endif -#endif - -/* #define _POSIX_C_SOURCE 200507 */ -#define _GNU_SOURCE - -#ifdef DNS_USE_CPU_CLOCK_FOR_ID -#ifdef DNS_USE_OPENSSL_FOR_ID -#error Multiple id options selected -#endif -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID -#error Multiple id options selected -#endif -#include -#endif - -#ifdef DNS_USE_OPENSSL_FOR_ID -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID -#error Multiple id options selected -#endif -#include -#endif - -#define _FORTIFY_SOURCE 3 - -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_STDINT_H -#include -#endif -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#include -#include -#include -#include - -#include "evdns.h" -#include "evutil.h" -#include "log.h" -#ifdef WIN32 -#include -#include -#include -#include -#else -#include -#include -#include -#endif - -#ifdef HAVE_NETINET_IN6_H -#include -#endif - -#define EVDNS_LOG_DEBUG 0 -#define EVDNS_LOG_WARN 1 - -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX 255 -#endif - -#include - -#undef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) - -#ifdef __USE_ISOC99B -/* libevent doesn't work without this */ -typedef ev_uint8_t u_char; -typedef unsigned int uint; -#endif -#include - -#define u64 ev_uint64_t -#define u32 ev_uint32_t -#define u16 ev_uint16_t -#define u8 ev_uint8_t - -#ifdef WIN32 -#define snprintf _snprintf -#define open _open -#define read _read -#define close _close -#define strdup _strdup -#endif - -#define MAX_ADDRS 32 /* maximum number of addresses from a single packet */ -/* which we bother recording */ - -#define TYPE_A EVDNS_TYPE_A -#define TYPE_CNAME 5 -#define TYPE_PTR EVDNS_TYPE_PTR -#define TYPE_AAAA EVDNS_TYPE_AAAA - -#define CLASS_INET EVDNS_CLASS_INET - -struct request { - u8 *request; /* the dns packet data */ - unsigned int request_len; - int reissue_count; - int tx_count; /* the number of times that this packet has been sent */ - unsigned int request_type; /* TYPE_PTR or TYPE_A */ - void *user_pointer; /* the pointer given to us for this request */ - evdns_callback_type user_callback; - struct nameserver *ns; /* the server which we last sent it */ - - /* elements used by the searching code */ - int search_index; - struct search_state *search_state; - char *search_origname; /* needs to be free()ed */ - int search_flags; - - /* these objects are kept in a circular list */ - struct request *next, *prev; - - struct event timeout_event; - - u16 trans_id; /* the transaction id */ - char request_appended; /* true if the request pointer is data which follows this struct */ - char transmit_me; /* needs to be transmitted */ -}; - -#ifndef HAVE_STRUCT_IN6_ADDR -struct in6_addr { - u8 s6_addr[16]; -}; -#endif - -struct reply { - unsigned int type; - unsigned int have_answer; - union { - struct { - u32 addrcount; - u32 addresses[MAX_ADDRS]; - } a; - struct { - u32 addrcount; - struct in6_addr addresses[MAX_ADDRS]; - } aaaa; - struct { - char name[HOST_NAME_MAX]; - } ptr; - } data; -}; - -struct nameserver { - int socket; /* a connected UDP socket */ - u32 address; - int failed_times; /* number of times which we have given this server a chance */ - int timedout; /* number of times in a row a request has timed out */ - struct event event; - /* these objects are kept in a circular list */ - struct nameserver *next, *prev; - struct event timeout_event; /* used to keep the timeout for */ - /* when we next probe this server. */ - /* Valid if state == 0 */ - char state; /* zero if we think that this server is down */ - char choked; /* true if we have an EAGAIN from this server's socket */ - char write_waiting; /* true if we are waiting for EV_WRITE events */ -}; - -static struct request *req_head = NULL, *req_waiting_head = NULL; -static struct nameserver *server_head = NULL; - -/* Represents a local port where we're listening for DNS requests. Right now, */ -/* only UDP is supported. */ -struct evdns_server_port { - int socket; /* socket we use to read queries and write replies. */ - int refcnt; /* reference count. */ - char choked; /* Are we currently blocked from writing? */ - char closing; /* Are we trying to close this port, pending writes? */ - evdns_request_callback_fn_type user_callback; /* Fn to handle requests */ - void *user_data; /* Opaque pointer passed to user_callback */ - struct event event; /* Read/write event */ - /* circular list of replies that we want to write. */ - struct server_request *pending_replies; -}; - -/* Represents part of a reply being built. (That is, a single RR.) */ -struct server_reply_item { - struct server_reply_item *next; /* next item in sequence. */ - char *name; /* name part of the RR */ - u16 type : 16; /* The RR type */ - u16 class : 16; /* The RR class (usually CLASS_INET) */ - u32 ttl; /* The RR TTL */ - char is_name; /* True iff data is a label */ - u16 datalen; /* Length of data; -1 if data is a label */ - void *data; /* The contents of the RR */ -}; - -/* Represents a request that we've received as a DNS server, and holds */ -/* the components of the reply as we're constructing it. */ -struct server_request { - /* Pointers to the next and previous entries on the list of replies */ - /* that we're waiting to write. Only set if we have tried to respond */ - /* and gotten EAGAIN. */ - struct server_request *next_pending; - struct server_request *prev_pending; - - u16 trans_id; /* Transaction id. */ - struct evdns_server_port *port; /* Which port received this request on? */ - struct sockaddr_storage addr; /* Where to send the response */ - socklen_t addrlen; /* length of addr */ - - int n_answer; /* how many answer RRs have been set? */ - int n_authority; /* how many authority RRs have been set? */ - int n_additional; /* how many additional RRs have been set? */ - - struct server_reply_item *answer; /* linked list of answer RRs */ - struct server_reply_item *authority; /* linked list of authority RRs */ - struct server_reply_item *additional; /* linked list of additional RRs */ - - /* Constructed response. Only set once we're ready to send a reply. */ - /* Once this is set, the RR fields are cleared, and no more should be set. */ - char *response; - size_t response_len; - - /* Caller-visible fields: flags, questions. */ - struct evdns_server_request base; -}; - -/* helper macro */ -#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0)) - -/* Given a pointer to an evdns_server_request, get the corresponding */ -/* server_request. */ -#define TO_SERVER_REQUEST(base_ptr) \ - ((struct server_request*) \ - (((char*)(base_ptr) - OFFSET_OF(struct server_request, base)))) - -/* The number of good nameservers that we have */ -static int global_good_nameservers = 0; - -/* inflight requests are contained in the req_head list */ -/* and are actually going out across the network */ -static int global_requests_inflight = 0; -/* requests which aren't inflight are in the waiting list */ -/* and are counted here */ -static int global_requests_waiting = 0; - -static int global_max_requests_inflight = 64; - -static struct timeval global_timeout = {5, 0}; /* 5 seconds */ -static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ -static int global_max_retransmits = 3; /* number of times we'll retransmit a request which timed out */ -/* number of timeouts in a row before we consider this server to be down */ -static int global_max_nameserver_timeout = 3; - -/* These are the timeout values for nameservers. If we find a nameserver is down */ -/* we try to probe it at intervals as given below. Values are in seconds. */ -static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}}; -static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval); - -static struct nameserver *nameserver_pick(void); -static void evdns_request_insert(struct request *req, struct request **head); -static void nameserver_ready_callback(int fd, short events, void *arg); -static int evdns_transmit(void); -static int evdns_request_transmit(struct request *req); -static void nameserver_send_probe(struct nameserver *const ns); -static void search_request_finished(struct request *const); -static int search_try_next(struct request *const req); -static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg); -static void evdns_requests_pump_waiting_queue(void); -static u16 transaction_id_pick(void); -static struct request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr); -static void request_submit(struct request *const req); - -static int server_request_free(struct server_request *req); -static void server_request_free_answers(struct server_request *req); -static void server_port_free(struct evdns_server_port *port); -static void server_port_ready_callback(int fd, short events, void *arg); - -static int strtoint(const char *const str); - -#ifdef WIN32 -static int -last_error(int sock) -{ - int optval, optvallen=sizeof(optval); - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK && sock >= 0) { - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval, - &optvallen)) - return err; - if (optval) - return optval; - } - return err; - -} -static int -error_is_eagain(int err) -{ - return err == EAGAIN || err == WSAEWOULDBLOCK; -} -static int -inet_aton(const char *c, struct in_addr *addr) -{ - ev_uint32_t r; - if (strcmp(c, "255.255.255.255") == 0) { - addr->s_addr = 0xffffffffu; - } else { - r = inet_addr(c); - if (r == INADDR_NONE) - return 0; - addr->s_addr = r; - } - return 1; -} -#else -#define last_error(sock) (errno) -#define error_is_eagain(err) ((err) == EAGAIN) -#endif -#define CLOSE_SOCKET(s) EVUTIL_CLOSESOCKET(s) - -#define ISSPACE(c) isspace((int)(unsigned char)(c)) -#define ISDIGIT(c) isdigit((int)(unsigned char)(c)) - -static const char * -debug_ntoa(u32 address) -{ - static char buf[32]; - u32 a = ntohl(address); - snprintf(buf, sizeof(buf), "%d.%d.%d.%d", - (int)(u8)((a>>24)&0xff), - (int)(u8)((a>>16)&0xff), - (int)(u8)((a>>8 )&0xff), - (int)(u8)((a )&0xff)); - return buf; -} - -static evdns_debug_log_fn_type evdns_log_fn = NULL; - -void -evdns_set_log_fn(evdns_debug_log_fn_type fn) -{ - evdns_log_fn = fn; -} - -#ifdef __GNUC__ -#define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3))) -#else -#define EVDNS_LOG_CHECK -#endif - -static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK; -static void -_evdns_log(int warn, const char *fmt, ...) -{ - va_list args; - static char buf[512]; - if (!evdns_log_fn) - return; - va_start(args,fmt); -#ifdef WIN32 - _vsnprintf(buf, sizeof(buf), fmt, args); -#else - vsnprintf(buf, sizeof(buf), fmt, args); -#endif - buf[sizeof(buf)-1] = '\0'; - evdns_log_fn(warn, buf); - va_end(args); -} - -#define log _evdns_log - -/* This walks the list of inflight requests to find the */ -/* one with a matching transaction id. Returns NULL on */ -/* failure */ -static struct request * -request_find_from_trans_id(u16 trans_id) { - struct request *req = req_head, *const started_at = req_head; - - if (req) { - do { - if (req->trans_id == trans_id) return req; - req = req->next; - } while (req != started_at); - } - - return NULL; -} - -/* a libevent callback function which is called when a nameserver */ -/* has gone down and we want to test if it has came back to life yet */ -static void -nameserver_prod_callback(int fd, short events, void *arg) { - struct nameserver *const ns = (struct nameserver *) arg; - (void)fd; - (void)events; - - nameserver_send_probe(ns); -} - -/* a libevent callback which is called when a nameserver probe (to see if */ -/* it has come back to life) times out. We increment the count of failed_times */ -/* and wait longer to send the next probe packet. */ -static void -nameserver_probe_failed(struct nameserver *const ns) { - const struct timeval * timeout; - (void) evtimer_del(&ns->timeout_event); - if (ns->state == 1) { - /* This can happen if the nameserver acts in a way which makes us mark */ - /* it as bad and then starts sending good replies. */ - return; - } - - timeout = - &global_nameserver_timeouts[MIN(ns->failed_times, - global_nameserver_timeouts_length - 1)]; - ns->failed_times++; - - evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns); - if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) { - log(EVDNS_LOG_WARN, - "Error from libevent when adding timer event for %s", - debug_ntoa(ns->address)); - /* ???? Do more? */ - } -} - -/* called when a nameserver has been deemed to have failed. For example, too */ -/* many packets have timed out etc */ -static void -nameserver_failed(struct nameserver *const ns, const char *msg) { - struct request *req, *started_at; - /* if this nameserver has already been marked as failed */ - /* then don't do anything */ - if (!ns->state) return; - - log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s", - debug_ntoa(ns->address), msg); - global_good_nameservers--; - assert(global_good_nameservers >= 0); - if (global_good_nameservers == 0) { - log(EVDNS_LOG_WARN, "All nameservers have failed"); - } - - ns->state = 0; - ns->failed_times = 1; - - evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns); - if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) { - log(EVDNS_LOG_WARN, - "Error from libevent when adding timer event for %s", - debug_ntoa(ns->address)); - /* ???? Do more? */ - } - - /* walk the list of inflight requests to see if any can be reassigned to */ - /* a different server. Requests in the waiting queue don't have a */ - /* nameserver assigned yet */ - - /* if we don't have *any* good nameservers then there's no point */ - /* trying to reassign requests to one */ - if (!global_good_nameservers) return; - - req = req_head; - started_at = req_head; - if (req) { - do { - if (req->tx_count == 0 && req->ns == ns) { - /* still waiting to go out, can be moved */ - /* to another server */ - req->ns = nameserver_pick(); - } - req = req->next; - } while (req != started_at); - } -} - -static void -nameserver_up(struct nameserver *const ns) { - if (ns->state) return; - log(EVDNS_LOG_WARN, "Nameserver %s is back up", - debug_ntoa(ns->address)); - evtimer_del(&ns->timeout_event); - ns->state = 1; - ns->failed_times = 0; - ns->timedout = 0; - global_good_nameservers++; -} - -static void -request_trans_id_set(struct request *const req, const u16 trans_id) { - req->trans_id = trans_id; - *((u16 *) req->request) = htons(trans_id); -} - -/* Called to remove a request from a list and dealloc it. */ -/* head is a pointer to the head of the list it should be */ -/* removed from or NULL if the request isn't in a list. */ -static void -request_finished(struct request *const req, struct request **head) { - if (head) { - if (req->next == req) { - /* only item in the list */ - *head = NULL; - } else { - req->next->prev = req->prev; - req->prev->next = req->next; - if (*head == req) *head = req->next; - } - } - - log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx", - (unsigned long) req); - evtimer_del(&req->timeout_event); - - search_request_finished(req); - global_requests_inflight--; - - if (!req->request_appended) { - /* need to free the request data on it's own */ - free(req->request); - } else { - /* the request data is appended onto the header */ - /* so everything gets free()ed when we: */ - } - - free(req); - - evdns_requests_pump_waiting_queue(); -} - -/* This is called when a server returns a funny error code. */ -/* We try the request again with another server. */ -/* */ -/* return: */ -/* 0 ok */ -/* 1 failed/reissue is pointless */ -static int -request_reissue(struct request *req) { - const struct nameserver *const last_ns = req->ns; - /* the last nameserver should have been marked as failing */ - /* by the caller of this function, therefore pick will try */ - /* not to return it */ - req->ns = nameserver_pick(); - if (req->ns == last_ns) { - /* ... but pick did return it */ - /* not a lot of point in trying again with the */ - /* same server */ - return 1; - } - - req->reissue_count++; - req->tx_count = 0; - req->transmit_me = 1; - - return 0; -} - -/* this function looks for space on the inflight queue and promotes */ -/* requests from the waiting queue if it can. */ -static void -evdns_requests_pump_waiting_queue(void) { - while (global_requests_inflight < global_max_requests_inflight && - global_requests_waiting) { - struct request *req; - /* move a request from the waiting queue to the inflight queue */ - assert(req_waiting_head); - if (req_waiting_head->next == req_waiting_head) { - /* only one item in the queue */ - req = req_waiting_head; - req_waiting_head = NULL; - } else { - req = req_waiting_head; - req->next->prev = req->prev; - req->prev->next = req->next; - req_waiting_head = req->next; - } - - global_requests_waiting--; - global_requests_inflight++; - - req->ns = nameserver_pick(); - request_trans_id_set(req, transaction_id_pick()); - - evdns_request_insert(req, &req_head); - evdns_request_transmit(req); - evdns_transmit(); - } -} - -static void -reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) { - switch (req->request_type) { - case TYPE_A: - if (reply) - req->user_callback(DNS_ERR_NONE, DNS_IPv4_A, - reply->data.a.addrcount, ttl, - reply->data.a.addresses, - req->user_pointer); - else - req->user_callback(err, 0, 0, 0, NULL, req->user_pointer); - return; - case TYPE_PTR: - if (reply) { - char *name = reply->data.ptr.name; - req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl, - &name, req->user_pointer); - } else { - req->user_callback(err, 0, 0, 0, NULL, - req->user_pointer); - } - return; - case TYPE_AAAA: - if (reply) - req->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA, - reply->data.aaaa.addrcount, ttl, - reply->data.aaaa.addresses, - req->user_pointer); - else - req->user_callback(err, 0, 0, 0, NULL, req->user_pointer); - return; - } - assert(0); -} - -/* this processes a parsed reply packet */ -static void -reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) { - int error; - static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED}; - - if (flags & 0x020f || !reply || !reply->have_answer) { - /* there was an error */ - if (flags & 0x0200) { - error = DNS_ERR_TRUNCATED; - } else { - u16 error_code = (flags & 0x000f) - 1; - if (error_code > 4) { - error = DNS_ERR_UNKNOWN; - } else { - error = error_codes[error_code]; - } - } - - switch(error) { - case DNS_ERR_NOTIMPL: - case DNS_ERR_REFUSED: - /* we regard these errors as marking a bad nameserver */ - if (req->reissue_count < global_max_reissues) { - char msg[64]; - snprintf(msg, sizeof(msg), "Bad response %d (%s)", - error, evdns_err_to_string(error)); - nameserver_failed(req->ns, msg); - if (!request_reissue(req)) return; - } - break; - case DNS_ERR_SERVERFAILED: - /* rcode 2 (servfailed) sometimes means "we are broken" and - * sometimes (with some binds) means "that request was very - * confusing." Treat this as a timeout, not a failure. - */ - log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; " - "will allow the request to time out.", - debug_ntoa(req->ns->address)); - break; - default: - /* we got a good reply from the nameserver */ - nameserver_up(req->ns); - } - - if (req->search_state && req->request_type != TYPE_PTR) { - /* if we have a list of domains to search in, try the next one */ - if (!search_try_next(req)) { - /* a new request was issued so this request is finished and */ - /* the user callback will be made when that request (or a */ - /* child of it) finishes. */ - request_finished(req, &req_head); - return; - } - } - - /* all else failed. Pass the failure up */ - reply_callback(req, 0, error, NULL); - request_finished(req, &req_head); - } else { - /* all ok, tell the user */ - reply_callback(req, ttl, 0, reply); - nameserver_up(req->ns); - request_finished(req, &req_head); - } -} - -static int -name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) { - int name_end = -1; - int j = *idx; - int ptr_count = 0; -#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0) -#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0) -#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0) - - char *cp = name_out; - const char *const end = name_out + name_out_len; - - /* Normally, names are a series of length prefixed strings terminated */ - /* with a length of 0 (the lengths are u8's < 63). */ - /* However, the length can start with a pair of 1 bits and that */ - /* means that the next 14 bits are a pointer within the current */ - /* packet. */ - - for(;;) { - u8 label_len; - if (j >= length) return -1; - GET8(label_len); - if (!label_len) break; - if (label_len & 0xc0) { - u8 ptr_low; - GET8(ptr_low); - if (name_end < 0) name_end = j; - j = (((int)label_len & 0x3f) << 8) + ptr_low; - /* Make sure that the target offset is in-bounds. */ - if (j < 0 || j >= length) return -1; - /* If we've jumped more times than there are characters in the - * message, we must have a loop. */ - if (++ptr_count > length) return -1; - continue; - } - if (label_len > 63) return -1; - if (cp != name_out) { - if (cp + 1 >= end) return -1; - *cp++ = '.'; - } - if (cp + label_len >= end) return -1; - memcpy(cp, packet + j, label_len); - cp += label_len; - j += label_len; - } - if (cp >= end) return -1; - *cp = '\0'; - if (name_end < 0) - *idx = j; - else - *idx = name_end; - return 0; - err: - return -1; -} - -/* parses a raw request from a nameserver */ -static int -reply_parse(u8 *packet, int length) { - int j = 0; /* index into packet */ - u16 _t; /* used by the macros */ - u32 _t32; /* used by the macros */ - char tmp_name[256]; /* used by the macros */ - - u16 trans_id, questions, answers, authority, additional, datalength; - u16 flags = 0; - u32 ttl, ttl_r = 0xffffffff; - struct reply reply; - struct request *req = NULL; - unsigned int i; - - GET16(trans_id); - GET16(flags); - GET16(questions); - GET16(answers); - GET16(authority); - GET16(additional); - (void) authority; /* suppress "unused variable" warnings. */ - (void) additional; /* suppress "unused variable" warnings. */ - - req = request_find_from_trans_id(trans_id); - if (!req) return -1; - - memset(&reply, 0, sizeof(reply)); - - /* If it's not an answer, it doesn't correspond to any request. */ - if (!(flags & 0x8000)) return -1; /* must be an answer */ - if (flags & 0x020f) { - /* there was an error */ - goto err; - } - /* if (!answers) return; */ /* must have an answer of some form */ - - /* This macro skips a name in the DNS reply. */ -#define SKIP_NAME \ - do { tmp_name[0] = '\0'; \ - if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \ - goto err; \ - } while(0); - - reply.type = req->request_type; - - /* skip over each question in the reply */ - for (i = 0; i < questions; ++i) { - /* the question looks like - * - */ - SKIP_NAME; - j += 4; - if (j > length) goto err; - } - - /* now we have the answer section which looks like - * - */ - - for (i = 0; i < answers; ++i) { - u16 type, class; - - SKIP_NAME; - GET16(type); - GET16(class); - GET32(ttl); - GET16(datalength); - - if (type == TYPE_A && class == CLASS_INET) { - int addrcount, addrtocopy; - if (req->request_type != TYPE_A) { - j += datalength; continue; - } - if ((datalength & 3) != 0) /* not an even number of As. */ - goto err; - addrcount = datalength >> 2; - addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount); - - ttl_r = MIN(ttl_r, ttl); - /* we only bother with the first four addresses. */ - if (j + 4*addrtocopy > length) goto err; - memcpy(&reply.data.a.addresses[reply.data.a.addrcount], - packet + j, 4*addrtocopy); - j += 4*addrtocopy; - reply.data.a.addrcount += addrtocopy; - reply.have_answer = 1; - if (reply.data.a.addrcount == MAX_ADDRS) break; - } else if (type == TYPE_PTR && class == CLASS_INET) { - if (req->request_type != TYPE_PTR) { - j += datalength; continue; - } - if (name_parse(packet, length, &j, reply.data.ptr.name, - sizeof(reply.data.ptr.name))<0) - goto err; - ttl_r = MIN(ttl_r, ttl); - reply.have_answer = 1; - break; - } else if (type == TYPE_AAAA && class == CLASS_INET) { - int addrcount, addrtocopy; - if (req->request_type != TYPE_AAAA) { - j += datalength; continue; - } - if ((datalength & 15) != 0) /* not an even number of AAAAs. */ - goto err; - addrcount = datalength >> 4; /* each address is 16 bytes long */ - addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount); - ttl_r = MIN(ttl_r, ttl); - - /* we only bother with the first four addresses. */ - if (j + 16*addrtocopy > length) goto err; - memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount], - packet + j, 16*addrtocopy); - reply.data.aaaa.addrcount += addrtocopy; - j += 16*addrtocopy; - reply.have_answer = 1; - if (reply.data.aaaa.addrcount == MAX_ADDRS) break; - } else { - /* skip over any other type of resource */ - j += datalength; - } - } - - reply_handle(req, flags, ttl_r, &reply); - return 0; - err: - if (req) - reply_handle(req, flags, 0, NULL); - return -1; -} - -/* Parse a raw request (packet,length) sent to a nameserver port (port) from */ -/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */ -/* callback. */ -static int -request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen) -{ - int j = 0; /* index into packet */ - u16 _t; /* used by the macros */ - char tmp_name[256]; /* used by the macros */ - - int i; - u16 trans_id, flags, questions, answers, authority, additional; - struct server_request *server_req = NULL; - - /* Get the header fields */ - GET16(trans_id); - GET16(flags); - GET16(questions); - GET16(answers); - GET16(authority); - GET16(additional); - - if (flags & 0x8000) return -1; /* Must not be an answer. */ - flags &= 0x0110; /* Only RD and CD get preserved. */ - - server_req = malloc(sizeof(struct server_request)); - if (server_req == NULL) return -1; - memset(server_req, 0, sizeof(struct server_request)); - - server_req->trans_id = trans_id; - memcpy(&server_req->addr, addr, addrlen); - server_req->addrlen = addrlen; - - server_req->base.flags = flags; - server_req->base.nquestions = 0; - server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions); - if (server_req->base.questions == NULL) - goto err; - - for (i = 0; i < questions; ++i) { - u16 type, class; - struct evdns_server_question *q; - int namelen; - if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) - goto err; - GET16(type); - GET16(class); - namelen = strlen(tmp_name); - q = malloc(sizeof(struct evdns_server_question) + namelen); - if (!q) - goto err; - q->type = type; - q->dns_question_class = class; - memcpy(q->name, tmp_name, namelen+1); - server_req->base.questions[server_req->base.nquestions++] = q; - } - - /* Ignore answers, authority, and additional. */ - - server_req->port = port; - port->refcnt++; - - /* Only standard queries are supported. */ - if (flags & 0x7800) { - evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL); - return -1; - } - - port->user_callback(&(server_req->base), port->user_data); - - return 0; -err: - if (server_req) { - if (server_req->base.questions) { - for (i = 0; i < server_req->base.nquestions; ++i) - free(server_req->base.questions[i]); - free(server_req->base.questions); - } - free(server_req); - } - return -1; - -#undef SKIP_NAME -#undef GET32 -#undef GET16 -#undef GET8 -} - -static u16 -default_transaction_id_fn(void) -{ - u16 trans_id; -#ifdef DNS_USE_CPU_CLOCK_FOR_ID - struct timespec ts; -#ifdef CLOCK_MONOTONIC - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) -#else - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) -#endif - event_err(1, "clock_gettime"); - trans_id = ts.tv_nsec & 0xffff; -#endif - -#ifdef DNS_USE_FTIME_FOR_ID - struct _timeb tb; - _ftime(&tb); - trans_id = tb.millitm & 0xffff; -#endif - -#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID - struct timeval tv; - gettimeofday(&tv, NULL); - trans_id = tv.tv_usec & 0xffff; -#endif - -#ifdef DNS_USE_OPENSSL_FOR_ID - if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) { - /* in the case that the RAND call fails we back */ - /* down to using gettimeofday. */ - /* - struct timeval tv; - gettimeofday(&tv, NULL); - trans_id = tv.tv_usec & 0xffff; - */ - abort(); - } -#endif - return trans_id; -} - -static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn; - -void -evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)) -{ - if (fn) - trans_id_function = fn; - else - trans_id_function = default_transaction_id_fn; -} - -/* Try to choose a strong transaction id which isn't already in flight */ -static u16 -transaction_id_pick(void) { - for (;;) { - const struct request *req = req_head, *started_at; - u16 trans_id = trans_id_function(); - - if (trans_id == 0xffff) continue; - /* now check to see if that id is already inflight */ - req = started_at = req_head; - if (req) { - do { - if (req->trans_id == trans_id) break; - req = req->next; - } while (req != started_at); - } - /* we didn't find it, so this is a good id */ - if (req == started_at) return trans_id; - } -} - -/* choose a namesever to use. This function will try to ignore */ -/* nameservers which we think are down and load balance across the rest */ -/* by updating the server_head global each time. */ -static struct nameserver * -nameserver_pick(void) { - struct nameserver *started_at = server_head, *picked; - if (!server_head) return NULL; - - /* if we don't have any good nameservers then there's no */ - /* point in trying to find one. */ - if (!global_good_nameservers) { - server_head = server_head->next; - return server_head; - } - - /* remember that nameservers are in a circular list */ - for (;;) { - if (server_head->state) { - /* we think this server is currently good */ - picked = server_head; - server_head = server_head->next; - return picked; - } - - server_head = server_head->next; - if (server_head == started_at) { - /* all the nameservers seem to be down */ - /* so we just return this one and hope for the */ - /* best */ - assert(global_good_nameservers == 0); - picked = server_head; - server_head = server_head->next; - return picked; - } - } -} - -/* this is called when a namesever socket is ready for reading */ -static void -nameserver_read(struct nameserver *ns) { - u8 packet[1500]; - - for (;;) { - const int r = recv(ns->socket, packet, sizeof(packet), 0); - if (r < 0) { - int err = last_error(ns->socket); - if (error_is_eagain(err)) return; - nameserver_failed(ns, strerror(err)); - return; - } - ns->timedout = 0; - reply_parse(packet, r); - } -} - -/* Read a packet from a DNS client on a server port s, parse it, and */ -/* act accordingly. */ -static void -server_port_read(struct evdns_server_port *s) { - u8 packet[1500]; - struct sockaddr_storage addr; - socklen_t addrlen; - int r; - - for (;;) { - addrlen = sizeof(struct sockaddr_storage); - r = recvfrom(s->socket, packet, sizeof(packet), 0, - (struct sockaddr*) &addr, &addrlen); - if (r < 0) { - int err = last_error(s->socket); - if (error_is_eagain(err)) return; - log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.", - strerror(err), err); - return; - } - request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen); - } -} - -/* Try to write all pending replies on a given DNS server port. */ -static void -server_port_flush(struct evdns_server_port *port) -{ - while (port->pending_replies) { - struct server_request *req = port->pending_replies; - int r = sendto(port->socket, req->response, req->response_len, 0, - (struct sockaddr*) &req->addr, req->addrlen); - if (r < 0) { - int err = last_error(port->socket); - if (error_is_eagain(err)) - return; - log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", strerror(err), err); - } - if (server_request_free(req)) { - /* we released the last reference to req->port. */ - return; - } - } - - /* We have no more pending requests; stop listening for 'writeable' events. */ - (void) event_del(&port->event); - event_set(&port->event, port->socket, EV_READ | EV_PERSIST, - server_port_ready_callback, port); - if (event_add(&port->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server."); - /* ???? Do more? */ - } -} - -/* set if we are waiting for the ability to write to this server. */ -/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */ -/* we stop these events. */ -static void -nameserver_write_waiting(struct nameserver *ns, char waiting) { - if (ns->write_waiting == waiting) return; - - ns->write_waiting = waiting; - (void) event_del(&ns->event); - event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST, - nameserver_ready_callback, ns); - if (event_add(&ns->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s", - debug_ntoa(ns->address)); - /* ???? Do more? */ - } -} - -/* a callback function. Called by libevent when the kernel says that */ -/* a nameserver socket is ready for writing or reading */ -static void -nameserver_ready_callback(int fd, short events, void *arg) { - struct nameserver *ns = (struct nameserver *) arg; - (void)fd; - - if (events & EV_WRITE) { - ns->choked = 0; - if (!evdns_transmit()) { - nameserver_write_waiting(ns, 0); - } - } - if (events & EV_READ) { - nameserver_read(ns); - } -} - -/* a callback function. Called by libevent when the kernel says that */ -/* a server socket is ready for writing or reading. */ -static void -server_port_ready_callback(int fd, short events, void *arg) { - struct evdns_server_port *port = (struct evdns_server_port *) arg; - (void) fd; - - if (events & EV_WRITE) { - port->choked = 0; - server_port_flush(port); - } - if (events & EV_READ) { - server_port_read(port); - } -} - -/* This is an inefficient representation; only use it via the dnslabel_table_* - * functions, so that is can be safely replaced with something smarter later. */ -#define MAX_LABELS 128 -/* Structures used to implement name compression */ -struct dnslabel_entry { char *v; off_t pos; }; -struct dnslabel_table { - int n_labels; /* number of current entries */ - /* map from name to position in message */ - struct dnslabel_entry labels[MAX_LABELS]; -}; - -/* Initialize dnslabel_table. */ -static void -dnslabel_table_init(struct dnslabel_table *table) -{ - table->n_labels = 0; -} - -/* Free all storage held by table, but not the table itself. */ -static void -dnslabel_clear(struct dnslabel_table *table) -{ - int i; - for (i = 0; i < table->n_labels; ++i) - free(table->labels[i].v); - table->n_labels = 0; -} - -/* return the position of the label in the current message, or -1 if the label */ -/* hasn't been used yet. */ -static int -dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label) -{ - int i; - for (i = 0; i < table->n_labels; ++i) { - if (!strcmp(label, table->labels[i].v)) - return table->labels[i].pos; - } - return -1; -} - -/* remember that we've used the label at position pos */ -static int -dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos) -{ - char *v; - int p; - if (table->n_labels == MAX_LABELS) - return (-1); - v = strdup(label); - if (v == NULL) - return (-1); - p = table->n_labels++; - table->labels[p].v = v; - table->labels[p].pos = pos; - - return (0); -} - -/* Converts a string to a length-prefixed set of DNS labels, starting */ -/* at buf[j]. name and buf must not overlap. name_len should be the length */ -/* of name. table is optional, and is used for compression. */ -/* */ -/* Input: abc.def */ -/* Output: <3>abc<3>def<0> */ -/* */ -/* Returns the first index after the encoded name, or negative on error. */ -/* -1 label was > 63 bytes */ -/* -2 name too long to fit in buffer. */ -/* */ -static off_t -dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j, - const char *name, const int name_len, - struct dnslabel_table *table) { - const char *end = name + name_len; - int ref = 0; - u16 _t; - -#define APPEND16(x) do { \ - if (j + 2 > (off_t)buf_len) \ - goto overflow; \ - _t = htons(x); \ - memcpy(buf + j, &_t, 2); \ - j += 2; \ - } while (0) -#define APPEND32(x) do { \ - if (j + 4 > (off_t)buf_len) \ - goto overflow; \ - _t32 = htonl(x); \ - memcpy(buf + j, &_t32, 4); \ - j += 4; \ - } while (0) - - if (name_len > 255) return -2; - - for (;;) { - const char *const start = name; - if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) { - APPEND16(ref | 0xc000); - return j; - } - name = strchr(name, '.'); - if (!name) { - const unsigned int label_len = end - start; - if (label_len > 63) return -1; - if ((size_t)(j+label_len+1) > buf_len) return -2; - if (table) dnslabel_table_add(table, start, j); - buf[j++] = label_len; - - memcpy(buf + j, start, end - start); - j += end - start; - break; - } else { - /* append length of the label. */ - const unsigned int label_len = name - start; - if (label_len > 63) return -1; - if ((size_t)(j+label_len+1) > buf_len) return -2; - if (table) dnslabel_table_add(table, start, j); - buf[j++] = label_len; - - memcpy(buf + j, start, name - start); - j += name - start; - /* hop over the '.' */ - name++; - } - } - - /* the labels must be terminated by a 0. */ - /* It's possible that the name ended in a . */ - /* in which case the zero is already there */ - if (!j || buf[j-1]) buf[j++] = 0; - return j; - overflow: - return (-2); -} - -/* Finds the length of a dns request for a DNS name of the given */ -/* length. The actual request may be smaller than the value returned */ -/* here */ -static int -evdns_request_len(const int name_len) { - return 96 + /* length of the DNS standard header */ - name_len + 2 + - 4; /* space for the resource type */ -} - -/* build a dns request packet into buf. buf should be at least as long */ -/* as evdns_request_len told you it should be. */ -/* */ -/* Returns the amount of space used. Negative on error. */ -static int -evdns_request_data_build(const char *const name, const int name_len, - const u16 trans_id, const u16 type, const u16 class, - u8 *const buf, size_t buf_len) { - off_t j = 0; /* current offset into buf */ - u16 _t; /* used by the macros */ - - APPEND16(trans_id); - APPEND16(0x0100); /* standard query, recusion needed */ - APPEND16(1); /* one question */ - APPEND16(0); /* no answers */ - APPEND16(0); /* no authority */ - APPEND16(0); /* no additional */ - - j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL); - if (j < 0) { - return (int)j; - } - - APPEND16(type); - APPEND16(class); - - return (int)j; - overflow: - return (-1); -} - -/* exported function */ -struct evdns_server_port * -evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data) -{ - struct evdns_server_port *port; - if (!(port = malloc(sizeof(struct evdns_server_port)))) - return NULL; - memset(port, 0, sizeof(struct evdns_server_port)); - - assert(!is_tcp); /* TCP sockets not yet implemented */ - port->socket = socket; - port->refcnt = 1; - port->choked = 0; - port->closing = 0; - port->user_callback = cb; - port->user_data = user_data; - port->pending_replies = NULL; - - event_set(&port->event, port->socket, EV_READ | EV_PERSIST, - server_port_ready_callback, port); - event_add(&port->event, NULL); /* check return. */ - return port; -} - -/* exported function */ -void -evdns_close_server_port(struct evdns_server_port *port) -{ - if (--port->refcnt == 0) - server_port_free(port); - port->closing = 1; -} - -/* exported function */ -int -evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - struct server_reply_item **itemp, *item; - int *countp; - - if (req->response) /* have we already answered? */ - return (-1); - - switch (section) { - case EVDNS_ANSWER_SECTION: - itemp = &req->answer; - countp = &req->n_answer; - break; - case EVDNS_AUTHORITY_SECTION: - itemp = &req->authority; - countp = &req->n_authority; - break; - case EVDNS_ADDITIONAL_SECTION: - itemp = &req->additional; - countp = &req->n_additional; - break; - default: - return (-1); - } - while (*itemp) { - itemp = &((*itemp)->next); - } - item = malloc(sizeof(struct server_reply_item)); - if (!item) - return -1; - item->next = NULL; - if (!(item->name = strdup(name))) { - free(item); - return -1; - } - item->type = type; - item->dns_question_class = class; - item->ttl = ttl; - item->is_name = is_name != 0; - item->datalen = 0; - item->data = NULL; - if (data) { - if (item->is_name) { - if (!(item->data = strdup(data))) { - free(item->name); - free(item); - return -1; - } - item->datalen = (u16)-1; - } else { - if (!(item->data = malloc(datalen))) { - free(item->name); - free(item); - return -1; - } - item->datalen = datalen; - memcpy(item->data, data, datalen); - } - } - - *itemp = item; - ++(*countp); - return 0; -} - -/* exported function */ -int -evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, - ttl, n*4, 0, addrs); -} - -/* exported function */ -int -evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET, - ttl, n*16, 0, addrs); -} - -/* exported function */ -int -evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl) -{ - u32 a; - char buf[32]; - assert(in || inaddr_name); - assert(!(in && inaddr_name)); - if (in) { - a = ntohl(in->s_addr); - snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", - (int)(u8)((a )&0xff), - (int)(u8)((a>>8 )&0xff), - (int)(u8)((a>>16)&0xff), - (int)(u8)((a>>24)&0xff)); - inaddr_name = buf; - } - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET, - ttl, -1, 1, hostname); -} - -/* exported function */ -int -evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl) -{ - return evdns_server_request_add_reply( - req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, - ttl, -1, 1, cname); -} - - -static int -evdns_server_request_format_response(struct server_request *req, int err) -{ - unsigned char buf[1500]; - size_t buf_len = sizeof(buf); - off_t j = 0, r; - u16 _t; - u32 _t32; - int i; - u16 flags; - struct dnslabel_table table; - - if (err < 0 || err > 15) return -1; - - /* Set response bit and error code; copy OPCODE and RD fields from - * question; copy RA and AA if set by caller. */ - flags = req->base.flags; - flags |= (0x8000 | err); - - dnslabel_table_init(&table); - APPEND16(req->trans_id); - APPEND16(flags); - APPEND16(req->base.nquestions); - APPEND16(req->n_answer); - APPEND16(req->n_authority); - APPEND16(req->n_additional); - - /* Add questions. */ - for (i=0; i < req->base.nquestions; ++i) { - const char *s = req->base.questions[i]->name; - j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table); - if (j < 0) { - dnslabel_clear(&table); - return (int) j; - } - APPEND16(req->base.questions[i]->type); - APPEND16(req->base.questions[i]->dns_question_class); - } - - /* Add answer, authority, and additional sections. */ - for (i=0; i<3; ++i) { - struct server_reply_item *item; - if (i==0) - item = req->answer; - else if (i==1) - item = req->authority; - else - item = req->additional; - while (item) { - r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table); - if (r < 0) - goto overflow; - j = r; - - APPEND16(item->type); - APPEND16(item->dns_question_class); - APPEND32(item->ttl); - if (item->is_name) { - off_t len_idx = j, name_start; - j += 2; - name_start = j; - r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table); - if (r < 0) - goto overflow; - j = r; - _t = htons( (short) (j-name_start) ); - memcpy(buf+len_idx, &_t, 2); - } else { - APPEND16(item->datalen); - if (j+item->datalen > (off_t)buf_len) - goto overflow; - memcpy(buf+j, item->data, item->datalen); - j += item->datalen; - } - item = item->next; - } - } - - if (j > 512) { -overflow: - j = 512; - buf[3] |= 0x02; /* set the truncated bit. */ - } - - req->response_len = j; - - if (!(req->response = malloc(req->response_len))) { - server_request_free_answers(req); - dnslabel_clear(&table); - return (-1); - } - memcpy(req->response, buf, req->response_len); - server_request_free_answers(req); - dnslabel_clear(&table); - return (0); -} - -/* exported function */ -int -evdns_server_request_respond(struct evdns_server_request *_req, int err) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - struct evdns_server_port *port = req->port; - int r; - if (!req->response) { - if ((r = evdns_server_request_format_response(req, err))<0) - return r; - } - - r = sendto(port->socket, req->response, req->response_len, 0, - (struct sockaddr*) &req->addr, req->addrlen); - if (r<0) { - int sock_err = last_error(port->socket); - if (! error_is_eagain(sock_err)) - return -1; - - if (port->pending_replies) { - req->prev_pending = port->pending_replies->prev_pending; - req->next_pending = port->pending_replies; - req->prev_pending->next_pending = - req->next_pending->prev_pending = req; - } else { - req->prev_pending = req->next_pending = req; - port->pending_replies = req; - port->choked = 1; - - (void) event_del(&port->event); - event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port); - - if (event_add(&port->event, NULL) < 0) { - log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server"); - } - - } - - return 1; - } - if (server_request_free(req)) - return 0; - - if (port->pending_replies) - server_port_flush(port); - - return 0; -} - -/* Free all storage held by RRs in req. */ -static void -server_request_free_answers(struct server_request *req) -{ - struct server_reply_item *victim, *next, **list; - int i; - for (i = 0; i < 3; ++i) { - if (i==0) - list = &req->answer; - else if (i==1) - list = &req->authority; - else - list = &req->additional; - - victim = *list; - while (victim) { - next = victim->next; - free(victim->name); - if (victim->data) - free(victim->data); - free(victim); - victim = next; - } - *list = NULL; - } -} - -/* Free all storage held by req, and remove links to it. */ -/* return true iff we just wound up freeing the server_port. */ -static int -server_request_free(struct server_request *req) -{ - int i, rc=1; - if (req->base.questions) { - for (i = 0; i < req->base.nquestions; ++i) - free(req->base.questions[i]); - free(req->base.questions); - } - - if (req->port) { - if (req->port->pending_replies == req) { - if (req->next_pending) - req->port->pending_replies = req->next_pending; - else - req->port->pending_replies = NULL; - } - rc = --req->port->refcnt; - } - - if (req->response) { - free(req->response); - } - - server_request_free_answers(req); - - if (req->next_pending && req->next_pending != req) { - req->next_pending->prev_pending = req->prev_pending; - req->prev_pending->next_pending = req->next_pending; - } - - if (rc == 0) { - server_port_free(req->port); - free(req); - return (1); - } - free(req); - return (0); -} - -/* Free all storage held by an evdns_server_port. Only called when */ -static void -server_port_free(struct evdns_server_port *port) -{ - assert(port); - assert(!port->refcnt); - assert(!port->pending_replies); - if (port->socket > 0) { - CLOSE_SOCKET(port->socket); - port->socket = -1; - } - (void) event_del(&port->event); - /* XXXX actually free the port? -NM */ -} - -/* exported function */ -int -evdns_server_request_drop(struct evdns_server_request *_req) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - server_request_free(req); - return 0; -} - -/* exported function */ -int -evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len) -{ - struct server_request *req = TO_SERVER_REQUEST(_req); - if (addr_len < (int)req->addrlen) - return -1; - memcpy(sa, &(req->addr), req->addrlen); - return req->addrlen; -} - -#undef APPEND16 -#undef APPEND32 - -/* this is a libevent callback function which is called when a request */ -/* has timed out. */ -static void -evdns_request_timeout_callback(int fd, short events, void *arg) { - struct request *const req = (struct request *) arg; - (void) fd; - (void) events; - - log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg); - - req->ns->timedout++; - if (req->ns->timedout > global_max_nameserver_timeout) { - req->ns->timedout = 0; - nameserver_failed(req->ns, "request timed out."); - } - - (void) evtimer_del(&req->timeout_event); - if (req->tx_count >= global_max_retransmits) { - /* this request has failed */ - reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); - request_finished(req, &req_head); - } else { - /* retransmit it */ - evdns_request_transmit(req); - } -} - -/* try to send a request to a given server. */ -/* */ -/* return: */ -/* 0 ok */ -/* 1 temporary failure */ -/* 2 other failure */ -static int -evdns_request_transmit_to(struct request *req, struct nameserver *server) { - const int r = send(server->socket, req->request, req->request_len, 0); - if (r < 0) { - int err = last_error(server->socket); - if (error_is_eagain(err)) return 1; - nameserver_failed(req->ns, strerror(err)); - return 2; - } else if (r != (int)req->request_len) { - return 1; /* short write */ - } else { - return 0; - } -} - -/* try to send a request, updating the fields of the request */ -/* as needed */ -/* */ -/* return: */ -/* 0 ok */ -/* 1 failed */ -static int -evdns_request_transmit(struct request *req) { - int retcode = 0, r; - - /* if we fail to send this packet then this flag marks it */ - /* for evdns_transmit */ - req->transmit_me = 1; - if (req->trans_id == 0xffff) abort(); - - if (req->ns->choked) { - /* don't bother trying to write to a socket */ - /* which we have had EAGAIN from */ - return 1; - } - - r = evdns_request_transmit_to(req, req->ns); - switch (r) { - case 1: - /* temp failure */ - req->ns->choked = 1; - nameserver_write_waiting(req->ns, 1); - return 1; - case 2: - /* failed in some other way */ - retcode = 1; - /* fall through */ - default: - /* all ok */ - log(EVDNS_LOG_DEBUG, - "Setting timeout for request %lx", (unsigned long) req); - evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req); - if (evtimer_add(&req->timeout_event, &global_timeout) < 0) { - log(EVDNS_LOG_WARN, - "Error from libevent when adding timer for request %lx", - (unsigned long) req); - /* ???? Do more? */ - } - req->tx_count++; - req->transmit_me = 0; - return retcode; - } -} - -static void -nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) { - struct nameserver *const ns = (struct nameserver *) arg; - (void) type; - (void) count; - (void) ttl; - (void) addresses; - - if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) { - /* this is a good reply */ - nameserver_up(ns); - } else nameserver_probe_failed(ns); -} - -static void -nameserver_send_probe(struct nameserver *const ns) { - struct request *req; - /* here we need to send a probe to a given nameserver */ - /* in the hope that it is up now. */ - - log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address)); - - req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns); - if (!req) return; - /* we force this into the inflight queue no matter what */ - request_trans_id_set(req, transaction_id_pick()); - req->ns = ns; - request_submit(req); -} - -/* returns: */ -/* 0 didn't try to transmit anything */ -/* 1 tried to transmit something */ -static int -evdns_transmit(void) { - char did_try_to_transmit = 0; - - if (req_head) { - struct request *const started_at = req_head, *req = req_head; - /* first transmit all the requests which are currently waiting */ - do { - if (req->transmit_me) { - did_try_to_transmit = 1; - evdns_request_transmit(req); - } - - req = req->next; - } while (req != started_at); - } - - return did_try_to_transmit; -} - -/* exported function */ -int -evdns_count_nameservers(void) -{ - const struct nameserver *server = server_head; - int n = 0; - if (!server) - return 0; - do { - ++n; - server = server->next; - } while (server != server_head); - return n; -} - -/* exported function */ -int -evdns_clear_nameservers_and_suspend(void) -{ - struct nameserver *server = server_head, *started_at = server_head; - struct request *req = req_head, *req_started_at = req_head; - - if (!server) - return 0; - while (1) { - struct nameserver *next = server->next; - (void) event_del(&server->event); - if (evtimer_initialized(&server->timeout_event)) - (void) evtimer_del(&server->timeout_event); - if (server->socket >= 0) - CLOSE_SOCKET(server->socket); - free(server); - if (next == started_at) - break; - server = next; - } - server_head = NULL; - global_good_nameservers = 0; - - while (req) { - struct request *next = req->next; - req->tx_count = req->reissue_count = 0; - req->ns = NULL; - /* ???? What to do about searches? */ - (void) evtimer_del(&req->timeout_event); - req->trans_id = 0; - req->transmit_me = 0; - - global_requests_waiting++; - evdns_request_insert(req, &req_waiting_head); - /* We want to insert these suspended elements at the front of - * the waiting queue, since they were pending before any of - * the waiting entries were added. This is a circular list, - * so we can just shift the start back by one.*/ - req_waiting_head = req_waiting_head->prev; - - if (next == req_started_at) - break; - req = next; - } - req_head = NULL; - global_requests_inflight = 0; - - return 0; -} - - -/* exported function */ -int -evdns_resume(void) -{ - evdns_requests_pump_waiting_queue(); - return 0; -} - -static int -_evdns_nameserver_add_impl(unsigned long int address, int port) { - /* first check to see if we already have this nameserver */ - - const struct nameserver *server = server_head, *const started_at = server_head; - struct nameserver *ns; - struct sockaddr_in sin; - int err = 0; - if (server) { - do { - if (server->address == address) return 3; - server = server->next; - } while (server != started_at); - } - - ns = (struct nameserver *) malloc(sizeof(struct nameserver)); - if (!ns) return -1; - - memset(ns, 0, sizeof(struct nameserver)); - - ns->socket = socket(PF_INET, SOCK_DGRAM, 0); - if (ns->socket < 0) { err = 1; goto out1; } - evutil_make_socket_nonblocking(ns->socket); - sin.sin_addr.s_addr = address; - sin.sin_port = htons(port); - sin.sin_family = AF_INET; - if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) { - err = 2; - goto out2; - } - - ns->address = address; - ns->state = 1; - event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns); - if (event_add(&ns->event, NULL) < 0) { - err = 2; - goto out2; - } - - log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address)); - - /* insert this nameserver into the list of them */ - if (!server_head) { - ns->next = ns->prev = ns; - server_head = ns; - } else { - ns->next = server_head->next; - ns->prev = server_head; - server_head->next = ns; - if (server_head->prev == server_head) { - server_head->prev = ns; - } - } - - global_good_nameservers++; - - return 0; - -out2: - CLOSE_SOCKET(ns->socket); -out1: - free(ns); - log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err); - return err; -} - -/* exported function */ -int -evdns_nameserver_add(unsigned long int address) { - return _evdns_nameserver_add_impl(address, 53); -} - -/* exported function */ -int -evdns_nameserver_ip_add(const char *ip_as_string) { - struct in_addr ina; - int port; - char buf[20]; - const char *cp; - cp = strchr(ip_as_string, ':'); - if (! cp) { - cp = ip_as_string; - port = 53; - } else { - port = strtoint(cp+1); - if (port < 0 || port > 65535) { - return 4; - } - if ((cp-ip_as_string) >= (int)sizeof(buf)) { - return 4; - } - memcpy(buf, ip_as_string, cp-ip_as_string); - buf[cp-ip_as_string] = '\0'; - cp = buf; - } - if (!inet_aton(cp, &ina)) { - return 4; - } - return _evdns_nameserver_add_impl(ina.s_addr, port); -} - -/* insert into the tail of the queue */ -static void -evdns_request_insert(struct request *req, struct request **head) { - if (!*head) { - *head = req; - req->next = req->prev = req; - return; - } - - req->prev = (*head)->prev; - req->prev->next = req; - req->next = *head; - (*head)->prev = req; -} - -static int -string_num_dots(const char *s) { - int count = 0; - while ((s = strchr(s, '.'))) { - s++; - count++; - } - return count; -} - -static struct request * -request_new(int type, const char *name, int flags, - evdns_callback_type callback, void *user_ptr) { - const char issuing_now = - (global_requests_inflight < global_max_requests_inflight) ? 1 : 0; - - const int name_len = strlen(name); - const int request_max_len = evdns_request_len(name_len); - const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff; - /* the request data is alloced in a single block with the header */ - struct request *const req = - (struct request *) malloc(sizeof(struct request) + request_max_len); - int rlen; - (void) flags; - - if (!req) return NULL; - memset(req, 0, sizeof(struct request)); - - /* request data lives just after the header */ - req->request = ((u8 *) req) + sizeof(struct request); - /* denotes that the request data shouldn't be free()ed */ - req->request_appended = 1; - rlen = evdns_request_data_build(name, name_len, trans_id, - type, CLASS_INET, req->request, request_max_len); - if (rlen < 0) - goto err1; - req->request_len = rlen; - req->trans_id = trans_id; - req->tx_count = 0; - req->request_type = type; - req->user_pointer = user_ptr; - req->user_callback = callback; - req->ns = issuing_now ? nameserver_pick() : NULL; - req->next = req->prev = NULL; - - return req; -err1: - free(req); - return NULL; -} - -static void -request_submit(struct request *const req) { - if (req->ns) { - /* if it has a nameserver assigned then this is going */ - /* straight into the inflight queue */ - evdns_request_insert(req, &req_head); - global_requests_inflight++; - evdns_request_transmit(req); - } else { - evdns_request_insert(req, &req_waiting_head); - global_requests_waiting++; - } -} - -/* exported function */ -int evdns_resolve_ipv4(const char *name, int flags, - evdns_callback_type callback, void *ptr) { - log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); - if (flags & DNS_QUERY_NO_SEARCH) { - struct request *const req = - request_new(TYPE_A, name, flags, callback, ptr); - if (req == NULL) - return (1); - request_submit(req); - return (0); - } else { - return (search_request_new(TYPE_A, name, flags, callback, ptr)); - } -} - -/* exported function */ -int evdns_resolve_ipv6(const char *name, int flags, - evdns_callback_type callback, void *ptr) { - log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); - if (flags & DNS_QUERY_NO_SEARCH) { - struct request *const req = - request_new(TYPE_AAAA, name, flags, callback, ptr); - if (req == NULL) - return (1); - request_submit(req); - return (0); - } else { - return (search_request_new(TYPE_AAAA, name, flags, callback, ptr)); - } -} - -int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { - char buf[32]; - struct request *req; - u32 a; - assert(in); - a = ntohl(in->s_addr); - snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", - (int)(u8)((a )&0xff), - (int)(u8)((a>>8 )&0xff), - (int)(u8)((a>>16)&0xff), - (int)(u8)((a>>24)&0xff)); - log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); - req = request_new(TYPE_PTR, buf, flags, callback, ptr); - if (!req) return 1; - request_submit(req); - return 0; -} - -int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { - /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */ - char buf[73]; - char *cp; - struct request *req; - int i; - assert(in); - cp = buf; - for (i=15; i >= 0; --i) { - u8 byte = in->s6_addr[i]; - *cp++ = "0123456789abcdef"[byte & 0x0f]; - *cp++ = '.'; - *cp++ = "0123456789abcdef"[byte >> 4]; - *cp++ = '.'; - } - assert(cp + strlen("ip6.arpa") < buf+sizeof(buf)); - memcpy(cp, "ip6.arpa", strlen("ip6.arpa")+1); - log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); - req = request_new(TYPE_PTR, buf, flags, callback, ptr); - if (!req) return 1; - request_submit(req); - return 0; -} - -/*/////////////////////////////////////////////////////////////////// */ -/* Search support */ -/* */ -/* the libc resolver has support for searching a number of domains */ -/* to find a name. If nothing else then it takes the single domain */ -/* from the gethostname() call. */ -/* */ -/* It can also be configured via the domain and search options in a */ -/* resolv.conf. */ -/* */ -/* The ndots option controls how many dots it takes for the resolver */ -/* to decide that a name is non-local and so try a raw lookup first. */ - -struct search_domain { - int len; - struct search_domain *next; - /* the text string is appended to this structure */ -}; - -struct search_state { - int refcount; - int ndots; - int num_domains; - struct search_domain *head; -}; - -static struct search_state *global_search_state = NULL; - -static void -search_state_decref(struct search_state *const state) { - if (!state) return; - state->refcount--; - if (!state->refcount) { - struct search_domain *next, *dom; - for (dom = state->head; dom; dom = next) { - next = dom->next; - free(dom); - } - free(state); - } -} - -static struct search_state * -search_state_new(void) { - struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state)); - if (!state) return NULL; - memset(state, 0, sizeof(struct search_state)); - state->refcount = 1; - state->ndots = 1; - - return state; -} - -static void -search_postfix_clear(void) { - search_state_decref(global_search_state); - - global_search_state = search_state_new(); -} - -/* exported function */ -void -evdns_search_clear(void) { - search_postfix_clear(); -} - -static void -search_postfix_add(const char *domain) { - int domain_len; - struct search_domain *sdomain; - while (domain[0] == '.') domain++; - domain_len = strlen(domain); - - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return; - global_search_state->num_domains++; - - sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len); - if (!sdomain) return; - memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len); - sdomain->next = global_search_state->head; - sdomain->len = domain_len; - - global_search_state->head = sdomain; -} - -/* reverse the order of members in the postfix list. This is needed because, */ -/* when parsing resolv.conf we push elements in the wrong order */ -static void -search_reverse(void) { - struct search_domain *cur, *prev = NULL, *next; - cur = global_search_state->head; - while (cur) { - next = cur->next; - cur->next = prev; - prev = cur; - cur = next; - } - - global_search_state->head = prev; -} - -/* exported function */ -void -evdns_search_add(const char *domain) { - search_postfix_add(domain); -} - -/* exported function */ -void -evdns_search_ndots_set(const int ndots) { - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return; - global_search_state->ndots = ndots; -} - -static void -search_set_from_hostname(void) { - char hostname[HOST_NAME_MAX + 1], *domainname; - - search_postfix_clear(); - if (gethostname(hostname, sizeof(hostname))) return; - domainname = strchr(hostname, '.'); - if (!domainname) return; - search_postfix_add(domainname); -} - -/* warning: returns malloced string */ -static char * -search_make_new(const struct search_state *const state, int n, const char *const base_name) { - const int base_len = strlen(base_name); - const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1; - struct search_domain *dom; - - for (dom = state->head; dom; dom = dom->next) { - if (!n--) { - /* this is the postfix we want */ - /* the actual postfix string is kept at the end of the structure */ - const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain); - const int postfix_len = dom->len; - char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1); - if (!newname) return NULL; - memcpy(newname, base_name, base_len); - if (need_to_append_dot) newname[base_len] = '.'; - memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len); - newname[base_len + need_to_append_dot + postfix_len] = 0; - return newname; - } - } - - /* we ran off the end of the list and still didn't find the requested string */ - abort(); - return NULL; /* unreachable; stops warnings in some compilers. */ -} - -static int -search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) { - assert(type == TYPE_A || type == TYPE_AAAA); - if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) && - global_search_state && - global_search_state->num_domains) { - /* we have some domains to search */ - struct request *req; - if (string_num_dots(name) >= global_search_state->ndots) { - req = request_new(type, name, flags, user_callback, user_arg); - if (!req) return 1; - req->search_index = -1; - } else { - char *const new_name = search_make_new(global_search_state, 0, name); - if (!new_name) return 1; - req = request_new(type, new_name, flags, user_callback, user_arg); - free(new_name); - if (!req) return 1; - req->search_index = 0; - } - req->search_origname = strdup(name); - req->search_state = global_search_state; - req->search_flags = flags; - global_search_state->refcount++; - request_submit(req); - return 0; - } else { - struct request *const req = request_new(type, name, flags, user_callback, user_arg); - if (!req) return 1; - request_submit(req); - return 0; - } -} - -/* this is called when a request has failed to find a name. We need to check */ -/* if it is part of a search and, if so, try the next name in the list */ -/* returns: */ -/* 0 another request has been submitted */ -/* 1 no more requests needed */ -static int -search_try_next(struct request *const req) { - if (req->search_state) { - /* it is part of a search */ - char *new_name; - struct request *newreq; - req->search_index++; - if (req->search_index >= req->search_state->num_domains) { - /* no more postfixes to try, however we may need to try */ - /* this name without a postfix */ - if (string_num_dots(req->search_origname) < req->search_state->ndots) { - /* yep, we need to try it raw */ - newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer); - log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname); - if (newreq) { - request_submit(newreq); - return 0; - } - } - return 1; - } - - new_name = search_make_new(req->search_state, req->search_index, req->search_origname); - if (!new_name) return 1; - log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index); - newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer); - free(new_name); - if (!newreq) return 1; - newreq->search_origname = req->search_origname; - req->search_origname = NULL; - newreq->search_state = req->search_state; - newreq->search_flags = req->search_flags; - newreq->search_index = req->search_index; - newreq->search_state->refcount++; - request_submit(newreq); - return 0; - } - return 1; -} - -static void -search_request_finished(struct request *const req) { - if (req->search_state) { - search_state_decref(req->search_state); - req->search_state = NULL; - } - if (req->search_origname) { - free(req->search_origname); - req->search_origname = NULL; - } -} - -/*/////////////////////////////////////////////////////////////////// */ -/* Parsing resolv.conf files */ - -static void -evdns_resolv_set_defaults(int flags) { - /* if the file isn't found then we assume a local resolver */ - if (flags & DNS_OPTION_SEARCH) search_set_from_hostname(); - if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1"); -} - -#ifndef HAVE_STRTOK_R -static char * -strtok_r(char *s, const char *delim, char **state) { - return strtok(s, delim); -} -#endif - -/* helper version of atoi which returns -1 on error */ -static int -strtoint(const char *const str) { - char *endptr; - const int r = strtol(str, &endptr, 10); - if (*endptr) return -1; - return r; -} - -/* helper version of atoi that returns -1 on error and clips to bounds. */ -static int -strtoint_clipped(const char *const str, int min, int max) -{ - int r = strtoint(str); - if (r == -1) - return r; - else if (rmax) - return max; - else - return r; -} - -/* exported function */ -int -evdns_set_option(const char *option, const char *val, int flags) -{ - if (!strncmp(option, "ndots:", 6)) { - const int ndots = strtoint(val); - if (ndots == -1) return -1; - if (!(flags & DNS_OPTION_SEARCH)) return 0; - log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots); - if (!global_search_state) global_search_state = search_state_new(); - if (!global_search_state) return -1; - global_search_state->ndots = ndots; - } else if (!strncmp(option, "timeout:", 8)) { - const int timeout = strtoint(val); - if (timeout == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout); - global_timeout.tv_sec = timeout; - } else if (!strncmp(option, "max-timeouts:", 12)) { - const int maxtimeout = strtoint_clipped(val, 1, 255); - if (maxtimeout == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d", - maxtimeout); - global_max_nameserver_timeout = maxtimeout; - } else if (!strncmp(option, "max-inflight:", 13)) { - const int maxinflight = strtoint_clipped(val, 1, 65000); - if (maxinflight == -1) return -1; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", - maxinflight); - global_max_requests_inflight = maxinflight; - } else if (!strncmp(option, "attempts:", 9)) { - int retries = strtoint(val); - if (retries == -1) return -1; - if (retries > 255) retries = 255; - if (!(flags & DNS_OPTION_MISC)) return 0; - log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries); - global_max_retransmits = retries; - } - return 0; -} - -static void -resolv_conf_parse_line(char *const start, int flags) { - char *strtok_state; - static const char *const delims = " \t"; -#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state) - - char *const first_token = strtok_r(start, delims, &strtok_state); - if (!first_token) return; - - if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) { - const char *const nameserver = NEXT_TOKEN; - struct in_addr ina; - - if (inet_aton(nameserver, &ina)) { - /* address is valid */ - evdns_nameserver_add(ina.s_addr); - } - } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) { - const char *const domain = NEXT_TOKEN; - if (domain) { - search_postfix_clear(); - search_postfix_add(domain); - } - } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) { - const char *domain; - search_postfix_clear(); - - while ((domain = NEXT_TOKEN)) { - search_postfix_add(domain); - } - search_reverse(); - } else if (!strcmp(first_token, "options")) { - const char *option; - while ((option = NEXT_TOKEN)) { - const char *val = strchr(option, ':'); - evdns_set_option(option, val ? val+1 : "", flags); - } - } -#undef NEXT_TOKEN -} - -/* exported function */ -/* returns: */ -/* 0 no errors */ -/* 1 failed to open file */ -/* 2 failed to stat file */ -/* 3 file too large */ -/* 4 out of memory */ -/* 5 short read from file */ -int -evdns_resolv_conf_parse(int flags, const char *const filename) { - struct stat st; - int fd, n, r; - u8 *resolv; - char *start; - int err = 0; - - log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); - - fd = open(filename, O_RDONLY); - if (fd < 0) { - evdns_resolv_set_defaults(flags); - return 1; - } - - if (fstat(fd, &st)) { err = 2; goto out1; } - if (!st.st_size) { - evdns_resolv_set_defaults(flags); - err = (flags & DNS_OPTION_NAMESERVERS) ? 6 : 0; - goto out1; - } - if (st.st_size > 65535) { err = 3; goto out1; } /* no resolv.conf should be any bigger */ - - resolv = (u8 *) malloc((size_t)st.st_size + 1); - if (!resolv) { err = 4; goto out1; } - - n = 0; - while ((r = read(fd, resolv+n, (size_t)st.st_size-n)) > 0) { - n += r; - if (n == st.st_size) - break; - assert(n < st.st_size); - } - if (r < 0) { err = 5; goto out2; } - resolv[n] = 0; /* we malloced an extra byte; this should be fine. */ - - start = (char *) resolv; - for (;;) { - char *const newline = strchr(start, '\n'); - if (!newline) { - resolv_conf_parse_line(start, flags); - break; - } else { - *newline = 0; - resolv_conf_parse_line(start, flags); - start = newline + 1; - } - } - - if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) { - /* no nameservers were configured. */ - evdns_nameserver_ip_add("127.0.0.1"); - err = 6; - } - if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) { - search_set_from_hostname(); - } - -out2: - free(resolv); -out1: - close(fd); - return err; -} - -#ifdef WIN32 -/* Add multiple nameservers from a space-or-comma-separated list. */ -static int -evdns_nameserver_ip_add_line(const char *ips) { - const char *addr; - char *buf; - int r; - while (*ips) { - while (ISSPACE(*ips) || *ips == ',' || *ips == '\t') - ++ips; - addr = ips; - while (ISDIGIT(*ips) || *ips == '.' || *ips == ':') - ++ips; - buf = malloc(ips-addr+1); - if (!buf) return 4; - memcpy(buf, addr, ips-addr); - buf[ips-addr] = '\0'; - r = evdns_nameserver_ip_add(buf); - free(buf); - if (r) return r; - } - return 0; -} - -typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*); - -/* Use the windows GetNetworkParams interface in iphlpapi.dll to */ -/* figure out what our nameservers are. */ -static int -load_nameservers_with_getnetworkparams(void) -{ - /* Based on MSDN examples and inspection of c-ares code. */ - FIXED_INFO *fixed; - HMODULE handle = 0; - ULONG size = sizeof(FIXED_INFO); - void *buf = NULL; - int status = 0, r, added_any; - IP_ADDR_STRING *ns; - GetNetworkParams_fn_t fn; - - if (!(handle = LoadLibrary("iphlpapi.dll"))) { - log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll"); - status = -1; - goto done; - } - if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { - log(EVDNS_LOG_WARN, "Could not get address of function."); - status = -1; - goto done; - } - - buf = malloc(size); - if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) { - status = -1; - goto done; - } - if (r != ERROR_SUCCESS) { - free(buf); - buf = malloc(size); - if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG, "fn() failed."); - status = -1; - goto done; - } - } - - assert(fixed); - added_any = 0; - ns = &(fixed->DnsServerList); - while (ns) { - r = evdns_nameserver_ip_add_line(ns->IpAddress.String); - if (r) { - log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d", - (ns->IpAddress.String),(int)GetLastError()); - status = r; - goto done; - } else { - log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String); - } - - added_any++; - ns = ns->Next; - } - - if (!added_any) { - log(EVDNS_LOG_DEBUG, "No nameservers added."); - status = -1; - } - - done: - if (buf) - free(buf); - if (handle) - FreeLibrary(handle); - return status; -} - -static int -config_nameserver_from_reg_key(HKEY key, const char *subkey) -{ - char *buf; - DWORD bufsz = 0, type = 0; - int status = 0; - - if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz) - != ERROR_MORE_DATA) - return -1; - if (!(buf = malloc(bufsz))) - return -1; - - if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz) - == ERROR_SUCCESS && bufsz > 1) { - status = evdns_nameserver_ip_add_line(buf); - } - - free(buf); - return status; -} - -#define SERVICES_KEY "System\\CurrentControlSet\\Services\\" -#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP" -#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters" - -static int -load_nameservers_from_registry(void) -{ - int found = 0; - int r; -#define TRY(k, name) \ - if (!found && config_nameserver_from_reg_key(k,name) == 0) { \ - log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \ - found = 1; \ - } else if (!found) { \ - log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \ - #k,#name); \ - } - - if (((int)GetVersion()) > 0) { /* NT */ - HKEY nt_key = 0, interfaces_key = 0; - - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, - KEY_READ, &nt_key) != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError()); - return -1; - } - r = RegOpenKeyEx(nt_key, "Interfaces", 0, - KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, - &interfaces_key); - if (r != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError()); - return -1; - } - TRY(nt_key, "NameServer"); - TRY(nt_key, "DhcpNameServer"); - TRY(interfaces_key, "NameServer"); - TRY(interfaces_key, "DhcpNameServer"); - RegCloseKey(interfaces_key); - RegCloseKey(nt_key); - } else { - HKEY win_key = 0; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, - KEY_READ, &win_key) != ERROR_SUCCESS) { - log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError()); - return -1; - } - TRY(win_key, "NameServer"); - RegCloseKey(win_key); - } - - if (found == 0) { - log(EVDNS_LOG_WARN,"Didn't find any nameservers."); - } - - return found ? 0 : -1; -#undef TRY -} - -static int -evdns_config_windows_nameservers(void) -{ - if (load_nameservers_with_getnetworkparams() == 0) - return 0; - return load_nameservers_from_registry(); -} -#endif - -int -evdns_init(void) -{ - int res = 0; -#ifdef WIN32 - res = evdns_config_windows_nameservers(); -#else - res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); -#endif - - return (res); -} - -const char * -evdns_err_to_string(int err) -{ - switch (err) { - case DNS_ERR_NONE: return "no error"; - case DNS_ERR_FORMAT: return "misformatted query"; - case DNS_ERR_SERVERFAILED: return "server failed"; - case DNS_ERR_NOTEXIST: return "name does not exist"; - case DNS_ERR_NOTIMPL: return "query not implemented"; - case DNS_ERR_REFUSED: return "refused"; - - case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed"; - case DNS_ERR_UNKNOWN: return "unknown"; - case DNS_ERR_TIMEOUT: return "request timed out"; - case DNS_ERR_SHUTDOWN: return "dns subsystem shut down"; - default: return "[Unknown error code]"; - } -} - -void -evdns_shutdown(int fail_requests) -{ - struct nameserver *server, *server_next; - struct search_domain *dom, *dom_next; - - while (req_head) { - if (fail_requests) - reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_head, &req_head); - } - while (req_waiting_head) { - if (fail_requests) - reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_waiting_head, &req_waiting_head); - } - global_requests_inflight = global_requests_waiting = 0; - - for (server = server_head; server; server = server_next) { - server_next = server->next; - if (server->socket >= 0) - CLOSE_SOCKET(server->socket); - (void) event_del(&server->event); - if (server->state == 0) - (void) event_del(&server->timeout_event); - free(server); - if (server_next == server_head) - break; - } - server_head = NULL; - global_good_nameservers = 0; - - if (global_search_state) { - for (dom = global_search_state->head; dom; dom = dom_next) { - dom_next = dom->next; - free(dom); - } - free(global_search_state); - global_search_state = NULL; - } - evdns_log_fn = NULL; -} - -#ifdef EVDNS_MAIN -void -main_callback(int result, char type, int count, int ttl, - void *addrs, void *orig) { - char *n = (char*)orig; - int i; - for (i = 0; i < count; ++i) { - if (type == DNS_IPv4_A) { - printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i])); - } else if (type == DNS_PTR) { - printf("%s: %s\n", n, ((char**)addrs)[i]); - } - } - if (!count) { - printf("%s: No answer (%d)\n", n, result); - } - fflush(stdout); -} -void -evdns_server_callback(struct evdns_server_request *req, void *data) -{ - int i, r; - (void)data; - /* dummy; give 192.168.11.11 as an answer for all A questions, - * give foo.bar.example.com as an answer for all PTR questions. */ - for (i = 0; i < req->nquestions; ++i) { - u32 ans = htonl(0xc0a80b0bUL); - if (req->questions[i]->type == EVDNS_TYPE_A && - req->questions[i]->dns_question_class == EVDNS_CLASS_INET) { - printf(" -- replying for %s (A)\n", req->questions[i]->name); - r = evdns_server_request_add_a_reply(req, req->questions[i]->name, - 1, &ans, 10); - if (r<0) - printf("eeep, didn't work.\n"); - } else if (req->questions[i]->type == EVDNS_TYPE_PTR && - req->questions[i]->dns_question_class == EVDNS_CLASS_INET) { - printf(" -- replying for %s (PTR)\n", req->questions[i]->name); - r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name, - "foo.bar.example.com", 10); - } else { - printf(" -- skipping %s [%d %d]\n", req->questions[i]->name, - req->questions[i]->type, req->questions[i]->dns_question_class); - } - } - - r = evdns_request_respond(req, 0); - if (r<0) - printf("eeek, couldn't send reply.\n"); -} - -void -logfn(int is_warn, const char *msg) { - (void) is_warn; - fprintf(stderr, "%s\n", msg); -} -int -main(int c, char **v) { - int idx; - int reverse = 0, verbose = 1, servertest = 0; - if (c<2) { - fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]); - fprintf(stderr, "syntax: %s [-servertest]\n", v[0]); - return 1; - } - idx = 1; - while (idx < c && v[idx][0] == '-') { - if (!strcmp(v[idx], "-x")) - reverse = 1; - else if (!strcmp(v[idx], "-v")) - verbose = 1; - else if (!strcmp(v[idx], "-servertest")) - servertest = 1; - else - fprintf(stderr, "Unknown option %s\n", v[idx]); - ++idx; - } - event_init(); - if (verbose) - evdns_set_log_fn(logfn); - evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf"); - if (servertest) { - int sock; - struct sockaddr_in my_addr; - sock = socket(PF_INET, SOCK_DGRAM, 0); - evutil_make_socket_nonblocking(sock); - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons(10053); - my_addr.sin_addr.s_addr = INADDR_ANY; - if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) { - perror("bind"); - exit(1); - } - evdns_add_server_port(sock, 0, evdns_server_callback, NULL); - } - for (; idx < c; ++idx) { - if (reverse) { - struct in_addr addr; - if (!inet_aton(v[idx], &addr)) { - fprintf(stderr, "Skipping non-IP %s\n", v[idx]); - continue; - } - fprintf(stderr, "resolving %s...\n",v[idx]); - evdns_resolve_reverse(&addr, 0, main_callback, v[idx]); - } else { - fprintf(stderr, "resolving (fwd) %s...\n",v[idx]); - evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]); - } - } - fflush(stdout); - event_dispatch(); - return 0; -} -#endif diff --git a/extra/libevent/evdns.h b/extra/libevent/evdns.h deleted file mode 100644 index 0ad22712962..00000000000 --- a/extra/libevent/evdns.h +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (c) 2006 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * The original DNS code is due to Adam Langley with heavy - * modifications by Nick Mathewson. Adam put his DNS software in the - * public domain. You can find his original copyright below. Please, - * aware that the code as part of libevent is governed by the 3-clause - * BSD license above. - * - * This software is Public Domain. To view a copy of the public domain dedication, - * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to - * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. - * - * I ask and expect, but do not require, that all derivative works contain an - * attribution similar to: - * Parts developed by Adam Langley - * - * You may wish to replace the word "Parts" with something else depending on - * the amount of original code. - * - * (Derivative works does not include programs which link against, run or include - * the source verbatim in their source distributions) - */ - -/** @file evdns.h - * - * Welcome, gentle reader - * - * Async DNS lookups are really a whole lot harder than they should be, - * mostly stemming from the fact that the libc resolver has never been - * very good at them. Before you use this library you should see if libc - * can do the job for you with the modern async call getaddrinfo_a - * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, - * please continue. - * - * This code is based on libevent and you must call event_init before - * any of the APIs in this file. You must also seed the OpenSSL random - * source if you are using OpenSSL for ids (see below). - * - * This library is designed to be included and shipped with your source - * code. You statically link with it. You should also test for the - * existence of strtok_r and define HAVE_STRTOK_R if you have it. - * - * The DNS protocol requires a good source of id numbers and these - * numbers should be unpredictable for spoofing reasons. There are - * three methods for generating them here and you must define exactly - * one of them. In increasing order of preference: - * - * DNS_USE_GETTIMEOFDAY_FOR_ID: - * Using the bottom 16 bits of the usec result from gettimeofday. This - * is a pretty poor solution but should work anywhere. - * DNS_USE_CPU_CLOCK_FOR_ID: - * Using the bottom 16 bits of the nsec result from the CPU's time - * counter. This is better, but may not work everywhere. Requires - * POSIX realtime support and you'll need to link against -lrt on - * glibc systems at least. - * DNS_USE_OPENSSL_FOR_ID: - * Uses the OpenSSL RAND_bytes call to generate the data. You must - * have seeded the pool before making any calls to this library. - * - * The library keeps track of the state of nameservers and will avoid - * them when they go down. Otherwise it will round robin between them. - * - * Quick start guide: - * #include "evdns.h" - * void callback(int result, char type, int count, int ttl, - * void *addresses, void *arg); - * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); - * evdns_resolve("www.hostname.com", 0, callback, NULL); - * - * When the lookup is complete the callback function is called. The - * first argument will be one of the DNS_ERR_* defines in evdns.h. - * Hopefully it will be DNS_ERR_NONE, in which case type will be - * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time - * which the data can be cached for (in seconds), addresses will point - * to an array of uint32_t's and arg will be whatever you passed to - * evdns_resolve. - * - * Searching: - * - * In order for this library to be a good replacement for glibc's resolver it - * supports searching. This involves setting a list of default domains, in - * which names will be queried for. The number of dots in the query name - * determines the order in which this list is used. - * - * Searching appears to be a single lookup from the point of view of the API, - * although many DNS queries may be generated from a single call to - * evdns_resolve. Searching can also drastically slow down the resolution - * of names. - * - * To disable searching: - * 1. Never set it up. If you never call evdns_resolv_conf_parse or - * evdns_search_add then no searching will occur. - * - * 2. If you do call evdns_resolv_conf_parse then don't pass - * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). - * - * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. - * - * The order of searches depends on the number of dots in the name. If the - * number is greater than the ndots setting then the names is first tried - * globally. Otherwise each search domain is appended in turn. - * - * The ndots setting can either be set from a resolv.conf, or by calling - * evdns_search_ndots_set. - * - * For example, with ndots set to 1 (the default) and a search domain list of - * ["myhome.net"]: - * Query: www - * Order: www.myhome.net, www. - * - * Query: www.abc - * Order: www.abc., www.abc.myhome.net - * - * Internals: - * - * Requests are kept in two queues. The first is the inflight queue. In - * this queue requests have an allocated transaction id and nameserver. - * They will soon be transmitted if they haven't already been. - * - * The second is the waiting queue. The size of the inflight ring is - * limited and all other requests wait in waiting queue for space. This - * bounds the number of concurrent requests so that we don't flood the - * nameserver. Several algorithms require a full walk of the inflight - * queue and so bounding its size keeps thing going nicely under huge - * (many thousands of requests) loads. - * - * If a nameserver loses too many requests it is considered down and we - * try not to use it. After a while we send a probe to that nameserver - * (a lookup for google.com) and, if it replies, we consider it working - * again. If the nameserver fails a probe we wait longer to try again - * with the next probe. - */ - -#ifndef EVENTDNS_H -#define EVENTDNS_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* For integer types. */ -#include - -/** Error codes 0-5 are as described in RFC 1035. */ -#define DNS_ERR_NONE 0 -/** The name server was unable to interpret the query */ -#define DNS_ERR_FORMAT 1 -/** The name server was unable to process this query due to a problem with the - * name server */ -#define DNS_ERR_SERVERFAILED 2 -/** The domain name does not exist */ -#define DNS_ERR_NOTEXIST 3 -/** The name server does not support the requested kind of query */ -#define DNS_ERR_NOTIMPL 4 -/** The name server refuses to reform the specified operation for policy - * reasons */ -#define DNS_ERR_REFUSED 5 -/** The reply was truncated or ill-formated */ -#define DNS_ERR_TRUNCATED 65 -/** An unknown error occurred */ -#define DNS_ERR_UNKNOWN 66 -/** Communication with the server timed out */ -#define DNS_ERR_TIMEOUT 67 -/** The request was canceled because the DNS subsystem was shut down. */ -#define DNS_ERR_SHUTDOWN 68 - -#define DNS_IPv4_A 1 -#define DNS_PTR 2 -#define DNS_IPv6_AAAA 3 - -#define DNS_QUERY_NO_SEARCH 1 - -#define DNS_OPTION_SEARCH 1 -#define DNS_OPTION_NAMESERVERS 2 -#define DNS_OPTION_MISC 4 -#define DNS_OPTIONS_ALL 7 - -/** - * The callback that contains the results from a lookup. - * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA - * - count contains the number of addresses of form type - * - ttl is the number of seconds the resolution may be cached for. - * - addresses needs to be cast according to type - */ -typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg); - -/** - Initialize the asynchronous DNS library. - - This function initializes support for non-blocking name resolution by - calling evdns_resolv_conf_parse() on UNIX and - evdns_config_windows_nameservers() on Windows. - - @return 0 if successful, or -1 if an error occurred - @see evdns_shutdown() - */ -int evdns_init(void); - - -/** - Shut down the asynchronous DNS resolver and terminate all active requests. - - If the 'fail_requests' option is enabled, all active requests will return - an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, - the requests will be silently discarded. - - @param fail_requests if zero, active requests will be aborted; if non-zero, - active requests will return DNS_ERR_SHUTDOWN. - @see evdns_init() - */ -void evdns_shutdown(int fail_requests); - - -/** - Convert a DNS error code to a string. - - @param err the DNS error code - @return a string containing an explanation of the error code -*/ -const char *evdns_err_to_string(int err); - - -/** - Add a nameserver. - - The address should be an IPv4 address in network byte order. - The type of address is chosen so that it matches in_addr.s_addr. - - @param address an IP address in network byte order - @return 0 if successful, or -1 if an error occurred - @see evdns_nameserver_ip_add() - */ -int evdns_nameserver_add(unsigned long int address); - - -/** - Get the number of configured nameservers. - - This returns the number of configured nameservers (not necessarily the - number of running nameservers). This is useful for double-checking - whether our calls to the various nameserver configuration functions - have been successful. - - @return the number of configured nameservers - @see evdns_nameserver_add() - */ -int evdns_count_nameservers(void); - - -/** - Remove all configured nameservers, and suspend all pending resolves. - - Resolves will not necessarily be re-attempted until evdns_resume() is called. - - @return 0 if successful, or -1 if an error occurred - @see evdns_resume() - */ -int evdns_clear_nameservers_and_suspend(void); - - -/** - Resume normal operation and continue any suspended resolve requests. - - Re-attempt resolves left in limbo after an earlier call to - evdns_clear_nameservers_and_suspend(). - - @return 0 if successful, or -1 if an error occurred - @see evdns_clear_nameservers_and_suspend() - */ -int evdns_resume(void); - - -/** - Add a nameserver. - - This wraps the evdns_nameserver_add() function by parsing a string as an IP - address and adds it as a nameserver. - - @return 0 if successful, or -1 if an error occurred - @see evdns_nameserver_add() - */ -int evdns_nameserver_ip_add(const char *ip_as_string); - - -/** - Lookup an A record for a given name. - - @param name a DNS hostname - @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. - @param callback a callback function to invoke when the request is completed - @param ptr an argument to pass to the callback function - @return 0 if successful, or -1 if an error occurred - @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() - */ -int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr); - - -/** - Lookup an AAAA record for a given name. - - @param name a DNS hostname - @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. - @param callback a callback function to invoke when the request is completed - @param ptr an argument to pass to the callback function - @return 0 if successful, or -1 if an error occurred - @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() - */ -int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); - -struct in_addr; -struct in6_addr; - -/** - Lookup a PTR record for a given IP address. - - @param in an IPv4 address - @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. - @param callback a callback function to invoke when the request is completed - @param ptr an argument to pass to the callback function - @return 0 if successful, or -1 if an error occurred - @see evdns_resolve_reverse_ipv6() - */ -int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); - - -/** - Lookup a PTR record for a given IPv6 address. - - @param in an IPv6 address - @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. - @param callback a callback function to invoke when the request is completed - @param ptr an argument to pass to the callback function - @return 0 if successful, or -1 if an error occurred - @see evdns_resolve_reverse_ipv6() - */ -int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); - - -/** - Set the value of a configuration option. - - The currently available configuration options are: - - ndots, timeout, max-timeouts, max-inflight, and attempts - - @param option the name of the configuration option to be modified - @param val the value to be set - @param flags either 0 | DNS_OPTION_SEARCH | DNS_OPTION_MISC - @return 0 if successful, or -1 if an error occurred - */ -int evdns_set_option(const char *option, const char *val, int flags); - - -/** - Parse a resolv.conf file. - - The 'flags' parameter determines what information is parsed from the - resolv.conf file. See the man page for resolv.conf for the format of this - file. - - The following directives are not parsed from the file: sortlist, rotate, - no-check-names, inet6, debug. - - If this function encounters an error, the possible return values are: 1 = - failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of - memory, 5 = short read from file, 6 = no nameservers listed in the file - - @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| - DNS_OPTIONS_ALL - @param filename the path to the resolv.conf file - @return 0 if successful, or various positive error codes if an error - occurred (see above) - @see resolv.conf(3), evdns_config_windows_nameservers() - */ -int evdns_resolv_conf_parse(int flags, const char *const filename); - - -/** - Obtain nameserver information using the Windows API. - - Attempt to configure a set of nameservers based on platform settings on - a win32 host. Preferentially tries to use GetNetworkParams; if that fails, - looks in the registry. - - @return 0 if successful, or -1 if an error occurred - @see evdns_resolv_conf_parse() - */ -#ifdef MS_WINDOWS -int evdns_config_windows_nameservers(void); -#endif - - -/** - Clear the list of search domains. - */ -void evdns_search_clear(void); - - -/** - Add a domain to the list of search domains - - @param domain the domain to be added to the search list - */ -void evdns_search_add(const char *domain); - - -/** - Set the 'ndots' parameter for searches. - - Sets the number of dots which, when found in a name, causes - the first query to be without any search domain. - - @param ndots the new ndots parameter - */ -void evdns_search_ndots_set(const int ndots); - -/** - A callback that is invoked when a log message is generated - - @param is_warning indicates if the log message is a 'warning' - @param msg the content of the log message - */ -typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); - - -/** - Set the callback function to handle log messages. - - @param fn the callback to be invoked when a log message is generated - */ -void evdns_set_log_fn(evdns_debug_log_fn_type fn); - -/** - Set a callback that will be invoked to generate transaction IDs. By - default, we pick transaction IDs based on the current clock time. - - @param fn the new callback, or NULL to use the default. - */ -void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); - -#define DNS_NO_SEARCH 1 - -/* - * Structures and functions used to implement a DNS server. - */ - -struct evdns_server_request { - int flags; - int nquestions; - struct evdns_server_question **questions; -}; -struct evdns_server_question { - int type; -#ifdef __cplusplus - int dns_question_class; -#else - /* You should refer to this field as "dns_question_class". The - * name "class" works in C for backward compatibility, and will be - * removed in a future version. (1.5 or later). */ - int class; -#define dns_question_class class -#endif - char name[1]; -}; -typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); -#define EVDNS_ANSWER_SECTION 0 -#define EVDNS_AUTHORITY_SECTION 1 -#define EVDNS_ADDITIONAL_SECTION 2 - -#define EVDNS_TYPE_A 1 -#define EVDNS_TYPE_NS 2 -#define EVDNS_TYPE_CNAME 5 -#define EVDNS_TYPE_SOA 6 -#define EVDNS_TYPE_PTR 12 -#define EVDNS_TYPE_MX 15 -#define EVDNS_TYPE_TXT 16 -#define EVDNS_TYPE_AAAA 28 - -#define EVDNS_QTYPE_AXFR 252 -#define EVDNS_QTYPE_ALL 255 - -#define EVDNS_CLASS_INET 1 - -struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data); -void evdns_close_server_port(struct evdns_server_port *port); - -int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data); -int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); -int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); -int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); -int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); - -int evdns_server_request_respond(struct evdns_server_request *req, int err); -int evdns_server_request_drop(struct evdns_server_request *req); -struct sockaddr; -int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len); - -#ifdef __cplusplus -} -#endif - -#endif /* !EVENTDNS_H */ diff --git a/extra/libevent/event-internal.h b/extra/libevent/event-internal.h deleted file mode 100644 index 59a089ddec4..00000000000 --- a/extra/libevent/event-internal.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVENT_INTERNAL_H_ -#define _EVENT_INTERNAL_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "config.h" -#include "min_heap.h" -#include "evsignal.h" - -struct eventop { - const char *name; - void *(*init)(struct event_base *); - int (*add)(void *, struct event *); - int (*del)(void *, struct event *); - int (*dispatch)(struct event_base *, void *, struct timeval *); - void (*dealloc)(struct event_base *, void *); - /* set if we need to reinitialize the event base */ - int need_reinit; -}; - -struct event_base { - const struct eventop *evsel; - void *evbase; - int event_count; /* counts number of total events */ - int event_count_active; /* counts number of active events */ - - int event_gotterm; /* Set to terminate loop */ - int event_break; /* Set to terminate loop immediately */ - - /* active event management */ - struct event_list **activequeues; - int nactivequeues; - - /* signal handling info */ - struct evsignal_info sig; - - struct event_list eventqueue; - struct timeval event_tv; - - struct min_heap timeheap; -}; - -/* Internal use only: Functions that might be missing from */ -/* These following macros are copied from BSD sys/queue.h - Copyright (c) 1991, 1993, The Regents of the University of California. - All rights reserved. -*/ -#ifndef TAILQ_EMPTY -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#endif /* TAILQ_EMPTY */ -#ifndef HAVE_TAILQFOREACH -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) -#endif /* TAILQ_FOREACH */ - -int _evsignal_set_handler(struct event_base *base, int evsignal, - void (*fn)(int)); -int _evsignal_restore_handler(struct event_base *base, int evsignal); - -#ifdef __cplusplus -} -#endif - -#endif /* _EVENT_INTERNAL_H_ */ diff --git a/extra/libevent/event.c b/extra/libevent/event.c deleted file mode 100644 index 07498c709c8..00000000000 --- a/extra/libevent/event.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Copyright (c) 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#include "misc.h" -#endif -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#ifndef WIN32 -#include -#endif -#include -#include -#include -#include -#include - -#include "event.h" -#include "event-internal.h" -#include "evutil.h" -#include "log.h" - -#ifdef HAVE_EVENT_PORTS -extern const struct eventop evportops; -#endif -#ifdef HAVE_SELECT -extern const struct eventop selectops; -#endif -#ifdef HAVE_POLL -extern const struct eventop pollops; -#endif -#ifdef HAVE_EPOLL -extern const struct eventop epollops; -#endif -#ifdef HAVE_WORKING_KQUEUE -extern const struct eventop kqops; -#endif -#ifdef HAVE_DEVPOLL -extern const struct eventop devpollops; -#endif -#ifdef WIN32 -extern const struct eventop win32ops; -#endif - -/* In order of preference */ -const struct eventop *eventops[] = { -#ifdef HAVE_EVENT_PORTS - &evportops, -#endif -#ifdef HAVE_WORKING_KQUEUE - &kqops, -#endif -#ifdef HAVE_EPOLL - &epollops, -#endif -#ifdef HAVE_DEVPOLL - &devpollops, -#endif -#ifdef HAVE_POLL - &pollops, -#endif -#ifdef HAVE_SELECT - &selectops, -#endif -#ifdef WIN32 - &win32ops, -#endif - NULL -}; - -/* Global state */ -struct event_base *current_base = NULL; -extern struct event_base *evsignal_base; -static int use_monotonic; - -/* Handle signals - This is a deprecated interface */ -int (*event_sigcb)(void); /* Signal callback when gotsig is set */ -volatile sig_atomic_t event_gotsig; /* Set in signal handler */ - -/* Prototypes */ -static void event_queue_insert(struct event_base *, struct event *, int); -static void event_queue_remove(struct event_base *, struct event *, int); -static int event_haveevents(struct event_base *); - -static void event_process_active(struct event_base *); - -static int timeout_next(struct event_base *, struct timeval **); -static void timeout_process(struct event_base *); -static void timeout_correct(struct event_base *, struct timeval *); - -static void -detect_monotonic(void) -{ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - use_monotonic = 1; -#endif -} - -static int -gettime(struct timeval *tp) -{ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - - if (use_monotonic) { - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) - return (-1); - - tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; - return (0); - } -#endif - - return (gettimeofday(tp, NULL)); -} - -struct event_base * -event_init(void) -{ - struct event_base *base = event_base_new(); - - if (base != NULL) - current_base = base; - - return (base); -} - -struct event_base * -event_base_new(void) -{ - int i; - struct event_base *base; - - if ((base = calloc(1, sizeof(struct event_base))) == NULL) - event_err(1, "%s: calloc", __func__); - - event_sigcb = NULL; - event_gotsig = 0; - - detect_monotonic(); - gettime(&base->event_tv); - - min_heap_ctor(&base->timeheap); - TAILQ_INIT(&base->eventqueue); - TAILQ_INIT(&base->sig.signalqueue); - base->sig.ev_signal_pair[0] = -1; - base->sig.ev_signal_pair[1] = -1; - - base->evbase = NULL; - for (i = 0; eventops[i] && !base->evbase; i++) { - base->evsel = eventops[i]; - - base->evbase = base->evsel->init(base); - } - - if (base->evbase == NULL) - event_errx(1, "%s: no event mechanism available", __func__); - - if (getenv("EVENT_SHOW_METHOD")) - event_msgx("libevent using: %s\n", - base->evsel->name); - - /* allocate a single active event queue */ - event_base_priority_init(base, 1); - - return (base); -} - -void -event_base_free(struct event_base *base) -{ - int i, n_deleted=0; - struct event *ev; - - if (base == NULL && current_base) - base = current_base; - if (base == current_base) - current_base = NULL; - - /* XXX(niels) - check for internal events first */ - assert(base); - /* Delete all non-internal events. */ - for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) { - struct event *next = TAILQ_NEXT(ev, ev_next); - if (!(ev->ev_flags & EVLIST_INTERNAL)) { - event_del(ev); - ++n_deleted; - } - ev = next; - } - while ((ev = min_heap_top(&base->timeheap)) != NULL) { - event_del(ev); - ++n_deleted; - } - - if (n_deleted) - event_debug(("%s: %d events were still set in base", - __func__, n_deleted)); - - if (base->evsel->dealloc != NULL) - base->evsel->dealloc(base, base->evbase); - - for (i = 0; i < base->nactivequeues; ++i) - assert(TAILQ_EMPTY(base->activequeues[i])); - - assert(min_heap_empty(&base->timeheap)); - min_heap_dtor(&base->timeheap); - - for (i = 0; i < base->nactivequeues; ++i) - free(base->activequeues[i]); - free(base->activequeues); - - assert(TAILQ_EMPTY(&base->eventqueue)); - - free(base); -} - -/* reinitialized the event base after a fork */ -int -event_reinit(struct event_base *base) -{ - const struct eventop *evsel = base->evsel; - void *evbase = base->evbase; - int res = 0; - struct event *ev; - - /* check if this event mechanism requires reinit */ - if (!evsel->need_reinit) - return (0); - - if (base->evsel->dealloc != NULL) - base->evsel->dealloc(base, base->evbase); - base->evbase = evsel->init(base); - if (base->evbase == NULL) - event_errx(1, "%s: could not reinitialize event mechanism", - __func__); - - TAILQ_FOREACH(ev, &base->eventqueue, ev_next) { - if (evsel->add(evbase, ev) == -1) - res = -1; - } - - return (res); -} - -int -event_priority_init(int npriorities) -{ - return event_base_priority_init(current_base, npriorities); -} - -int -event_base_priority_init(struct event_base *base, int npriorities) -{ - int i; - - if (base->event_count_active) - return (-1); - - if (base->nactivequeues && npriorities != base->nactivequeues) { - for (i = 0; i < base->nactivequeues; ++i) { - free(base->activequeues[i]); - } - free(base->activequeues); - } - - /* Allocate our priority queues */ - base->nactivequeues = npriorities; - base->activequeues = (struct event_list **)calloc(base->nactivequeues, - npriorities * sizeof(struct event_list *)); - if (base->activequeues == NULL) - event_err(1, "%s: calloc", __func__); - - for (i = 0; i < base->nactivequeues; ++i) { - base->activequeues[i] = malloc(sizeof(struct event_list)); - if (base->activequeues[i] == NULL) - event_err(1, "%s: malloc", __func__); - TAILQ_INIT(base->activequeues[i]); - } - - return (0); -} - -int -event_haveevents(struct event_base *base) -{ - return (base->event_count > 0); -} - -/* - * Active events are stored in priority queues. Lower priorities are always - * process before higher priorities. Low priority events can starve high - * priority ones. - */ - -static void -event_process_active(struct event_base *base) -{ - struct event *ev; - struct event_list *activeq = NULL; - int i; - short ncalls; - - for (i = 0; i < base->nactivequeues; ++i) { - if (TAILQ_FIRST(base->activequeues[i]) != NULL) { - activeq = base->activequeues[i]; - break; - } - } - - assert(activeq != NULL); - - for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { - if (ev->ev_events & EV_PERSIST) - event_queue_remove(base, ev, EVLIST_ACTIVE); - else - event_del(ev); - - /* Allows deletes to work */ - ncalls = ev->ev_ncalls; - ev->ev_pncalls = &ncalls; - while (ncalls) { - ncalls--; - ev->ev_ncalls = ncalls; - (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); - if (event_gotsig || base->event_break) - return; - } - } -} - -/* - * Wait continously for events. We exit only if no events are left. - */ - -int -event_dispatch(void) -{ - return (event_loop(0)); -} - -int -event_base_dispatch(struct event_base *event_base) -{ - return (event_base_loop(event_base, 0)); -} - -const char * -event_base_get_method(struct event_base *base) -{ - assert(base); - return (base->evsel->name); -} - -static void -event_loopexit_cb(int fd __attribute__((unused)), - short what __attribute__((unused)), void *arg) -{ - struct event_base *base = arg; - base->event_gotterm = 1; -} - -/* not thread safe */ -int -event_loopexit(struct timeval *tv) -{ - return (event_once(-1, EV_TIMEOUT, &event_loopexit_cb, - current_base, tv)); -} - -int -event_base_loopexit(struct event_base *event_base, struct timeval *tv) -{ - return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb, - event_base, tv)); -} - -/* not thread safe */ -int -event_loopbreak(void) -{ - return (event_base_loopbreak(current_base)); -} - -int -event_base_loopbreak(struct event_base *event_base) -{ - if (event_base == NULL) - return (-1); - - event_base->event_break = 1; - return (0); -} - - - -/* not thread safe */ - -int -event_loop(int flags) -{ - return event_base_loop(current_base, flags); -} - -int -event_base_loop(struct event_base *base, int flags) -{ - const struct eventop *evsel = base->evsel; - void *evbase = base->evbase; - struct timeval tv; - struct timeval *tv_p; - int res, done; - - if(!TAILQ_EMPTY(&base->sig.signalqueue)) - evsignal_base = base; - done = 0; - while (!done) { - /* Terminate the loop if we have been asked to */ - if (base->event_gotterm) { - base->event_gotterm = 0; - break; - } - - if (base->event_break) { - base->event_break = 0; - break; - } - - /* You cannot use this interface for multi-threaded apps */ - while (event_gotsig) { - event_gotsig = 0; - if (event_sigcb) { - res = (*event_sigcb)(); - if (res == -1) { - errno = EINTR; - return (-1); - } - } - } - - timeout_correct(base, &tv); - - tv_p = &tv; - if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { - timeout_next(base, &tv_p); - } else { - /* - * if we have active events, we just poll new events - * without waiting. - */ - evutil_timerclear(&tv); - } - - /* If we have no events, we just exit */ - if (!event_haveevents(base)) { - event_debug(("%s: no events registered.", __func__)); - return (1); - } - - res = evsel->dispatch(base, evbase, tv_p); - - if (res == -1) - return (-1); - - timeout_process(base); - - if (base->event_count_active) { - event_process_active(base); - if (!base->event_count_active && (flags & EVLOOP_ONCE)) - done = 1; - } else if (flags & EVLOOP_NONBLOCK) - done = 1; - } - - event_debug(("%s: asked to terminate loop.", __func__)); - return (0); -} - -/* Sets up an event for processing once */ - -struct event_once { - struct event ev; - - void (*cb)(int, short, void *); - void *arg; -}; - -/* One-time callback, it deletes itself */ - -static void -event_once_cb(int fd, short events, void *arg) -{ - struct event_once *eonce = arg; - - (*eonce->cb)(fd, events, eonce->arg); - free(eonce); -} - -/* not threadsafe, event scheduled once. */ -int -event_once(int fd, short events, - void (*callback)(int, short, void *), void *arg, struct timeval *tv) -{ - return event_base_once(current_base, fd, events, callback, arg, tv); -} - -/* Schedules an event once */ -int -event_base_once(struct event_base *base, int fd, short events, - void (*callback)(int, short, void *), void *arg, struct timeval *tv) -{ - struct event_once *eonce; - struct timeval etv; - int res; - - /* We cannot support signals that just fire once */ - if (events & EV_SIGNAL) - return (-1); - - if ((eonce = calloc(1, sizeof(struct event_once))) == NULL) - return (-1); - - eonce->cb = callback; - eonce->arg = arg; - - if (events == EV_TIMEOUT) { - if (tv == NULL) { - evutil_timerclear(&etv); - tv = &etv; - } - - evtimer_set(&eonce->ev, event_once_cb, eonce); - } else if (events & (EV_READ|EV_WRITE)) { - events &= EV_READ|EV_WRITE; - - event_set(&eonce->ev, fd, events, event_once_cb, eonce); - } else { - /* Bad event combination */ - free(eonce); - return (-1); - } - - res = event_base_set(base, &eonce->ev); - if (res == 0) - res = event_add(&eonce->ev, tv); - if (res != 0) { - free(eonce); - return (res); - } - - return (0); -} - -void -event_set(struct event *ev, int fd, short events, - void (*callback)(int, short, void *), void *arg) -{ - /* Take the current base - caller needs to set the real base later */ - ev->ev_base = current_base; - - ev->ev_callback = callback; - ev->ev_arg = arg; - ev->ev_fd = fd; - ev->ev_events = events; - ev->ev_res = 0; - ev->ev_flags = EVLIST_INIT; - ev->ev_ncalls = 0; - ev->ev_pncalls = NULL; - - min_heap_elem_init(ev); - - /* by default, we put new events into the middle priority */ - if(current_base) - ev->ev_pri = current_base->nactivequeues/2; -} - -int -event_base_set(struct event_base *base, struct event *ev) -{ - /* Only innocent events may be assigned to a different base */ - if (ev->ev_flags != EVLIST_INIT) - return (-1); - - ev->ev_base = base; - ev->ev_pri = base->nactivequeues/2; - - return (0); -} - -/* - * Set's the priority of an event - if an event is already scheduled - * changing the priority is going to fail. - */ - -int -event_priority_set(struct event *ev, int pri) -{ - if (ev->ev_flags & EVLIST_ACTIVE) - return (-1); - if (pri < 0 || pri >= ev->ev_base->nactivequeues) - return (-1); - - ev->ev_pri = pri; - - return (0); -} - -/* - * Checks if a specific event is pending or scheduled. - */ - -int -event_pending(struct event *ev, short event, struct timeval *tv) -{ - struct timeval now, res; - int flags = 0; - - if (ev->ev_flags & EVLIST_INSERTED) - flags |= (ev->ev_events & (EV_READ|EV_WRITE)); - if (ev->ev_flags & EVLIST_ACTIVE) - flags |= ev->ev_res; - if (ev->ev_flags & EVLIST_TIMEOUT) - flags |= EV_TIMEOUT; - if (ev->ev_flags & EVLIST_SIGNAL) - flags |= EV_SIGNAL; - - event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL); - - /* See if there is a timeout that we should report */ - if (tv != NULL && (flags & event & EV_TIMEOUT)) { - gettime(&now); - evutil_timersub(&ev->ev_timeout, &now, &res); - /* correctly remap to real time */ - gettimeofday(&now, NULL); - evutil_timeradd(&now, &res, tv); - } - - return (flags & event); -} - -int -event_add(struct event *ev, struct timeval *tv) -{ - struct event_base *base = ev->ev_base; - const struct eventop *evsel = base->evsel; - void *evbase = base->evbase; - - event_debug(( - "event_add: event: %p, %s%s%scall %p", - ev, - ev->ev_events & EV_READ ? "EV_READ " : " ", - ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", - tv ? "EV_TIMEOUT " : " ", - ev->ev_callback)); - - assert(!(ev->ev_flags & ~EVLIST_ALL)); - - if (tv != NULL) { - struct timeval now; - - if (ev->ev_flags & EVLIST_TIMEOUT) - event_queue_remove(base, ev, EVLIST_TIMEOUT); - else if (min_heap_reserve(&base->timeheap, - 1 + min_heap_size(&base->timeheap)) == -1) - return (-1); /* ENOMEM == errno */ - - /* Check if it is active due to a timeout. Rescheduling - * this timeout before the callback can be executed - * removes it from the active list. */ - if ((ev->ev_flags & EVLIST_ACTIVE) && - (ev->ev_res & EV_TIMEOUT)) { - /* See if we are just active executing this - * event in a loop - */ - if (ev->ev_ncalls && ev->ev_pncalls) { - /* Abort loop */ - *ev->ev_pncalls = 0; - } - - event_queue_remove(base, ev, EVLIST_ACTIVE); - } - - gettime(&now); - evutil_timeradd(&now, tv, &ev->ev_timeout); - - event_debug(( - "event_add: timeout in %d seconds, call %p", - tv->tv_sec, ev->ev_callback)); - - event_queue_insert(base, ev, EVLIST_TIMEOUT); - } - - if ((ev->ev_events & (EV_READ|EV_WRITE)) && - !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { - int res = evsel->add(evbase, ev); - if (res != -1) - event_queue_insert(base, ev, EVLIST_INSERTED); - - return (res); - } else if ((ev->ev_events & EV_SIGNAL) && - !(ev->ev_flags & EVLIST_SIGNAL)) { - int res = evsel->add(evbase, ev); - if (res != -1) - event_queue_insert(base, ev, EVLIST_SIGNAL); - - return (res); - } - - return (0); -} - -int -event_del(struct event *ev) -{ - struct event_base *base; - const struct eventop *evsel; - void *evbase; - - event_debug(("event_del: %p, callback %p", - ev, ev->ev_callback)); - - /* An event without a base has not been added */ - if (ev->ev_base == NULL) - return (-1); - - base = ev->ev_base; - evsel = base->evsel; - evbase = base->evbase; - - assert(!(ev->ev_flags & ~EVLIST_ALL)); - - /* See if we are just active executing this event in a loop */ - if (ev->ev_ncalls && ev->ev_pncalls) { - /* Abort loop */ - *ev->ev_pncalls = 0; - } - - if (ev->ev_flags & EVLIST_TIMEOUT) - event_queue_remove(base, ev, EVLIST_TIMEOUT); - - if (ev->ev_flags & EVLIST_ACTIVE) - event_queue_remove(base, ev, EVLIST_ACTIVE); - - if (ev->ev_flags & EVLIST_INSERTED) { - event_queue_remove(base, ev, EVLIST_INSERTED); - return (evsel->del(evbase, ev)); - } else if (ev->ev_flags & EVLIST_SIGNAL) { - event_queue_remove(base, ev, EVLIST_SIGNAL); - return (evsel->del(evbase, ev)); - } - - return (0); -} - -void -event_active(struct event *ev, int res, short ncalls) -{ - /* We get different kinds of events, add them together */ - if (ev->ev_flags & EVLIST_ACTIVE) { - ev->ev_res |= res; - return; - } - - ev->ev_res = res; - ev->ev_ncalls = ncalls; - ev->ev_pncalls = NULL; - event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE); -} - -static int -timeout_next(struct event_base *base, struct timeval **tv_p) -{ - struct timeval now; - struct event *ev; - struct timeval *tv = *tv_p; - - if ((ev = min_heap_top(&base->timeheap)) == NULL) { - /* if no time-based events are active wait for I/O */ - *tv_p = NULL; - return (0); - } - - if (gettime(&now) == -1) - return (-1); - - if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { - evutil_timerclear(tv); - return (0); - } - - evutil_timersub(&ev->ev_timeout, &now, tv); - - assert(tv->tv_sec >= 0); - assert(tv->tv_usec >= 0); - - event_debug(("timeout_next: in %d seconds", tv->tv_sec)); - return (0); -} - -/* - * Determines if the time is running backwards by comparing the current - * time against the last time we checked. Not needed when using clock - * monotonic. - */ - -static void -timeout_correct(struct event_base *base, struct timeval *tv) -{ - struct event **pev; - unsigned int size; - struct timeval off; - - if (use_monotonic) - return; - - /* Check if time is running backwards */ - gettime(tv); - if (evutil_timercmp(tv, &base->event_tv, >=)) { - base->event_tv = *tv; - return; - } - - event_debug(("%s: time is running backwards, corrected", - __func__)); - evutil_timersub(&base->event_tv, tv, &off); - - /* - * We can modify the key element of the node without destroying - * the key, beause we apply it to all in the right order. - */ - pev = base->timeheap.p; - size = base->timeheap.n; - for (; size-- > 0; ++pev) { - struct timeval *ev_tv = &(**pev).ev_timeout; - evutil_timersub(ev_tv, &off, ev_tv); - } -} - -void -timeout_process(struct event_base *base) -{ - struct timeval now; - struct event *ev; - - if (min_heap_empty(&base->timeheap)) - return; - - gettime(&now); - - while ((ev = min_heap_top(&base->timeheap))) { - if (evutil_timercmp(&ev->ev_timeout, &now, >)) - break; - - /* delete this event from the I/O queues */ - event_del(ev); - - event_debug(("timeout_process: call %p", - ev->ev_callback)); - event_active(ev, EV_TIMEOUT, 1); - } -} - -void -event_queue_remove(struct event_base *base, struct event *ev, int queue) -{ - if (!(ev->ev_flags & queue)) - event_errx(1, "%s: %p(fd %d) not on queue %x", __func__, - ev, ev->ev_fd, queue); - - if (~ev->ev_flags & EVLIST_INTERNAL) - base->event_count--; - - ev->ev_flags &= ~queue; - switch (queue) { - case EVLIST_ACTIVE: - base->event_count_active--; - TAILQ_REMOVE(base->activequeues[ev->ev_pri], - ev, ev_active_next); - break; - case EVLIST_SIGNAL: - TAILQ_REMOVE(&base->sig.signalqueue, ev, ev_signal_next); - break; - case EVLIST_TIMEOUT: - min_heap_erase(&base->timeheap, ev); - break; - case EVLIST_INSERTED: - TAILQ_REMOVE(&base->eventqueue, ev, ev_next); - break; - default: - event_errx(1, "%s: unknown queue %x", __func__, queue); - } -} - -void -event_queue_insert(struct event_base *base, struct event *ev, int queue) -{ - if (ev->ev_flags & queue) { - /* Double insertion is possible for active events */ - if (queue & EVLIST_ACTIVE) - return; - - event_errx(1, "%s: %p(fd %d) already on queue %x", __func__, - ev, ev->ev_fd, queue); - } - - if (~ev->ev_flags & EVLIST_INTERNAL) - base->event_count++; - - ev->ev_flags |= queue; - switch (queue) { - case EVLIST_ACTIVE: - base->event_count_active++; - TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], - ev,ev_active_next); - break; - case EVLIST_SIGNAL: - TAILQ_INSERT_TAIL(&base->sig.signalqueue, ev, ev_signal_next); - break; - case EVLIST_TIMEOUT: { - min_heap_push(&base->timeheap, ev); - break; - } - case EVLIST_INSERTED: - TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); - break; - default: - event_errx(1, "%s: unknown queue %x", __func__, queue); - } -} - -/* Functions for debugging */ - -const char * -event_get_version(void) -{ - return (VERSION); -} - -/* - * No thread-safe interface needed - the information should be the same - * for all threads. - */ - -const char * -event_get_method(void) -{ - return (current_base->evsel->name); -} diff --git a/extra/libevent/event.h b/extra/libevent/event.h deleted file mode 100644 index 078c940770e..00000000000 --- a/extra/libevent/event.h +++ /dev/null @@ -1,1129 +0,0 @@ -/* - * Copyright (c) 2000-2007 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVENT_H_ -#define _EVENT_H_ - -/** @mainpage - - @section intro Introduction - - libevent is an event notification library for developing scalable network - servers. The libevent API provides a mechanism to execute a callback - function when a specific event occurs on a file descriptor or after a - timeout has been reached. Furthermore, libevent also support callbacks due - to signals or regular timeouts. - - libevent is meant to replace the event loop found in event driven network - servers. An application just needs to call event_dispatch() and then add or - remove events dynamically without having to change the event loop. - - Currently, libevent supports /dev/poll, kqueue(2), select(2), poll(2) and - epoll(4). It also has experimental support for real-time signals. The - internal event mechanism is completely independent of the exposed event API, - and a simple update of libevent can provide new functionality without having - to redesign the applications. As a result, Libevent allows for portable - application development and provides the most scalable event notification - mechanism available on an operating system. Libevent can also be used for - multi-threaded aplications; see Steven Grimm's explanation. Libevent should - compile on Linux, *BSD, Mac OS X, Solaris and Windows. - - @section usage Standard usage - - Every program that uses libevent must include the header, and pass - the -levent flag to the linker. Before using any of the functions in the - library, you must call event_init() or event_base_new() to perform one-time - initialization of the libevent library. - - @section event Event notification - - For each file descriptor that you wish to monitor, you must declare an event - structure and call event_set() to initialize the members of the structure. - To enable notification, you add the structure to the list of monitored - events by calling event_add(). The event structure must remain allocated as - long as it is active, so it should be allocated on the heap. Finally, you - call event_dispatch() to loop and dispatch events. - - @section bufferevent I/O Buffers - - libevent provides an abstraction on top of the regular event callbacks. This - abstraction is called a buffered event. A buffered event provides input and - output buffers that get filled and drained automatically. The user of a - buffered event no longer deals directly with the I/O, but instead is reading - from input and writing to output buffers. - - Once initialized via bufferevent_new(), the bufferevent structure can be - used repeatedly with bufferevent_enable() and bufferevent_disable(). - Instead of reading and writing directly to a socket, you would call - bufferevent_read() and bufferevent_write(). - - When read enabled the bufferevent will try to read from the file descriptor - and call the read callback. The write callback is executed whenever the - output buffer is drained below the write low watermark, which is 0 by - default. - - @section timers Timers - - libevent can also be used to create timers that invoke a callback after a - certain amount of time has expired. The evtimer_set() function prepares an - event struct to be used as a timer. To activate the timer, call - evtimer_add(). Timers can be deactivated by calling evtimer_del(). - - @section timeouts Timeouts - - In addition to simple timers, libevent can assign timeout events to file - descriptors that are triggered whenever a certain amount of time has passed - with no activity on a file descriptor. The timeout_set() function - initializes an event struct for use as a timeout. Once initialized, the - event must be activated by using timeout_add(). To cancel the timeout, call - timeout_del(). - - @section evdns Asynchronous DNS resolution - - libevent provides an asynchronous DNS resolver that should be used instead - of the standard DNS resolver functions. These functions can be imported by - including the header in your program. Before using any of the - resolver functions, you must call evdns_init() to initialize the library. To - convert a hostname to an IP address, you call the evdns_resolve_ipv4() - function. To perform a reverse lookup, you would call the - evdns_resolve_reverse() function. All of these functions use callbacks to - avoid blocking while the lookup is performed. - - @section evhttp Event-driven HTTP servers - - libevent provides a very simple event-driven HTTP server that can be - embedded in your program and used to service HTTP requests. - - To use this capability, you need to include the header in your - program. You create the server by calling evhttp_new(). Add addresses and - ports to listen on with evhttp_bind_socket(). You then register one or more - callbacks to handle incoming requests. Each URI can be assigned a callback - via the evhttp_set_cb() function. A generic callback function can also be - registered via evhttp_set_gencb(); this callback will be invoked if no other - callbacks have been registered for a given URI. - - @section evrpc A framework for RPC servers and clients - - libevents provides a framework for creating RPC servers and clients. It - takes care of marshaling and unmarshaling all data structures. - - @section api API Reference - - To browse the complete documentation of the libevent API, click on any of - the following links. - - event.h - The primary libevent header - - evdns.h - Asynchronous DNS resolution - - evhttp.h - An embedded libevent-based HTTP server - - evrpc.h - A framework for creating RPC servers and clients - - */ - -/** @file event.h - - A library for writing event-driven network servers - - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -#ifdef _EVENT_HAVE_SYS_TYPES_H -#include -#endif -#ifdef _EVENT_HAVE_SYS_TIME_H -#include -#endif -#ifdef _EVENT_HAVE_STDINT_H -#include -#endif -#include - -/* For int types. */ -#include - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -typedef unsigned char u_char; -typedef unsigned short u_short; -#endif - -#define EVLIST_TIMEOUT 0x01 -#define EVLIST_INSERTED 0x02 -#define EVLIST_SIGNAL 0x04 -#define EVLIST_ACTIVE 0x08 -#define EVLIST_INTERNAL 0x10 -#define EVLIST_INIT 0x80 - -/* EVLIST_X_ Private space: 0x1000-0xf000 */ -#define EVLIST_ALL (0xf000 | 0x9f) - -#define EV_TIMEOUT 0x01 -#define EV_READ 0x02 -#define EV_WRITE 0x04 -#define EV_SIGNAL 0x08 -#define EV_PERSIST 0x10 /* Persistant event */ - -/* Fix so that ppl dont have to run with */ -#ifndef TAILQ_ENTRY -#define _EVENT_DEFINED_TQENTRY -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} -#endif /* !TAILQ_ENTRY */ - -struct event_base; -struct event { - TAILQ_ENTRY (event) ev_next; - TAILQ_ENTRY (event) ev_active_next; - TAILQ_ENTRY (event) ev_signal_next; - unsigned int min_heap_idx; /* for managing timeouts */ - - struct event_base *ev_base; - - int ev_fd; - short ev_events; - short ev_ncalls; - short *ev_pncalls; /* Allows deletes in callback */ - - struct timeval ev_timeout; - - int ev_pri; /* smaller numbers are higher priority */ - - void (*ev_callback)(int, short, void *arg); - void *ev_arg; - - int ev_res; /* result passed to event callback */ - int ev_flags; -}; - -#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd -#define EVENT_FD(ev) (int)(ev)->ev_fd - -/* - * Key-Value pairs. Can be used for HTTP headers but also for - * query argument parsing. - */ -struct evkeyval { - TAILQ_ENTRY(evkeyval) next; - - char *key; - char *value; -}; - -#ifdef _EVENT_DEFINED_TQENTRY -#undef TAILQ_ENTRY -struct event_list; -struct evkeyvalq; -#undef _EVENT_DEFINED_TQENTRY -#else -TAILQ_HEAD (event_list, event); -TAILQ_HEAD (evkeyvalq, evkeyval); -#endif /* _EVENT_DEFINED_TQENTRY */ - -/** - Initialize the event API. - - Use event_base_new() to initialize a new event base, but does not set - the current_base global. If using only event_base_new(), each event - added must have an event base set with event_base_set() - - @see event_base_set(), event_base_free(), event_init() - */ -struct event_base *event_base_new(void); - -/** - Initialize the event API. - - The event API needs to be initialized with event_init() before it can be - used. Sets the current_base global representing the default base for - events that have no base associated with them. - - @see event_base_set(), event_base_new() - */ -struct event_base *event_init(void); - -/** - Reinitialized the event base after a fork - - Some event mechanisms do not survive across fork. The event base needs - to be reinitialized with the event_reinit() function. - - @param base the event base that needs to be re-initialized - @return 0 if successful, or -1 if some events could not be re-added. - @see event_base_new(), event_init() -*/ -int event_reinit(struct event_base *base); - -/** - Loop to process events. - - In order to process events, an application needs to call - event_dispatch(). This function only returns on error, and should - replace the event core of the application program. - - @see event_base_dispatch() - */ -int event_dispatch(void); - - -/** - Threadsafe event dispatching loop. - - @param eb the event_base structure returned by event_init() - @see event_init(), event_dispatch() - */ -int event_base_dispatch(struct event_base *); - - -/** - Get the kernel event notification mechanism used by libevent. - - @param eb the event_base structure returned by event_base_new() - @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) - */ -const char *event_base_get_method(struct event_base *); - - -/** - Deallocate all memory associated with an event_base, and free the base. - - Note that this function will not close any fds or free any memory passed - to event_set as the argument to callback. - - @param eb an event_base to be freed - */ -void event_base_free(struct event_base *); - - -#define _EVENT_LOG_DEBUG 0 -#define _EVENT_LOG_MSG 1 -#define _EVENT_LOG_WARN 2 -#define _EVENT_LOG_ERR 3 -typedef void (*event_log_cb)(int severity, const char *msg); -/** - Redirect libevent's log messages. - - @param cb a function taking two arguments: an integer severity between - _EVENT_LOG_DEBUG and _EVENT_LOG_ERR, and a string. If cb is NULL, - then the default log is used. - */ -void event_set_log_callback(event_log_cb cb); - -/** - Associate a different event base with an event. - - @param eb the event base - @param ev the event - */ -int event_base_set(struct event_base *, struct event *); - -/** - event_loop() flags - */ -/*@{*/ -#define EVLOOP_ONCE 0x01 /**< Block at most once. */ -#define EVLOOP_NONBLOCK 0x02 /**< Do not block. */ -/*@}*/ - -/** - Handle events. - - This is a more flexible version of event_dispatch(). - - @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK - @return 0 if successful, -1 if an error occurred, or 1 if no events were - registered. - @see event_loopexit(), event_base_loop() -*/ -int event_loop(int); - -/** - Handle events (threadsafe version). - - This is a more flexible version of event_base_dispatch(). - - @param eb the event_base structure returned by event_init() - @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK - @return 0 if successful, -1 if an error occurred, or 1 if no events were - registered. - @see event_loopexit(), event_base_loop() - */ -int event_base_loop(struct event_base *, int); - -/** - Exit the event loop after the specified time. - - The next event_loop() iteration after the given timer expires will - complete normally (handling all queued events) then exit without - blocking for events again. - - Subsequent invocations of event_loop() will proceed normally. - - @param tv the amount of time after which the loop should terminate. - @return 0 if successful, or -1 if an error occurred - @see event_loop(), event_base_loop(), event_base_loopexit() - */ -int event_loopexit(struct timeval *); - - -/** - Exit the event loop after the specified time (threadsafe variant). - - The next event_base_loop() iteration after the given timer expires will - complete normally (handling all queued events) then exit without - blocking for events again. - - Subsequent invocations of event_base_loop() will proceed normally. - - @param eb the event_base structure returned by event_init() - @param tv the amount of time after which the loop should terminate. - @return 0 if successful, or -1 if an error occurred - @see event_loopexit() - */ -int event_base_loopexit(struct event_base *, struct timeval *); - -/** - Abort the active event_loop() immediately. - - event_loop() will abort the loop after the next event is completed; - event_loopbreak() is typically invoked from this event's callback. - This behavior is analogous to the "break;" statement. - - Subsequent invocations of event_loop() will proceed normally. - - @return 0 if successful, or -1 if an error occurred - @see event_base_loopbreak(), event_loopexit() - */ -int event_loopbreak(void); - -/** - Abort the active event_base_loop() immediately. - - event_base_loop() will abort the loop after the next event is completed; - event_base_loopbreak() is typically invoked from this event's callback. - This behavior is analogous to the "break;" statement. - - Subsequent invocations of event_loop() will proceed normally. - - @param eb the event_base structure returned by event_init() - @return 0 if successful, or -1 if an error occurred - @see event_base_loopexit - */ -int event_base_loopbreak(struct event_base *); - - -/** - Add a timer event. - - @param ev the event struct - @param tv timeval struct - */ -#define evtimer_add(ev, tv) event_add(ev, tv) - - -/** - Define a timer event. - - @param ev event struct to be modified - @param cb callback function - @param arg argument that will be passed to the callback function - */ -#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) - - -/** - * Delete a timer event. - * - * @param ev the event struct to be disabled - */ -#define evtimer_del(ev) event_del(ev) -#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv) -#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) - -/** - * Add a timeout event. - * - * @param ev the event struct to be disabled - * @param tv the timeout value, in seconds - */ -#define timeout_add(ev, tv) event_add(ev, tv) - - -/** - * Define a timeout event. - * - * @param ev the event struct to be defined - * @param cb the callback to be invoked when the timeout expires - * @param arg the argument to be passed to the callback - */ -#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) - - -/** - * Disable a timeout event. - * - * @param ev the timeout event to be disabled - */ -#define timeout_del(ev) event_del(ev) - -#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv) -#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) - -#define signal_add(ev, tv) event_add(ev, tv) -#define signal_set(ev, x, cb, arg) \ - event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) -#define signal_del(ev) event_del(ev) -#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv) -#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) - -/** - Prepare an event structure to be added. - - The function event_set() prepares the event structure ev to be used in - future calls to event_add() and event_del(). The event will be prepared to - call the function specified by the fn argument with an int argument - indicating the file descriptor, a short argument indicating the type of - event, and a void * argument given in the arg argument. The fd indicates - the file descriptor that should be monitored for events. The events can be - either EV_READ, EV_WRITE, or both. Indicating that an application can read - or write from the file descriptor respectively without blocking. - - The function fn will be called with the file descriptor that triggered the - event and the type of event which will be either EV_TIMEOUT, EV_SIGNAL, - EV_READ, or EV_WRITE. The additional flag EV_PERSIST makes an event_add() - persistent until event_del() has been called. - - @param ev an event struct to be modified - @param fd the file descriptor to be monitored - @param event desired events to monitor; can be EV_READ and/or EV_WRITE - @param fn callback function to be invoked when the event occurs - @param arg an argument to be passed to the callback function - - @see event_add(), event_del(), event_once() - - */ -void event_set(struct event *, int, short, void (*)(int, short, void *), void *); - -/** - Schedule a one-time event to occur. - - The function event_once() is similar to event_set(). However, it schedules - a callback to be called exactly once and does not require the caller to - prepare an event structure. - - @param fd a file descriptor to monitor - @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ | - EV_WRITE - @param callback callback function to be invoked when the event occurs - @param arg an argument to be passed to the callback function - @param timeout the maximum amount of time to wait for the event, or NULL - to wait forever - @return 0 if successful, or -1 if an error occurred - @see event_set() - - */ -int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *); - - -/** - Schedule a one-time event (threadsafe variant) - - The function event_base_once() is similar to event_set(). However, it - schedules a callback to be called exactly once and does not require the - caller to prepare an event structure. - - @param base an event_base returned by event_init() - @param fd a file descriptor to monitor - @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ | - EV_WRITE - @param callback callback function to be invoked when the event occurs - @param arg an argument to be passed to the callback function - @param timeout the maximum amount of time to wait for the event, or NULL - to wait forever - @return 0 if successful, or -1 if an error occurred - @see event_once() - */ -int event_base_once(struct event_base *, int, short, void (*)(int, short, void *), void *, struct timeval *); - - -/** - Add an event to the set of monitored events. - - The function event_add() schedules the execution of the ev event when the - event specified in event_set() occurs or in at least the time specified in - the tv. If tv is NULL, no timeout occurs and the function will only be - called if a matching event occurs on the file descriptor. The event in the - ev argument must be already initialized by event_set() and may not be used - in calls to event_set() until it has timed out or been removed with - event_del(). If the event in the ev argument already has a scheduled - timeout, the old timeout will be replaced by the new one. - - @param ev an event struct initialized via event_set() - @param timeout the maximum amount of time to wait for the event, or NULL - to wait forever - @return 0 if successful, or -1 if an error occurred - @see event_del(), event_set() - */ -int event_add(struct event *, struct timeval *); - - -/** - Remove an event from the set of monitored events. - - The function event_del() will cancel the event in the argument ev. If the - event has already executed or has never been added the call will have no - effect. - - @param ev an event struct to be removed from the working set - @return 0 if successful, or -1 if an error occurred - @see event_add() - */ -int event_del(struct event *); - -void event_active(struct event *, int, short); - - -/** - Checks if a specific event is pending or scheduled. - - @param ev an event struct previously passed to event_add() - @param event the requested event type; any of EV_TIMEOUT|EV_READ| - EV_WRITE|EV_SIGNAL - @param tv an alternate timeout (FIXME - is this true?) - - @return 1 if the event is pending, or 0 if the event has not occurred - - */ -int event_pending(struct event *, short, struct timeval *); - - -/** - Test if an event structure has been initialized. - - The event_initialized() macro can be used to check if an event has been - initialized. - - @param ev an event structure to be tested - @return 1 if the structure has been initialized, or 0 if it has not been - initialized - */ -#ifdef WIN32 -#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != (int)INVALID_HANDLE_VALUE) -#else -#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) -#endif - - -/** - Get the libevent version number. - - @return a string containing the version number of libevent - */ -const char *event_get_version(void); - - -/** - Get the kernel event notification mechanism used by libevent. - - @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) - */ -const char *event_get_method(void); - - -/** - Set the number of different event priorities. - - By default libevent schedules all active events with the same priority. - However, some time it is desirable to process some events with a higher - priority than others. For that reason, libevent supports strict priority - queues. Active events with a lower priority are always processed before - events with a higher priority. - - The number of different priorities can be set initially with the - event_priority_init() function. This function should be called before the - first call to event_dispatch(). The event_priority_set() function can be - used to assign a priority to an event. By default, libevent assigns the - middle priority to all events unless their priority is explicitly set. - - @param npriorities the maximum number of priorities - @return 0 if successful, or -1 if an error occurred - @see event_base_priority_init(), event_priority_set() - - */ -int event_priority_init(int); - - -/** - Set the number of different event priorities (threadsafe variant). - - See the description of event_priority_init() for more information. - - @param eb the event_base structure returned by event_init() - @param npriorities the maximum number of priorities - @return 0 if successful, or -1 if an error occurred - @see event_priority_init(), event_priority_set() - */ -int event_base_priority_init(struct event_base *, int); - - -/** - Assign a priority to an event. - - @param ev an event struct - @param priority the new priority to be assigned - @return 0 if successful, or -1 if an error occurred - @see event_priority_init() - */ -int event_priority_set(struct event *, int); - - -/* These functions deal with buffering input and output */ - -struct evbuffer { - u_char *buffer; - u_char *orig_buffer; - - size_t misalign; - size_t totallen; - size_t off; - - void (*cb)(struct evbuffer *, size_t, size_t, void *); - void *cbarg; -}; - -/* Just for error reporting - use other constants otherwise */ -#define EVBUFFER_READ 0x01 -#define EVBUFFER_WRITE 0x02 -#define EVBUFFER_EOF 0x10 -#define EVBUFFER_ERROR 0x20 -#define EVBUFFER_TIMEOUT 0x40 - -struct bufferevent; -typedef void (*evbuffercb)(struct bufferevent *, void *); -typedef void (*everrorcb)(struct bufferevent *, short what, void *); - -struct event_watermark { - size_t low; - size_t high; -}; - -struct bufferevent { - struct event ev_read; - struct event ev_write; - - struct evbuffer *input; - struct evbuffer *output; - - struct event_watermark wm_read; - struct event_watermark wm_write; - - evbuffercb readcb; - evbuffercb writecb; - everrorcb errorcb; - void *cbarg; - - int timeout_read; /* in seconds */ - int timeout_write; /* in seconds */ - - short enabled; /* events that are currently enabled */ -}; - - -/** - Create a new bufferevent. - - libevent provides an abstraction on top of the regular event callbacks. - This abstraction is called a buffered event. A buffered event provides - input and output buffers that get filled and drained automatically. The - user of a buffered event no longer deals directly with the I/O, but - instead is reading from input and writing to output buffers. - - Once initialized, the bufferevent structure can be used repeatedly with - bufferevent_enable() and bufferevent_disable(). - - When read enabled the bufferevent will try to read from the file descriptor - and call the read callback. The write callback is executed whenever the - output buffer is drained below the write low watermark, which is 0 by - default. - - If multiple bases are in use, bufferevent_base_set() must be called before - enabling the bufferevent for the first time. - - @param fd the file descriptor from which data is read and written to. - This file descriptor is not allowed to be a pipe(2). - @param readcb callback to invoke when there is data to be read, or NULL if - no callback is desired - @param writecb callback to invoke when the file descriptor is ready for - writing, or NULL if no callback is desired - @param errorcb callback to invoke when there is an error on the file - descriptor - @param cbarg an argument that will be supplied to each of the callbacks - (readcb, writecb, and errorcb) - @return a pointer to a newly allocated bufferevent struct, or NULL if an - error occurred - @see bufferevent_base_set(), bufferevent_free() - */ -struct bufferevent *bufferevent_new(int fd, - evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); - - -/** - Assign a bufferevent to a specific event_base. - - @param base an event_base returned by event_init() - @param bufev a bufferevent struct returned by bufferevent_new() - @return 0 if successful, or -1 if an error occurred - @see bufferevent_new() - */ -int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); - - -/** - Assign a priority to a bufferevent. - - @param bufev a bufferevent struct - @param pri the priority to be assigned - @return 0 if successful, or -1 if an error occurred - */ -int bufferevent_priority_set(struct bufferevent *bufev, int pri); - - -/** - Deallocate the storage associated with a bufferevent structure. - - @param bufev the bufferevent structure to be freed. - */ -void bufferevent_free(struct bufferevent *bufev); - - -/** - Write data to a bufferevent buffer. - - The bufferevent_write() function can be used to write data to the file - descriptor. The data is appended to the output buffer and written to the - descriptor automatically as it becomes available for writing. - - @param bufev the bufferevent to be written to - @param data a pointer to the data to be written - @param size the length of the data, in bytes - @return 0 if successful, or -1 if an error occurred - @see bufferevent_write_buffer() - */ -int bufferevent_write(struct bufferevent *bufev, - const void *data, size_t size); - - -/** - Write data from an evbuffer to a bufferevent buffer. The evbuffer is - being drained as a result. - - @param bufev the bufferevent to be written to - @param buf the evbuffer to be written - @return 0 if successful, or -1 if an error occurred - @see bufferevent_write() - */ -int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); - - -/** - Read data from a bufferevent buffer. - - The bufferevent_read() function is used to read data from the input buffer. - - @param bufev the bufferevent to be read from - @param data pointer to a buffer that will store the data - @param size the size of the data buffer, in bytes - @return the amount of data read, in bytes. - */ -size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); - -/** - Enable a bufferevent. - - @param bufev the bufferevent to be enabled - @param event any combination of EV_READ | EV_WRITE. - @return 0 if successful, or -1 if an error occurred - @see bufferevent_disable() - */ -int bufferevent_enable(struct bufferevent *bufev, short event); - - -/** - Disable a bufferevent. - - @param bufev the bufferevent to be disabled - @param event any combination of EV_READ | EV_WRITE. - @return 0 if successful, or -1 if an error occurred - @see bufferevent_enable() - */ -int bufferevent_disable(struct bufferevent *bufev, short event); - - -/** - Set the read and write timeout for a buffered event. - - @param bufev the bufferevent to be modified - @param timeout_read the read timeout - @param timeout_write the write timeout - */ -void bufferevent_settimeout(struct bufferevent *bufev, - int timeout_read, int timeout_write); - - -#define EVBUFFER_LENGTH(x) (x)->off -#define EVBUFFER_DATA(x) (x)->buffer -#define EVBUFFER_INPUT(x) (x)->input -#define EVBUFFER_OUTPUT(x) (x)->output - - -/** - Allocate storage for a new evbuffer. - - @return a pointer to a newly allocated evbuffer struct, or NULL if an error - occurred - */ -struct evbuffer *evbuffer_new(void); - - -/** - Deallocate storage for an evbuffer. - - @param pointer to the evbuffer to be freed - */ -void evbuffer_free(struct evbuffer *); - - -/** - Expands the available space in an event buffer. - - Expands the available space in the event buffer to at least datlen - - @param buf the event buffer to be expanded - @param datlen the new minimum length requirement - @return 0 if successful, or -1 if an error occurred -*/ -int evbuffer_expand(struct evbuffer *, size_t); - - -/** - Append data to the end of an evbuffer. - - @param buf the event buffer to be appended to - @param data pointer to the beginning of the data buffer - @param datlen the number of bytes to be copied from the data buffer - */ -int evbuffer_add(struct evbuffer *, const void *, size_t); - - - -/** - Read data from an event buffer and drain the bytes read. - - @param buf the event buffer to be read from - @param data the destination buffer to store the result - @param datlen the maximum size of the destination buffer - @return the number of bytes read - */ -int evbuffer_remove(struct evbuffer *, void *, size_t); - - -/** - * Read a single line from an event buffer. - * - * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. - * The returned buffer needs to be freed by the caller. - * - * @param buffer the evbuffer to read from - * @return pointer to a single line, or NULL if an error occurred - */ -char *evbuffer_readline(struct evbuffer *); - - -/** - Move data from one evbuffer into another evbuffer. - - This is a destructive add. The data from one buffer moves into - the other buffer. The destination buffer is expanded as needed. - - @param outbuf the output buffer - @param inbuf the input buffer - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *); - - -/** - Append a formatted string to the end of an evbuffer. - - @param buf the evbuffer that will be appended to - @param fmt a format string - @param ... arguments that will be passed to printf(3) - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) -#endif -; - - -/** - Append a va_list formatted string to the end of an evbuffer. - - @param buf the evbuffer that will be appended to - @param fmt a format string - @param ap a varargs va_list argument array that will be passed to vprintf(3) - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap); - - -/** - Remove a specified number of bytes data from the beginning of an evbuffer. - - @param buf the evbuffer to be drained - @param len the number of bytes to drain from the beginning of the buffer - @return 0 if successful, or -1 if an error occurred - */ -void evbuffer_drain(struct evbuffer *, size_t); - - -/** - Write the contents of an evbuffer to a file descriptor. - - The evbuffer will be drained after the bytes have been successfully written. - - @param buffer the evbuffer to be written and drained - @param fd the file descriptor to be written to - @return the number of bytes written, or -1 if an error occurred - @see evbuffer_read() - */ -int evbuffer_write(struct evbuffer *, int); - - -/** - Read from a file descriptor and store the result in an evbuffer. - - @param buf the evbuffer to store the result - @param fd the file descriptor to read from - @param howmuch the number of bytes to be read - @return the number of bytes read, or -1 if an error occurred - @see evbuffer_write() - */ -int evbuffer_read(struct evbuffer *, int, int); - - -/** - Find a string within an evbuffer. - - @param buffer the evbuffer to be searched - @param what the string to be searched for - @param len the length of the search string - @return a pointer to the beginning of the search string, or NULL if the search failed. - */ -u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t); - -/** - Set a callback to invoke when the evbuffer is modified. - - @param buffer the evbuffer to be monitored - @param cb the callback function to invoke when the evbuffer is modified - @param cbarg an argument to be provided to the callback function - */ -void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *); - -/* - * Marshaling tagged data - We assume that all tags are inserted in their - * numeric order - so that unknown tags will always be higher than the - * known ones - and we can just ignore the end of an event buffer. - */ - -void evtag_init(void); - -void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, - ev_uint32_t len); - -/** - Encode an integer and store it in an evbuffer. - - We encode integer's by nibbles; the first nibble contains the number - of significant nibbles - 1; this allows us to encode up to 64-bit - integers. This function is byte-order independent. - - @param evbuf evbuffer to store the encoded number - @param number a 32-bit integer - */ -void encode_int(struct evbuffer *evbuf, ev_uint32_t number); - -void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, - ev_uint32_t integer); - -void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, - const char *string); - -void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, - struct timeval *tv); - -int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, - struct evbuffer *dst); -int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag); -int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength); -int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength); -int evtag_consume(struct evbuffer *evbuf); - -int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, - ev_uint32_t *pinteger); - -int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, - void *data, size_t len); - -int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, - char **pstring); - -int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, - struct timeval *ptv); - -#ifdef __cplusplus -} -#endif - -#endif /* _EVENT_H_ */ diff --git a/extra/libevent/event_tagging.c b/extra/libevent/event_tagging.c deleted file mode 100644 index c67ea8a57af..00000000000 --- a/extra/libevent/event_tagging.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (c) 2003, 2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#undef WIN32_LEAN_AND_MEAN -#else -#include -#endif - -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "event.h" -#include "evutil.h" -#include "log.h" - -int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf); -int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag); -int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf); - -static struct evbuffer *_buf; /* not thread safe */ - -void -evtag_init(void) -{ - if (_buf != NULL) - return; - - if ((_buf = evbuffer_new()) == NULL) - event_err(1, "%s: malloc", __func__); -} - -/* - * We encode integer's by nibbles; the first nibble contains the number - * of significant nibbles - 1; this allows us to encode up to 64-bit - * integers. This function is byte-order independent. - */ - -void -encode_int(struct evbuffer *evbuf, ev_uint32_t number) -{ - int off = 1, nibbles = 0; - ev_uint8_t data[5]; - - memset(data, 0, sizeof(data)); - while (number) { - if (off & 0x1) - data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f); - else - data[off/2] = (data[off/2] & 0x0f) | - ((number & 0x0f) << 4); - number >>= 4; - off++; - } - - if (off > 2) - nibbles = off - 2; - - /* Off - 1 is the number of encoded nibbles */ - data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4); - - evbuffer_add(evbuf, data, (off + 1) / 2); -} - -/* - * Support variable length encoding of tags; we use the high bit in each - * octet as a continuation signal. - */ - -int -evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag) -{ - int bytes = 0; - ev_uint8_t data[5]; - - memset(data, 0, sizeof(data)); - do { - ev_uint8_t lower = tag & 0x7f; - tag >>= 7; - - if (tag) - lower |= 0x80; - - data[bytes++] = lower; - } while (tag); - - if (evbuf != NULL) - evbuffer_add(evbuf, data, bytes); - - return (bytes); -} - -static int -decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain) -{ - ev_uint32_t number = 0; - ev_uint8_t *data = EVBUFFER_DATA(evbuf); - int len = EVBUFFER_LENGTH(evbuf); - int count = 0, shift = 0, done = 0; - - while (count++ < len) { - ev_uint8_t lower = *data++; - number |= (lower & 0x7f) << shift; - shift += 7; - - if (!(lower & 0x80)) { - done = 1; - break; - } - } - - if (!done) - return (-1); - - if (dodrain) - evbuffer_drain(evbuf, count); - - if (ptag != NULL) - *ptag = number; - - return (count); -} - -int -evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf) -{ - return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */)); -} - -/* - * Marshal a data type, the general format is as follows: - * - * tag number: one byte; length: var bytes; payload: var bytes - */ - -void -evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, - const void *data, ev_uint32_t len) -{ - evtag_encode_tag(evbuf, tag); - encode_int(evbuf, len); - evbuffer_add(evbuf, (void *)data, len); -} - -/* Marshaling for integers */ -void -evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer) -{ - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - encode_int(_buf, integer); - - evtag_encode_tag(evbuf, tag); - encode_int(evbuf, EVBUFFER_LENGTH(_buf)); - evbuffer_add_buffer(evbuf, _buf); -} - -void -evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string) -{ - evtag_marshal(buf, tag, string, strlen(string)); -} - -void -evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv) -{ - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - - encode_int(_buf, tv->tv_sec); - encode_int(_buf, tv->tv_usec); - - evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), - EVBUFFER_LENGTH(_buf)); -} - -static int -decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) -{ - ev_uint32_t number = 0; - ev_uint8_t *data = EVBUFFER_DATA(evbuf); - int len = EVBUFFER_LENGTH(evbuf); - int nibbles = 0; - - if (!len) - return (-1); - - nibbles = ((data[0] & 0xf0) >> 4) + 1; - if (nibbles > 8 || (nibbles >> 1) + 1 > len) - return (-1); - len = (nibbles >> 1) + 1; - - while (nibbles > 0) { - number <<= 4; - if (nibbles & 0x1) - number |= data[nibbles >> 1] & 0x0f; - else - number |= (data[nibbles >> 1] & 0xf0) >> 4; - nibbles--; - } - - if (dodrain) - evbuffer_drain(evbuf, len); - - *pnumber = number; - - return (len); -} - -int -evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf) -{ - return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0); -} - -int -evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag) -{ - return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */)); -} - -int -evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength) -{ - struct evbuffer tmp; - int res, len; - - len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); - if (len == -1) - return (-1); - - tmp = *evbuf; - tmp.buffer += len; - tmp.off -= len; - - res = decode_int_internal(plength, &tmp, 0); - if (res == -1) - return (-1); - - *plength += res + len; - - return (0); -} - -int -evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength) -{ - struct evbuffer tmp; - int res, len; - - len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); - if (len == -1) - return (-1); - - tmp = *evbuf; - tmp.buffer += len; - tmp.off -= len; - - res = decode_int_internal(plength, &tmp, 0); - if (res == -1) - return (-1); - - return (0); -} - -int -evtag_consume(struct evbuffer *evbuf) -{ - ev_uint32_t len; - if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1) - return (-1); - if (evtag_decode_int(&len, evbuf) == -1) - return (-1); - evbuffer_drain(evbuf, len); - - return (0); -} - -/* Reads the data type from an event buffer */ - -int -evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst) -{ - ev_uint32_t len; - ev_uint32_t integer; - - if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1) - return (-1); - if (evtag_decode_int(&integer, src) == -1) - return (-1); - len = integer; - - if (EVBUFFER_LENGTH(src) < len) - return (-1); - - if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) - return (-1); - - evbuffer_drain(src, len); - - return (len); -} - -/* Marshaling for integers */ - -int -evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, - ev_uint32_t *pinteger) -{ - ev_uint32_t tag; - ev_uint32_t len; - ev_uint32_t integer; - - if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1) - return (-1); - if (need_tag != tag) - return (-1); - if (evtag_decode_int(&integer, evbuf) == -1) - return (-1); - len = integer; - - if (EVBUFFER_LENGTH(evbuf) < len) - return (-1); - - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1) - return (-1); - - evbuffer_drain(evbuf, len); - - return (evtag_decode_int(pinteger, _buf)); -} - -/* Unmarshal a fixed length tag */ - -int -evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data, - size_t len) -{ - ev_uint32_t tag; - - /* Initialize this event buffer so that we can read into it */ - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - - /* Now unmarshal a tag and check that it matches the tag we want */ - if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag) - return (-1); - - if (EVBUFFER_LENGTH(_buf) != len) - return (-1); - - memcpy(data, EVBUFFER_DATA(_buf), len); - return (0); -} - -int -evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, - char **pstring) -{ - ev_uint32_t tag; - - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - - if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) - return (-1); - - *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1); - if (*pstring == NULL) - event_err(1, "%s: calloc", __func__); - evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf)); - - return (0); -} - -int -evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, - struct timeval *ptv) -{ - ev_uint32_t tag; - ev_uint32_t integer; - - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) - return (-1); - - if (evtag_decode_int(&integer, _buf) == -1) - return (-1); - ptv->tv_sec = integer; - if (evtag_decode_int(&integer, _buf) == -1) - return (-1); - ptv->tv_usec = integer; - - return (0); -} diff --git a/extra/libevent/evhttp.h b/extra/libevent/evhttp.h deleted file mode 100644 index c20b1a7165e..00000000000 --- a/extra/libevent/evhttp.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVHTTP_H_ -#define _EVHTTP_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#undef WIN32_LEAN_AND_MEAN -#endif - -/** @file evhttp.h - * - * Basic support for HTTP serving. - * - * As libevent is a library for dealing with event notification and most - * interesting applications are networked today, I have often found the - * need to write HTTP code. The following prototypes and definitions provide - * an application with a minimal interface for making HTTP requests and for - * creating a very simple HTTP server. - */ - -/* Response codes */ -#define HTTP_OK 200 -#define HTTP_NOCONTENT 204 -#define HTTP_MOVEPERM 301 -#define HTTP_MOVETEMP 302 -#define HTTP_NOTMODIFIED 304 -#define HTTP_BADREQUEST 400 -#define HTTP_NOTFOUND 404 -#define HTTP_SERVUNAVAIL 503 - -struct evhttp; -struct evhttp_request; -struct evkeyvalq; - -/** Create a new HTTP server - * - * @param base (optional) the event base to receive the HTTP events - * @return a pointer to a newly initialized evhttp server structure - */ -struct evhttp *evhttp_new(struct event_base *base); - -/** - * Binds an HTTP server on the specified address and port. - * - * Can be called multiple times to bind the same http server - * to multiple different ports. - * - * @param http a pointer to an evhttp object - * @param address a string containing the IP address to listen(2) on - * @param port the port number to listen on - * @return a newly allocated evhttp struct - * @see evhttp_free() - */ -int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port); - -/** - * Free the previously created HTTP server. - * - * Works only if no requests are currently being served. - * - * @param http the evhttp server object to be freed - * @see evhttp_start() - */ -void evhttp_free(struct evhttp* http); - -/** Set a callback for a specified URI */ -void evhttp_set_cb(struct evhttp *, const char *, - void (*)(struct evhttp_request *, void *), void *); - -/** Removes the callback for a specified URI */ -int evhttp_del_cb(struct evhttp *, const char *); - -/** Set a callback for all requests that are not caught by specific callbacks - */ -void evhttp_set_gencb(struct evhttp *, - void (*)(struct evhttp_request *, void *), void *); - -/** - * Set the timeout for an HTTP request. - * - * @param http an evhttp object - * @param timeout_in_secs the timeout, in seconds - */ -void evhttp_set_timeout(struct evhttp *, int timeout_in_secs); - -/* Request/Response functionality */ - -/** - * Send an HTML error message to the client. - * - * @param req a request object - * @param error the HTTP error code - * @param reason a brief explanation of the error - */ -void evhttp_send_error(struct evhttp_request *req, int error, - const char *reason); - -/** - * Send an HTML reply to the client. - * - * @param req a request object - * @param code the HTTP response code to send - * @param reason a brief message to send with the response code - * @param databuf the body of the response - */ -void evhttp_send_reply(struct evhttp_request *req, int code, - const char *reason, struct evbuffer *databuf); - -/* Low-level response interface, for streaming/chunked replies */ -void evhttp_send_reply_start(struct evhttp_request *, int, const char *); -void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); -void evhttp_send_reply_end(struct evhttp_request *); - -/** - * Start an HTTP server on the specified address and port - * - * DEPRECATED: it does not allow an event base to be specified - * - * @param address the address to which the HTTP server should be bound - * @param port the port number on which the HTTP server should listen - * @return an struct evhttp object - */ -struct evhttp *evhttp_start(const char *address, u_short port); - -/* - * Interfaces for making requests - */ -enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD }; - -enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE }; - -/** - * the request structure that a server receives. - * WARNING: expect this structure to change. I will try to provide - * reasonable accessors. - */ -struct evhttp_request { -#if defined(TAILQ_ENTRY) - TAILQ_ENTRY(evhttp_request) next; -#else -struct { - struct evhttp_request *tqe_next; - struct evhttp_request **tqe_prev; -} next; -#endif - - /* the connection object that this request belongs to */ - struct evhttp_connection *evcon; - int flags; -#define EVHTTP_REQ_OWN_CONNECTION 0x0001 -#define EVHTTP_PROXY_REQUEST 0x0002 - - struct evkeyvalq *input_headers; - struct evkeyvalq *output_headers; - - /* address of the remote host and the port connection came from */ - char *remote_host; - u_short remote_port; - - enum evhttp_request_kind kind; - enum evhttp_cmd_type type; - - char *uri; /* uri after HTTP request was parsed */ - - char major; /* HTTP Major number */ - char minor; /* HTTP Minor number */ - - int got_firstline; - int response_code; /* HTTP Response code */ - char *response_code_line; /* Readable response */ - - struct evbuffer *input_buffer; /* read data */ - ev_int64_t ntoread; - int chunked; - - struct evbuffer *output_buffer; /* outgoing post or data */ - - /* Callback */ - void (*cb)(struct evhttp_request *, void *); - void *cb_arg; - - /* - * Chunked data callback - call for each completed chunk if - * specified. If not specified, all the data is delivered via - * the regular callback. - */ - void (*chunk_cb)(struct evhttp_request *, void *); -}; - -/** - * Creates a new request object that needs to be filled in with the request - * parameters. The callback is executed when the request completed or an - * error occurred. - */ -struct evhttp_request *evhttp_request_new( - void (*cb)(struct evhttp_request *, void *), void *arg); - -/** enable delivery of chunks to requestor */ -void evhttp_request_set_chunked_cb(struct evhttp_request *, - void (*cb)(struct evhttp_request *, void *)); - -/** Frees the request object and removes associated events. */ -void evhttp_request_free(struct evhttp_request *req); - -/** - * A connection object that can be used to for making HTTP requests. The - * connection object tries to establish the connection when it is given an - * http request object. - */ -struct evhttp_connection *evhttp_connection_new( - const char *address, unsigned short port); - -/** Frees an http connection */ -void evhttp_connection_free(struct evhttp_connection *evcon); - -/** sets the ip address from which http connections are made */ -void evhttp_connection_set_local_address(struct evhttp_connection *evcon, - const char *address); - -/** Sets the timeout for events related to this connection */ -void evhttp_connection_set_timeout(struct evhttp_connection *evcon, - int timeout_in_secs); - -/** Sets the retry limit for this connection - -1 repeats indefnitely */ -void evhttp_connection_set_retries(struct evhttp_connection *evcon, - int retry_max); - -/** Set a callback for connection close. */ -void evhttp_connection_set_closecb(struct evhttp_connection *evcon, - void (*)(struct evhttp_connection *, void *), void *); - -/** - * Associates an event base with the connection - can only be called - * on a freshly created connection object that has not been used yet. - */ -void evhttp_connection_set_base(struct evhttp_connection *evcon, - struct event_base *base); - -/** Get the remote address and port associated with this connection. */ -void evhttp_connection_get_peer(struct evhttp_connection *evcon, - char **address, u_short *port); - -/** The connection gets ownership of the request */ -int evhttp_make_request(struct evhttp_connection *evcon, - struct evhttp_request *req, - enum evhttp_cmd_type type, const char *uri); - -const char *evhttp_request_uri(struct evhttp_request *req); - -/* Interfaces for dealing with HTTP headers */ - -const char *evhttp_find_header(const struct evkeyvalq *, const char *); -int evhttp_remove_header(struct evkeyvalq *, const char *); -int evhttp_add_header(struct evkeyvalq *, const char *, const char *); -void evhttp_clear_headers(struct evkeyvalq *); - -/* Miscellaneous utility functions */ - - -/** - Helper function to encode a URI. - - The returned string must be freed by the caller. - - @param uri an unencoded URI - @return a newly allocated URI-encoded string - */ -char *evhttp_encode_uri(const char *uri); - - -/** - Helper function to decode a URI. - - The returned string must be freed by the caller. - - @param uri an encoded URI - @return a newly allocated unencoded URI - */ -char *evhttp_decode_uri(const char *uri); - - -/** - * Helper function to parse out arguments in a query. - * The arguments are separated by key and value. - * URI should already be decoded. - */ -void evhttp_parse_query(const char *uri, struct evkeyvalq *); - - -/** - * Escape HTML character entities in a string. - * - * Replaces <, >, ", ' and & with <, >, ", - * ' and & correspondingly. - * - * The returned string needs to be freed by the caller. - * - * @param html an unescaped HTML string - * @return an escaped HTML string - */ -char *evhttp_htmlescape(const char *html); - -#ifdef __cplusplus -} -#endif - -#endif /* _EVHTTP_H_ */ diff --git a/extra/libevent/evport.c b/extra/libevent/evport.c deleted file mode 100644 index c3250531d45..00000000000 --- a/extra/libevent/evport.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Submitted by David Pacheco (dp.spambait@gmail.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Copyright (c) 2007 Sun Microsystems. All rights reserved. - * Use is subject to license terms. - */ - -/* - * evport.c: event backend using Solaris 10 event ports. See port_create(3C). - * This implementation is loosely modeled after the one used for select(2) (in - * select.c). - * - * The outstanding events are tracked in a data structure called evport_data. - * Each entry in the ed_fds array corresponds to a file descriptor, and contains - * pointers to the read and write events that correspond to that fd. (That is, - * when the file is readable, the "read" event should handle it, etc.) - * - * evport_add and evport_del update this data structure. evport_dispatch uses it - * to determine where to callback when an event occurs (which it gets from - * port_getn). - * - * Helper functions are used: grow() grows the file descriptor array as - * necessary when large fd's come in. reassociate() takes care of maintaining - * the proper file-descriptor/event-port associations. - * - * As in the select(2) implementation, signals are handled by evsignal. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_EVENT_PORTS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CHECK_INVARIANTS -#include -#endif - -#include "event.h" -#include "event-internal.h" -#include "log.h" -#include "evsignal.h" - - -/* - * Default value for ed_nevents, which is the maximum file descriptor number we - * can handle. If an event comes in for a file descriptor F > nevents, we will - * grow the array of file descriptors, doubling its size. - */ -#define DEFAULT_NFDS 16 - - -/* - * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on - * any particular call. You can speed things up by increasing this, but it will - * (obviously) require more memory. - */ -#define EVENTS_PER_GETN 8 - -/* - * Per-file-descriptor information about what events we're subscribed to. These - * fields are NULL if no event is subscribed to either of them. - */ - -struct fd_info { - struct event* fdi_revt; /* the event responsible for the "read" */ - struct event* fdi_wevt; /* the event responsible for the "write" */ -}; - -#define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL) -#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL) -#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi)) -#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \ - (FDI_HAS_WRITE(fdi) ? POLLOUT : 0) - -struct evport_data { - int ed_port; /* event port for system events */ - int ed_nevents; /* number of allocated fdi's */ - struct fd_info *ed_fds; /* allocated fdi table */ - /* fdi's that we need to reassoc */ - int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */ -}; - -static void* evport_init (struct event_base *); -static int evport_add (void *, struct event *); -static int evport_del (void *, struct event *); -static int evport_dispatch (struct event_base *, void *, struct timeval *); -static void evport_dealloc (struct event_base *, void *); - -const struct eventop evportops = { - "event ports", - evport_init, - evport_add, - evport_del, - evport_dispatch, - evport_dealloc, - 1 /* need reinit */ -}; - -/* - * Initialize the event port implementation. - */ - -static void* -evport_init(struct event_base *base) -{ - struct evport_data *evpd; - int i; - /* - * Disable event ports when this environment variable is set - */ - if (getenv("EVENT_NOEVPORT")) - return (NULL); - - if (!(evpd = calloc(1, sizeof(struct evport_data)))) - return (NULL); - - if ((evpd->ed_port = port_create()) == -1) { - free(evpd); - return (NULL); - } - - /* - * Initialize file descriptor structure - */ - evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info)); - if (evpd->ed_fds == NULL) { - close(evpd->ed_port); - free(evpd); - return (NULL); - } - evpd->ed_nevents = DEFAULT_NFDS; - for (i = 0; i < EVENTS_PER_GETN; i++) - evpd->ed_pending[i] = -1; - - evsignal_init(base); - - return (evpd); -} - -#ifdef CHECK_INVARIANTS -/* - * Checks some basic properties about the evport_data structure. Because it - * checks all file descriptors, this function can be expensive when the maximum - * file descriptor ever used is rather large. - */ - -static void -check_evportop(struct evport_data *evpd) -{ - assert(evpd); - assert(evpd->ed_nevents > 0); - assert(evpd->ed_port > 0); - assert(evpd->ed_fds > 0); - - /* - * Verify the integrity of the fd_info struct as well as the events to - * which it points (at least, that they're valid references and correct - * for their position in the structure). - */ - int i; - for (i = 0; i < evpd->ed_nevents; ++i) { - struct event *ev; - struct fd_info *fdi; - - fdi = &evpd->ed_fds[i]; - if ((ev = fdi->fdi_revt) != NULL) { - assert(ev->ev_fd == i); - } - if ((ev = fdi->fdi_wevt) != NULL) { - assert(ev->ev_fd == i); - } - } -} - -/* - * Verifies very basic integrity of a given port_event. - */ -static void -check_event(port_event_t* pevt) -{ - /* - * We've only registered for PORT_SOURCE_FD events. The only - * other thing we can legitimately receive is PORT_SOURCE_ALERT, - * but since we're not using port_alert either, we can assume - * PORT_SOURCE_FD. - */ - assert(pevt->portev_source == PORT_SOURCE_FD); - assert(pevt->portev_user == NULL); -} - -#else -#define check_evportop(epop) -#define check_event(pevt) -#endif /* CHECK_INVARIANTS */ - -/* - * Doubles the size of the allocated file descriptor array. - */ -static int -grow(struct evport_data *epdp, int factor) -{ - struct fd_info *tmp; - int oldsize = epdp->ed_nevents; - int newsize = factor * oldsize; - assert(factor > 1); - - check_evportop(epdp); - - tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize); - if (NULL == tmp) - return -1; - epdp->ed_fds = tmp; - memset((char*) (epdp->ed_fds + oldsize), 0, - (newsize - oldsize)*sizeof(struct fd_info)); - epdp->ed_nevents = newsize; - - check_evportop(epdp); - - return 0; -} - - -/* - * (Re)associates the given file descriptor with the event port. The OS events - * are specified (implicitly) from the fd_info struct. - */ -static int -reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd) -{ - int sysevents = FDI_TO_SYSEVENTS(fdip); - - if (sysevents != 0) { - if (port_associate(epdp->ed_port, PORT_SOURCE_FD, - fd, sysevents, NULL) == -1) { - event_warn("port_associate"); - return (-1); - } - } - - check_evportop(epdp); - - return (0); -} - -/* - * Main event loop - polls port_getn for some number of events, and processes - * them. - */ - -static int -evport_dispatch(struct event_base *base, void *arg, struct timeval *tv) -{ - int i, res; - struct evport_data *epdp = arg; - port_event_t pevtlist[EVENTS_PER_GETN]; - - /* - * port_getn will block until it has at least nevents events. It will - * also return how many it's given us (which may be more than we asked - * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in - * nevents. - */ - int nevents = 1; - - /* - * We have to convert a struct timeval to a struct timespec - * (only difference is nanoseconds vs. microseconds). If no time-based - * events are active, we should wait for I/O (and tv == NULL). - */ - struct timespec ts; - struct timespec *ts_p = NULL; - if (tv != NULL) { - ts.tv_sec = tv->tv_sec; - ts.tv_nsec = tv->tv_usec * 1000; - ts_p = &ts; - } - - /* - * Before doing anything else, we need to reassociate the events we hit - * last time which need reassociation. See comment at the end of the - * loop below. - */ - for (i = 0; i < EVENTS_PER_GETN; ++i) { - struct fd_info *fdi = NULL; - if (epdp->ed_pending[i] != -1) { - fdi = &(epdp->ed_fds[epdp->ed_pending[i]]); - } - - if (fdi != NULL && FDI_HAS_EVENTS(fdi)) { - int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd : - fdi->fdi_wevt->ev_fd; - reassociate(epdp, fdi, fd); - epdp->ed_pending[i] = -1; - } - } - - if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, - (unsigned int *) &nevents, ts_p)) == -1) { - if (errno == EINTR || errno == EAGAIN) { - evsignal_process(base); - return (0); - } else if (errno == ETIME) { - if (nevents == 0) - return (0); - } else { - event_warn("port_getn"); - return (-1); - } - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - event_debug(("%s: port_getn reports %d events", __func__, nevents)); - - for (i = 0; i < nevents; ++i) { - struct event *ev; - struct fd_info *fdi; - port_event_t *pevt = &pevtlist[i]; - int fd = (int) pevt->portev_object; - - check_evportop(epdp); - check_event(pevt); - epdp->ed_pending[i] = fd; - - /* - * Figure out what kind of event it was - * (because we have to pass this to the callback) - */ - res = 0; - if (pevt->portev_events & POLLIN) - res |= EV_READ; - if (pevt->portev_events & POLLOUT) - res |= EV_WRITE; - - assert(epdp->ed_nevents > fd); - fdi = &(epdp->ed_fds[fd]); - - /* - * We now check for each of the possible events (READ - * or WRITE). Then, we activate the event (which will - * cause its callback to be executed). - */ - - if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) { - event_active(ev, res, 1); - } - - if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) { - event_active(ev, res, 1); - } - } /* end of all events gotten */ - - check_evportop(epdp); - - return (0); -} - - -/* - * Adds the given event (so that you will be notified when it happens via - * the callback function). - */ - -static int -evport_add(void *arg, struct event *ev) -{ - struct evport_data *evpd = arg; - struct fd_info *fdi; - int factor; - - check_evportop(evpd); - - /* - * Delegate, if it's not ours to handle. - */ - if (ev->ev_events & EV_SIGNAL) - return (evsignal_add(ev)); - - /* - * If necessary, grow the file descriptor info table - */ - - factor = 1; - while (ev->ev_fd >= factor * evpd->ed_nevents) - factor *= 2; - - if (factor > 1) { - if (-1 == grow(evpd, factor)) { - return (-1); - } - } - - fdi = &evpd->ed_fds[ev->ev_fd]; - if (ev->ev_events & EV_READ) - fdi->fdi_revt = ev; - if (ev->ev_events & EV_WRITE) - fdi->fdi_wevt = ev; - - return reassociate(evpd, fdi, ev->ev_fd); -} - -/* - * Removes the given event from the list of events to wait for. - */ - -static int -evport_del(void *arg, struct event *ev) -{ - struct evport_data *evpd = arg; - struct fd_info *fdi; - int i; - int associated = 1; - - check_evportop(evpd); - - /* - * Delegate, if it's not ours to handle - */ - if (ev->ev_events & EV_SIGNAL) { - return (evsignal_del(ev)); - } - - if (evpd->ed_nevents < ev->ev_fd) { - return (-1); - } - - for (i = 0; i < EVENTS_PER_GETN; ++i) { - if (evpd->ed_pending[i] == ev->ev_fd) { - associated = 0; - break; - } - } - - fdi = &evpd->ed_fds[ev->ev_fd]; - if (ev->ev_events & EV_READ) - fdi->fdi_revt = NULL; - if (ev->ev_events & EV_WRITE) - fdi->fdi_wevt = NULL; - - if (associated) { - if (!FDI_HAS_EVENTS(fdi) && - port_dissociate(evpd->ed_port, PORT_SOURCE_FD, - ev->ev_fd) == -1) { - /* - * Ignre EBADFD error the fd could have been closed - * before event_del() was called. - */ - if (errno != EBADFD) { - event_warn("port_dissociate"); - return (-1); - } - } else { - if (FDI_HAS_EVENTS(fdi)) { - return (reassociate(evpd, fdi, ev->ev_fd)); - } - } - } else { - if (fdi->fdi_revt == NULL && fdi->fdi_wevt == NULL) { - evpd->ed_pending[i] = -1; - } - } - return 0; -} - - -static void -evport_dealloc(struct event_base *base, void *arg) -{ - struct evport_data *evpd = arg; - - evsignal_dealloc(base); - - close(evpd->ed_port); - - if (evpd->ed_fds) - free(evpd->ed_fds); - free(evpd); -} - -#endif /* HAVE_EVENT_PORTS */ diff --git a/extra/libevent/evrpc-internal.h b/extra/libevent/evrpc-internal.h deleted file mode 100644 index c900f959f97..00000000000 --- a/extra/libevent/evrpc-internal.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2006 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVRPC_INTERNAL_H_ -#define _EVRPC_INTERNAL_H_ - -#include "http-internal.h" - -struct evrpc; - -#define EVRPC_URI_PREFIX "/.rpc." - -struct evrpc_hook { - TAILQ_ENTRY(evrpc_hook) (next); - - /* returns -1; if the rpc should be aborted, is allowed to rewrite */ - int (*process)(struct evhttp_request *, struct evbuffer *, void *); - void *process_arg; -}; - -TAILQ_HEAD(evrpc_hook_list, evrpc_hook); - -/* - * this is shared between the base and the pool, so that we can reuse - * the hook adding functions; we alias both evrpc_pool and evrpc_base - * to this common structure. - */ -struct _evrpc_hooks { - /* hooks for processing outbound and inbound rpcs */ - struct evrpc_hook_list in_hooks; - struct evrpc_hook_list out_hooks; -}; - -#define input_hooks common.in_hooks -#define output_hooks common.out_hooks - -struct evrpc_base { - struct _evrpc_hooks common; - - /* the HTTP server under which we register our RPC calls */ - struct evhttp* http_server; - - /* a list of all RPCs registered with us */ - TAILQ_HEAD(evrpc_list, evrpc) registered_rpcs; -}; - -struct evrpc_req_generic; -void evrpc_reqstate_free(struct evrpc_req_generic* rpc_state); - -/* A pool for holding evhttp_connection objects */ -struct evrpc_pool { - struct _evrpc_hooks common; - - struct event_base *base; - - struct evconq connections; - - int timeout; - - TAILQ_HEAD(evrpc_requestq, evrpc_request_wrapper) requests; -}; - - -#endif /* _EVRPC_INTERNAL_H_ */ diff --git a/extra/libevent/evrpc.c b/extra/libevent/evrpc.c deleted file mode 100644 index bfe3087ef8a..00000000000 --- a/extra/libevent/evrpc.c +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright (c) 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#undef WIN32_LEAN_AND_MEAN -#include "misc.h" -#endif - -#include -#ifndef WIN32 -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#include -#include -#ifndef WIN32 -#include -#endif -#include -#include -#include -#include - -#include "event.h" -#include "evrpc.h" -#include "evrpc-internal.h" -#include "evhttp.h" -#include "evutil.h" -#include "log.h" - -struct evrpc_base * -evrpc_init(struct evhttp *http_server) -{ - struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base)); - if (base == NULL) - return (NULL); - - /* we rely on the tagging sub system */ - evtag_init(); - - TAILQ_INIT(&base->registered_rpcs); - TAILQ_INIT(&base->input_hooks); - TAILQ_INIT(&base->output_hooks); - base->http_server = http_server; - - return (base); -} - -void -evrpc_free(struct evrpc_base *base) -{ - struct evrpc *rpc; - struct evrpc_hook *hook; - - while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) { - assert(evrpc_unregister_rpc(base, rpc->uri)); - } - while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) { - assert(evrpc_remove_hook(base, INPUT, hook)); - } - while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) { - assert(evrpc_remove_hook(base, OUTPUT, hook)); - } - free(base); -} - -void * -evrpc_add_hook(void *vbase, - enum EVRPC_HOOK_TYPE hook_type, - int (*cb)(struct evhttp_request *, struct evbuffer *, void *), - void *cb_arg) -{ - struct _evrpc_hooks *base = vbase; - struct evrpc_hook_list *head = NULL; - struct evrpc_hook *hook = NULL; - switch (hook_type) { - case INPUT: - head = &base->in_hooks; - break; - case OUTPUT: - head = &base->out_hooks; - break; - default: - assert(hook_type == INPUT || hook_type == OUTPUT); - } - - hook = calloc(1, sizeof(struct evrpc_hook)); - assert(hook != NULL); - - hook->process = cb; - hook->process_arg = cb_arg; - TAILQ_INSERT_TAIL(head, hook, next); - - return (hook); -} - -static int -evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle) -{ - struct evrpc_hook *hook = NULL; - TAILQ_FOREACH(hook, head, next) { - if (hook == handle) { - TAILQ_REMOVE(head, hook, next); - free(hook); - return (1); - } - } - - return (0); -} - -/* - * remove the hook specified by the handle - */ - -int -evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle) -{ - struct _evrpc_hooks *base = vbase; - struct evrpc_hook_list *head = NULL; - switch (hook_type) { - case INPUT: - head = &base->in_hooks; - break; - case OUTPUT: - head = &base->out_hooks; - break; - default: - assert(hook_type == INPUT || hook_type == OUTPUT); - } - - return (evrpc_remove_hook_internal(head, handle)); -} - -static int -evrpc_process_hooks(struct evrpc_hook_list *head, - struct evhttp_request *req, struct evbuffer *evbuf) -{ - struct evrpc_hook *hook; - TAILQ_FOREACH(hook, head, next) { - if (hook->process(req, evbuf, hook->process_arg) == -1) - return (-1); - } - - return (0); -} - -static void evrpc_pool_schedule(struct evrpc_pool *pool); -static void evrpc_request_cb(struct evhttp_request *, void *); -void evrpc_request_done(struct evrpc_req_generic*); - -/* - * Registers a new RPC with the HTTP server. The evrpc object is expected - * to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn - * calls this function. - */ - -static char * -evrpc_construct_uri(const char *uri) -{ - char *constructed_uri; - int constructed_uri_len; - - constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1; - if ((constructed_uri = malloc(constructed_uri_len)) == NULL) - event_err(1, "%s: failed to register rpc at %s", - __func__, uri); - memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX)); - memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri)); - constructed_uri[constructed_uri_len - 1] = '\0'; - - return (constructed_uri); -} - -int -evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc, - void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg) -{ - char *constructed_uri = evrpc_construct_uri(rpc->uri); - - rpc->base = base; - rpc->cb = cb; - rpc->cb_arg = cb_arg; - - TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next); - - evhttp_set_cb(base->http_server, - constructed_uri, - evrpc_request_cb, - rpc); - - free(constructed_uri); - - return (0); -} - -int -evrpc_unregister_rpc(struct evrpc_base *base, const char *name) -{ - char *registered_uri = NULL; - struct evrpc *rpc; - - /* find the right rpc; linear search might be slow */ - TAILQ_FOREACH(rpc, &base->registered_rpcs, next) { - if (strcmp(rpc->uri, name) == 0) - break; - } - if (rpc == NULL) { - /* We did not find an RPC with this name */ - return (-1); - } - TAILQ_REMOVE(&base->registered_rpcs, rpc, next); - - free((char *)rpc->uri); - free(rpc); - - registered_uri = evrpc_construct_uri(name); - - /* remove the http server callback */ - assert(evhttp_del_cb(base->http_server, registered_uri) == 0); - - free(registered_uri); - return (0); -} - -static void -evrpc_request_cb(struct evhttp_request *req, void *arg) -{ - struct evrpc *rpc = arg; - struct evrpc_req_generic *rpc_state = NULL; - - /* let's verify the outside parameters */ - if (req->type != EVHTTP_REQ_POST || - EVBUFFER_LENGTH(req->input_buffer) <= 0) - goto error; - - /* - * we might want to allow hooks to suspend the processing, - * but at the moment, we assume that they just act as simple - * filters. - */ - if (evrpc_process_hooks(&rpc->base->input_hooks, - req, req->input_buffer) == -1) - goto error; - - rpc_state = calloc(1, sizeof(struct evrpc_req_generic)); - if (rpc_state == NULL) - goto error; - - /* let's check that we can parse the request */ - rpc_state->request = rpc->request_new(); - if (rpc_state->request == NULL) - goto error; - - rpc_state->rpc = rpc; - - if (rpc->request_unmarshal( - rpc_state->request, req->input_buffer) == -1) { - /* we failed to parse the request; that's a bummer */ - goto error; - } - - /* at this point, we have a well formed request, prepare the reply */ - - rpc_state->reply = rpc->reply_new(); - if (rpc_state->reply == NULL) - goto error; - - rpc_state->http_req = req; - rpc_state->done = evrpc_request_done; - - /* give the rpc to the user; they can deal with it */ - rpc->cb(rpc_state, rpc->cb_arg); - - return; - -error: - evrpc_reqstate_free(rpc_state); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error"); - return; -} - -void -evrpc_reqstate_free(struct evrpc_req_generic* rpc_state) -{ - /* clean up all memory */ - if (rpc_state != NULL) { - struct evrpc *rpc = rpc_state->rpc; - - if (rpc_state->request != NULL) - rpc->request_free(rpc_state->request); - if (rpc_state->reply != NULL) - rpc->reply_free(rpc_state->reply); - free(rpc_state); - } -} - -void -evrpc_request_done(struct evrpc_req_generic* rpc_state) -{ - struct evhttp_request *req = rpc_state->http_req; - struct evrpc *rpc = rpc_state->rpc; - struct evbuffer* data = NULL; - - if (rpc->reply_complete(rpc_state->reply) == -1) { - /* the reply was not completely filled in. error out */ - goto error; - } - - if ((data = evbuffer_new()) == NULL) { - /* out of memory */ - goto error; - } - - /* serialize the reply */ - rpc->reply_marshal(data, rpc_state->reply); - - /* do hook based tweaks to the request */ - if (evrpc_process_hooks(&rpc->base->output_hooks, - req, data) == -1) - goto error; - - /* on success, we are going to transmit marshaled binary data */ - if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) { - evhttp_add_header(req->output_headers, - "Content-Type", "application/octet-stream"); - } - - evhttp_send_reply(req, HTTP_OK, "OK", data); - - evbuffer_free(data); - - evrpc_reqstate_free(rpc_state); - - return; - -error: - if (data != NULL) - evbuffer_free(data); - evrpc_reqstate_free(rpc_state); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error"); - return; -} - -/* Client implementation of RPC site */ - -static int evrpc_schedule_request(struct evhttp_connection *connection, - struct evrpc_request_wrapper *ctx); - -struct evrpc_pool * -evrpc_pool_new(struct event_base *base) -{ - struct evrpc_pool *pool = calloc(1, sizeof(struct evrpc_pool)); - if (pool == NULL) - return (NULL); - - TAILQ_INIT(&pool->connections); - TAILQ_INIT(&pool->requests); - - TAILQ_INIT(&pool->input_hooks); - TAILQ_INIT(&pool->output_hooks); - - pool->base = base; - pool->timeout = -1; - - return (pool); -} - -static void -evrpc_request_wrapper_free(struct evrpc_request_wrapper *request) -{ - free(request->name); - free(request); -} - -void -evrpc_pool_free(struct evrpc_pool *pool) -{ - struct evhttp_connection *connection; - struct evrpc_request_wrapper *request; - struct evrpc_hook *hook; - - while ((request = TAILQ_FIRST(&pool->requests)) != NULL) { - TAILQ_REMOVE(&pool->requests, request, next); - /* if this gets more complicated we need our own function */ - evrpc_request_wrapper_free(request); - } - - while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) { - TAILQ_REMOVE(&pool->connections, connection, next); - evhttp_connection_free(connection); - } - - while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) { - assert(evrpc_remove_hook(pool, INPUT, hook)); - } - - while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) { - assert(evrpc_remove_hook(pool, OUTPUT, hook)); - } - - free(pool); -} - -/* - * Add a connection to the RPC pool. A request scheduled on the pool - * may use any available connection. - */ - -void -evrpc_pool_add_connection(struct evrpc_pool *pool, - struct evhttp_connection *connection) { - assert(connection->http_server == NULL); - TAILQ_INSERT_TAIL(&pool->connections, connection, next); - - /* - * associate an event base with this connection - */ - if (pool->base != NULL) - evhttp_connection_set_base(connection, pool->base); - - /* - * unless a timeout was specifically set for a connection, - * the connection inherits the timeout from the pool. - */ - if (connection->timeout == -1) - connection->timeout = pool->timeout; - - /* - * if we have any requests pending, schedule them with the new - * connections. - */ - - if (TAILQ_FIRST(&pool->requests) != NULL) { - struct evrpc_request_wrapper *request = - TAILQ_FIRST(&pool->requests); - TAILQ_REMOVE(&pool->requests, request, next); - evrpc_schedule_request(connection, request); - } -} - -void -evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs) -{ - struct evhttp_connection *evcon; - TAILQ_FOREACH(evcon, &pool->connections, next) { - evcon->timeout = timeout_in_secs; - } - pool->timeout = timeout_in_secs; -} - - -static void evrpc_reply_done(struct evhttp_request *, void *); -static void evrpc_request_timeout(int, short, void *); - -/* - * Finds a connection object associated with the pool that is currently - * idle and can be used to make a request. - */ -static struct evhttp_connection * -evrpc_pool_find_connection(struct evrpc_pool *pool) -{ - struct evhttp_connection *connection; - TAILQ_FOREACH(connection, &pool->connections, next) { - if (TAILQ_FIRST(&connection->requests) == NULL) - return (connection); - } - - return (NULL); -} - -/* - * We assume that the ctx is no longer queued on the pool. - */ -static int -evrpc_schedule_request(struct evhttp_connection *connection, - struct evrpc_request_wrapper *ctx) -{ - struct evhttp_request *req = NULL; - struct evrpc_pool *pool = ctx->pool; - struct evrpc_status status; - char *uri = NULL; - int res = 0; - - if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL) - goto error; - - /* serialize the request data into the output buffer */ - ctx->request_marshal(req->output_buffer, ctx->request); - - uri = evrpc_construct_uri(ctx->name); - if (uri == NULL) - goto error; - - /* we need to know the connection that we might have to abort */ - ctx->evcon = connection; - - /* apply hooks to the outgoing request */ - if (evrpc_process_hooks(&pool->output_hooks, - req, req->output_buffer) == -1) - goto error; - - if (pool->timeout > 0) { - /* - * a timeout after which the whole rpc is going to be aborted. - */ - struct timeval tv; - evutil_timerclear(&tv); - tv.tv_sec = pool->timeout; - evtimer_add(&ctx->ev_timeout, &tv); - } - - /* start the request over the connection */ - res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri); - free(uri); - - if (res == -1) - goto error; - - return (0); - -error: - memset(&status, 0, sizeof(status)); - status.error = EVRPC_STATUS_ERR_UNSTARTED; - (*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg); - evrpc_request_wrapper_free(ctx); - return (-1); -} - -int -evrpc_make_request(struct evrpc_request_wrapper *ctx) -{ - struct evrpc_pool *pool = ctx->pool; - - /* initialize the event structure for this rpc */ - evtimer_set(&ctx->ev_timeout, evrpc_request_timeout, ctx); - if (pool->base != NULL) - event_base_set(pool->base, &ctx->ev_timeout); - - /* we better have some available connections on the pool */ - assert(TAILQ_FIRST(&pool->connections) != NULL); - - /* - * if no connection is available, we queue the request on the pool, - * the next time a connection is empty, the rpc will be send on that. - */ - TAILQ_INSERT_TAIL(&pool->requests, ctx, next); - - evrpc_pool_schedule(pool); - - return (0); -} - -static void -evrpc_reply_done(struct evhttp_request *req, void *arg) -{ - struct evrpc_request_wrapper *ctx = arg; - struct evrpc_pool *pool = ctx->pool; - struct evrpc_status status; - int res = -1; - - /* cancel any timeout we might have scheduled */ - event_del(&ctx->ev_timeout); - - memset(&status, 0, sizeof(status)); - status.http_req = req; - - /* we need to get the reply now */ - if (req != NULL) { - /* apply hooks to the incoming request */ - if (evrpc_process_hooks(&pool->input_hooks, - req, req->input_buffer) == -1) { - status.error = EVRPC_STATUS_ERR_HOOKABORTED; - res = -1; - } else { - res = ctx->reply_unmarshal(ctx->reply, - req->input_buffer); - if (res == -1) { - status.error = EVRPC_STATUS_ERR_BADPAYLOAD; - } - } - } else { - status.error = EVRPC_STATUS_ERR_TIMEOUT; - } - - if (res == -1) { - /* clear everything that we might have written previously */ - ctx->reply_clear(ctx->reply); - } - - (*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg); - - evrpc_request_wrapper_free(ctx); - - /* the http layer owns the request structure */ - - /* see if we can schedule another request */ - evrpc_pool_schedule(pool); -} - -static void -evrpc_pool_schedule(struct evrpc_pool *pool) -{ - struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests); - struct evhttp_connection *evcon; - - /* if no requests are pending, we have no work */ - if (ctx == NULL) - return; - - if ((evcon = evrpc_pool_find_connection(pool)) != NULL) { - TAILQ_REMOVE(&pool->requests, ctx, next); - evrpc_schedule_request(evcon, ctx); - } -} - -static void -evrpc_request_timeout(int fd, short what, void *arg) -{ - struct evrpc_request_wrapper *ctx = arg; - struct evhttp_connection *evcon = ctx->evcon; - assert(evcon != NULL); - - evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); -} diff --git a/extra/libevent/evrpc.h b/extra/libevent/evrpc.h deleted file mode 100644 index 45f684ac5ac..00000000000 --- a/extra/libevent/evrpc.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (c) 2006 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVRPC_H_ -#define _EVRPC_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** @file evrpc.h - * - * This header files provides basic support for an RPC server and client. - * - * To support RPCs in a server, every supported RPC command needs to be - * defined and registered. - * - * EVRPC_HEADER(SendCommand, Request, Reply); - * - * SendCommand is the name of the RPC command. - * Request is the name of a structure generated by event_rpcgen.py. - * It contains all parameters relating to the SendCommand RPC. The - * server needs to fill in the Reply structure. - * Reply is the name of a structure generated by event_rpcgen.py. It - * contains the answer to the RPC. - * - * To register an RPC with an HTTP server, you need to first create an RPC - * base with: - * - * struct evrpc_base *base = evrpc_init(http); - * - * A specific RPC can then be registered with - * - * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg); - * - * when the server receives an appropriately formatted RPC, the user callback - * is invokved. The callback needs to fill in the reply structure. - * - * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg); - * - * To send the reply, call EVRPC_REQUEST_DONE(rpc); - * - * See the regression test for an example. - */ - -struct evbuffer; -struct event_base; -struct evrpc_req_generic; - -/* Encapsulates a request */ -struct evrpc { - TAILQ_ENTRY(evrpc) next; - - /* the URI at which the request handler lives */ - const char* uri; - - /* creates a new request structure */ - void *(*request_new)(void); - - /* frees the request structure */ - void (*request_free)(void *); - - /* unmarshals the buffer into the proper request structure */ - int (*request_unmarshal)(void *, struct evbuffer *); - - /* creates a new reply structure */ - void *(*reply_new)(void); - - /* creates a new reply structure */ - void (*reply_free)(void *); - - /* verifies that the reply is valid */ - int (*reply_complete)(void *); - - /* marshals the reply into a buffer */ - void (*reply_marshal)(struct evbuffer*, void *); - - /* the callback invoked for each received rpc */ - void (*cb)(struct evrpc_req_generic *, void *); - void *cb_arg; - - /* reference for further configuration */ - struct evrpc_base *base; -}; - -/** The type of a specific RPC Message - * - * @param rpcname the name of the RPC message - */ -#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname - -struct evhttp_request; -struct evrpc_status; - -/* We alias the RPC specific structs to this voided one */ -struct evrpc_req_generic { - /* the unmarshaled request object */ - void *request; - - /* the empty reply object that needs to be filled in */ - void *reply; - - /* - * the static structure for this rpc; that can be used to - * automatically unmarshal and marshal the http buffers. - */ - struct evrpc *rpc; - - /* - * the http request structure on which we need to answer. - */ - struct evhttp_request* http_req; - - /* - * callback to reply and finish answering this rpc - */ - void (*done)(struct evrpc_req_generic* rpc); -}; - -/** Creates the definitions and prototypes for an RPC - * - * You need to use EVRPC_HEADER to create structures and function prototypes - * needed by the server and client implementation. The structures have to be - * defined in an .rpc file and converted to source code via event_rpcgen.py - * - * @param rpcname the name of the RPC - * @param reqstruct the name of the RPC request structure - * @param replystruct the name of the RPC reply structure - * @see EVRPC_GENERATE() - */ -#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \ -EVRPC_STRUCT(rpcname) { \ - struct reqstruct* request; \ - struct rplystruct* reply; \ - struct evrpc* rpc; \ - struct evhttp_request* http_req; \ - void (*done)(struct evrpc_status *, \ - struct evrpc* rpc, void *request, void *reply); \ -}; \ -int evrpc_send_request_##rpcname(struct evrpc_pool *, \ - struct reqstruct *, struct rplystruct *, \ - void (*)(struct evrpc_status *, \ - struct reqstruct *, struct rplystruct *, void *cbarg), \ - void *); - -/** Generates the code for receiving and sending an RPC message - * - * EVRPC_GENERATE is used to create the code corresponding to sending - * and receiving a particular RPC message - * - * @param rpcname the name of the RPC - * @param reqstruct the name of the RPC request structure - * @param replystruct the name of the RPC reply structure - * @see EVRPC_HEADER() - */ -#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \ -int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \ - struct reqstruct *request, struct rplystruct *reply, \ - void (*cb)(struct evrpc_status *, \ - struct reqstruct *, struct rplystruct *, void *cbarg), \ - void *cbarg) { \ - struct evrpc_status status; \ - struct evrpc_request_wrapper *ctx; \ - ctx = (struct evrpc_request_wrapper *) \ - malloc(sizeof(struct evrpc_request_wrapper)); \ - if (ctx == NULL) \ - goto error; \ - ctx->pool = pool; \ - ctx->evcon = NULL; \ - ctx->name = strdup(#rpcname); \ - if (ctx->name == NULL) { \ - free(ctx); \ - goto error; \ - } \ - ctx->cb = (void (*)(struct evrpc_status *, \ - void *, void *, void *))cb; \ - ctx->cb_arg = cbarg; \ - ctx->request = (void *)request; \ - ctx->reply = (void *)reply; \ - ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \ - ctx->reply_clear = (void (*)(void *))rplystruct##_clear; \ - ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \ - return (evrpc_make_request(ctx)); \ -error: \ - memset(&status, 0, sizeof(status)); \ - status.error = EVRPC_STATUS_ERR_UNSTARTED; \ - (*(cb))(&status, request, reply, cbarg); \ - return (-1); \ -} - -/** Provides access to the HTTP request object underlying an RPC - * - * Access to the underlying http object; can be used to look at headers or - * for getting the remote ip address - * - * @param rpc_req the rpc request structure provided to the server callback - * @return an struct evhttp_request object that can be inspected for - * HTTP headers or sender information. - */ -#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req - -/** Creates the reply to an RPC request - * - * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected - * to have been filled in. The request and reply pointers become invalid - * after this call has finished. - * - * @param rpc_req the rpc request structure provided to the server callback - */ -#define EVRPC_REQUEST_DONE(rpc_req) do { \ - struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \ - _req->done(_req); \ -} while (0) - - -/* Takes a request object and fills it in with the right magic */ -#define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \ - do { \ - (rpc)->uri = strdup(#name); \ - if ((rpc)->uri == NULL) { \ - fprintf(stderr, "failed to register object\n"); \ - exit(1); \ - } \ - (rpc)->request_new = (void *(*)(void))request##_new; \ - (rpc)->request_free = (void (*)(void *))request##_free; \ - (rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \ - (rpc)->reply_new = (void *(*)(void))reply##_new; \ - (rpc)->reply_free = (void (*)(void *))reply##_free; \ - (rpc)->reply_complete = (int (*)(void *))reply##_complete; \ - (rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \ - } while (0) - -struct evrpc_base; -struct evhttp; - -/* functions to start up the rpc system */ - -/** Creates a new rpc base from which RPC requests can be received - * - * @param server a pointer to an existing HTTP server - * @return a newly allocated evrpc_base struct - * @see evrpc_free() - */ -struct evrpc_base *evrpc_init(struct evhttp *server); - -/** - * Frees the evrpc base - * - * For now, you are responsible for making sure that no rpcs are ongoing. - * - * @param base the evrpc_base object to be freed - * @see evrpc_init - */ -void evrpc_free(struct evrpc_base *base); - -/** register RPCs with the HTTP Server - * - * registers a new RPC with the HTTP server, each RPC needs to have - * a unique name under which it can be identified. - * - * @param base the evrpc_base structure in which the RPC should be - * registered. - * @param name the name of the RPC - * @param request the name of the RPC request structure - * @param reply the name of the RPC reply structure - * @param callback the callback that should be invoked when the RPC - * is received. The callback has the following prototype - * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg) - * @param cbarg an additional parameter that can be passed to the callback. - * The parameter can be used to carry around state. - */ -#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \ - do { \ - struct evrpc* rpc = (struct evrpc *)calloc(1, sizeof(struct evrpc)); \ - EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \ - evrpc_register_rpc(base, rpc, \ - (void (*)(struct evrpc_req_generic*, void *))callback, cbarg); \ - } while (0) - -int evrpc_register_rpc(struct evrpc_base *, struct evrpc *, - void (*)(struct evrpc_req_generic*, void *), void *); - -/** - * Unregisters an already registered RPC - * - * @param base the evrpc_base object from which to unregister an RPC - * @param name the name of the rpc to unregister - * @return -1 on error or 0 when successful. - * @see EVRPC_REGISTER() - */ -#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc(base, #name) - -int evrpc_unregister_rpc(struct evrpc_base *base, const char *name); - -/* - * Client-side RPC support - */ - -struct evrpc_pool; -struct evhttp_connection; - -/** - * provides information about the completed RPC request. - */ -struct evrpc_status { -#define EVRPC_STATUS_ERR_NONE 0 -#define EVRPC_STATUS_ERR_TIMEOUT 1 -#define EVRPC_STATUS_ERR_BADPAYLOAD 2 -#define EVRPC_STATUS_ERR_UNSTARTED 3 -#define EVRPC_STATUS_ERR_HOOKABORTED 4 - int error; - - /* for looking at headers or other information */ - struct evhttp_request *http_req; -}; - -struct evrpc_request_wrapper { - TAILQ_ENTRY(evrpc_request_wrapper) next; - - /* pool on which this rpc request is being made */ - struct evrpc_pool *pool; - - /* connection on which the request is being sent */ - struct evhttp_connection *evcon; - - /* event for implementing request timeouts */ - struct event ev_timeout; - - /* the name of the rpc */ - char *name; - - /* callback */ - void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg); - void *cb_arg; - - void *request; - void *reply; - - /* unmarshals the buffer into the proper request structure */ - void (*request_marshal)(struct evbuffer *, void *); - - /* removes all stored state in the reply */ - void (*reply_clear)(void *); - - /* marshals the reply into a buffer */ - int (*reply_unmarshal)(void *, struct evbuffer*); -}; - -/** launches an RPC and sends it to the server - * - * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server. - * - * @param name the name of the RPC - * @param pool the evrpc_pool that contains the connection objects over which - * the request should be sent. - * @param request a pointer to the RPC request structure - it contains the - * data to be sent to the server. - * @param reply a pointer to the RPC reply structure. It is going to be filled - * if the request was answered successfully - * @param cb the callback to invoke when the RPC request has been answered - * @param cbarg an additional argument to be passed to the client - * @return 0 on success, -1 on failure - */ -#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \ - evrpc_send_request_##name(pool, request, reply, cb, cbarg) - -int evrpc_make_request(struct evrpc_request_wrapper *); - -/** creates an rpc connection pool - * - * a pool has a number of connections associated with it. - * rpc requests are always made via a pool. - * - * @param base a pointer to an struct event_based object; can be left NULL - * in singled-threaded applications - * @return a newly allocated struct evrpc_pool object - * @see evrpc_pool_free() - */ -struct evrpc_pool *evrpc_pool_new(struct event_base *base); -/** frees an rpc connection pool - * - * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new() - * @see evrpc_pool_new() - */ -void evrpc_pool_free(struct evrpc_pool *pool); -/* - * adds a connection over which rpc can be dispatched. the connection - * object must have been newly created. - */ -void evrpc_pool_add_connection(struct evrpc_pool *, - struct evhttp_connection *); - -/** - * Sets the timeout in secs after which a request has to complete. The - * RPC is completely aborted if it does not complete by then. Setting - * the timeout to 0 means that it never timeouts and can be used to - * implement callback type RPCs. - * - * Any connection already in the pool will be updated with the new - * timeout. Connections added to the pool after set_timeout has be - * called receive the pool timeout only if no timeout has been set - * for the connection itself. - * - * @param pool a pointer to a struct evrpc_pool object - * @param timeout_in_secs the number of seconds after which a request should - * timeout and a failure be returned to the callback. - */ -void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs); - -/** - * Hooks for changing the input and output of RPCs; this can be used to - * implement compression, authentication, encryption, ... - */ - -enum EVRPC_HOOK_TYPE { - INPUT, /**< apply the function to an input hook */ - OUTPUT /**< apply the function to an output hook */ -}; - -/** adds a processing hook to either an rpc base or rpc pool - * - * If a hook returns -1, the processing is aborted. - * - * The add functions return handles that can be used for removing hooks. - * - * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool - * @param hook_type either INPUT or OUTPUT - * @param cb the callback to call when the hook is activated - * @param cb_arg an additional argument for the callback - * @return a handle to the hook so it can be removed later - * @see evrpc_remove_hook() - */ -void *evrpc_add_hook(void *vbase, - enum EVRPC_HOOK_TYPE hook_type, - int (*cb)(struct evhttp_request *, struct evbuffer *, void *), - void *cb_arg); - -/** removes a previously added hook - * - * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool - * @param hook_type either INPUT or OUTPUT - * @param handle a handle returned by evrpc_add_hook() - * @return 1 on success or 0 on failure - * @see evrpc_add_hook() - */ -int evrpc_remove_hook(void *vbase, - enum EVRPC_HOOK_TYPE hook_type, - void *handle); - -#ifdef __cplusplus -} -#endif - -#endif /* _EVRPC_H_ */ diff --git a/extra/libevent/evsignal.h b/extra/libevent/evsignal.h deleted file mode 100644 index 0d1e83140bd..00000000000 --- a/extra/libevent/evsignal.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2000-2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVSIGNAL_H_ -#define _EVSIGNAL_H_ - -typedef void (*ev_sighandler_t)(int); - -struct evsignal_info { - struct event_list signalqueue; - struct event ev_signal; - int ev_signal_pair[2]; - int ev_signal_added; - volatile sig_atomic_t evsignal_caught; - sig_atomic_t evsigcaught[NSIG]; -#ifdef HAVE_SIGACTION - struct sigaction **sh_old; -#else - ev_sighandler_t **sh_old; -#endif - int sh_old_max; -}; -void evsignal_init(struct event_base *); -void evsignal_process(struct event_base *); -int evsignal_add(struct event *); -int evsignal_del(struct event *); -void evsignal_dealloc(struct event_base *); - -#endif /* _EVSIGNAL_H_ */ diff --git a/extra/libevent/evutil.c b/extra/libevent/evutil.c deleted file mode 100644 index 1202d354fe4..00000000000 --- a/extra/libevent/evutil.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2007 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#include -#include "misc.h" -#endif - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#include - -#include "evutil.h" -#include "log.h" - -int -evutil_socketpair(int family, int type, int protocol, int fd[2]) -{ -#ifndef WIN32 - return socketpair(family, type, protocol, fd); -#else - /* This code is originally from Tor. Used with permission. */ - - /* This socketpair does not work when localhost is down. So - * it's really not the same thing at all. But it's close enough - * for now, and really, when localhost is down sometimes, we - * have other problems too. - */ - int listener = -1; - int connector = -1; - int acceptor = -1; - struct sockaddr_in listen_addr; - struct sockaddr_in connect_addr; - int size; - int saved_errno = -1; - - if (protocol -#ifdef AF_UNIX - || family != AF_UNIX -#endif - ) { - EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT); - return -1; - } - if (!fd) { - EVUTIL_SET_SOCKET_ERROR(WSAEINVAL); - return -1; - } - - listener = (int)socket(AF_INET, type, 0); - if (listener < 0) - return -1; - memset(&listen_addr, 0, sizeof(listen_addr)); - listen_addr.sin_family = AF_INET; - listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - listen_addr.sin_port = 0; /* kernel chooses port. */ - if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr)) - == -1) - goto tidy_up_and_fail; - if (listen(listener, 1) == -1) - goto tidy_up_and_fail; - - connector = (int)socket(AF_INET, type, 0); - if (connector < 0) - goto tidy_up_and_fail; - /* We want to find out the port number to connect to. */ - size = sizeof(connect_addr); - if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1) - goto tidy_up_and_fail; - if (size != sizeof (connect_addr)) - goto abort_tidy_up_and_fail; - if (connect(connector, (struct sockaddr *) &connect_addr, - sizeof(connect_addr)) == -1) - goto tidy_up_and_fail; - - size = sizeof(listen_addr); - acceptor = (int)accept(listener, (struct sockaddr *) &listen_addr, &size); - if (acceptor < 0) - goto tidy_up_and_fail; - if (size != sizeof(listen_addr)) - goto abort_tidy_up_and_fail; - EVUTIL_CLOSESOCKET(listener); - /* Now check we are talking to ourself by matching port and host on the - two sockets. */ - if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1) - goto tidy_up_and_fail; - if (size != sizeof (connect_addr) - || listen_addr.sin_family != connect_addr.sin_family - || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr - || listen_addr.sin_port != connect_addr.sin_port) - goto abort_tidy_up_and_fail; - fd[0] = connector; - fd[1] = acceptor; - - return 0; - - abort_tidy_up_and_fail: - saved_errno = WSAECONNABORTED; - tidy_up_and_fail: - if (saved_errno < 0) - saved_errno = WSAGetLastError(); - if (listener != -1) - EVUTIL_CLOSESOCKET(listener); - if (connector != -1) - EVUTIL_CLOSESOCKET(connector); - if (acceptor != -1) - EVUTIL_CLOSESOCKET(acceptor); - - EVUTIL_SET_SOCKET_ERROR(saved_errno); - return -1; -#endif -} - -int -evutil_make_socket_nonblocking(int fd) -{ -#ifdef WIN32 - { - unsigned long nonblocking = 1; - ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking); - } -#else - if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - event_warn("fcntl(O_NONBLOCK)"); - return -1; -} -#endif - return 0; -} - -ev_int64_t -evutil_strtoll(const char *s, char **endptr, int base) -{ -#ifdef HAVE_STRTOLL - return (ev_int64_t)strtoll(s, endptr, base); -#elif SIZEOF_LONG == 8 - return (ev_int64_t)strtol(s, endptr, base); -#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300 - /* XXXX on old versions of MS APIs, we only support base - * 10. */ - ev_int64_t r; - if (base != 10) - return 0; - r = (ev_int64_t) _atoi64(s); - while (isspace(*s)) - ++s; - while (isdigit(*s)) - ++s; - if (endptr) - *endptr = (char*) s; - return r; -#elif defined(WIN32) - return (ev_int64_t) _strtoi64(s, endptr, base); -#else -#error "I don't know how to parse 64-bit integers." -#endif -} diff --git a/extra/libevent/evutil.h b/extra/libevent/evutil.h deleted file mode 100644 index 2cfcacb2e0e..00000000000 --- a/extra/libevent/evutil.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2007 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _EVUTIL_H_ -#define _EVUTIL_H_ - -/** @file evutil.h - - Common convenience functions for cross-platform portability and - related socket manipulations. - - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#ifdef _EVENT_HAVE_SYS_TIME_H -#include -#endif -#ifdef _EVENT_HAVE_STDINT_H -#include -#elif defined(_EVENT_HAVE_INTTYPES_H) -#include -#endif -#ifdef _EVENT_HAVE_SYS_TYPES_H -#include -#endif - -#ifdef _EVENT_HAVE_UINT64_T -#define ev_uint64_t uint64_t -#define ev_int64_t int64_t -#elif defined(WIN32) -#define ev_uint64_t unsigned __int64 -#define ev_int64_t __int64 -#elif _EVENT_SIZEOF_LONG_LONG == 8 -#define ev_uint64_t unsigned long long -#define ev_int64_t long long -#elif _EVENT_SIZEOF_LONG == 8 -#define ev_uint64_t unsigned long -#define ev_int64_t long -#else -#error "No way to define ev_uint64_t" -#endif - -#ifdef _EVENT_HAVE_UINT32_T -#define ev_uint32_t uint32_t -#elif defined(WIN32) -#define ev_uint32_t unsigned int -#elif _EVENT_SIZEOF_LONG == 4 -#define ev_uint32_t unsigned long -#elif _EVENT_SIZEOF_INT == 4 -#define ev_uint32_t unsigned int -#else -#error "No way to define ev_uint32_t" -#endif - -#ifdef _EVENT_HAVE_UINT16_T -#define ev_uint16_t uint16_t -#elif defined(WIN32) -#define ev_uint16_t unsigned short -#elif _EVENT_SIZEOF_INT == 2 -#define ev_uint16_t unsigned int -#elif _EVENT_SIZEOF_SHORT == 2 -#define ev_uint16_t unsigned short -#else -#error "No way to define ev_uint16_t" -#endif - -#ifdef _EVENT_HAVE_UINT8_T -#define ev_uint8_t uint8_t -#else -#define ev_uint8_t unsigned char -#endif - -int evutil_socketpair(int d, int type, int protocol, int sv[2]); -int evutil_make_socket_nonblocking(int sock); -#ifdef WIN32 -#define EVUTIL_CLOSESOCKET(s) closesocket(s) -#else -#define EVUTIL_CLOSESOCKET(s) close(s) -#endif - -#ifdef WIN32 -#define EVUTIL_SOCKET_ERROR() WSAGetLastError() -#define EVUTIL_SET_SOCKET_ERROR(errcode) \ - do { WSASetLastError(errcode); } while (0) -#else -#define EVUTIL_SOCKET_ERROR() (errno) -#define EVUTIL_SET_SOCKET_ERROR(errcode) \ - do { errno = (errcode); } while (0) -#endif - -/* - * Manipulation functions for struct timeval - */ -#ifdef _EVENT_HAVE_TIMERADD -#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) -#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) -#else -#define evutil_timeradd(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ - if ((vvp)->tv_usec >= 1000000) { \ - (vvp)->tv_sec++; \ - (vvp)->tv_usec -= 1000000; \ - } \ - } while (0) -#define evutil_timersub(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ - if ((vvp)->tv_usec < 0) { \ - (vvp)->tv_sec--; \ - (vvp)->tv_usec += 1000000; \ - } \ - } while (0) -#endif /* !_EVENT_HAVE_HAVE_TIMERADD */ - -#ifdef _EVENT_HAVE_TIMERCLEAR -#define evutil_timerclear(tvp) timerclear(tvp) -#else -#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 -#endif - -#ifdef _EVENT_HAVE_TIMERCMP -#define evutil_timercmp(tvp, uvp, cmp) timercmp((tvp), (uvp), cmp) -#else -#define evutil_timercmp(tvp, uvp, cmp) \ - (((tvp)->tv_sec == (uvp)->tv_sec) ? \ - ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ - ((tvp)->tv_sec cmp (uvp)->tv_sec)) -#endif - -#ifdef _EVENT_HAVE_TIMERISSET -#define evutil_timerisset(tvp) timerisset(tvp) -#else -#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) -#endif - - -/* big-int related functions */ -ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); - -#ifdef __cplusplus -} -#endif - -#endif /* _EVUTIL_H_ */ diff --git a/extra/libevent/http-internal.h b/extra/libevent/http-internal.h deleted file mode 100644 index e9a45f124d8..00000000000 --- a/extra/libevent/http-internal.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2001 Niels Provos - * All rights reserved. - * - * This header file contains definitions for dealing with HTTP requests - * that are internal to libevent. As user of the library, you should not - * need to know about these. - */ - -#ifndef _HTTP_H_ -#define _HTTP_H_ - -#define HTTP_CONNECT_TIMEOUT 45 -#define HTTP_WRITE_TIMEOUT 50 -#define HTTP_READ_TIMEOUT 50 - -#define HTTP_PREFIX "http://" -#define HTTP_DEFAULTPORT 80 - -enum evhttp_connection_error { - EVCON_HTTP_TIMEOUT, - EVCON_HTTP_EOF, - EVCON_HTTP_INVALID_HEADER -}; - -struct evbuffer; -struct addrinfo; -struct evhttp_request; - -/* A stupid connection object - maybe make this a bufferevent later */ - -enum evhttp_connection_state { - EVCON_DISCONNECTED, /* not currently connected not trying either */ - EVCON_CONNECTING, /* tries to currently connect */ - EVCON_CONNECTED /* connection is established */ -}; - -struct event_base; - -struct evhttp_connection { - /* we use tailq only if they were created for an http server */ - TAILQ_ENTRY(evhttp_connection) (next); - - int fd; - struct event ev; - struct event close_ev; - struct evbuffer *input_buffer; - struct evbuffer *output_buffer; - - char *bind_address; /* address to use for binding the src */ - - char *address; /* address to connect to */ - u_short port; - - int flags; -#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */ -#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */ -#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */ - - int timeout; /* timeout in seconds for events */ - int retry_cnt; /* retry count */ - int retry_max; /* maximum number of retries */ - - enum evhttp_connection_state state; - - /* for server connections, the http server they are connected with */ - struct evhttp *http_server; - - TAILQ_HEAD(evcon_requestq, evhttp_request) requests; - - void (*cb)(struct evhttp_connection *, void *); - void *cb_arg; - - void (*closecb)(struct evhttp_connection *, void *); - void *closecb_arg; - - struct event_base *base; -}; - -struct evhttp_cb { - TAILQ_ENTRY(evhttp_cb) next; - - char *what; - - void (*cb)(struct evhttp_request *req, void *); - void *cbarg; -}; - -/* both the http server as well as the rpc system need to queue connections */ -TAILQ_HEAD(evconq, evhttp_connection); - -struct evhttp { - struct event bind_ev; - - TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; - struct evconq connections; - - int timeout; - - void (*gencb)(struct evhttp_request *req, void *); - void *gencbarg; - - struct event_base *base; -}; - -/* resets the connection; can be reused for more requests */ -void evhttp_connection_reset(struct evhttp_connection *); - -/* connects if necessary */ -int evhttp_connection_connect(struct evhttp_connection *); - -/* notifies the current request that it failed; resets connection */ -void evhttp_connection_fail(struct evhttp_connection *, - enum evhttp_connection_error error); - -void evhttp_get_request(struct evhttp *, int, struct sockaddr *, socklen_t); - -int evhttp_hostportfile(char *, char **, u_short *, char **); - -int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*); - -void evhttp_start_read(struct evhttp_connection *); -void evhttp_read_header(int, short, void *); -void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *); - -void evhttp_write_buffer(struct evhttp_connection *, - void (*)(struct evhttp_connection *, void *), void *); - -/* response sending HTML the data in the buffer */ -void evhttp_response_code(struct evhttp_request *, int, const char *); -void evhttp_send_page(struct evhttp_request *, struct evbuffer *); - -#endif /* _HTTP_H */ diff --git a/extra/libevent/http.c b/extra/libevent/http.c deleted file mode 100644 index d56a34ca122..00000000000 --- a/extra/libevent/http.c +++ /dev/null @@ -1,2512 +0,0 @@ -/* - * Copyright (c) 2002-2006 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SYS_PARAM_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_SYS_IOCCOM_H -#include -#endif - -#ifndef WIN32 -#include -#include -#include -#include -#endif - -#include - -#ifndef WIN32 -#include -#include -#endif - -#ifdef WIN32 -#include -#endif - -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif - -#undef timeout_pending -#undef timeout_initialized - -#include "strlcpy-internal.h" -#include "event.h" -#include "evhttp.h" -#include "evutil.h" -#include "log.h" -#include "http-internal.h" - -#ifdef WIN32 -#define snprintf _snprintf -#define strcasecmp _stricmp -#define strncasecmp _strnicmp -#define strdup _strdup -#endif - -#ifndef HAVE_GETADDRINFO -struct addrinfo { - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - struct sockaddr *ai_addr; - struct addrinfo *ai_next; -}; -static int -fake_getaddrinfo(const char *hostname, struct addrinfo *ai) -{ - struct hostent *he = NULL; - struct sockaddr_in *sa; - if (hostname) { - he = gethostbyname(hostname); - if (!he) - return (-1); - } - ai->ai_family = he ? he->h_addrtype : AF_INET; - ai->ai_socktype = SOCK_STREAM; - ai->ai_protocol = 0; - ai->ai_addrlen = sizeof(struct sockaddr_in); - if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen))) - return (-1); - sa = (struct sockaddr_in*)ai->ai_addr; - memset(sa, 0, ai->ai_addrlen); - if (he) { - sa->sin_family = he->h_addrtype; - memcpy(&sa->sin_addr, he->h_addr_list[0], he->h_length); - } else { - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = INADDR_ANY; - } - ai->ai_next = NULL; - return (0); -} -static void -fake_freeaddrinfo(struct addrinfo *ai) -{ - free(ai->ai_addr); -} -#endif - -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif - -/* wrapper for setting the base from the http server */ -#define EVHTTP_BASE_SET(x, y) do { \ - if ((x)->base != NULL) event_base_set((x)->base, y); \ -} while (0) - -extern int debug; - -static int socket_connect(int fd, const char *address, unsigned short port); -static int bind_socket_ai(struct addrinfo *); -static int bind_socket(const char *, u_short); -static void name_from_addr(struct sockaddr *, socklen_t, char **, char **); -static int evhttp_associate_new_request_with_connection( - struct evhttp_connection *evcon); -static void evhttp_connection_start_detectclose( - struct evhttp_connection *evcon); -static void evhttp_connection_stop_detectclose( - struct evhttp_connection *evcon); -static void evhttp_request_dispatch(struct evhttp_connection* evcon); - -void evhttp_read(int, short, void *); -void evhttp_write(int, short, void *); - -#ifndef HAVE_STRSEP -/* strsep replacement for platforms that lack it. Only works if - * del is one character long. */ -static char * -strsep(char **s, const char *del) -{ - char *d, *tok; - assert(strlen(del) == 1); - if (!s || !*s) - return NULL; - tok = *s; - d = strstr(tok, del); - if (d) { - *d = '\0'; - *s = d + 1; - } else - *s = NULL; - return tok; -} -#endif - -static const char * -html_replace(char ch, char *buf) -{ - switch (ch) { - case '<': - return "<"; - case '>': - return ">"; - case '"': - return """; - case '\'': - return "'"; - case '&': - return "&"; - default: - break; - } - - /* Echo the character back */ - buf[0] = ch; - buf[1] = '\0'; - - return buf; -} - -/* - * Replaces <, >, ", ' and & with <, >, ", - * ' and & correspondingly. - * - * The returned string needs to be freed by the caller. - */ - -char * -evhttp_htmlescape(const char *html) -{ - int i, new_size = 0, old_size = strlen(html); - char *escaped_html, *p; - char scratch_space[2]; - - for (i = 0; i < old_size; ++i) - new_size += strlen(html_replace(html[i], scratch_space)); - - p = escaped_html = malloc(new_size + 1); - if (escaped_html == NULL) - event_err(1, "%s: malloc(%d)", __func__, new_size + 1); - for (i = 0; i < old_size; ++i) { - const char *replaced = html_replace(html[i], scratch_space); - /* this is length checked */ - strcpy(p, replaced); - p += strlen(replaced); - } - - *p = '\0'; - - return (escaped_html); -} - -static const char * -evhttp_method(enum evhttp_cmd_type type) -{ - const char *method; - - switch (type) { - case EVHTTP_REQ_GET: - method = "GET"; - break; - case EVHTTP_REQ_POST: - method = "POST"; - break; - case EVHTTP_REQ_HEAD: - method = "HEAD"; - break; - default: - method = NULL; - break; - } - - return (method); -} - -static void -evhttp_add_event(struct event *ev, int timeout, int default_timeout) -{ - if (timeout != 0) { - struct timeval tv; - - evutil_timerclear(&tv); - tv.tv_sec = timeout != -1 ? timeout : default_timeout; - event_add(ev, &tv); - } else { - event_add(ev, NULL); - } -} - -void -evhttp_write_buffer(struct evhttp_connection *evcon, - void (*cb)(struct evhttp_connection *, void *), void *arg) -{ - event_debug(("%s: preparing to write buffer\n", __func__)); - - /* Set call back */ - evcon->cb = cb; - evcon->cb_arg = arg; - - /* check if the event is already pending */ - if (event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) - event_del(&evcon->ev); - - event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon); - EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT); -} - -/* - * Create the headers need for an HTTP request - */ -static void -evhttp_make_header_request(struct evhttp_connection *evcon, - struct evhttp_request *req) -{ - char line[1024]; - const char *method; - - evhttp_remove_header(req->output_headers, "Accept-Encoding"); - evhttp_remove_header(req->output_headers, "Proxy-Connection"); - - /* Generate request line */ - method = evhttp_method(req->type); - snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n", - method, req->uri, req->major, req->minor); - evbuffer_add(evcon->output_buffer, line, strlen(line)); - - /* Add the content length on a post request if missing */ - if (req->type == EVHTTP_REQ_POST && - evhttp_find_header(req->output_headers, "Content-Length") == NULL){ - char size[12]; - snprintf(size, sizeof(size), "%ld", - (long)EVBUFFER_LENGTH(req->output_buffer)); - evhttp_add_header(req->output_headers, "Content-Length", size); - } -} - -static int -evhttp_is_connection_close(int flags, struct evkeyvalq* headers) -{ - if (flags & EVHTTP_PROXY_REQUEST) { - /* proxy connection */ - const char *connection = evhttp_find_header(headers, "Proxy-Connection"); - return (connection == NULL || strcasecmp(connection, "keep-alive") != 0); - } else { - const char *connection = evhttp_find_header(headers, "Connection"); - return (connection != NULL && strcasecmp(connection, "close") == 0); - } -} - -static int -evhttp_is_connection_keepalive(struct evkeyvalq* headers) -{ - const char *connection = evhttp_find_header(headers, "Connection"); - return (connection != NULL - && strncasecmp(connection, "keep-alive", 10) == 0); -} - -static void -evhttp_maybe_add_date_header(struct evkeyvalq *headers) -{ - if (evhttp_find_header(headers, "Date") == NULL) { - char date[50]; -#ifndef WIN32 - struct tm cur; -#endif - struct tm *cur_p; - time_t t = time(NULL); -#ifdef WIN32 - cur_p = gmtime(&t); -#else - gmtime_r(&t, &cur); - cur_p = &cur; -#endif - if (strftime(date, sizeof(date), - "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) { - evhttp_add_header(headers, "Date", date); - } - } -} - -static void -evhttp_maybe_add_content_length_header(struct evkeyvalq *headers, - long content_length) -{ - if (evhttp_find_header(headers, "Transfer-Encoding") == NULL && - evhttp_find_header(headers, "Content-Length") == NULL) { - char len[12]; - snprintf(len, sizeof(len), "%ld", content_length); - evhttp_add_header(headers, "Content-Length", len); - } -} - -/* - * Create the headers needed for an HTTP reply - */ - -static void -evhttp_make_header_response(struct evhttp_connection *evcon, - struct evhttp_request *req) -{ - char line[1024]; - snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n", - req->major, req->minor, req->response_code, - req->response_code_line); - evbuffer_add(evcon->output_buffer, line, strlen(line)); - - if (req->major == 1 && req->minor == 1) - evhttp_maybe_add_date_header(req->output_headers); - - if (req->major == 1 && - (req->minor == 1 || - evhttp_is_connection_keepalive(req->input_headers))) { - /* - * we need to add the content length if the user did - * not give it, this is required for persistent - * connections to work. - */ - evhttp_maybe_add_content_length_header(req->output_headers, - (long)EVBUFFER_LENGTH(req->output_buffer)); - } - - /* Potentially add headers for unidentified content. */ - if (EVBUFFER_LENGTH(req->output_buffer)) { - if (evhttp_find_header(req->output_headers, - "Content-Type") == NULL) { - evhttp_add_header(req->output_headers, - "Content-Type", "text/html; charset=ISO-8859-1"); - } - } - - /* if the request asked for a close, we send a close, too */ - if (evhttp_is_connection_close(req->flags, req->input_headers)) { - evhttp_remove_header(req->output_headers, "Connection"); - if (!(req->flags & EVHTTP_PROXY_REQUEST)) - evhttp_add_header(req->output_headers, "Connection", "close"); - evhttp_remove_header(req->output_headers, "Proxy-Connection"); - } -} - -void -evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req) -{ - char line[1024]; - struct evkeyval *header; - - /* - * Depending if this is a HTTP request or response, we might need to - * add some new headers or remove existing headers. - */ - if (req->kind == EVHTTP_REQUEST) { - evhttp_make_header_request(evcon, req); - } else { - evhttp_make_header_response(evcon, req); - } - - TAILQ_FOREACH(header, req->output_headers, next) { - snprintf(line, sizeof(line), "%s: %s\r\n", - header->key, header->value); - evbuffer_add(evcon->output_buffer, line, strlen(line)); - } - evbuffer_add(evcon->output_buffer, "\r\n", 2); - - if (EVBUFFER_LENGTH(req->output_buffer) > 0) { - /* - * For a request, we add the POST data, for a reply, this - * is the regular data. - */ - evbuffer_add_buffer(evcon->output_buffer, req->output_buffer); - } -} - -/* Separated host, port and file from URI */ - -int -evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile) -{ - /* XXX not threadsafe. */ - static char host[1024]; - static char file[1024]; - char *p; - const char *p2; - int len; - u_short port; - - len = strlen(HTTP_PREFIX); - if (strncasecmp(url, HTTP_PREFIX, len)) - return (-1); - - url += len; - - /* We might overrun */ - if (strlcpy(host, url, sizeof (host)) >= sizeof(host)) - return (-1); - - p = strchr(host, '/'); - if (p != NULL) { - *p = '\0'; - p2 = p + 1; - } else - p2 = NULL; - - if (pfile != NULL) { - /* Generate request file */ - if (p2 == NULL) - p2 = ""; - snprintf(file, sizeof(file), "/%s", p2); - } - - p = strchr(host, ':'); - if (p != NULL) { - *p = '\0'; - port = atoi(p + 1); - - if (port == 0) - return (-1); - } else - port = HTTP_DEFAULTPORT; - - if (phost != NULL) - *phost = host; - if (pport != NULL) - *pport = port; - if (pfile != NULL) - *pfile = file; - - return (0); -} - -static int -evhttp_connection_incoming_fail(struct evhttp_request *req, - enum evhttp_connection_error error) -{ - switch (error) { - case EVCON_HTTP_TIMEOUT: - case EVCON_HTTP_EOF: - /* - * these are cases in which we probably should just - * close the connection and not send a reply. this - * case may happen when a browser keeps a persistent - * connection open and we timeout on the read. - */ - return (-1); - case EVCON_HTTP_INVALID_HEADER: - default: /* xxx: probably should just error on default */ - /* the callback looks at the uri to determine errors */ - if (req->uri) { - free(req->uri); - req->uri = NULL; - } - - /* - * the callback needs to send a reply, once the reply has - * been send, the connection should get freed. - */ - (*req->cb)(req, req->cb_arg); - } - - return (0); -} - -void -evhttp_connection_fail(struct evhttp_connection *evcon, - enum evhttp_connection_error error) -{ - struct evhttp_request* req = TAILQ_FIRST(&evcon->requests); - void (*cb)(struct evhttp_request *, void *); - void *cb_arg; - assert(req != NULL); - - if (evcon->flags & EVHTTP_CON_INCOMING) { - /* - * for incoming requests, there are two different - * failure cases. it's either a network level error - * or an http layer error. for problems on the network - * layer like timeouts we just drop the connections. - * For HTTP problems, we might have to send back a - * reply before the connection can be freed. - */ - if (evhttp_connection_incoming_fail(req, error) == -1) - evhttp_connection_free(evcon); - return; - } - - /* save the callback for later; the cb might free our object */ - cb = req->cb; - cb_arg = req->cb_arg; - - TAILQ_REMOVE(&evcon->requests, req, next); - evhttp_request_free(req); - - /* xxx: maybe we should fail all requests??? */ - - /* reset the connection */ - evhttp_connection_reset(evcon); - - /* We are trying the next request that was queued on us */ - if (TAILQ_FIRST(&evcon->requests) != NULL) - evhttp_connection_connect(evcon); - - /* inform the user */ - if (cb != NULL) - (*cb)(NULL, cb_arg); -} - -void -evhttp_write(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - int n; - - if (what == EV_TIMEOUT) { - evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); - return; - } - - n = evbuffer_write(evcon->output_buffer, fd); - if (n == -1) { - event_debug(("%s: evbuffer_write", __func__)); - evhttp_connection_fail(evcon, EVCON_HTTP_EOF); - return; - } - - if (n == 0) { - event_debug(("%s: write nothing", __func__)); - evhttp_connection_fail(evcon, EVCON_HTTP_EOF); - return; - } - - if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) { - evhttp_add_event(&evcon->ev, - evcon->timeout, HTTP_WRITE_TIMEOUT); - return; - } - - /* Activate our call back */ - if (evcon->cb != NULL) - (*evcon->cb)(evcon, evcon->cb_arg); -} - -static void -evhttp_connection_done(struct evhttp_connection *evcon) -{ - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING; - - /* - * if this is an incoming connection, we need to leave the request - * on the connection, so that we can reply to it. - */ - if (con_outgoing) { - int need_close; - TAILQ_REMOVE(&evcon->requests, req, next); - req->evcon = NULL; - - need_close = - evhttp_is_connection_close(req->flags, req->input_headers) || - evhttp_is_connection_close(req->flags, req->output_headers); - - /* check if we got asked to close the connection */ - if (need_close) - evhttp_connection_reset(evcon); - - if (TAILQ_FIRST(&evcon->requests) != NULL) { - /* - * We have more requests; reset the connection - * and deal with the next request. xxx: no - * persistent connection right now - */ - if (evcon->state != EVCON_CONNECTED) - evhttp_connection_connect(evcon); - else - evhttp_request_dispatch(evcon); - } else if (!need_close) { - /* - * The connection is going to be persistent, but we - * need to detect if the other side closes it. - */ - evhttp_connection_start_detectclose(evcon); - } - } - - /* notify the user of the request */ - (*req->cb)(req, req->cb_arg); - - /* if this was an outgoing request, we own and it's done. so free it */ - if (con_outgoing) { - evhttp_request_free(req); - } -} - -/* - * Handles reading from a chunked request. - * return 1: all data has been read - * return 0: more data is expected - * return -1: data is corrupted - */ - -static int -evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) -{ - int len; - - while ((len = EVBUFFER_LENGTH(buf)) > 0) { - if (req->ntoread < 0) { - /* Read chunk size */ - char *p = evbuffer_readline(buf); - char *endp; - int error; - if (p == NULL) - break; - /* the last chunk is on a new line? */ - if (strlen(p) == 0) { - free(p); - continue; - } - req->ntoread = evutil_strtoll(p, &endp, 16); - error = *p == '\0' || (*endp != '\0' && *endp != ' '); - free(p); - if (error) { - /* could not get chunk size */ - return (-1); - } - if (req->ntoread == 0) { - /* Last chunk */ - return (1); - } - continue; - } - - /* don't have enough to complete a chunk; wait for more */ - if (len < req->ntoread) - return (0); - - /* Completed chunk */ - evbuffer_add(req->input_buffer, - EVBUFFER_DATA(buf), req->ntoread); - evbuffer_drain(buf, req->ntoread); - req->ntoread = -1; - if (req->chunk_cb != NULL) { - (*req->chunk_cb)(req, req->cb_arg); - evbuffer_drain(req->input_buffer, - EVBUFFER_LENGTH(req->input_buffer)); - } - } - - return (0); -} - -static void -evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) -{ - struct evbuffer *buf = evcon->input_buffer; - - if (req->chunked) { - int res = evhttp_handle_chunked_read(req, buf); - if (res == 1) { - /* finished last chunk */ - evhttp_connection_done(evcon); - return; - } else if (res == -1) { - /* corrupted data */ - evhttp_connection_fail(evcon, - EVCON_HTTP_INVALID_HEADER); - return; - } - } else if (req->ntoread < 0) { - /* Read until connection close. */ - evbuffer_add_buffer(req->input_buffer, buf); - } else if (EVBUFFER_LENGTH(buf) >= req->ntoread) { - /* Completed content length */ - evbuffer_add(req->input_buffer, EVBUFFER_DATA(buf), - req->ntoread); - evbuffer_drain(buf, req->ntoread); - req->ntoread = 0; - evhttp_connection_done(evcon); - return; - } - /* Read more! */ - event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon); - EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT); -} - -/* - * Reads data into a buffer structure until no more data - * can be read on the file descriptor or we have read all - * the data that we wanted to read. - * Execute callback when done. - */ - -void -evhttp_read(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - struct evbuffer *buf = evcon->input_buffer; - int n, len; - - if (what == EV_TIMEOUT) { - evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); - return; - } - n = evbuffer_read(buf, fd, -1); - len = EVBUFFER_LENGTH(buf); - event_debug(("%s: got %d on %d\n", __func__, n, fd)); - - if (n == -1) { - event_debug(("%s: evbuffer_read", __func__)); - evhttp_connection_fail(evcon, EVCON_HTTP_EOF); - return; - } else if (n == 0) { - /* Connection closed */ - evhttp_connection_done(evcon); - return; - } - evhttp_read_body(evcon, req); -} - -static void -evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg) -{ - /* This is after writing the request to the server */ - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - assert(req != NULL); - - /* We are done writing our header and are now expecting the response */ - req->kind = EVHTTP_RESPONSE; - - evhttp_start_read(evcon); -} - -/* - * Clean up a connection object - */ - -void -evhttp_connection_free(struct evhttp_connection *evcon) -{ - struct evhttp_request *req; - - /* notify interested parties that this connection is going down */ - if (evcon->fd != -1) { - if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL) - (*evcon->closecb)(evcon, evcon->closecb_arg); - } - - /* remove all requests that might be queued on this connection */ - while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) { - TAILQ_REMOVE(&evcon->requests, req, next); - evhttp_request_free(req); - } - - if (evcon->http_server != NULL) { - struct evhttp *http = evcon->http_server; - TAILQ_REMOVE(&http->connections, evcon, next); - } - - if (event_initialized(&evcon->close_ev)) - event_del(&evcon->close_ev); - - if (event_initialized(&evcon->ev)) - event_del(&evcon->ev); - - if (evcon->fd != -1) - EVUTIL_CLOSESOCKET(evcon->fd); - - if (evcon->bind_address != NULL) - free(evcon->bind_address); - - if (evcon->address != NULL) - free(evcon->address); - - if (evcon->input_buffer != NULL) - evbuffer_free(evcon->input_buffer); - - if (evcon->output_buffer != NULL) - evbuffer_free(evcon->output_buffer); - - free(evcon); -} - -void -evhttp_connection_set_local_address(struct evhttp_connection *evcon, - const char *address) -{ - assert(evcon->state == EVCON_DISCONNECTED); - if (evcon->bind_address) - free(evcon->bind_address); - if ((evcon->bind_address = strdup(address)) == NULL) - event_err(1, "%s: strdup", __func__); -} - - -static void -evhttp_request_dispatch(struct evhttp_connection* evcon) -{ - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - - /* this should not usually happy but it's possible */ - if (req == NULL) - return; - - /* delete possible close detection events */ - evhttp_connection_stop_detectclose(evcon); - - /* we assume that the connection is connected already */ - assert(evcon->state == EVCON_CONNECTED); - - /* Create the header from the store arguments */ - evhttp_make_header(evcon, req); - - evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL); -} - -/* Reset our connection state */ -void -evhttp_connection_reset(struct evhttp_connection *evcon) -{ - if (event_initialized(&evcon->ev)) - event_del(&evcon->ev); - - if (evcon->fd != -1) { - /* inform interested parties about connection close */ - if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL) - (*evcon->closecb)(evcon, evcon->closecb_arg); - - EVUTIL_CLOSESOCKET(evcon->fd); - evcon->fd = -1; - } - evcon->state = EVCON_DISCONNECTED; - - /* remove unneeded flags */ - evcon->flags &= ~EVHTTP_CON_CLOSEDETECT; -} - -static void -evhttp_detect_close_cb(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - evhttp_connection_reset(evcon); -} - -static void -evhttp_connection_start_detectclose(struct evhttp_connection *evcon) -{ - evcon->flags |= EVHTTP_CON_CLOSEDETECT; - - if (event_initialized(&evcon->close_ev)) - event_del(&evcon->close_ev); - event_set(&evcon->close_ev, evcon->fd, EV_READ, - evhttp_detect_close_cb, evcon); - EVHTTP_BASE_SET(evcon, &evcon->close_ev); - event_add(&evcon->close_ev, NULL); -} - -static void -evhttp_connection_stop_detectclose(struct evhttp_connection *evcon) -{ - evcon->flags &= ~EVHTTP_CON_CLOSEDETECT; - event_del(&evcon->close_ev); -} - -static void -evhttp_connection_retry(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - - evcon->state = EVCON_DISCONNECTED; - evhttp_connection_connect(evcon); -} - -/* - * Call back for asynchronous connection attempt. - */ - -static void -evhttp_connectioncb(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - int error; - socklen_t errsz = sizeof(error); - - if (what == EV_TIMEOUT) { - event_debug(("%s: connection timeout for \"%s:%d\" on %d", - __func__, evcon->address, evcon->port, evcon->fd)); - goto cleanup; - } - - /* Check if the connection completed */ - if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error, - &errsz) == -1) { - event_debug(("%s: getsockopt for \"%s:%d\" on %d", - __func__, evcon->address, evcon->port, evcon->fd)); - goto cleanup; - } - - if (error) { - event_debug(("%s: connect failed for \"%s:%d\" on %d: %s", - __func__, evcon->address, evcon->port, evcon->fd, - strerror(error))); - goto cleanup; - } - - /* We are connected to the server now */ - event_debug(("%s: connected to \"%s:%d\" on %d\n", - __func__, evcon->address, evcon->port, evcon->fd)); - - /* Reset the retry count as we were successful in connecting */ - evcon->retry_cnt = 0; - evcon->state = EVCON_CONNECTED; - - /* try to start requests that have queued up on this connection */ - evhttp_request_dispatch(evcon); - return; - - cleanup: - if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) { - evtimer_set(&evcon->ev, evhttp_connection_retry, evcon); - EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, MIN(3600, 2 << evcon->retry_cnt), - HTTP_CONNECT_TIMEOUT); - evcon->retry_cnt++; - return; - } - evhttp_connection_reset(evcon); - - /* for now, we just signal all requests by executing their callbacks */ - while (TAILQ_FIRST(&evcon->requests) != NULL) { - struct evhttp_request *request = TAILQ_FIRST(&evcon->requests); - TAILQ_REMOVE(&evcon->requests, request, next); - request->evcon = NULL; - - /* we might want to set an error here */ - request->cb(request, request->cb_arg); - evhttp_request_free(request); - } -} - -/* - * Check if we got a valid response code. - */ - -static int -evhttp_valid_response_code(int code) -{ - if (code == 0) - return (0); - - return (1); -} - -/* Parses the status line of a web server */ - -static int -evhttp_parse_response_line(struct evhttp_request *req, char *line) -{ - char *protocol; - char *number; - char *readable; - - protocol = strsep(&line, " "); - if (line == NULL) - return (-1); - number = strsep(&line, " "); - if (line == NULL) - return (-1); - readable = line; - - if (strcmp(protocol, "HTTP/1.0") == 0) { - req->major = 1; - req->minor = 0; - } else if (strcmp(protocol, "HTTP/1.1") == 0) { - req->major = 1; - req->minor = 1; - } else { - event_debug(("%s: bad protocol \"%s\"", - __func__, protocol)); - return (-1); - } - - req->response_code = atoi(number); - if (!evhttp_valid_response_code(req->response_code)) { - event_debug(("%s: bad response code \"%s\"", - __func__, number)); - return (-1); - } - - if ((req->response_code_line = strdup(readable)) == NULL) - event_err(1, "%s: strdup", __func__); - - return (0); -} - -/* Parse the first line of a HTTP request */ - -static int -evhttp_parse_request_line(struct evhttp_request *req, char *line) -{ - char *method; - char *uri; - char *version; - - /* Parse the request line */ - method = strsep(&line, " "); - if (line == NULL) - return (-1); - uri = strsep(&line, " "); - if (line == NULL) - return (-1); - version = strsep(&line, " "); - if (line != NULL) - return (-1); - - /* First line */ - if (strcmp(method, "GET") == 0) { - req->type = EVHTTP_REQ_GET; - } else if (strcmp(method, "POST") == 0) { - req->type = EVHTTP_REQ_POST; - } else if (strcmp(method, "HEAD") == 0) { - req->type = EVHTTP_REQ_HEAD; - } else { - event_debug(("%s: bad method %s on request %p from %s", - __func__, method, req, req->remote_host)); - return (-1); - } - - if (strcmp(version, "HTTP/1.0") == 0) { - req->major = 1; - req->minor = 0; - } else if (strcmp(version, "HTTP/1.1") == 0) { - req->major = 1; - req->minor = 1; - } else { - event_debug(("%s: bad version %s on request %p from %s", - __func__, version, req, req->remote_host)); - return (-1); - } - - if ((req->uri = strdup(uri)) == NULL) { - event_debug(("%s: evhttp_decode_uri", __func__)); - return (-1); - } - - /* determine if it's a proxy request */ - if (strlen(req->uri) > 0 && req->uri[0] != '/') - req->flags |= EVHTTP_PROXY_REQUEST; - - return (0); -} - -const char * -evhttp_find_header(const struct evkeyvalq *headers, const char *key) -{ - struct evkeyval *header; - - TAILQ_FOREACH(header, headers, next) { - if (strcasecmp(header->key, key) == 0) - return (header->value); - } - - return (NULL); -} - -void -evhttp_clear_headers(struct evkeyvalq *headers) -{ - struct evkeyval *header; - - for (header = TAILQ_FIRST(headers); - header != NULL; - header = TAILQ_FIRST(headers)) { - TAILQ_REMOVE(headers, header, next); - free(header->key); - free(header->value); - free(header); - } -} - -/* - * Returns 0, if the header was successfully removed. - * Returns -1, if the header could not be found. - */ - -int -evhttp_remove_header(struct evkeyvalq *headers, const char *key) -{ - struct evkeyval *header; - - TAILQ_FOREACH(header, headers, next) { - if (strcasecmp(header->key, key) == 0) - break; - } - - if (header == NULL) - return (-1); - - /* Free and remove the header that we found */ - TAILQ_REMOVE(headers, header, next); - free(header->key); - free(header->value); - free(header); - - return (0); -} - -int -evhttp_add_header(struct evkeyvalq *headers, - const char *key, const char *value) -{ - struct evkeyval *header = NULL; - - event_debug(("%s: key: %s val: %s\n", __func__, key, value)); - - if (strchr(value, '\r') != NULL || strchr(value, '\n') != NULL || - strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) { - /* drop illegal headers */ - event_debug(("%s: dropping illegal header\n", __func__)); - return (-1); - } - - header = calloc(1, sizeof(struct evkeyval)); - if (header == NULL) { - event_warn("%s: calloc", __func__); - return (-1); - } - if ((header->key = strdup(key)) == NULL) { - free(header); - event_warn("%s: strdup", __func__); - return (-1); - } - if ((header->value = strdup(value)) == NULL) { - free(header->key); - free(header); - event_warn("%s: strdup", __func__); - return (-1); - } - - TAILQ_INSERT_TAIL(headers, header, next); - - return (0); -} - -/* - * Parses header lines from a request or a response into the specified - * request object given an event buffer. - * - * Returns - * -1 on error - * 0 when we need to read more headers - * 1 when all headers have been read. - */ - -int -evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer) -{ - char *line; - int done = 0; - - struct evkeyvalq* headers = req->input_headers; - while ((line = evbuffer_readline(buffer)) != NULL) { - char *skey, *svalue; - - if (*line == '\0') { /* Last header - Done */ - done = 1; - free (line); - break; - } - - /* Processing of header lines */ - if (req->got_firstline == 0) { - switch (req->kind) { - case EVHTTP_REQUEST: - if (evhttp_parse_request_line(req, line) == -1) - goto error; - break; - case EVHTTP_RESPONSE: - if (evhttp_parse_response_line(req, line) == -1) - goto error; - break; - default: - goto error; - } - req->got_firstline = 1; - } else { - /* Regular header */ - svalue = line; - skey = strsep(&svalue, ":"); - if (svalue == NULL) - goto error; - - svalue += strspn(svalue, " "); - - if (evhttp_add_header(headers, skey, svalue) == -1) - goto error; - } - - free (line); - } - - return (done); - - error: - free (line); - return (-1); -} - -static int -evhttp_get_body_length(struct evhttp_request *req) -{ - struct evkeyvalq *headers = req->input_headers; - const char *content_length; - const char *connection; - - content_length = evhttp_find_header(headers, "Content-Length"); - connection = evhttp_find_header(headers, "Connection"); - - if (content_length == NULL && connection == NULL) - req->ntoread = -1; - else if (content_length == NULL && - strcasecmp(connection, "Close") != 0) { - /* Bad combination, we don't know when it will end */ - event_warnx("%s: we got no content length, but the " - "server wants to keep the connection open: %s.", - __func__, connection); - return (-1); - } else if (content_length == NULL) { - req->ntoread = -1; - } else { - char *endp; - req->ntoread = evutil_strtoll(content_length, &endp, 10); - if (*content_length == '\0' || *endp != '\0') { - event_warnx("%s: illegal content length: %s", - __func__, content_length); - return (-1); - } - } - - event_debug(("%s: bytes to read: %d (in buffer %d)\n", - __func__, req->ntoread, - EVBUFFER_LENGTH(req->evcon->input_buffer))); - - return (0); -} - -static void -evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req) -{ - const char *xfer_enc; - - /* If this is a request without a body, then we are done */ - if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) { - evhttp_connection_done(evcon); - return; - } - xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding"); - if (xfer_enc != NULL && strcasecmp(xfer_enc, "chunked") == 0) { - req->chunked = 1; - req->ntoread = -1; - } else { - if (evhttp_get_body_length(req) == -1) { - evhttp_connection_fail(evcon, - EVCON_HTTP_INVALID_HEADER); - return; - } - } - evhttp_read_body(evcon, req); -} - -void -evhttp_read_header(int fd, short what, void *arg) -{ - struct evhttp_connection *evcon = arg; - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - int n, res; - - if (what == EV_TIMEOUT) { - event_debug(("%s: timeout on %d\n", __func__, fd)); - evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); - return; - } - - n = evbuffer_read(evcon->input_buffer, fd, -1); - if (n == 0) { - event_debug(("%s: no more data on %d", __func__, fd)); - evhttp_connection_fail(evcon, EVCON_HTTP_EOF); - return; - } - if (n == -1) { - event_debug(("%s: bad read on %d", __func__, fd)); - evhttp_connection_fail(evcon, EVCON_HTTP_EOF); - return; - } - - res = evhttp_parse_lines(req, evcon->input_buffer); - if (res == -1) { - /* Error while reading, terminate */ - event_debug(("%s: bad header lines on %d\n", __func__, fd)); - evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); - return; - } else if (res == 0) { - /* Need more header lines */ - evhttp_add_event(&evcon->ev, - evcon->timeout, HTTP_READ_TIMEOUT); - return; - } - - /* Done reading headers, do the real work */ - switch (req->kind) { - case EVHTTP_REQUEST: - event_debug(("%s: checking for post data on %d\n", - __func__, fd)); - evhttp_get_body(evcon, req); - break; - - case EVHTTP_RESPONSE: - if (req->response_code == HTTP_NOCONTENT || - req->response_code == HTTP_NOTMODIFIED || - (req->response_code >= 100 && req->response_code < 200)) { - event_debug(("%s: skipping body for code %d\n", - __func__, req->response_code)); - evhttp_connection_done(evcon); - } else { - event_debug(("%s: start of read body for %s on %d\n", - __func__, req->remote_host, fd)); - evhttp_get_body(evcon, req); - } - break; - - default: - event_warnx("%s: bad header on %d", __func__, fd); - evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); - break; - } -} - -/* - * Creates a TCP connection to the specified port and executes a callback - * when finished. Failure or sucess is indicate by the passed connection - * object. - * - * Although this interface accepts a hostname, it is intended to take - * only numeric hostnames so that non-blocking DNS resolution can - * happen elsewhere. - */ - -struct evhttp_connection * -evhttp_connection_new(const char *address, unsigned short port) -{ - struct evhttp_connection *evcon = NULL; - - event_debug(("Attempting connection to %s:%d\n", address, port)); - - if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) { - event_warn("%s: calloc failed", __func__); - goto error; - } - - evcon->fd = -1; - evcon->port = port; - - evcon->timeout = -1; - evcon->retry_cnt = evcon->retry_max = 0; - - if ((evcon->address = strdup(address)) == NULL) { - event_warn("%s: strdup failed", __func__); - goto error; - } - - if ((evcon->input_buffer = evbuffer_new()) == NULL) { - event_warn("%s: evbuffer_new failed", __func__); - goto error; - } - - if ((evcon->output_buffer = evbuffer_new()) == NULL) { - event_warn("%s: evbuffer_new failed", __func__); - goto error; - } - - evcon->state = EVCON_DISCONNECTED; - TAILQ_INIT(&evcon->requests); - - return (evcon); - - error: - if (evcon != NULL) - evhttp_connection_free(evcon); - return (NULL); -} - -void evhttp_connection_set_base(struct evhttp_connection *evcon, - struct event_base *base) -{ - assert(evcon->base == NULL); - assert(evcon->state == EVCON_DISCONNECTED); - evcon->base = base; -} - -void -evhttp_connection_set_timeout(struct evhttp_connection *evcon, - int timeout_in_secs) -{ - evcon->timeout = timeout_in_secs; -} - -void -evhttp_connection_set_retries(struct evhttp_connection *evcon, - int retry_max) -{ - evcon->retry_max = retry_max; -} - -void -evhttp_connection_set_closecb(struct evhttp_connection *evcon, - void (*cb)(struct evhttp_connection *, void *), void *cbarg) -{ - evcon->closecb = cb; - evcon->closecb_arg = cbarg; -} - -void -evhttp_connection_get_peer(struct evhttp_connection *evcon, - char **address, u_short *port) -{ - *address = evcon->address; - *port = evcon->port; -} - -int -evhttp_connection_connect(struct evhttp_connection *evcon) -{ - if (evcon->state == EVCON_CONNECTING) - return (0); - - evhttp_connection_reset(evcon); - - assert(!(evcon->flags & EVHTTP_CON_INCOMING)); - evcon->flags |= EVHTTP_CON_OUTGOING; - - evcon->fd = bind_socket(evcon->bind_address, 0); - if (evcon->fd == -1) { - event_debug(("%s: failed to bind to \"%s\"", - __func__, evcon->bind_address)); - return (-1); - } - - if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) { - EVUTIL_CLOSESOCKET(evcon->fd); evcon->fd = -1; - return (-1); - } - - /* Set up a callback for successful connection setup */ - event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon); - EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_CONNECT_TIMEOUT); - - evcon->state = EVCON_CONNECTING; - - return (0); -} - -/* - * Starts an HTTP request on the provided evhttp_connection object. - * If the connection object is not connected to the web server already, - * this will start the connection. - */ - -int -evhttp_make_request(struct evhttp_connection *evcon, - struct evhttp_request *req, - enum evhttp_cmd_type type, const char *uri) -{ - /* We are making a request */ - req->kind = EVHTTP_REQUEST; - req->type = type; - if (req->uri != NULL) - free(req->uri); - if ((req->uri = strdup(uri)) == NULL) - event_err(1, "%s: strdup", __func__); - - /* Set the protocol version if it is not supplied */ - if (!req->major && !req->minor) { - req->major = 1; - req->minor = 1; - } - - assert(req->evcon == NULL); - req->evcon = evcon; - assert(!(req->flags & EVHTTP_REQ_OWN_CONNECTION)); - - TAILQ_INSERT_TAIL(&evcon->requests, req, next); - - /* If the connection object is not connected; make it so */ - if (evcon->state != EVCON_CONNECTED) - return (evhttp_connection_connect(evcon)); - - /* - * If it's connected already and we are the first in the queue, - * then we can dispatch this request immediately. Otherwise, it - * will be dispatched once the pending requests are completed. - */ - if (TAILQ_FIRST(&evcon->requests) == req) - evhttp_request_dispatch(evcon); - - return (0); -} - -/* - * Reads data from file descriptor into request structure - * Request structure needs to be set up correctly. - */ - -void -evhttp_start_read(struct evhttp_connection *evcon) -{ - /* Set up an event to read the headers */ - if (event_initialized(&evcon->ev)) - event_del(&evcon->ev); - event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon); - EVHTTP_BASE_SET(evcon, &evcon->ev); - - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT); -} - -static void -evhttp_send_done(struct evhttp_connection *evcon, void *arg) -{ - int need_close; - struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); - TAILQ_REMOVE(&evcon->requests, req, next); - - /* delete possible close detection events */ - evhttp_connection_stop_detectclose(evcon); - - need_close = - (req->minor == 0 && - !evhttp_is_connection_keepalive(req->input_headers))|| - evhttp_is_connection_close(req->flags, req->input_headers) || - evhttp_is_connection_close(req->flags, req->output_headers); - - assert(req->flags & EVHTTP_REQ_OWN_CONNECTION); - evhttp_request_free(req); - - if (need_close) { - evhttp_connection_free(evcon); - return; - } - - /* we have a persistent connection; try to accept another request. */ - if (evhttp_associate_new_request_with_connection(evcon) == -1) - evhttp_connection_free(evcon); -} - -/* - * Returns an error page. - */ - -void -evhttp_send_error(struct evhttp_request *req, int error, const char *reason) -{ -#define ERR_FORMAT "\n" \ - "%d %s\n" \ - "\n" \ - "

Method Not Implemented

\n" \ - "Invalid method in request

\n" \ - "\n" - - struct evbuffer *buf = evbuffer_new(); - - /* close the connection on error */ - evhttp_add_header(req->output_headers, "Connection", "close"); - - evhttp_response_code(req, error, reason); - - evbuffer_add_printf(buf, ERR_FORMAT, error, reason); - - evhttp_send_page(req, buf); - - evbuffer_free(buf); -#undef ERR_FORMAT -} - -/* Requires that headers and response code are already set up */ - -static inline void -evhttp_send(struct evhttp_request *req, struct evbuffer *databuf) -{ - struct evhttp_connection *evcon = req->evcon; - - assert(TAILQ_FIRST(&evcon->requests) == req); - - /* xxx: not sure if we really should expose the data buffer this way */ - if (databuf != NULL) - evbuffer_add_buffer(req->output_buffer, databuf); - - /* Adds headers to the response */ - evhttp_make_header(evcon, req); - - evhttp_write_buffer(evcon, evhttp_send_done, NULL); -} - -void -evhttp_send_reply(struct evhttp_request *req, int code, const char *reason, - struct evbuffer *databuf) -{ - /* set up to watch for client close */ - evhttp_connection_start_detectclose(req->evcon); - evhttp_response_code(req, code, reason); - - evhttp_send(req, databuf); -} - -void -evhttp_send_reply_start(struct evhttp_request *req, int code, - const char *reason) -{ - /* set up to watch for client close */ - evhttp_connection_start_detectclose(req->evcon); - evhttp_response_code(req, code, reason); - if (req->major == 1 && req->minor == 1) { - /* use chunked encoding for HTTP/1.1 */ - evhttp_add_header(req->output_headers, "Transfer-Encoding", - "chunked"); - req->chunked = 1; - } - evhttp_make_header(req->evcon, req); - evhttp_write_buffer(req->evcon, NULL, NULL); -} - -void -evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf) -{ - if (req->chunked) { - evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", - (unsigned)EVBUFFER_LENGTH(databuf)); - } - evbuffer_add_buffer(req->evcon->output_buffer, databuf); - if (req->chunked) { - evbuffer_add(req->evcon->output_buffer, "\r\n", 2); - } - evhttp_write_buffer(req->evcon, NULL, NULL); -} - -void -evhttp_send_reply_end(struct evhttp_request *req) -{ - struct evhttp_connection *evcon = req->evcon; - - if (req->chunked) { - evbuffer_add(req->evcon->output_buffer, "0\r\n\r\n", 5); - evhttp_write_buffer(req->evcon, evhttp_send_done, NULL); - req->chunked = 0; - } else if (!event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) { - /* let the connection know that we are done with the request */ - evhttp_send_done(evcon, NULL); - } else { - /* make the callback execute after all data has been written */ - evcon->cb = evhttp_send_done; - evcon->cb_arg = NULL; - } -} - -void -evhttp_response_code(struct evhttp_request *req, int code, const char *reason) -{ - req->kind = EVHTTP_RESPONSE; - req->response_code = code; - if (req->response_code_line != NULL) - free(req->response_code_line); - req->response_code_line = strdup(reason); -} - -void -evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf) -{ - if (!req->major || !req->minor) { - req->major = 1; - req->minor = 1; - } - - if (req->kind != EVHTTP_RESPONSE) - evhttp_response_code(req, 200, "OK"); - - evhttp_clear_headers(req->output_headers); - evhttp_add_header(req->output_headers, "Content-Type", "text/html"); - evhttp_add_header(req->output_headers, "Connection", "close"); - - evhttp_send(req, databuf); -} - -static const char uri_chars[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, - /* 64 */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, - /* 128 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 192 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* - * Helper functions to encode/decode a URI. - * The returned string must be freed by the caller. - */ -char * -evhttp_encode_uri(const char *uri) -{ - struct evbuffer *buf = evbuffer_new(); - char *p; - - for (p = (char *)uri; *p != '\0'; p++) { - if (uri_chars[(u_char)(*p)]) { - evbuffer_add(buf, p, 1); - } else { - evbuffer_add_printf(buf, "%%%02X", (u_char)(*p)); - } - } - evbuffer_add(buf, "", 1); - p = strdup((char *)EVBUFFER_DATA(buf)); - evbuffer_free(buf); - - return (p); -} - -char * -evhttp_decode_uri(const char *uri) -{ - char c, *ret; - int i, j, in_query = 0; - - ret = malloc(strlen(uri) + 1); - if (ret == NULL) - event_err(1, "%s: malloc(%lu)", __func__, - (unsigned long)(strlen(uri) + 1)); - - for (i = j = 0; uri[i] != '\0'; i++) { - c = uri[i]; - if (c == '?') { - in_query = 1; - } else if (c == '+' && in_query) { - c = ' '; - } else if (c == '%' && isxdigit((unsigned char)uri[i+1]) && - isxdigit((unsigned char)uri[i+2])) { - char tmp[] = { uri[i+1], uri[i+2], '\0' }; - c = (char)strtol(tmp, NULL, 16); - i += 2; - } - ret[j++] = c; - } - ret[j] = '\0'; - - return (ret); -} - -/* - * Helper function to parse out arguments in a query. - * The arguments are separated by key and value. - * URI should already be decoded. - */ - -void -evhttp_parse_query(const char *uri, struct evkeyvalq *headers) -{ - char *line; - char *argument; - char *p; - - TAILQ_INIT(headers); - - /* No arguments - we are done */ - if (strchr(uri, '?') == NULL) - return; - - if ((line = strdup(uri)) == NULL) - event_err(1, "%s: strdup", __func__); - - - argument = line; - - /* We already know that there has to be a ? */ - strsep(&argument, "?"); - - p = argument; - while (p != NULL && *p != '\0') { - char *key, *value; - argument = strsep(&p, "&"); - - value = argument; - key = strsep(&value, "="); - if (value == NULL) - goto error; - - value = evhttp_decode_uri(value); - event_debug(("Query Param: %s -> %s\n", key, value)); - evhttp_add_header(headers, key, value); - free(value); - } - - error: - free(line); -} - -static struct evhttp_cb * -evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req) -{ - struct evhttp_cb *cb; - size_t offset = 0; - - /* Test for different URLs */ - char *p = strchr(req->uri, '?'); - if (p != NULL) - offset = (size_t)(p - req->uri); - - TAILQ_FOREACH(cb, callbacks, next) { - int res = 0; - if (p == NULL) { - res = strcmp(cb->what, req->uri) == 0; - } else { - res = ((strncmp(cb->what, req->uri, offset) == 0) && - (cb->what[offset] == '\0')); - } - - if (res) - return (cb); - } - - return (NULL); -} - -static void -evhttp_handle_request(struct evhttp_request *req, void *arg) -{ - struct evhttp *http = arg; - struct evhttp_cb *cb = NULL; - - if (req->uri == NULL) { - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); - return; - } - - if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) { - (*cb->cb)(req, cb->cbarg); - return; - } - - /* Generic call back */ - if (http->gencb) { - (*http->gencb)(req, http->gencbarg); - return; - } else { - /* We need to send a 404 here */ -#define ERR_FORMAT "" \ - "404 Not Found" \ - "" \ - "

Not Found

" \ - "

The requested URL %s was not found on this server.

"\ - "\n" - - char *escaped_html = evhttp_htmlescape(req->uri); - struct evbuffer *buf = evbuffer_new(); - - evhttp_response_code(req, HTTP_NOTFOUND, "Not Found"); - - evbuffer_add_printf(buf, ERR_FORMAT, escaped_html); - - free(escaped_html); - - evhttp_send_page(req, buf); - - evbuffer_free(buf); -#undef ERR_FORMAT - } -} - -static void -accept_socket(int fd, short what, void *arg) -{ - struct evhttp *http = arg; - struct sockaddr_storage ss; - socklen_t addrlen = sizeof(ss); - int nfd; - - if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) { - event_warn("%s: bad accept", __func__); - return; - } - if (evutil_make_socket_nonblocking(nfd) < 0) - return; - - evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen); -} - -int -evhttp_bind_socket(struct evhttp *http, const char *address, u_short port) -{ - struct event *ev = &http->bind_ev; - int fd; - - if ((fd = bind_socket(address, port)) == -1) - return (-1); - - if (listen(fd, 10) == -1) { - event_warn("%s: listen", __func__); - EVUTIL_CLOSESOCKET(fd); - return (-1); - } - - /* Schedule the socket for accepting */ - event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http); - EVHTTP_BASE_SET(http, ev); - event_add(ev, NULL); - - event_debug(("Bound to port %d - Awaiting connections ... ", port)); - - return (0); -} - -static struct evhttp* -evhttp_new_object(void) -{ - struct evhttp *http = NULL; - - if ((http = calloc(1, sizeof(struct evhttp))) == NULL) { - event_warn("%s: calloc", __func__); - return (NULL); - } - - http->timeout = -1; - - TAILQ_INIT(&http->callbacks); - TAILQ_INIT(&http->connections); - - return (http); -} - -struct evhttp * -evhttp_new(struct event_base *base) -{ - struct evhttp *http = evhttp_new_object(); - - http->base = base; - - return (http); -} - -/* - * Start a web server on the specified address and port. - */ - -struct evhttp * -evhttp_start(const char *address, u_short port) -{ - struct evhttp *http = evhttp_new_object(); - - if (evhttp_bind_socket(http, address, port) == -1) { - free(http); - return (NULL); - } - - return (http); -} - -void -evhttp_free(struct evhttp* http) -{ - struct evhttp_cb *http_cb; - struct evhttp_connection *evcon; - int fd = http->bind_ev.ev_fd; - - /* Remove the accepting part */ - event_del(&http->bind_ev); - EVUTIL_CLOSESOCKET(fd); - - while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) { - /* evhttp_connection_free removes the connection */ - evhttp_connection_free(evcon); - } - - while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) { - TAILQ_REMOVE(&http->callbacks, http_cb, next); - free(http_cb->what); - free(http_cb); - } - - free(http); -} - -void -evhttp_set_timeout(struct evhttp* http, int timeout_in_secs) -{ - http->timeout = timeout_in_secs; -} - -void -evhttp_set_cb(struct evhttp *http, const char *uri, - void (*cb)(struct evhttp_request *, void *), void *cbarg) -{ - struct evhttp_cb *http_cb; - - if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL) - event_err(1, "%s: calloc", __func__); - - http_cb->what = strdup(uri); - http_cb->cb = cb; - http_cb->cbarg = cbarg; - - TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next); -} - -int -evhttp_del_cb(struct evhttp *http, const char *uri) -{ - struct evhttp_cb *http_cb; - - TAILQ_FOREACH(http_cb, &http->callbacks, next) { - if (strcmp(http_cb->what, uri) == 0) - break; - } - if (http_cb == NULL) - return (-1); - - TAILQ_REMOVE(&http->callbacks, http_cb, next); - free(http_cb->what); - free(http_cb); - - return (0); -} - -void -evhttp_set_gencb(struct evhttp *http, - void (*cb)(struct evhttp_request *, void *), void *cbarg) -{ - http->gencb = cb; - http->gencbarg = cbarg; -} - -/* - * Request related functions - */ - -struct evhttp_request * -evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg) -{ - struct evhttp_request *req = NULL; - - /* Allocate request structure */ - if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) { - event_warn("%s: calloc", __func__); - goto error; - } - - req->kind = EVHTTP_RESPONSE; - req->input_headers = calloc(1, sizeof(struct evkeyvalq)); - if (req->input_headers == NULL) { - event_warn("%s: calloc", __func__); - goto error; - } - TAILQ_INIT(req->input_headers); - - req->output_headers = calloc(1, sizeof(struct evkeyvalq)); - if (req->output_headers == NULL) { - event_warn("%s: calloc", __func__); - goto error; - } - TAILQ_INIT(req->output_headers); - - if ((req->input_buffer = evbuffer_new()) == NULL) { - event_warn("%s: evbuffer_new", __func__); - goto error; - } - - if ((req->output_buffer = evbuffer_new()) == NULL) { - event_warn("%s: evbuffer_new", __func__); - goto error; - } - - req->cb = cb; - req->cb_arg = arg; - - return (req); - - error: - if (req != NULL) - evhttp_request_free(req); - return (NULL); -} - -void -evhttp_request_free(struct evhttp_request *req) -{ - if (req->remote_host != NULL) - free(req->remote_host); - if (req->uri != NULL) - free(req->uri); - if (req->response_code_line != NULL) - free(req->response_code_line); - - evhttp_clear_headers(req->input_headers); - free(req->input_headers); - - evhttp_clear_headers(req->output_headers); - free(req->output_headers); - - if (req->input_buffer != NULL) - evbuffer_free(req->input_buffer); - - if (req->output_buffer != NULL) - evbuffer_free(req->output_buffer); - - free(req); -} - -void -evhttp_request_set_chunked_cb(struct evhttp_request *req, - void (*cb)(struct evhttp_request *, void *)) -{ - req->chunk_cb = cb; -} - -/* - * Allows for inspection of the request URI - */ - -const char * -evhttp_request_uri(struct evhttp_request *req) { - if (req->uri == NULL) - event_debug(("%s: request %p has no uri\n", req)); - return (req->uri); -} - -/* - * Takes a file descriptor to read a request from. - * The callback is executed once the whole request has been read. - */ - -static struct evhttp_connection* -evhttp_get_request_connection( - struct evhttp* http, - int fd, struct sockaddr *sa, socklen_t salen) -{ - struct evhttp_connection *evcon; - char *hostname, *portname; - - name_from_addr(sa, salen, &hostname, &portname); - event_debug(("%s: new request from %s:%s on %d\n", - __func__, hostname, portname, fd)); - - /* we need a connection object to put the http request on */ - if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL) - return (NULL); - - /* associate the base if we have one*/ - evhttp_connection_set_base(evcon, http->base); - - evcon->flags |= EVHTTP_CON_INCOMING; - evcon->state = EVCON_CONNECTED; - - evcon->fd = fd; - - return (evcon); -} - -static int -evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon) -{ - struct evhttp *http = evcon->http_server; - struct evhttp_request *req; - if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL) - return (-1); - - req->evcon = evcon; /* the request ends up owning the connection */ - req->flags |= EVHTTP_REQ_OWN_CONNECTION; - - TAILQ_INSERT_TAIL(&evcon->requests, req, next); - - req->kind = EVHTTP_REQUEST; - - if ((req->remote_host = strdup(evcon->address)) == NULL) - event_err(1, "%s: strdup", __func__); - req->remote_port = evcon->port; - - evhttp_start_read(evcon); - - return (0); -} - -void -evhttp_get_request(struct evhttp *http, int fd, - struct sockaddr *sa, socklen_t salen) -{ - struct evhttp_connection *evcon; - - evcon = evhttp_get_request_connection(http, fd, sa, salen); - if (evcon == NULL) - return; - - /* the timeout can be used by the server to close idle connections */ - if (http->timeout != -1) - evhttp_connection_set_timeout(evcon, http->timeout); - - /* - * if we want to accept more than one request on a connection, - * we need to know which http server it belongs to. - */ - evcon->http_server = http; - TAILQ_INSERT_TAIL(&http->connections, evcon, next); - - if (evhttp_associate_new_request_with_connection(evcon) == -1) - evhttp_connection_free(evcon); -} - - -/* - * Network helper functions that we do not want to export to the rest of - * the world. - */ -#if 0 /* Unused */ -static struct addrinfo * -addr_from_name(char *address) -{ -#ifdef HAVE_GETADDRINFO - struct addrinfo ai, *aitop; - int ai_result; - - memset(&ai, 0, sizeof(ai)); - ai.ai_family = AF_INET; - ai.ai_socktype = SOCK_RAW; - ai.ai_flags = 0; - if ((ai_result = getaddrinfo(address, NULL, &ai, &aitop)) != 0) { - if ( ai_result == EAI_SYSTEM ) - event_warn("getaddrinfo"); - else - event_warnx("getaddrinfo: %s", gai_strerror(ai_result)); - } - - return (aitop); -#else - assert(0); - return NULL; /* XXXXX Use gethostbyname, if this function is ever used. */ -#endif -} -#endif - -static void -name_from_addr(struct sockaddr *sa, socklen_t salen, - char **phost, char **pport) -{ -#ifdef HAVE_GETNAMEINFO - /* XXXX not threadsafe. */ - static char ntop[NI_MAXHOST]; - static char strport[NI_MAXSERV]; - int ni_result; - - if ((ni_result = getnameinfo(sa, salen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { - if (ni_result == EAI_SYSTEM) - event_err(1, "getnameinfo failed"); - else - event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); - } - - *phost = ntop; - *pport = strport; -#else - /* XXXX */ -#endif -} - -/* Either connect or bind */ - -static int -bind_socket_ai(struct addrinfo *ai) -{ - int fd, on = 1, r; - int serrno; - - /* Create listen socket */ - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd == -1) { - event_warn("socket"); - return (-1); - } - - if (evutil_make_socket_nonblocking(fd) < 0) - goto out; - -#ifndef WIN32 - if (fcntl(fd, F_SETFD, 1) == -1) { - event_warn("fcntl(F_SETFD)"); - goto out; - } -#endif - - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); - - r = bind(fd, ai->ai_addr, ai->ai_addrlen); - if (r == -1) - goto out; - - return (fd); - - out: - serrno = EVUTIL_SOCKET_ERROR(); - EVUTIL_CLOSESOCKET(fd); - EVUTIL_SET_SOCKET_ERROR(serrno); - return (-1); -} - -static struct addrinfo * -make_addrinfo(const char *address, u_short port) -{ - struct addrinfo *aitop = NULL; - -#ifdef HAVE_GETADDRINFO - struct addrinfo ai; - char strport[NI_MAXSERV]; - int ai_result; - - memset(&ai, 0, sizeof(ai)); - ai.ai_family = AF_INET; - ai.ai_socktype = SOCK_STREAM; - ai.ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */ - snprintf(strport, sizeof(strport), "%d", port); - if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) { - if ( ai_result == EAI_SYSTEM ) - event_warn("getaddrinfo"); - else - event_warnx("getaddrinfo: %s", gai_strerror(ai_result)); - return (NULL); - } -#else - static int cur; - static struct addrinfo ai[2]; /* We will be returning the address of some of this memory so it has to last even after this call. */ - if (++cur == 2) cur = 0; /* allow calling this function twice */ - - if (fake_getaddrinfo(address, &ai[cur]) < 0) { - event_warn("fake_getaddrinfo"); - return (NULL); - } - aitop = &ai[cur]; - ((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port); -#endif - - return (aitop); -} - -static int -bind_socket(const char *address, u_short port) -{ - int fd; - struct addrinfo *aitop = make_addrinfo(address, port); - - if (aitop == NULL) - return (-1); - - fd = bind_socket_ai(aitop); - -#ifdef HAVE_GETADDRINFO - freeaddrinfo(aitop); -#else - fake_freeaddrinfo(aitop); -#endif - - return (fd); -} - -static int -socket_connect(int fd, const char *address, unsigned short port) -{ - struct addrinfo *ai = make_addrinfo(address, port); - int res = -1; - - if (ai == NULL) { - event_debug(("%s: make_addrinfo: \"%s:%d\"", - __func__, address, port)); - return (-1); - } - - if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) { -#ifdef WIN32 - int tmp_error = WSAGetLastError(); - if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL && - tmp_error != WSAEINPROGRESS) { - goto out; - } -#else - if (errno != EINPROGRESS) { - goto out; - } -#endif - } - - /* everything is fine */ - res = 0; - -out: -#ifdef HAVE_GETADDRINFO - freeaddrinfo(ai); -#else - fake_freeaddrinfo(ai); -#endif - - return (res); -} diff --git a/extra/libevent/kqueue.c b/extra/libevent/kqueue.c deleted file mode 100644 index 763236e40ac..00000000000 --- a/extra/libevent/kqueue.c +++ /dev/null @@ -1,426 +0,0 @@ -/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */ - -/* - * Copyright 2000-2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_WORKING_KQUEUE - -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_INTTYPES_H -#include -#endif - -/* Some platforms apparently define the udata field of struct kevent as - * intptr_t, whereas others define it as void*. There doesn't seem to be an - * easy way to tell them apart via autoconf, so we need to use OS macros. */ -#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) -#define PTR_TO_UDATA(x) ((intptr_t)(x)) -#else -#define PTR_TO_UDATA(x) (x) -#endif - -#include "event.h" -#include "event-internal.h" -#include "log.h" -#include "event-internal.h" - -#define EVLIST_X_KQINKERNEL 0x1000 - -#define NEVENT 64 - -struct kqop { - struct kevent *changes; - int nchanges; - struct kevent *events; - int nevents; - int kq; - pid_t pid; -}; - -static void *kq_init (struct event_base *); -static int kq_add (void *, struct event *); -static int kq_del (void *, struct event *); -static int kq_dispatch (struct event_base *, void *, struct timeval *); -static int kq_insert (struct kqop *, struct kevent *); -static void kq_dealloc (struct event_base *, void *); - -const struct eventop kqops = { - "kqueue", - kq_init, - kq_add, - kq_del, - kq_dispatch, - kq_dealloc, - 1 /* need reinit */ -}; - -static void * -kq_init(struct event_base *base __attribute__((unused))) -{ - int kq; - struct kqop *kqueueop; - - /* Disable kqueue when this environment variable is set */ - if (getenv("EVENT_NOKQUEUE")) - return (NULL); - - if (!(kqueueop = calloc(1, sizeof(struct kqop)))) - return (NULL); - - /* Initalize the kernel queue */ - - if ((kq = kqueue()) == -1) { - event_warn("kqueue"); - free (kqueueop); - return (NULL); - } - - kqueueop->kq = kq; - - kqueueop->pid = getpid(); - - /* Initalize fields */ - kqueueop->changes = malloc(NEVENT * sizeof(struct kevent)); - if (kqueueop->changes == NULL) { - free (kqueueop); - return (NULL); - } - kqueueop->events = malloc(NEVENT * sizeof(struct kevent)); - if (kqueueop->events == NULL) { - free (kqueueop->changes); - free (kqueueop); - return (NULL); - } - kqueueop->nevents = NEVENT; - - /* Check for Mac OS X kqueue bug. */ - kqueueop->changes[0].ident = -1; - kqueueop->changes[0].filter = EVFILT_READ; - kqueueop->changes[0].flags = EV_ADD; - /* - * If kqueue works, then kevent will succeed, and it will - * stick an error in events[0]. If kqueue is broken, then - * kevent will fail. - */ - if (kevent(kq, - kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 || - ((int) kqueueop->events[0].ident) != -1 || - kqueueop->events[0].flags != EV_ERROR) { - event_warn("%s: detected broken kqueue; not using.", __func__); - free(kqueueop->changes); - free(kqueueop->events); - free(kqueueop); - close(kq); - return (NULL); - } - - return (kqueueop); -} - -static int -kq_insert(struct kqop *kqop, struct kevent *kev) -{ - int nevents = kqop->nevents; - - if (kqop->nchanges == nevents) { - struct kevent *newchange; - struct kevent *newresult; - - nevents *= 2; - - newchange = realloc(kqop->changes, - nevents * sizeof(struct kevent)); - if (newchange == NULL) { - event_warn("%s: malloc", __func__); - return (-1); - } - kqop->changes = newchange; - - newresult = realloc(kqop->events, - nevents * sizeof(struct kevent)); - - /* - * If we fail, we don't have to worry about freeing, - * the next realloc will pick it up. - */ - if (newresult == NULL) { - event_warn("%s: malloc", __func__); - return (-1); - } - kqop->events = newresult; - - kqop->nevents = nevents; - } - - memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent)); - - event_debug(("%s: fd %d %s%s", - __func__, kev->ident, - kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", - kev->flags == EV_DELETE ? " (del)" : "")); - - return (0); -} - -static void -kq_sighandler(int sig __attribute__((unused))) -{ - /* Do nothing here */ -} - -static int -kq_dispatch(struct event_base *base __attribute__((unused)), void *arg, - struct timeval *tv) -{ - struct kqop *kqop = arg; - struct kevent *changes = kqop->changes; - struct kevent *events = kqop->events; - struct event *ev; - struct timespec ts, *ts_p = NULL; - int i, res; - - if (tv != NULL) { - TIMEVAL_TO_TIMESPEC(tv, &ts); - ts_p = &ts; - } - - res = kevent(kqop->kq, changes, kqop->nchanges, - events, kqop->nevents, ts_p); - kqop->nchanges = 0; - if (res == -1) { - if (errno != EINTR) { - event_warn("kevent"); - return (-1); - } - - return (0); - } - - event_debug(("%s: kevent reports %d", __func__, res)); - - for (i = 0; i < res; i++) { - int which = 0; - - if (events[i].flags & EV_ERROR) { - /* - * Error messages that can happen, when a delete fails. - * EBADF happens when the file discriptor has been - * closed, - * ENOENT when the file discriptor was closed and - * then reopened. - * EINVAL for some reasons not understood; EINVAL - * should not be returned ever; but FreeBSD does :-\ - * An error is also indicated when a callback deletes - * an event we are still processing. In that case - * the data field is set to ENOENT. - */ - if (events[i].data == EBADF || - events[i].data == EINVAL || - events[i].data == ENOENT) - continue; - errno = events[i].data; - return (-1); - } - - ev = (struct event *)events[i].udata; - - if (events[i].filter == EVFILT_READ) { - which |= EV_READ; - } else if (events[i].filter == EVFILT_WRITE) { - which |= EV_WRITE; - } else if (events[i].filter == EVFILT_SIGNAL) { - which |= EV_SIGNAL; - } - - if (!which) - continue; - - if (!(ev->ev_events & EV_PERSIST)) - ev->ev_flags &= ~EVLIST_X_KQINKERNEL; - - event_active(ev, which, - ev->ev_events & EV_SIGNAL ? events[i].data : 1); - } - - return (0); -} - - -static int -kq_add(void *arg, struct event *ev) -{ - struct kqop *kqop = arg; - struct kevent kev; - - if (ev->ev_events & EV_SIGNAL) { - int nsignal = EVENT_SIGNAL(ev); - struct timespec timeout = { 0, 0 }; - - memset(&kev, 0, sizeof(kev)); - kev.ident = nsignal; - kev.filter = EVFILT_SIGNAL; - kev.flags = EV_ADD; - if (!(ev->ev_events & EV_PERSIST)) - kev.flags |= EV_ONESHOT; - kev.udata = PTR_TO_UDATA(ev); - - /* Be ready for the signal if it is sent any time between - * now and the next call to kq_dispatch. */ - if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) - return (-1); - - if (_evsignal_set_handler(ev->ev_base, nsignal, - kq_sighandler) == -1) - return (-1); - - ev->ev_flags |= EVLIST_X_KQINKERNEL; - return (0); - } - - if (ev->ev_events & EV_READ) { - memset(&kev, 0, sizeof(kev)); - kev.ident = ev->ev_fd; - kev.filter = EVFILT_READ; -#ifdef NOTE_EOF - /* Make it behave like select() and poll() */ - kev.fflags = NOTE_EOF; -#endif - kev.flags = EV_ADD; - if (!(ev->ev_events & EV_PERSIST)) - kev.flags |= EV_ONESHOT; - kev.udata = PTR_TO_UDATA(ev); - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - ev->ev_flags |= EVLIST_X_KQINKERNEL; - } - - if (ev->ev_events & EV_WRITE) { - memset(&kev, 0, sizeof(kev)); - kev.ident = ev->ev_fd; - kev.filter = EVFILT_WRITE; - kev.flags = EV_ADD; - if (!(ev->ev_events & EV_PERSIST)) - kev.flags |= EV_ONESHOT; - kev.udata = PTR_TO_UDATA(ev); - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - ev->ev_flags |= EVLIST_X_KQINKERNEL; - } - - return (0); -} - -static int -kq_del(void *arg, struct event *ev) -{ - struct kqop *kqop = arg; - struct kevent kev; - - if (!(ev->ev_flags & EVLIST_X_KQINKERNEL)) - return (0); - - if (ev->ev_events & EV_SIGNAL) { - int nsignal = EVENT_SIGNAL(ev); - - memset(&kev, 0, sizeof(kev)); - kev.ident = nsignal; - kev.filter = EVFILT_SIGNAL; - kev.flags = EV_DELETE; - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - if (_evsignal_restore_handler(ev->ev_base, nsignal) == -1) - return (-1); - - ev->ev_flags &= ~EVLIST_X_KQINKERNEL; - return (0); - } - - if (ev->ev_events & EV_READ) { - memset(&kev, 0, sizeof(kev)); - kev.ident = ev->ev_fd; - kev.filter = EVFILT_READ; - kev.flags = EV_DELETE; - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - ev->ev_flags &= ~EVLIST_X_KQINKERNEL; - } - - if (ev->ev_events & EV_WRITE) { - memset(&kev, 0, sizeof(kev)); - kev.ident = ev->ev_fd; - kev.filter = EVFILT_WRITE; - kev.flags = EV_DELETE; - - if (kq_insert(kqop, &kev) == -1) - return (-1); - - ev->ev_flags &= ~EVLIST_X_KQINKERNEL; - } - - return (0); -} - -static void -kq_dealloc(struct event_base *base __attribute__((unused)), void *arg) -{ - struct kqop *kqop = arg; - - if (kqop->changes) - free(kqop->changes); - if (kqop->events) - free(kqop->events); - if (kqop->kq >= 0 && kqop->pid == getpid()) - close(kqop->kq); - memset(kqop, 0, sizeof(struct kqop)); - free(kqop); -} - -#endif /* HAVE_WORKING_KQUEUE */ diff --git a/extra/libevent/log.c b/extra/libevent/log.c deleted file mode 100644 index aab47182af5..00000000000 --- a/extra/libevent/log.c +++ /dev/null @@ -1,218 +0,0 @@ -/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ - -/* - * log.c - * - * Based on err.c, which was adapted from OpenBSD libc *err* *warn* code. - * - * Copyright (c) 2005 Nick Mathewson - * - * Copyright (c) 2000 Dug Song - * - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#include "misc.h" -#endif -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include "event.h" - -#include "log.h" - -static void _warn_helper(int severity, int log_errno, const char *fmt, - va_list ap); -static void event_log(int severity, const char *msg); - -static int -event_vsnprintf(char *str, size_t size, const char *format, va_list args) -{ - int r; - if (size == 0) - return -1; -#ifdef WIN32 - r = _vsnprintf(str, size, format, args); -#else - r = vsnprintf(str, size, format, args); -#endif - str[size-1] = '\0'; - if (r < 0 || ((size_t)r) >= size) { - /* different platforms behave differently on overflow; - * handle both kinds. */ - return -1; - } - return r; -} - -static int -event_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int r; - va_start(ap, format); - r = event_vsnprintf(str, size, format, ap); - va_end(ap); - return r; -} - -void -event_err(int eval, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_ERR, errno, fmt, ap); - va_end(ap); - exit(eval); -} - -void -event_warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_WARN, errno, fmt, ap); - va_end(ap); -} - -void -event_errx(int eval, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_ERR, -1, fmt, ap); - va_end(ap); - exit(eval); -} - -void -event_warnx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_WARN, -1, fmt, ap); - va_end(ap); -} - -void -event_msgx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_MSG, -1, fmt, ap); - va_end(ap); -} - -void -_event_debugx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - _warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap); - va_end(ap); -} - -static void -_warn_helper(int severity, int log_errno, const char *fmt, va_list ap) -{ - char buf[1024]; - size_t len; - - if (fmt != NULL) - event_vsnprintf(buf, sizeof(buf), fmt, ap); - else - buf[0] = '\0'; - - if (log_errno >= 0) { - len = strlen(buf); - if (len < sizeof(buf) - 3) { - event_snprintf(buf + len, sizeof(buf) - len, ": %s", - strerror(log_errno)); - } - } - - event_log(severity, buf); -} - -static event_log_cb log_fn = NULL; - -void -event_set_log_callback(event_log_cb cb) -{ - log_fn = cb; -} - -static void -event_log(int severity, const char *msg) -{ - if (log_fn) - log_fn(severity, msg); - else { - const char *severity_str; - switch (severity) { - case _EVENT_LOG_DEBUG: - severity_str = "debug"; - break; - case _EVENT_LOG_MSG: - severity_str = "msg"; - break; - case _EVENT_LOG_WARN: - severity_str = "warn"; - break; - case _EVENT_LOG_ERR: - severity_str = "err"; - break; - default: - severity_str = "???"; - break; - } - (void)fprintf(stderr, "[%s] %s\n", severity_str, msg); - } -} diff --git a/extra/libevent/log.h b/extra/libevent/log.h deleted file mode 100644 index 7bc6632b8dd..00000000000 --- a/extra/libevent/log.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2000-2004 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _LOG_H_ -#define _LOG_H_ - -#ifdef __GNUC__ -#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b))) -#else -#define EV_CHECK_FMT(a,b) -#endif - -void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3); -void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2); -void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3); -void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2); -void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2); -void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2); - -#ifdef USE_DEBUG -#define event_debug(x) _event_debugx x -#else -#define event_debug(x) do {;} while (0) -#endif - -#undef EV_CHECK_FMT - -#endif diff --git a/extra/libevent/min_heap.h b/extra/libevent/min_heap.h deleted file mode 100644 index 389d6dbed4e..00000000000 --- a/extra/libevent/min_heap.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2006 Maxim Yegorushkin - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _MIN_HEAP_H_ -#define _MIN_HEAP_H_ - -#include "event.h" - -typedef struct min_heap -{ - struct event** p; - unsigned n, a; -} min_heap_t; - -static inline void min_heap_ctor(min_heap_t* s); -static inline void min_heap_dtor(min_heap_t* s); -static inline void min_heap_elem_init(struct event* e); -static inline int min_heap_elem_greater(struct event *a, struct event *b); -static inline int min_heap_empty(min_heap_t* s); -static inline unsigned min_heap_size(min_heap_t* s); -static inline struct event* min_heap_top(min_heap_t* s); -static inline int min_heap_reserve(min_heap_t* s, unsigned n); -static inline int min_heap_push(min_heap_t* s, struct event* e); -static inline struct event* min_heap_pop(min_heap_t* s); -static inline int min_heap_erase(min_heap_t* s, struct event* e); -static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e); -static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e); - -int min_heap_elem_greater(struct event *a, struct event *b) -{ - return timercmp(&a->ev_timeout, &b->ev_timeout, >); -} - -void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; } -void min_heap_dtor(min_heap_t* s) { free(s->p); } -void min_heap_elem_init(struct event* e) { e->min_heap_idx = -1; } -int min_heap_empty(min_heap_t* s) { return 0u == s->n; } -unsigned min_heap_size(min_heap_t* s) { return s->n; } -struct event* min_heap_top(min_heap_t* s) { return s->n ? *s->p : 0; } - -int min_heap_push(min_heap_t* s, struct event* e) -{ - if(min_heap_reserve(s, s->n + 1)) - return -1; - min_heap_shift_up_(s, s->n++, e); - return 0; -} - -struct event* min_heap_pop(min_heap_t* s) -{ - if(s->n) - { - struct event* e = *s->p; - e->min_heap_idx = -1; - min_heap_shift_down_(s, 0u, s->p[--s->n]); - return e; - } - return 0; -} - -int min_heap_erase(min_heap_t* s, struct event* e) -{ - if(((unsigned int)-1) != e->min_heap_idx) - { - min_heap_shift_down_(s, e->min_heap_idx, s->p[--s->n]); - e->min_heap_idx = -1; - return 0; - } - return -1; -} - -int min_heap_reserve(min_heap_t* s, unsigned n) -{ - if(s->a < n) - { - struct event** p; - unsigned a = s->a ? s->a * 2 : 8; - if(a < n) - a = n; - if(!(p = (struct event**)realloc(s->p, a * sizeof *p))) - return -1; - s->p = p; - s->a = a; - } - return 0; -} - -void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e) -{ - unsigned parent = (hole_index - 1) / 2; - while(hole_index && min_heap_elem_greater(s->p[parent], e)) - { - (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index; - hole_index = parent; - parent = (hole_index - 1) / 2; - } - (s->p[hole_index] = e)->min_heap_idx = hole_index; -} - -void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e) -{ - unsigned min_child = 2 * (hole_index + 1); - while(min_child <= s->n) - { - min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]); - if(!(min_heap_elem_greater(e, s->p[min_child]))) - break; - (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index; - hole_index = min_child; - min_child = 2 * (hole_index + 1); - } - min_heap_shift_up_(s, hole_index, e); -} - -#endif /* _MIN_HEAP_H_ */ diff --git a/extra/libevent/poll.c b/extra/libevent/poll.c deleted file mode 100644 index 6c99e2ca79b..00000000000 --- a/extra/libevent/poll.c +++ /dev/null @@ -1,378 +0,0 @@ -/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ - -/* - * Copyright 2000-2003 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_POLL - -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CHECK_INVARIANTS -#include -#endif - -#include "event.h" -#include "event-internal.h" -#include "evsignal.h" -#include "log.h" - -struct pollop { - int event_count; /* Highest number alloc */ - int nfds; /* Size of event_* */ - int fd_count; /* Size of idxplus1_by_fd */ - struct pollfd *event_set; - struct event **event_r_back; - struct event **event_w_back; - int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so - * that 0 (which is easy to memset) can mean - * "no entry." */ -}; - -static void *poll_init (struct event_base *); -static int poll_add (void *, struct event *); -static int poll_del (void *, struct event *); -static int poll_dispatch (struct event_base *, void *, struct timeval *); -static void poll_dealloc (struct event_base *, void *); - -const struct eventop pollops = { - "poll", - poll_init, - poll_add, - poll_del, - poll_dispatch, - poll_dealloc, - 0 -}; - -static void * -poll_init(struct event_base *base) -{ - struct pollop *pollop; - - /* Disable poll when this environment variable is set */ - if (getenv("EVENT_NOPOLL")) - return (NULL); - - if (!(pollop = calloc(1, sizeof(struct pollop)))) - return (NULL); - - evsignal_init(base); - - return (pollop); -} - -#ifdef CHECK_INVARIANTS -static void -poll_check_ok(struct pollop *pop) -{ - int i, idx; - struct event *ev; - - for (i = 0; i < pop->fd_count; ++i) { - idx = pop->idxplus1_by_fd[i]-1; - if (idx < 0) - continue; - assert(pop->event_set[idx].fd == i); - if (pop->event_set[idx].events & POLLIN) { - ev = pop->event_r_back[idx]; - assert(ev); - assert(ev->ev_events & EV_READ); - assert(ev->ev_fd == i); - } - if (pop->event_set[idx].events & POLLOUT) { - ev = pop->event_w_back[idx]; - assert(ev); - assert(ev->ev_events & EV_WRITE); - assert(ev->ev_fd == i); - } - } - for (i = 0; i < pop->nfds; ++i) { - struct pollfd *pfd = &pop->event_set[i]; - assert(pop->idxplus1_by_fd[pfd->fd] == i+1); - } -} -#else -#define poll_check_ok(pop) -#endif - -static int -poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) -{ - int res, i, msec = -1, nfds; - struct pollop *pop = arg; - - poll_check_ok(pop); - - if (tv != NULL) - msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; - - nfds = pop->nfds; - res = poll(pop->event_set, nfds, msec); - - if (res == -1) { - if (errno != EINTR) { - event_warn("poll"); - return (-1); - } - - evsignal_process(base); - return (0); - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - event_debug(("%s: poll reports %d", __func__, res)); - - if (res == 0) - return (0); - - for (i = 0; i < nfds; i++) { - int what = pop->event_set[i].revents; - struct event *r_ev = NULL, *w_ev = NULL; - if (!what) - continue; - - res = 0; - - /* If the file gets closed notify */ - if (what & (POLLHUP|POLLERR)) - what |= POLLIN|POLLOUT; - if (what & POLLIN) { - res |= EV_READ; - r_ev = pop->event_r_back[i]; - } - if (what & POLLOUT) { - res |= EV_WRITE; - w_ev = pop->event_w_back[i]; - } - if (res == 0) - continue; - - if (r_ev && (res & r_ev->ev_events)) { - event_active(r_ev, res & r_ev->ev_events, 1); - } - if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { - event_active(w_ev, res & w_ev->ev_events, 1); - } - } - - return (0); -} - -static int -poll_add(void *arg, struct event *ev) -{ - struct pollop *pop = arg; - struct pollfd *pfd = NULL; - int i; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_add(ev)); - if (!(ev->ev_events & (EV_READ|EV_WRITE))) - return (0); - - poll_check_ok(pop); - if (pop->nfds + 1 >= pop->event_count) { - struct pollfd *tmp_event_set; - struct event **tmp_event_r_back; - struct event **tmp_event_w_back; - int tmp_event_count; - - if (pop->event_count < 32) - tmp_event_count = 32; - else - tmp_event_count = pop->event_count * 2; - - /* We need more file descriptors */ - tmp_event_set = realloc(pop->event_set, - tmp_event_count * sizeof(struct pollfd)); - if (tmp_event_set == NULL) { - event_warn("realloc"); - return (-1); - } - pop->event_set = tmp_event_set; - - tmp_event_r_back = realloc(pop->event_r_back, - tmp_event_count * sizeof(struct event *)); - if (tmp_event_r_back == NULL) { - /* event_set overallocated; that's okay. */ - event_warn("realloc"); - return (-1); - } - pop->event_r_back = tmp_event_r_back; - - tmp_event_w_back = realloc(pop->event_w_back, - tmp_event_count * sizeof(struct event *)); - if (tmp_event_w_back == NULL) { - /* event_set and event_r_back overallocated; that's - * okay. */ - event_warn("realloc"); - return (-1); - } - pop->event_w_back = tmp_event_w_back; - - pop->event_count = tmp_event_count; - } - if (ev->ev_fd >= pop->fd_count) { - int *tmp_idxplus1_by_fd; - int new_count; - if (pop->fd_count < 32) - new_count = 32; - else - new_count = pop->fd_count * 2; - while (new_count <= ev->ev_fd) - new_count *= 2; - tmp_idxplus1_by_fd = - realloc(pop->idxplus1_by_fd, new_count * sizeof(int)); - if (tmp_idxplus1_by_fd == NULL) { - event_warn("realloc"); - return (-1); - } - pop->idxplus1_by_fd = tmp_idxplus1_by_fd; - memset(pop->idxplus1_by_fd + pop->fd_count, - 0, sizeof(int)*(new_count - pop->fd_count)); - pop->fd_count = new_count; - } - - i = pop->idxplus1_by_fd[ev->ev_fd] - 1; - if (i >= 0) { - pfd = &pop->event_set[i]; - } else { - i = pop->nfds++; - pfd = &pop->event_set[i]; - pfd->events = 0; - pfd->fd = ev->ev_fd; - pop->event_w_back[i] = pop->event_r_back[i] = NULL; - pop->idxplus1_by_fd[ev->ev_fd] = i + 1; - } - - pfd->revents = 0; - if (ev->ev_events & EV_WRITE) { - pfd->events |= POLLOUT; - pop->event_w_back[i] = ev; - } - if (ev->ev_events & EV_READ) { - pfd->events |= POLLIN; - pop->event_r_back[i] = ev; - } - poll_check_ok(pop); - - return (0); -} - -/* - * Nothing to be done here. - */ - -static int -poll_del(void *arg, struct event *ev) -{ - struct pollop *pop = arg; - struct pollfd *pfd = NULL; - int i; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_del(ev)); - - if (!(ev->ev_events & (EV_READ|EV_WRITE))) - return (0); - - poll_check_ok(pop); - i = pop->idxplus1_by_fd[ev->ev_fd] - 1; - if (i < 0) - return (-1); - - /* Do we still want to read or write? */ - pfd = &pop->event_set[i]; - if (ev->ev_events & EV_READ) { - pfd->events &= ~POLLIN; - pop->event_r_back[i] = NULL; - } - if (ev->ev_events & EV_WRITE) { - pfd->events &= ~POLLOUT; - pop->event_w_back[i] = NULL; - } - poll_check_ok(pop); - if (pfd->events) - /* Another event cares about that fd. */ - return (0); - - /* Okay, so we aren't interested in that fd anymore. */ - pop->idxplus1_by_fd[ev->ev_fd] = 0; - - --pop->nfds; - if (i != pop->nfds) { - /* - * Shift the last pollfd down into the now-unoccupied - * position. - */ - memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], - sizeof(struct pollfd)); - pop->event_r_back[i] = pop->event_r_back[pop->nfds]; - pop->event_w_back[i] = pop->event_w_back[pop->nfds]; - pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1; - } - - poll_check_ok(pop); - return (0); -} - -static void -poll_dealloc(struct event_base *base, void *arg) -{ - struct pollop *pop = arg; - - evsignal_dealloc(base); - if (pop->event_set) - free(pop->event_set); - if (pop->event_r_back) - free(pop->event_r_back); - if (pop->event_w_back) - free(pop->event_w_back); - if (pop->idxplus1_by_fd) - free(pop->idxplus1_by_fd); - - memset(pop, 0, sizeof(struct pollop)); - free(pop); -} - -#endif /* HAVE_POLL */ diff --git a/extra/libevent/select.c b/extra/libevent/select.c deleted file mode 100644 index b8bd1a1c361..00000000000 --- a/extra/libevent/select.c +++ /dev/null @@ -1,356 +0,0 @@ -/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ - -/* - * Copyright 2000-2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SELECT - -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif -#ifdef HAVE_SYS_SELECT_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#ifdef CHECK_INVARIANTS -#include -#endif - -#include "event.h" -#include "event-internal.h" -#include "evsignal.h" -#include "log.h" - -#ifndef howmany -#define howmany(x, y) (((x)+((y)-1))/(y)) -#endif - -struct selectop { - int event_fds; /* Highest fd in fd set */ - int event_fdsz; - fd_set *event_readset_in; - fd_set *event_writeset_in; - fd_set *event_readset_out; - fd_set *event_writeset_out; - struct event **event_r_by_fd; - struct event **event_w_by_fd; -}; - -static void *select_init (struct event_base *); -static int select_add (void *, struct event *); -static int select_del (void *, struct event *); -static int select_dispatch (struct event_base *, void *, struct timeval *); -static void select_dealloc (struct event_base *, void *); - -const struct eventop selectops = { - "select", - select_init, - select_add, - select_del, - select_dispatch, - select_dealloc, - 0 -}; - -static int select_resize(struct selectop *sop, int fdsz); - -static void * -select_init(struct event_base *base) -{ - struct selectop *sop; - - /* Disable select when this environment variable is set */ - if (getenv("EVENT_NOSELECT")) - return (NULL); - - if (!(sop = calloc(1, sizeof(struct selectop)))) - return (NULL); - - select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask)); - - evsignal_init(base); - - return (sop); -} - -#ifdef CHECK_INVARIANTS -static void -check_selectop(struct selectop *sop) -{ - int i; - for (i = 0; i <= sop->event_fds; ++i) { - if (FD_ISSET(i, sop->event_readset_in)) { - assert(sop->event_r_by_fd[i]); - assert(sop->event_r_by_fd[i]->ev_events & EV_READ); - assert(sop->event_r_by_fd[i]->ev_fd == i); - } else { - assert(! sop->event_r_by_fd[i]); - } - if (FD_ISSET(i, sop->event_writeset_in)) { - assert(sop->event_w_by_fd[i]); - assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE); - assert(sop->event_w_by_fd[i]->ev_fd == i); - } else { - assert(! sop->event_w_by_fd[i]); - } - } - -} -#else -#define check_selectop(sop) do { (void) sop; } while (0) -#endif - -static int -select_dispatch(struct event_base *base, void *arg, struct timeval *tv) -{ - int res, i; - struct selectop *sop = arg; - - check_selectop(sop); - - memcpy(sop->event_readset_out, sop->event_readset_in, - sop->event_fdsz); - memcpy(sop->event_writeset_out, sop->event_writeset_in, - sop->event_fdsz); - - res = select(sop->event_fds + 1, sop->event_readset_out, - sop->event_writeset_out, NULL, tv); - - check_selectop(sop); - - if (res == -1) { - if (errno != EINTR) { - event_warn("select"); - return (-1); - } - - evsignal_process(base); - return (0); - } else if (base->sig.evsignal_caught) { - evsignal_process(base); - } - - event_debug(("%s: select reports %d", __func__, res)); - - check_selectop(sop); - for (i = 0; i <= sop->event_fds; ++i) { - struct event *r_ev = NULL, *w_ev = NULL; - res = 0; - if (FD_ISSET(i, sop->event_readset_out)) { - r_ev = sop->event_r_by_fd[i]; - res |= EV_READ; - } - if (FD_ISSET(i, sop->event_writeset_out)) { - w_ev = sop->event_w_by_fd[i]; - res |= EV_WRITE; - } - if (r_ev && (res & r_ev->ev_events)) { - event_active(r_ev, res & r_ev->ev_events, 1); - } - if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { - event_active(w_ev, res & w_ev->ev_events, 1); - } - } - check_selectop(sop); - - return (0); -} - - -static int -select_resize(struct selectop *sop, int fdsz) -{ - int n_events, n_events_old; - - fd_set *readset_in = NULL; - fd_set *writeset_in = NULL; - fd_set *readset_out = NULL; - fd_set *writeset_out = NULL; - struct event **r_by_fd = NULL; - struct event **w_by_fd = NULL; - - n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; - n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; - - if (sop->event_readset_in) - check_selectop(sop); - - if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) - goto error; - sop->event_readset_in = readset_in; - if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) - goto error; - sop->event_readset_out = readset_out; - if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) - goto error; - sop->event_writeset_in = writeset_in; - if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) - goto error; - sop->event_writeset_out = writeset_out; - if ((r_by_fd = realloc(sop->event_r_by_fd, - n_events*sizeof(struct event*))) == NULL) - goto error; - sop->event_r_by_fd = r_by_fd; - if ((w_by_fd = realloc(sop->event_w_by_fd, - n_events * sizeof(struct event*))) == NULL) - goto error; - sop->event_w_by_fd = w_by_fd; - - memset((char *)sop->event_readset_in + sop->event_fdsz, 0, - fdsz - sop->event_fdsz); - memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, - fdsz - sop->event_fdsz); - memset(sop->event_r_by_fd + n_events_old, 0, - (n_events-n_events_old) * sizeof(struct event*)); - memset(sop->event_w_by_fd + n_events_old, 0, - (n_events-n_events_old) * sizeof(struct event*)); - - sop->event_fdsz = fdsz; - check_selectop(sop); - - return (0); - - error: - event_warn("malloc"); - return (-1); -} - - -static int -select_add(void *arg, struct event *ev) -{ - struct selectop *sop = arg; - - if (ev->ev_events & EV_SIGNAL) - return (evsignal_add(ev)); - - check_selectop(sop); - /* - * Keep track of the highest fd, so that we can calculate the size - * of the fd_sets for select(2) - */ - if (sop->event_fds < ev->ev_fd) { - unsigned int fdsz = sop->event_fdsz; - - if (fdsz < sizeof(fd_mask)) - fdsz = sizeof(fd_mask); - - while (fdsz < - (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) - fdsz *= 2; - - if (fdsz != (unsigned int) sop->event_fdsz) { - if (select_resize(sop, fdsz)) { - check_selectop(sop); - return (-1); - } - } - - sop->event_fds = ev->ev_fd; - } - - if (ev->ev_events & EV_READ) { - FD_SET(ev->ev_fd, sop->event_readset_in); - sop->event_r_by_fd[ev->ev_fd] = ev; - } - if (ev->ev_events & EV_WRITE) { - FD_SET(ev->ev_fd, sop->event_writeset_in); - sop->event_w_by_fd[ev->ev_fd] = ev; - } - check_selectop(sop); - - return (0); -} - -/* - * Nothing to be done here. - */ - -static int -select_del(void *arg, struct event *ev) -{ - struct selectop *sop = arg; - - check_selectop(sop); - if (ev->ev_events & EV_SIGNAL) - return (evsignal_del(ev)); - - if (sop->event_fds < ev->ev_fd) { - check_selectop(sop); - return (0); - } - - if (ev->ev_events & EV_READ) { - FD_CLR(ev->ev_fd, sop->event_readset_in); - sop->event_r_by_fd[ev->ev_fd] = NULL; - } - - if (ev->ev_events & EV_WRITE) { - FD_CLR(ev->ev_fd, sop->event_writeset_in); - sop->event_w_by_fd[ev->ev_fd] = NULL; - } - - check_selectop(sop); - return (0); -} - -static void -select_dealloc(struct event_base *base, void *arg) -{ - struct selectop *sop = arg; - - evsignal_dealloc(base); - if (sop->event_readset_in) - free(sop->event_readset_in); - if (sop->event_writeset_in) - free(sop->event_writeset_in); - if (sop->event_readset_out) - free(sop->event_readset_out); - if (sop->event_writeset_out) - free(sop->event_writeset_out); - if (sop->event_r_by_fd) - free(sop->event_r_by_fd); - if (sop->event_w_by_fd) - free(sop->event_w_by_fd); - - memset(sop, 0, sizeof(struct selectop)); - free(sop); -} - -#endif /* HAVE_SELECT */ diff --git a/extra/libevent/signal.c b/extra/libevent/signal.c deleted file mode 100644 index ce164f2f5ea..00000000000 --- a/extra/libevent/signal.c +++ /dev/null @@ -1,305 +0,0 @@ -/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ - -/* - * Copyright 2000-2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#undef WIN32_LEAN_AND_MEAN -#endif -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_FCNTL_H -#include -#endif -#include - -#include "event.h" -#include "event-internal.h" -#include "evsignal.h" -#include "evutil.h" -#include "log.h" - -struct event_base *evsignal_base = NULL; - -static void evsignal_handler(int sig); - -/* Callback for when the signal handler write a byte to our signaling socket */ -static void -evsignal_cb(int fd, short what __attribute__((unused)), - void *arg __attribute__((unused))) -{ - static char signals[100]; -#ifdef WIN32 - SSIZE_T n; -#else - ssize_t n; -#endif - - n = recv(fd, signals, sizeof(signals), 0); - if (n == -1) - event_err(1, "%s: read", __func__); -} - -#ifdef HAVE_SETFD -#define FD_CLOSEONEXEC(x) do { \ - if (fcntl(x, F_SETFD, 1) == -1) \ - event_warn("fcntl(%d, F_SETFD)", x); \ -} while (0) -#else -#define FD_CLOSEONEXEC(x) -#endif - -void -evsignal_init(struct event_base *base) -{ - /* - * Our signal handler is going to write to one end of the socket - * pair to wake up our event loop. The event loop then scans for - * signals that got delivered. - */ - if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) - event_err(1, "%s: socketpair", __func__); - - FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]); - FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]); - base->sig.sh_old = NULL; - base->sig.sh_old_max = 0; - base->sig.evsignal_caught = 0; - memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG); - - evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]); - - event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], - EV_READ | EV_PERSIST, &evsignal_cb, &base->sig.ev_signal); - base->sig.ev_signal.ev_base = base; - base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL; -} - -/* Helper: set the signal handler for evsignal to handler in base, so that - * we can restore the original handler when we clear the current one. */ -int -_evsignal_set_handler(struct event_base *base, - int evsignal, void (*handler)(int)) -{ -#ifdef HAVE_SIGACTION - struct sigaction sa; -#else - ev_sighandler_t sh; -#endif - struct evsignal_info *sig = &base->sig; - void *p; - - /* - * resize saved signal handler array up to the highest signal number. - * a dynamic array is used to keep footprint on the low side. - */ - if (evsignal >= sig->sh_old_max) { - event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing", - __func__, evsignal, sig->sh_old_max)); - sig->sh_old_max = evsignal + 1; - p = realloc(sig->sh_old, sig->sh_old_max * sizeof *sig->sh_old); - if (p == NULL) { - event_warn("realloc"); - return (-1); - } - sig->sh_old = p; - } - - /* allocate space for previous handler out of dynamic array */ - sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]); - if (sig->sh_old[evsignal] == NULL) { - event_warn("malloc"); - return (-1); - } - - /* save previous handler and setup new handler */ -#ifdef HAVE_SIGACTION - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; - sa.sa_flags |= SA_RESTART; - sigfillset(&sa.sa_mask); - - if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) { - event_warn("sigaction"); - free(sig->sh_old[evsignal]); - return (-1); - } -#else - if ((sh = signal(evsignal, handler)) == SIG_ERR) { - event_warn("signal"); - free(sig->sh_old[evsignal]); - return (-1); - } - *sig->sh_old[evsignal] = sh; -#endif - - return (0); -} - -int -evsignal_add(struct event *ev) -{ - int evsignal; - struct event_base *base = ev->ev_base; - struct evsignal_info *sig = &ev->ev_base->sig; - - if (ev->ev_events & (EV_READ|EV_WRITE)) - event_errx(1, "%s: EV_SIGNAL incompatible use", __func__); - evsignal = EVENT_SIGNAL(ev); - - event_debug(("%s: %p: changing signal handler", __func__, ev)); - if (_evsignal_set_handler(base, evsignal, evsignal_handler) == -1) - return (-1); - - /* catch signals if they happen quickly */ - evsignal_base = base; - - if (!sig->ev_signal_added) { - sig->ev_signal_added = 1; - event_add(&sig->ev_signal, NULL); - } - - return (0); -} - -int -_evsignal_restore_handler(struct event_base *base, int evsignal) -{ - int ret = 0; - struct evsignal_info *sig = &base->sig; -#ifdef HAVE_SIGACTION - struct sigaction *sh; -#else - ev_sighandler_t *sh; -#endif - - /* restore previous handler */ - sh = sig->sh_old[evsignal]; - sig->sh_old[evsignal] = NULL; -#ifdef HAVE_SIGACTION - if (sigaction(evsignal, sh, NULL) == -1) { - event_warn("sigaction"); - ret = -1; - } -#else - if (signal(evsignal, *sh) == SIG_ERR) { - event_warn("signal"); - ret = -1; - } -#endif - free(sh); - - return ret; -} - -int -evsignal_del(struct event *ev) -{ - event_debug(("%s: %p: restoring signal handler", __func__, ev)); - return _evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev)); -} - -static void -evsignal_handler(int sig) -{ - int save_errno = errno; - - if(evsignal_base == NULL) { - event_warn( - "%s: received signal %d, but have no base configured", - __func__, sig); - return; - } - - evsignal_base->sig.evsigcaught[sig]++; - evsignal_base->sig.evsignal_caught = 1; - -#ifndef HAVE_SIGACTION - signal(sig, evsignal_handler); -#endif - - /* Wake up our notification mechanism */ - send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); - errno = save_errno; -} - -void -evsignal_process(struct event_base *base) -{ - struct event *ev; - sig_atomic_t ncalls; - - base->sig.evsignal_caught = 0; - TAILQ_FOREACH(ev, &base->sig.signalqueue, ev_signal_next) { - ncalls = base->sig.evsigcaught[EVENT_SIGNAL(ev)]; - if (ncalls) { - if (!(ev->ev_events & EV_PERSIST)) - event_del(ev); - event_active(ev, EV_SIGNAL, ncalls); - base->sig.evsigcaught[EVENT_SIGNAL(ev)] = 0; - } - } -} - -void -evsignal_dealloc(struct event_base *base) -{ - if(base->sig.ev_signal_added) { - event_del(&base->sig.ev_signal); - base->sig.ev_signal_added = 0; - } - assert(TAILQ_EMPTY(&base->sig.signalqueue)); - - EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]); - base->sig.ev_signal_pair[0] = -1; - EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]); - base->sig.ev_signal_pair[1] = -1; - base->sig.sh_old_max = 0; - - /* per index frees are handled in evsignal_del() */ - free(base->sig.sh_old); -} diff --git a/extra/libevent/strlcpy-internal.h b/extra/libevent/strlcpy-internal.h deleted file mode 100644 index 22b5f61d45e..00000000000 --- a/extra/libevent/strlcpy-internal.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _STRLCPY_INTERNAL_H_ -#define _STRLCPY_INTERNAL_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#ifndef HAVE_STRLCPY -#include -size_t _event_strlcpy(char *dst, const char *src, size_t siz); -#define strlcpy _event_strlcpy -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extra/libevent/strlcpy.c b/extra/libevent/strlcpy.c deleted file mode 100644 index 5d194527c8c..00000000000 --- a/extra/libevent/strlcpy.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#ifndef HAVE_STRLCPY -#include "strlcpy-internal.h" - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -_event_strlcpy(dst, src, siz) - char *dst; - const char *src; - size_t siz; -{ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} -#endif From b64a2fef59bf7269a51884e74197d78c5ff203ff Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 27 Feb 2012 19:54:30 +0100 Subject: [PATCH 51/51] precache some more system checks on Windows --- cmake/os/WindowsCache.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake index b0dc2c04250..fb3d3d88678 100644 --- a/cmake/os/WindowsCache.cmake +++ b/cmake/os/WindowsCache.cmake @@ -360,4 +360,6 @@ SET(HAVE_WCTYPE_H 1 CACHE INTERNAL "") SET(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP CACHE INTERNAL "") SET(HAVE_SOCKADDR_IN_SIN_LEN CACHE INTERNAL "") SET(HAVE_SOCKADDR_IN6_SIN6_LEN CACHE INTERNAL "") +SET(HAVE_VALGRIND CACHE INTERNAL "") +SET(HAVE_EVENT_H CACHE INTERNAL "") ENDIF()