diff options
Diffstat (limited to 'sql/scheduler.cc')
-rw-r--r-- | sql/scheduler.cc | 780 |
1 files changed, 116 insertions, 664 deletions
diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 5b8f834aecc..4a5a99bf40e 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2007 MySQL AB +/* Copyright (C) 2007 MySQL AB, 2008-2010 Sun Microsystems, Inc. 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 @@ -21,34 +21,13 @@ #pragma implementation #endif -#include <mysql_priv.h> -#if MYSQL_VERSION_ID >= 60000 +#include <sql_priv.h> +#include "unireg.h" // REQUIRED: for other includes +#include "scheduler.h" +#include "sql_connect.h" // init_new_connection_handler_thread +#include "scheduler.h" +#include "sql_callback.h" #include "sql_audit.h" -#endif - -/* - 'Dummy' functions to be used when we don't need any handling for a scheduler - event - */ - -static bool init_dummy(void) {return 0;} -static void post_kill_dummy(THD *thd) {} -static void end_dummy(void) {} -static bool end_thread_dummy(THD *thd, bool cache_thread) { return 0; } - -/* - Initialize default scheduler with dummy functions so that setup functions - only need to declare those that are relvant for their usage -*/ - -scheduler_functions::scheduler_functions() - :init(init_dummy), - init_new_connection_thread(init_new_connection_handler_thread), - add_connection(0), // Must be defined - post_kill_notification(post_kill_dummy), - end_thread(end_thread_dummy), end(end_dummy) -{} - /* End connection, in case when we are using 'no-threads' @@ -57,700 +36,173 @@ scheduler_functions::scheduler_functions() static bool no_threads_end(THD *thd, bool put_in_cache) { unlink_thd(thd); - pthread_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thread_count); return 1; // Abort handle_one_connection } - -/* - Initailize scheduler for --thread-handling=no-threads -*/ - -void one_thread_scheduler(scheduler_functions *func) +static scheduler_functions one_thread_scheduler_functions= { - func->max_threads= 1; - max_connections= 1; - func->max_connections= &max_connections; - func->connection_count= &connection_count; + 1, // max_threads + NULL, NULL, + NULL, // init + init_new_connection_handler_thread, // init_new_connection_thread #ifndef EMBEDDED_LIBRARY - func->add_connection= handle_connection_in_main_thread; -#endif - func->init_new_connection_thread= init_dummy; - func->end_thread= no_threads_end; -} - - -/* - Initialize scheduler for --thread-handling=one-thread-per-connection -*/ + handle_connection_in_main_thread, // add_connection +#else + NULL, // add_connection +#endif // EMBEDDED_LIBRARY + NULL, // thd_wait_begin + NULL, // thd_wait_end + NULL, // post_kill_notification + no_threads_end, // end_thread + NULL, // end +}; #ifndef EMBEDDED_LIBRARY -void one_thread_per_connection_scheduler(scheduler_functions *func, - ulong *arg_max_connections, - uint *arg_connection_count) -{ - func->max_threads= *arg_max_connections + 1; - func->max_connections= arg_max_connections; - func->connection_count= arg_connection_count; - func->add_connection= create_thread_to_handle_connection; - func->end_thread= one_thread_per_connection_end; -} -#endif /* EMBEDDED_LIBRARY */ - - -#if defined(HAVE_LIBEVENT) && HAVE_POOL_OF_THREADS == 1 - -#include "event.h" - -static struct event_base *base; - -static uint created_threads, killed_threads; -static bool kill_pool_threads; - -static struct event thd_add_event; -static struct event thd_kill_event; - -static pthread_mutex_t LOCK_thd_add; /* protects thds_need_adding */ -static LIST *thds_need_adding; /* list of thds to add to libevent queue */ - -static int thd_add_pair[2]; /* pipe to signal add a connection to libevent*/ -static int thd_kill_pair[2]; /* pipe to signal kill a connection in libevent */ - -/* - LOCK_event_loop protects the non-thread safe libevent calls (event_add and - event_del) and thds_need_processing and thds_waiting_for_io. -*/ -static pthread_mutex_t LOCK_event_loop; -static LIST *thds_need_processing; /* list of thds that needs some processing */ -static LIST *thds_waiting_for_io; /* list of thds with added events */ - -pthread_handler_t libevent_thread_proc(void *arg); -static void libevent_end(); -static bool libevent_needs_immediate_processing(THD *thd); -static void libevent_connection_close(THD *thd); -static bool libevent_should_close_connection(THD* thd); -static void libevent_thd_add(THD* thd); -void libevent_io_callback(int Fd, short Operation, void *ctx); -void libevent_add_thd_callback(int Fd, short Operation, void *ctx); -void libevent_kill_thd_callback(int Fd, short Operation, void *ctx); - - -/* - Create a pipe and set to non-blocking. - Returns TRUE if there is an error. -*/ - -static bool init_socketpair(int sock_pair[]) -{ - sock_pair[0]= sock_pair[1]= -1; - return (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair) < 0 || - evutil_make_socket_nonblocking(sock_pair[0]) == -1 || - evutil_make_socket_nonblocking(sock_pair[1]) == -1); -} - -static void close_socketpair(int sock_pair[]) +static scheduler_functions one_thread_per_connection_scheduler_functions= { - if (sock_pair[0] != -1) - EVUTIL_CLOSESOCKET(sock_pair[0]); - if (sock_pair[1] != -1) - EVUTIL_CLOSESOCKET(sock_pair[1]); -} + 0, // max_threads + NULL, NULL, + NULL, // init + init_new_connection_handler_thread, // init_new_connection_thread + create_thread_to_handle_connection, // add_connection + NULL, // thd_wait_begin + NULL, // thd_wait_end + NULL, // post_kill_notification + one_thread_per_connection_end, // end_thread + NULL, // end +}; +#endif // EMBEDDED_LIBRARY -/* - thd_scheduler keeps the link between THD and events. - It's embedded in the THD class. +/** @internal + Helper functions to allow mysys to call the thread scheduler when + waiting for locks. */ -thd_scheduler::thd_scheduler() - : logged_in(FALSE), io_event(NULL), thread_attached(FALSE) -{ -#ifndef DBUG_OFF - dbug_explain[0]= '\0'; - set_explain= FALSE; -#endif +/**@{*/ +static void scheduler_wait_begin(void) { + THD *thd=current_thd; + scheduler_functions *func= thd->scheduler; + MYSQL_CALLBACK(func, + thd_wait_begin, (thd, THD_WAIT_ROW_TABLE_LOCK)); } - -thd_scheduler::~thd_scheduler() -{ - my_free(io_event, MYF(MY_ALLOW_ZERO_PTR)); -} - - -bool thd_scheduler::init(THD *parent_thd) -{ - io_event= - (struct event*)my_malloc(sizeof(*io_event),MYF(MY_ZEROFILL|MY_WME)); - - if (!io_event) - { - sql_print_error("Memory allocation error in thd_scheduler::init\n"); - return TRUE; - } - - event_set(io_event, (int)parent_thd->net.vio->sd, EV_READ, - libevent_io_callback, (void*)parent_thd); - - list.data= parent_thd; - - return FALSE; -} - - -/* - Attach/associate the connection with the OS thread, for command processing. -*/ - -bool thd_scheduler::thread_attach() -{ - DBUG_ASSERT(!thread_attached); - THD* thd = (THD*)list.data; - if (libevent_should_close_connection(thd) || - setup_connection_thread_globals(thd)) - { - return TRUE; - } - my_errno= 0; - thd->mysys_var->abort= 0; - thread_attached= TRUE; -#ifndef DBUG_OFF - /* - When we attach the thread for a connection for the first time, - we know that there is no session value set yet. Thus - the initial setting of set_explain to FALSE is OK. - */ - if (set_explain) - DBUG_SET(dbug_explain); -#endif - return FALSE; -} - - -/* - Detach/disassociate the connection with the OS thread. -*/ - -void thd_scheduler::thread_detach() -{ - if (thread_attached) - { - THD* thd = (THD*)list.data; - thd->reset_globals(); - thread_attached= FALSE; -#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_()) - { - set_explain= TRUE; - if (DBUG_EXPLAIN(dbug_explain, sizeof(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 - } +static void scheduler_wait_end(void) { + THD *thd=current_thd; + scheduler_functions *func= thd->scheduler; + MYSQL_CALLBACK(func, thd_wait_end, (thd)); } +/**@}*/ /** - Create all threads for the thread pool + Common scheduler init function. - NOTES - After threads are created we wait until all threads has signaled that - they have started before we return - - RETURN - 0 ok - 1 We got an error creating the thread pool - In this case we will abort all created threads -*/ - -static bool libevent_init(void) -{ - uint i; - DBUG_ENTER("libevent_init"); - - base= (struct event_base *) event_init(); - - created_threads= 0; - killed_threads= 0; - kill_pool_threads= FALSE; - - pthread_mutex_init(&LOCK_event_loop, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_thd_add, MY_MUTEX_INIT_FAST); - - /* set up sockets used to add new thds to the event pool */ - if (init_socketpair(thd_add_pair)) - { - sql_print_error("init_socketpair(thd_add_spair) error in libevent_init"); - DBUG_RETURN(1); - } - /* set up sockets used to kill thds in the event queue */ - if (init_socketpair(thd_kill_pair)) - { - sql_print_error("init_socketpair(thd_kill_pair) error in libevent_init"); - close_socketpair(thd_add_pair); - DBUG_RETURN(1); - } - event_set(&thd_add_event, thd_add_pair[0], EV_READ|EV_PERSIST, - libevent_add_thd_callback, NULL); - event_set(&thd_kill_event, thd_kill_pair[0], EV_READ|EV_PERSIST, - libevent_kill_thd_callback, NULL); - - if (event_add(&thd_add_event, NULL) || event_add(&thd_kill_event, NULL)) - { - sql_print_error("thd_add_event event_add error in libevent_init"); - libevent_end(); - DBUG_RETURN(1); - } - /* Set up the thread pool */ - created_threads= killed_threads= 0; - pthread_mutex_lock(&LOCK_thread_count); - - for (i= 0; i < thread_pool_size; i++) - { - pthread_t thread; - int error; - if ((error= pthread_create(&thread, &connection_attrib, - libevent_thread_proc, 0))) - { - sql_print_error("Can't create completion port thread (error %d)", - error); - pthread_mutex_unlock(&LOCK_thread_count); - libevent_end(); // Cleanup - DBUG_RETURN(TRUE); - } - } - - /* Wait until all threads are created */ - while (created_threads != thread_pool_size) - pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); - pthread_mutex_unlock(&LOCK_thread_count); - - DBUG_PRINT("info", ("%u threads created", (uint) thread_pool_size)); - DBUG_RETURN(FALSE); + The scheduler is either initialized by calling + one_thread_scheduler() or one_thread_per_connection_scheduler() in + mysqld.cc, so this init function will always be called. + */ +static void scheduler_init() { + mysys_var->scheduler_before_lock_wait= &scheduler_wait_begin; + mysys_var->scheduler_after_lock_wait= &scheduler_wait_end; } - /* - This is called when data is ready on the socket. - - NOTES - This is only called by the thread that owns LOCK_event_loop. - - We add the thd that got the data to thds_need_processing, and - cause the libevent event_loop() to terminate. Then this same thread will - return from event_loop and pick the thd value back up for processing. + Initialize scheduler for --thread-handling=one-thread-per-connection */ -void libevent_io_callback(int, short, void *ctx) +#ifndef EMBEDDED_LIBRARY +scheduler_functions *one_thread_per_connection_scheduler( + ulong *arg_max_connections, + uint *arg_connection_count) { - safe_mutex_assert_owner(&LOCK_event_loop); - THD *thd= (THD*)ctx; - thds_waiting_for_io= list_delete(thds_waiting_for_io, &thd->event_scheduler.list); - thds_need_processing= list_add(thds_need_processing, &thd->event_scheduler.list); + scheduler_init(); + one_thread_per_connection_scheduler_functions.max_threads= *arg_max_connections + 1; + one_thread_per_connection_scheduler_functions.max_connections= arg_max_connections; + one_thread_per_connection_scheduler_functions.connection_count= arg_connection_count; + return &one_thread_per_connection_scheduler_functions; } +#endif /* - This is called when we have a thread we want to be killed. - - NOTES - This is only called by the thread that owns LOCK_event_loop. + Initailize scheduler for --thread-handling=no-threads */ -void libevent_kill_thd_callback(int Fd, short, void*) +scheduler_functions *one_thread_scheduler() { - safe_mutex_assert_owner(&LOCK_event_loop); - - /* clear the pending events */ - char c; - while (recv(Fd, &c, sizeof(c), 0) == sizeof(c)) - {} - - LIST* list= thds_waiting_for_io; - while (list) - { - THD *thd= (THD*)list->data; - list= list_rest(list); - if (thd->killed == THD::KILL_CONNECTION) - { - /* - Delete from libevent and add to the processing queue. - */ - event_del(thd->event_scheduler.io_event); - thds_waiting_for_io= list_delete(thds_waiting_for_io, - &thd->event_scheduler.list); - thds_need_processing= list_add(thds_need_processing, - &thd->event_scheduler.list); - } - } + scheduler_init(); + return &one_thread_scheduler_functions; } /* - This is used to add connections to the pool. This callback is invoked from - the libevent event_loop() call whenever the thd_add_pair[1] has a byte - written to it. - - NOTES - This is only called by the thread that owns LOCK_event_loop. -*/ - -void libevent_add_thd_callback(int Fd, short, void *) -{ - safe_mutex_assert_owner(&LOCK_event_loop); - - /* clear the pending events */ - char c; - while (recv(Fd, &c, sizeof(c), 0) == sizeof(c)) - {} - - pthread_mutex_lock(&LOCK_thd_add); - while (thds_need_adding) - { - /* pop the first thd off the list */ - THD* thd= (THD*)thds_need_adding->data; - thds_need_adding= list_delete(thds_need_adding, thds_need_adding); - - pthread_mutex_unlock(&LOCK_thd_add); - - if (!thd->event_scheduler.logged_in || libevent_should_close_connection(thd)) - { - /* - Add thd to thds_need_processing list. If it needs closing we'll close - it outside of event_loop(). - */ - thds_need_processing= list_add(thds_need_processing, - &thd->event_scheduler.list); - } - else - { - /* Add to libevent */ - if (event_add(thd->event_scheduler.io_event, NULL)) - { - sql_print_error("event_add error in libevent_add_thd_callback"); - libevent_connection_close(thd); - } - else - { - thds_waiting_for_io= list_add(thds_waiting_for_io, - &thd->event_scheduler.list); - } - } - pthread_mutex_lock(&LOCK_thd_add); - } - pthread_mutex_unlock(&LOCK_thd_add); -} - - -/** - Notify the thread pool about a new connection - - NOTES - LOCK_thread_count is locked on entry. This function MUST unlock it! -*/ - -static void libevent_add_connection(THD *thd) -{ - DBUG_ENTER("libevent_add_connection"); - DBUG_PRINT("enter", ("thd: %p thread_id: %lu", - thd, thd->thread_id)); - - if (thd->event_scheduler.init(thd)) - { - sql_print_error("Scheduler init error in libevent_add_new_connection"); - pthread_mutex_unlock(&LOCK_thread_count); - libevent_connection_close(thd); - DBUG_VOID_RETURN; - } - threads.append(thd); - libevent_thd_add(thd); - - pthread_mutex_unlock(&LOCK_thread_count); - DBUG_VOID_RETURN; -} - - -/** - @brief Signal a waiting connection it's time to die. - - @details This function will signal libevent the THD should be killed. - Either the global LOCK_thd_count or the THD's LOCK_thd_data must be locked - upon entry. - - @param[in] thd The connection to kill + Initialize scheduler for --thread-handling=one-thread-per-connection */ -static void libevent_post_kill_notification(THD *) -{ - /* - Note, we just wake up libevent with an event that a THD should be killed, - It will search its list of thds for thd->killed == KILL_CONNECTION to - find the THDs it should kill. - - So we don't actually tell it which one and we don't actually use the - THD being passed to us, but that's just a design detail that could change - later. - */ - char c= 0; - send(thd_kill_pair[1], &c, sizeof(c), 0); -} - - /* - Close and delete a connection. + thd_scheduler keeps the link between THD and events. + It's embedded in the THD class. */ -static void libevent_connection_close(THD *thd) +thd_scheduler::thd_scheduler() + : m_psi(NULL), data(NULL) { - DBUG_ENTER("libevent_connection_close"); - DBUG_PRINT("enter", ("thd: %p", thd)); - - thd->killed= THD::KILL_CONNECTION; // Avoid error messages - - if (thd->net.vio->sd >= 0) // not already closed - { - end_connection(thd); - close_connection(thd, 0, 1); - } - thd->event_scheduler.thread_detach(); - unlink_thd(thd); /* locks LOCK_thread_count and deletes thd */ - pthread_mutex_unlock(&LOCK_thread_count); - - DBUG_VOID_RETURN; +#ifndef DBUG_OFF + dbug_explain[0]= '\0'; + set_explain= FALSE; +#endif } -/* - Returns true if we should close and delete a THD connection. -*/ - -static bool libevent_should_close_connection(THD* thd) +thd_scheduler::~thd_scheduler() { - return thd->net.error || - thd->net.vio == 0 || - thd->killed == THD::KILL_CONNECTION; } - /* - libevent_thread_proc is the outer loop of each thread in the thread pool. - These procs only return/terminate on shutdown (kill_pool_threads == true). + no pluggable schedulers in mariadb. + when we'll want it, we'll do it properly */ +#if 0 -pthread_handler_t libevent_thread_proc(void *arg) -{ - if (init_new_connection_handler_thread()) - { - my_thread_global_end(); - sql_print_error("libevent_thread_proc: my_thread_init() failed"); - exit(1); - } - DBUG_ENTER("libevent_thread_proc"); - - /* - Signal libevent_init() when all threads has been created and are ready to - receive events. - */ - (void) pthread_mutex_lock(&LOCK_thread_count); - created_threads++; - thread_created++; - if (created_threads == thread_pool_size) - (void) pthread_cond_signal(&COND_thread_count); - (void) pthread_mutex_unlock(&LOCK_thread_count); - - for (;;) - { - THD *thd= NULL; - (void) pthread_mutex_lock(&LOCK_event_loop); - - /* get thd(s) to process */ - while (!thds_need_processing) - { - if (kill_pool_threads) - { - /* the flag that we should die has been set */ - (void) pthread_mutex_unlock(&LOCK_event_loop); - goto thread_exit; - } - event_loop(EVLOOP_ONCE); - } - - /* pop the first thd off the list */ - thd= (THD*)thds_need_processing->data; - thds_need_processing= list_delete(thds_need_processing, - thds_need_processing); - - (void) pthread_mutex_unlock(&LOCK_event_loop); - - /* now we process the connection (thd) */ - - /* set up the thd<->thread links. */ - thd->thread_stack= (char*) &thd; - - if (thd->event_scheduler.thread_attach()) - { - libevent_connection_close(thd); - continue; - } - - /* is the connection logged in yet? */ - if (!thd->event_scheduler.logged_in) - { - DBUG_PRINT("info", ("init new connection. sd: %d", - thd->net.vio->sd)); - lex_start(thd); - if (login_connection(thd)) - { - /* Failed to log in */ - libevent_connection_close(thd); - continue; - } - else - { - /* login successful */ -#if MYSQL_VERSION_ID >= 60000 - MYSQL_CONNECTION_START(thd->thread_id, thd->security_ctx->priv_user, - (char *) thd->security_ctx->host_or_ip); -#endif - thd->event_scheduler.logged_in= TRUE; - prepare_new_connection_state(thd); - if (!libevent_needs_immediate_processing(thd)) - continue; /* New connection is now waiting for data in libevent*/ - } - } - - do - { - /* Process a query */ - if (do_command(thd)) - { - libevent_connection_close(thd); - break; - } - } while (libevent_needs_immediate_processing(thd)); - } - -thread_exit: - DBUG_PRINT("exit", ("ending thread")); - (void) pthread_mutex_lock(&LOCK_thread_count); - killed_threads++; - pthread_cond_broadcast(&COND_thread_count); - (void) pthread_mutex_unlock(&LOCK_thread_count); - my_thread_end(); - pthread_exit(0); - DBUG_RETURN(0); /* purify: deadcode */ -} - - -/* - Returns TRUE if the connection needs immediate processing and FALSE if - instead it's queued for libevent processing or closed, -*/ +static scheduler_functions *saved_thread_scheduler; +static uint saved_thread_handling; -static bool libevent_needs_immediate_processing(THD *thd) +extern "C" +int my_thread_scheduler_set(scheduler_functions *scheduler) { - if (libevent_should_close_connection(thd)) - { - libevent_connection_close(thd); - return FALSE; - } - /* - If more data in the socket buffer, return TRUE to process another command. - - Note: we cannot add for event processing because the whole request might - already be buffered and we wouldn't receive an event. - */ - if (vio_pending(thd->net.vio) > 0) - return TRUE; - - thd->event_scheduler.thread_detach(); - libevent_thd_add(thd); - return FALSE; -} - + DBUG_ASSERT(scheduler != 0); -/* - Adds a THD to queued for libevent processing. - - This call does not actually register the event with libevent. - Instead, it places the THD onto a queue and signals libevent by writing - a byte into thd_add_pair, which will cause our libevent_add_thd_callback to - be invoked which will find the THD on the queue and add it to libevent. -*/ + if (scheduler == NULL) + return 1; -static void libevent_thd_add(THD* thd) -{ - char c= 0; - /* release any audit resources, this thd is going to sleep */ -#if MYSQL_VERSION_ID >= 60000 - mysql_audit_release(thd); -#endif - pthread_mutex_lock(&LOCK_thd_add); - /* queue for libevent */ - thds_need_adding= list_add(thds_need_adding, &thd->event_scheduler.list); - /* notify libevent */ - send(thd_add_pair[1], &c, sizeof(c), 0); - pthread_mutex_unlock(&LOCK_thd_add); + saved_thread_scheduler= thread_scheduler; + saved_thread_handling= thread_handling; + thread_scheduler= scheduler; + // Scheduler loaded dynamically + thread_handling= SCHEDULER_TYPES_COUNT; + return 0; } -/** - Wait until all pool threads have been deleted for clean shutdown -*/ - -static void libevent_end() +extern "C" +int my_thread_scheduler_reset() { - DBUG_ENTER("libevent_end"); - DBUG_PRINT("enter", ("created_threads: %d killed_threads: %u", - created_threads, killed_threads)); - - /* - check if initialized. This may not be the case if get an error at - startup - */ - if (!base) - DBUG_VOID_RETURN; - - (void) pthread_mutex_lock(&LOCK_thread_count); - - - kill_pool_threads= TRUE; - while (killed_threads != created_threads) - { - /* wake up the event loop */ - char c= 0; - send(thd_add_pair[1], &c, sizeof(c), 0); - pthread_cond_wait(&COND_thread_count, &LOCK_thread_count); - } - (void) pthread_mutex_unlock(&LOCK_thread_count); - - event_del(&thd_add_event); - close_socketpair(thd_add_pair); - event_del(&thd_kill_event); - close_socketpair(thd_kill_pair); - event_base_free(base); - base= 0; - - (void) pthread_mutex_destroy(&LOCK_event_loop); - (void) pthread_mutex_destroy(&LOCK_thd_add); - DBUG_VOID_RETURN; -} + DBUG_ASSERT(saved_thread_scheduler != NULL); + if (saved_thread_scheduler == NULL) + return 1; -void pool_of_threads_scheduler(scheduler_functions* func) -{ - func->max_threads= thread_pool_size; - func->max_connections= &max_connections; - func->connection_count= &connection_count; - func->init= libevent_init; - func->end= libevent_end; - func->post_kill_notification= libevent_post_kill_notification; - func->add_connection= libevent_add_connection; + thread_scheduler= saved_thread_scheduler; + thread_handling= saved_thread_handling; + saved_thread_scheduler= 0; + return 0; } +#else +extern "C" int my_thread_scheduler_set(scheduler_functions *scheduler) +{ return 1; } +extern "C" int my_thread_scheduler_reset() +{ return 1; } #endif + +#warning restore libevent + |