Drizzled Public API Documentation

multi_thread.cc
00001 /* Copyright (C) 2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include <config.h>
00017 
00018 #include <iostream>
00019 
00020 #include <drizzled/pthread_globals.h>
00021 #include <drizzled/module/option_map.h>
00022 #include <drizzled/errmsg_print.h>
00023 #include <drizzled/session.h>
00024 #include <drizzled/session/cache.h>
00025 #include <drizzled/abort_exception.h>
00026 #include <drizzled/transaction_services.h>
00027 #include <drizzled/gettext.h>
00028 #include <drizzled/plugin.h>
00029 #include <drizzled/statistics_variables.h>
00030 
00031 #include <boost/thread.hpp>
00032 #include <boost/bind.hpp>
00033 #include <boost/program_options.hpp>
00034 
00035 #include "multi_thread.h"
00036 
00037 namespace po= boost::program_options;
00038 using namespace std;
00039 using namespace drizzled;
00040 
00041 /* Configuration variables. */
00042 typedef constrained_check<uint32_t, 4096, 1> max_threads_constraint;
00043 static max_threads_constraint max_threads;
00044 
00045 namespace drizzled
00046 {
00047   extern size_t my_thread_stack_size;
00048 }
00049 
00050 namespace multi_thread {
00051 
00052 void MultiThreadScheduler::runSession(drizzled::session_id_t id)
00053 {
00054   char stack_dummy;
00055   boost::this_thread::disable_interruption disable_by_default;
00056 
00057   Session::shared_ptr session(session::Cache::find(id));
00058 
00059   try
00060   {
00061 
00062     if (not session)
00063     {
00064       std::cerr << _("Session killed before thread could execute") << endl;
00065       return;
00066     }
00067     session->pushInterrupt(&disable_by_default);
00068     drizzled::internal::my_thread_init();
00069     session->thread_stack= (char*) &stack_dummy;
00070     session->run();
00071 
00072     killSessionNow(session);
00073   }
00074   catch (abort_exception& ex)
00075   {
00076     cout << _("Drizzle has receieved an abort event.") << endl;
00077     cout << _("In Function: ") << *::boost::get_error_info<boost::throw_function>(ex) << endl;
00078     cout << _("In File: ") << *::boost::get_error_info<boost::throw_file>(ex) << endl;
00079     cout << _("On Line: ") << *::boost::get_error_info<boost::throw_line>(ex) << endl;
00080 
00081     TransactionServices::sendShutdownEvent(*session.get());
00082   }
00083   // @todo remove hard spin by disconnection the session first from the
00084   // thread.
00085   while (not session.unique()) {}
00086 }
00087 
00088 void MultiThreadScheduler::setStackSize()
00089 {
00090   pthread_attr_t attr;
00091 
00092   (void) pthread_attr_init(&attr);
00093 
00094   /* Get the thread stack size that the OS will use and make sure
00095     that we update our global variable. */
00096   int err= pthread_attr_getstacksize(&attr, &my_thread_stack_size);
00097   pthread_attr_destroy(&attr);
00098 
00099   if (err != 0)
00100   {
00101     errmsg_printf(error::ERROR, _("Unable to get thread stack size"));
00102     my_thread_stack_size= 524288; // At the time of the writing of this code, this was OSX's
00103   }
00104 
00105   if (my_thread_stack_size == 0)
00106   {
00107     my_thread_stack_size= 524288; // At the time of the writing of this code, this was OSX's
00108   }
00109 #ifdef __sun
00110   /*
00111    * Solaris will return zero for the stack size in a call to
00112    * pthread_attr_getstacksize() to indicate that the OS default stack
00113    * size is used. We need an actual value in my_thread_stack_size so that
00114    * check_stack_overrun() will work. The Solaris man page for the
00115    * pthread_attr_getstacksize() function says that 2M is used for 64-bit
00116    * processes. We'll explicitly set it here to make sure that is what
00117    * will be used.
00118    */
00119   if (my_thread_stack_size == 0)
00120   {
00121     my_thread_stack_size= 2 * 1024 * 1024;
00122   }
00123 #endif
00124 }
00125 
00126 bool MultiThreadScheduler::addSession(const Session::shared_ptr& session)
00127 {
00128   if (thread_count >= max_threads)
00129     return true;
00130 
00131   thread_count.increment();
00132   try
00133   {
00134     session->getThread().reset(new boost::thread((boost::bind(&MultiThreadScheduler::runSession, this, session->getSessionId()))));
00135   }
00136   catch (std::exception&)
00137   {
00138     thread_count.decrement();
00139     return true;
00140   }
00141 
00142   if (not session->getThread())
00143   {
00144     thread_count.decrement();
00145     return true;
00146   }
00147 
00148   if (not session->getThread()->joinable())
00149   {
00150     thread_count.decrement();
00151     return true;
00152   }
00153 
00154   return false;
00155 }
00156 
00157 
00158 void MultiThreadScheduler::killSession(Session *session)
00159 {
00160   thread_ptr thread(session->getThread());
00161 
00162   if (thread)
00163   {
00164     thread->interrupt();
00165   }
00166 }
00167 
00168 void MultiThreadScheduler::killSessionNow(const Session::shared_ptr& session)
00169 {
00170   killSession(session.get());
00171 
00172   session->disconnect();
00173 
00174   /* Locks LOCK_thread_count and deletes session */
00175   Session::unlink(session);
00176   thread_count.decrement();
00177 }
00178 
00179 MultiThreadScheduler::~MultiThreadScheduler()
00180 {
00181   boost::mutex::scoped_lock scopedLock(drizzled::session::Cache::mutex());
00182   while (thread_count)
00183   {
00184     COND_thread_count.wait(scopedLock);
00185   }
00186 }
00187 
00188 } // multi_thread namespace
00189 
00190   
00191 static int init(drizzled::module::Context &context)
00192 {
00193   
00194   context.add(new multi_thread::MultiThreadScheduler("multi_thread"));
00195 
00196   return 0;
00197 }
00198 
00199 static void init_options(drizzled::module::option_context &context)
00200 {
00201   context("max-threads",
00202           po::value<max_threads_constraint>(&max_threads)->default_value(2048),
00203           _("Maximum number of user threads available."));
00204 }
00205 
00206 DRIZZLE_DECLARE_PLUGIN
00207 {
00208   DRIZZLE_VERSION_ID,
00209   "multi_thread",
00210   "0.1",
00211   "Brian Aker",
00212   "One Thread Per Session Scheduler",
00213   PLUGIN_LICENSE_GPL,
00214   init, /* Plugin Init */
00215   NULL,   /* depends */
00216   init_options    /* config options */
00217 }
00218 DRIZZLE_DECLARE_PLUGIN_END;