StandardApplicationPool.h

00001 /*
00002  *  Phusion Passenger - http://www.modrails.com/
00003  *  Copyright (C) 2008  Phusion
00004  *
00005  *  Phusion Passenger is a trademark of Hongli Lai & Ninh Bui.
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; version 2 of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #ifndef _PASSENGER_STANDARD_APPLICATION_POOL_H_
00021 #define _PASSENGER_STANDARD_APPLICATION_POOL_H_
00022 
00023 #include <boost/shared_ptr.hpp>
00024 #include <boost/weak_ptr.hpp>
00025 #include <boost/thread.hpp>
00026 #include <boost/bind.hpp>
00027 #include <boost/date_time/microsec_time_clock.hpp>
00028 #include <boost/date_time/posix_time/posix_time.hpp>
00029 
00030 #include <string>
00031 #include <sstream>
00032 #include <map>
00033 #include <list>
00034 
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include <stdio.h>
00038 #include <unistd.h>
00039 #include <ctime>
00040 #include <cerrno>
00041 #ifdef TESTING_APPLICATION_POOL
00042         #include <cstdlib>
00043 #endif
00044 
00045 #include "ApplicationPool.h"
00046 #include "Logging.h"
00047 #include "System.h"
00048 #ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
00049         #include "DummySpawnManager.h"
00050 #else
00051         #include "SpawnManager.h"
00052 #endif
00053 
00054 namespace Passenger {
00055 
00056 using namespace std;
00057 using namespace boost;
00058 
00059 class ApplicationPoolServer;
00060 
00061 /****************************************************************
00062  *
00063  *  See "doc/ApplicationPool algorithm.txt" for a more readable
00064  *  and detailed description of the algorithm implemented here.
00065  *
00066  ****************************************************************/
00067 
00068 /**
00069  * A standard implementation of ApplicationPool for single-process environments.
00070  *
00071  * The environment may or may not be multithreaded - StandardApplicationPool is completely
00072  * thread-safe. Apache with the threading MPM is an example of a multithreaded single-process
00073  * environment.
00074  *
00075  * This class is unusable in multi-process environments such as Apache with the prefork MPM.
00076  * The reasons are as follows:
00077  *  - StandardApplicationPool uses threads internally. Because threads disappear after a fork(),
00078  *    a StandardApplicationPool object will become unusable after a fork().
00079  *  - StandardApplicationPool stores its internal cache on the heap. Different processes
00080  *    cannot share their heaps, so they will not be able to access each others' pool cache.
00081  *  - StandardApplicationPool has a connection to the spawn server. If there are multiple
00082  *    processes, and they all use the spawn servers's connection at the same time without
00083  *    some sort of synchronization, then bad things will happen.
00084  *
00085  * (Of course, StandardApplicationPool <em>is</em> usable if each process creates its own
00086  * StandardApplicationPool object, but that would defeat the point of having a shared pool.)
00087  *
00088  * For multi-process environments, one should use ApplicationPoolServer instead.
00089  *
00090  * @ingroup Support
00091  */
00092 class StandardApplicationPool: public ApplicationPool {
00093 private:
00094         static const int DEFAULT_MAX_IDLE_TIME = 120;
00095         static const int DEFAULT_MAX_POOL_SIZE = 20;
00096         static const int DEFAULT_MAX_INSTANCES_PER_APP = 0;
00097         static const int CLEANER_THREAD_STACK_SIZE = 1024 * 128;
00098         static const unsigned int MAX_GET_ATTEMPTS = 10;
00099         static const unsigned int GET_TIMEOUT = 5000; // In milliseconds.
00100 
00101         friend class ApplicationPoolServer;
00102         struct AppContainer;
00103         
00104         typedef shared_ptr<AppContainer> AppContainerPtr;
00105         typedef list<AppContainerPtr> AppContainerList;
00106         typedef shared_ptr<AppContainerList> AppContainerListPtr;
00107         typedef map<string, AppContainerListPtr> ApplicationMap;
00108         
00109         struct AppContainer {
00110                 ApplicationPtr app;
00111                 time_t lastUsed;
00112                 unsigned int sessions;
00113                 AppContainerList::iterator iterator;
00114                 AppContainerList::iterator ia_iterator;
00115         };
00116         
00117         struct SharedData {
00118                 mutex lock;
00119                 condition activeOrMaxChanged;
00120                 
00121                 ApplicationMap apps;
00122                 unsigned int max;
00123                 unsigned int count;
00124                 unsigned int active;
00125                 unsigned int maxPerApp;
00126                 AppContainerList inactiveApps;
00127                 map<string, time_t> restartFileTimes;
00128                 map<string, unsigned int> appInstanceCount;
00129         };
00130         
00131         typedef shared_ptr<SharedData> SharedDataPtr;
00132         
00133         struct SessionCloseCallback {
00134                 SharedDataPtr data;
00135                 weak_ptr<AppContainer> container;
00136                 
00137                 SessionCloseCallback(SharedDataPtr data,
00138                                      const weak_ptr<AppContainer> &container) {
00139                         this->data = data;
00140                         this->container = container;
00141                 }
00142                 
00143                 void operator()() {
00144                         mutex::scoped_lock l(data->lock);
00145                         AppContainerPtr container(this->container.lock());
00146                         
00147                         if (container == NULL) {
00148                                 return;
00149                         }
00150                         
00151                         ApplicationMap::iterator it;
00152                         it = data->apps.find(container->app->getAppRoot());
00153                         if (it != data->apps.end()) {
00154                                 AppContainerListPtr list(it->second);
00155                                 container->lastUsed = time(NULL);
00156                                 container->sessions--;
00157                                 if (container->sessions == 0) {
00158                                         list->erase(container->iterator);
00159                                         list->push_front(container);
00160                                         container->iterator = list->begin();
00161                                         data->inactiveApps.push_back(container);
00162                                         container->ia_iterator = data->inactiveApps.end();
00163                                         container->ia_iterator--;
00164                                         data->active--;
00165                                         data->activeOrMaxChanged.notify_all();
00166                                 }
00167                         }
00168                 }
00169         };
00170 
00171         #ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
00172                 DummySpawnManager spawnManager;
00173         #else
00174                 SpawnManager spawnManager;
00175         #endif
00176         SharedDataPtr data;
00177         thread *cleanerThread;
00178         bool detached;
00179         bool done;
00180         unsigned int maxIdleTime;
00181         condition cleanerThreadSleeper;
00182         
00183         // Shortcuts for instance variables in SharedData. Saves typing in get().
00184         mutex &lock;
00185         condition &activeOrMaxChanged;
00186         ApplicationMap &apps;
00187         unsigned int &max;
00188         unsigned int &count;
00189         unsigned int &active;
00190         unsigned int &maxPerApp;
00191         AppContainerList &inactiveApps;
00192         map<string, time_t> &restartFileTimes;
00193         map<string, unsigned int> &appInstanceCount;
00194         
00195         /**
00196          * Verify that all the invariants are correct.
00197          */
00198         bool inline verifyState() {
00199         #if PASSENGER_DEBUG
00200                 // Invariant for _apps_.
00201                 ApplicationMap::const_iterator it;
00202                 for (it = apps.begin(); it != apps.end(); it++) {
00203                         AppContainerList *list = it->second.get();
00204                         P_ASSERT(!list->empty(), false, "List for '" << it->first << "' is nonempty.");
00205                         
00206                         AppContainerList::const_iterator prev_lit;
00207                         AppContainerList::const_iterator lit;
00208                         prev_lit = list->begin();
00209                         lit = prev_lit;
00210                         lit++;
00211                         for (; lit != list->end(); lit++) {
00212                                 if ((*prev_lit)->sessions > 0) {
00213                                         P_ASSERT((*lit)->sessions > 0, false,
00214                                                 "List for '" << it->first <<
00215                                                 "' is sorted from nonactive to active");
00216                                 }
00217                         }
00218                 }
00219                 
00220                 P_ASSERT(active <= count, false,
00221                         "active (" << active << ") < count (" << count << ")");
00222                 P_ASSERT(inactiveApps.size() == count - active, false,
00223                         "inactive_apps.size() == count - active");
00224         #endif
00225                 return true;
00226         }
00227         
00228         template<typename LockActionType>
00229         string toString(LockActionType lockAction) const {
00230                 unique_lock<mutex> l(lock, lockAction);
00231                 stringstream result;
00232                 
00233                 result << "----------- General information -----------" << endl;
00234                 result << "max      = " << max << endl;
00235                 result << "count    = " << count << endl;
00236                 result << "active   = " << active << endl;
00237                 result << "inactive = " << inactiveApps.size() << endl;
00238                 result << endl;
00239                 
00240                 result << "----------- Applications -----------" << endl;
00241                 ApplicationMap::const_iterator it;
00242                 for (it = apps.begin(); it != apps.end(); it++) {
00243                         AppContainerList *list = it->second.get();
00244                         AppContainerList::const_iterator lit;
00245                         
00246                         result << it->first << ": " << endl;
00247                         for (lit = list->begin(); lit != list->end(); lit++) {
00248                                 AppContainer *container = lit->get();
00249                                 char buf[128];
00250                                 
00251                                 snprintf(buf, sizeof(buf), "PID: %-8d  Sessions: %d",
00252                                         container->app->getPid(), container->sessions);
00253                                 result << "  " << buf << endl;
00254                         }
00255                         result << endl;
00256                 }
00257                 return result.str();
00258         }
00259         
00260         bool needsRestart(const string &appRoot) {
00261                 string restartFile(appRoot);
00262                 restartFile.append("/tmp/restart.txt");
00263                 
00264                 struct stat buf;
00265                 bool result;
00266                 int ret;
00267                 
00268                 do {
00269                         ret = stat(restartFile.c_str(), &buf);
00270                 } while (ret == -1 && errno == EINTR);
00271                 if (ret == 0) {
00272                         do {
00273                                 ret = unlink(restartFile.c_str());
00274                         } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
00275                         if (ret == 0 || errno == ENOENT) {
00276                                 restartFileTimes.erase(appRoot);
00277                                 result = true;
00278                         } else {
00279                                 map<string, time_t>::const_iterator it;
00280                                 
00281                                 it = restartFileTimes.find(appRoot);
00282                                 if (it == restartFileTimes.end()) {
00283                                         result = true;
00284                                 } else {
00285                                         result = buf.st_mtime != restartFileTimes[appRoot];
00286                                 }
00287                                 restartFileTimes[appRoot] = buf.st_mtime;
00288                         }
00289                 } else {
00290                         restartFileTimes.erase(appRoot);
00291                         result = false;
00292                 }
00293                 return result;
00294         }
00295         
00296         void cleanerThreadMainLoop() {
00297                 this_thread::disable_syscall_interruption dsi;
00298                 unique_lock<mutex> l(lock);
00299                 try {
00300                         while (!done && !this_thread::interruption_requested()) {
00301                                 xtime xt;
00302                                 xtime_get(&xt, TIME_UTC);
00303                                 xt.sec += maxIdleTime + 1;
00304                                 if (cleanerThreadSleeper.timed_wait(l, xt)) {
00305                                         // Condition was woken up.
00306                                         if (done) {
00307                                                 // StandardApplicationPool is being destroyed.
00308                                                 break;
00309                                         } else {
00310                                                 // maxIdleTime changed.
00311                                                 continue;
00312                                         }
00313                                 }
00314                                 
00315                                 time_t now = InterruptableCalls::time(NULL);
00316                                 AppContainerList::iterator it;
00317                                 for (it = inactiveApps.begin(); it != inactiveApps.end(); it++) {
00318                                         AppContainer &container(*it->get());
00319                                         ApplicationPtr app(container.app);
00320                                         AppContainerListPtr appList(apps[app->getAppRoot()]);
00321                                         
00322                                         if (now - container.lastUsed > (time_t) maxIdleTime) {
00323                                                 P_DEBUG("Cleaning idle app " << app->getAppRoot() <<
00324                                                         " (PID " << app->getPid() << ")");
00325                                                 appList->erase(container.iterator);
00326                                                 
00327                                                 AppContainerList::iterator prev = it;
00328                                                 prev--;
00329                                                 inactiveApps.erase(it);
00330                                                 it = prev;
00331                                                 
00332                                                 appInstanceCount[app->getAppRoot()]--;
00333                                                 
00334                                                 count--;
00335                                         }
00336                                         if (appList->empty()) {
00337                                                 apps.erase(app->getAppRoot());
00338                                                 appInstanceCount.erase(app->getAppRoot());
00339                                                 data->restartFileTimes.erase(app->getAppRoot());
00340                                         }
00341                                 }
00342                         }
00343                 } catch (const exception &e) {
00344                         P_ERROR("Uncaught exception: " << e.what());
00345                 }
00346         }
00347         
00348         /**
00349          * @throws boost::thread_interrupted
00350          * @throws SpawnException
00351          * @throws SystemException
00352          */
00353         pair<AppContainerPtr, AppContainerList *>
00354         spawnOrUseExisting(
00355                 mutex::scoped_lock &l,
00356                 const string &appRoot,
00357                 bool lowerPrivilege,
00358                 const string &lowestUser,
00359                 const string &environment,
00360                 const string &spawnMethod,
00361                 const string &appType
00362         ) {
00363                 this_thread::disable_interruption di;
00364                 this_thread::disable_syscall_interruption dsi;
00365                 AppContainerPtr container;
00366                 AppContainerList *list;
00367                 
00368                 try {
00369                         ApplicationMap::iterator it(apps.find(appRoot));
00370                         
00371                         if (it != apps.end() && needsRestart(appRoot)) {
00372                                 AppContainerList::iterator it2;
00373                                 list = it->second.get();
00374                                 for (it2 = list->begin(); it2 != list->end(); it2++) {
00375                                         container = *it2;
00376                                         if (container->sessions == 0) {
00377                                                 inactiveApps.erase(container->ia_iterator);
00378                                         } else {
00379                                                 active--;
00380                                         }
00381                                         it2--;
00382                                         list->erase(container->iterator);
00383                                         count--;
00384                                 }
00385                                 apps.erase(appRoot);
00386                                 appInstanceCount.erase(appRoot);
00387                                 spawnManager.reload(appRoot);
00388                                 it = apps.end();
00389                                 activeOrMaxChanged.notify_all();
00390                         }
00391                         
00392                         if (it != apps.end()) {
00393                                 list = it->second.get();
00394                                 
00395                                 if (list->front()->sessions == 0) {
00396                                         container = list->front();
00397                                         list->pop_front();
00398                                         list->push_back(container);
00399                                         container->iterator = list->end();
00400                                         container->iterator--;
00401                                         inactiveApps.erase(container->ia_iterator);
00402                                         active++;
00403                                         activeOrMaxChanged.notify_all();
00404                                 } else if (count >= max || (
00405                                         maxPerApp != 0 && appInstanceCount[appRoot] >= maxPerApp )
00406                                         ) {
00407                                         AppContainerList::iterator it(list->begin());
00408                                         AppContainerList::iterator smallest(list->begin());
00409                                         it++;
00410                                         for (; it != list->end(); it++) {
00411                                                 if ((*it)->sessions < (*smallest)->sessions) {
00412                                                         smallest = it;
00413                                                 }
00414                                         }
00415                                         container = *smallest;
00416                                         list->erase(smallest);
00417                                         list->push_back(container);
00418                                         container->iterator = list->end();
00419                                         container->iterator--;
00420                                 } else {
00421                                         container = ptr(new AppContainer());
00422                                         {
00423                                                 this_thread::restore_interruption ri(di);
00424                                                 this_thread::restore_syscall_interruption rsi(dsi);
00425                                                 container->app = spawnManager.spawn(appRoot,
00426                                                         lowerPrivilege, lowestUser, environment,
00427                                                         spawnMethod, appType);
00428                                         }
00429                                         container->sessions = 0;
00430                                         list->push_back(container);
00431                                         container->iterator = list->end();
00432                                         container->iterator--;
00433                                         appInstanceCount[appRoot]++;
00434                                         count++;
00435                                         active++;
00436                                         activeOrMaxChanged.notify_all();
00437                                 }
00438                         } else {
00439                                 while (!(
00440                                         active < max &&
00441                                         (maxPerApp == 0 || appInstanceCount[appRoot] < maxPerApp)
00442                                 )) {
00443                                         activeOrMaxChanged.wait(l);
00444                                 }
00445                                 if (count == max) {
00446                                         container = inactiveApps.front();
00447                                         inactiveApps.pop_front();
00448                                         list = apps[container->app->getAppRoot()].get();
00449                                         list->erase(container->iterator);
00450                                         if (list->empty()) {
00451                                                 apps.erase(container->app->getAppRoot());
00452                                                 restartFileTimes.erase(container->app->getAppRoot());
00453                                                 appInstanceCount.erase(container->app->getAppRoot());
00454                                         } else {
00455                                                 appInstanceCount[container->app->getAppRoot()]--;
00456                                         }
00457                                         count--;
00458                                 }
00459                                 container = ptr(new AppContainer());
00460                                 {
00461                                         this_thread::restore_interruption ri(di);
00462                                         this_thread::restore_syscall_interruption rsi(dsi);
00463                                         container->app = spawnManager.spawn(appRoot, lowerPrivilege, lowestUser,
00464                                                 environment, spawnMethod, appType);
00465                                 }
00466                                 container->sessions = 0;
00467                                 it = apps.find(appRoot);
00468                                 if (it == apps.end()) {
00469                                         list = new AppContainerList();
00470                                         apps[appRoot] = ptr(list);
00471                                         appInstanceCount[appRoot] = 1;
00472                                 } else {
00473                                         list = it->second.get();
00474                                         appInstanceCount[appRoot]++;
00475                                 }
00476                                 list->push_back(container);
00477                                 container->iterator = list->end();
00478                                 container->iterator--;
00479                                 count++;
00480                                 active++;
00481                                 activeOrMaxChanged.notify_all();
00482                         }
00483                 } catch (const SpawnException &e) {
00484                         string message("Cannot spawn application '");
00485                         message.append(appRoot);
00486                         message.append("': ");
00487                         message.append(e.what());
00488                         if (e.hasErrorPage()) {
00489                                 throw SpawnException(message, e.getErrorPage());
00490                         } else {
00491                                 throw SpawnException(message);
00492                         }
00493                 } catch (const exception &e) {
00494                         string message("Cannot spawn application '");
00495                         message.append(appRoot);
00496                         message.append("': ");
00497                         message.append(e.what());
00498                         throw SpawnException(message);
00499                 }
00500                 
00501                 return make_pair(container, list);
00502         }
00503         
00504 public:
00505         /**
00506          * Create a new StandardApplicationPool object.
00507          *
00508          * @param spawnServerCommand The filename of the spawn server to use.
00509          * @param logFile Specify a log file that the spawn server should use.
00510          *            Messages on its standard output and standard error channels
00511          *            will be written to this log file. If an empty string is
00512          *            specified, no log file will be used, and the spawn server
00513          *            will use the same standard output/error channels as the
00514          *            current process.
00515          * @param rubyCommand The Ruby interpreter's command.
00516          * @param user The user that the spawn manager should run as. This
00517          *             parameter only has effect if the current process is
00518          *             running as root. If the empty string is given, or if
00519          *             the <tt>user</tt> is not a valid username, then
00520          *             the spawn manager will be run as the current user.
00521          * @param rubyCommand The Ruby interpreter's command.
00522          * @throws SystemException An error occured while trying to setup the spawn server.
00523          * @throws IOException The specified log file could not be opened.
00524          */
00525         StandardApplicationPool(const string &spawnServerCommand,
00526                      const string &logFile = "",
00527                      const string &rubyCommand = "ruby",
00528                      const string &user = "")
00529                 :
00530                 #ifndef PASSENGER_USE_DUMMY_SPAWN_MANAGER
00531                 spawnManager(spawnServerCommand, logFile, rubyCommand, user),
00532                 #endif
00533                 data(new SharedData()),
00534                 lock(data->lock),
00535                 activeOrMaxChanged(data->activeOrMaxChanged),
00536                 apps(data->apps),
00537                 max(data->max),
00538                 count(data->count),
00539                 active(data->active),
00540                 maxPerApp(data->maxPerApp),
00541                 inactiveApps(data->inactiveApps),
00542                 restartFileTimes(data->restartFileTimes),
00543                 appInstanceCount(data->appInstanceCount)
00544         {
00545                 detached = false;
00546                 done = false;
00547                 max = DEFAULT_MAX_POOL_SIZE;
00548                 count = 0;
00549                 active = 0;
00550                 maxPerApp = DEFAULT_MAX_INSTANCES_PER_APP;
00551                 maxIdleTime = DEFAULT_MAX_IDLE_TIME;
00552                 cleanerThread = new thread(
00553                         bind(&StandardApplicationPool::cleanerThreadMainLoop, this),
00554                         CLEANER_THREAD_STACK_SIZE
00555                 );
00556         }
00557         
00558         virtual ~StandardApplicationPool() {
00559                 if (!detached) {
00560                         this_thread::disable_interruption di;
00561                         {
00562                                 mutex::scoped_lock l(lock);
00563                                 done = true;
00564                                 cleanerThreadSleeper.notify_one();
00565                         }
00566                         cleanerThread->join();
00567                 }
00568                 delete cleanerThread;
00569         }
00570         
00571         virtual Application::SessionPtr get(
00572                 const string &appRoot,
00573                 bool lowerPrivilege = true,
00574                 const string &lowestUser = "nobody",
00575                 const string &environment = "production",
00576                 const string &spawnMethod = "smart",
00577                 const string &appType = "rails"
00578         ) {
00579                 using namespace boost::posix_time;
00580                 unsigned int attempt = 0;
00581                 ptime timeLimit(get_system_time() + millisec(GET_TIMEOUT));
00582                 unique_lock<mutex> l(lock);
00583                 
00584                 while (true) {
00585                         attempt++;
00586                         
00587                         pair<AppContainerPtr, AppContainerList *> p(
00588                                 spawnOrUseExisting(l, appRoot, lowerPrivilege, lowestUser,
00589                                         environment, spawnMethod, appType)
00590                         );
00591                         AppContainerPtr &container(p.first);
00592                         AppContainerList &list(*p.second);
00593 
00594                         container->lastUsed = time(NULL);
00595                         container->sessions++;
00596                         
00597                         P_ASSERT(verifyState(), Application::SessionPtr(),
00598                                 "State is valid:\n" << toString(false));
00599                         try {
00600                                 return container->app->connect(SessionCloseCallback(data, container));
00601                         } catch (const exception &e) {
00602                                 container->sessions--;
00603                                 if (attempt == MAX_GET_ATTEMPTS) {
00604                                         string message("Cannot connect to an existing "
00605                                                 "application instance for '");
00606                                         message.append(appRoot);
00607                                         message.append("': ");
00608                                         try {
00609                                                 const SystemException &syse =
00610                                                         dynamic_cast<const SystemException &>(e);
00611                                                 message.append(syse.sys());
00612                                         } catch (const bad_cast &) {
00613                                                 message.append(e.what());
00614                                         }
00615                                         throw IOException(message);
00616                                 } else {
00617                                         list.erase(container->iterator);
00618                                         if (list.empty()) {
00619                                                 apps.erase(appRoot);
00620                                                 appInstanceCount.erase(appRoot);
00621                                         }
00622                                         count--;
00623                                         active--;
00624                                         activeOrMaxChanged.notify_all();
00625                                         P_ASSERT(verifyState(), Application::SessionPtr(),
00626                                                 "State is valid.");
00627                                 }
00628                         }
00629                 }
00630                 // Never reached; shut up compiler warning
00631                 return Application::SessionPtr();
00632         }
00633         
00634         virtual void clear() {
00635                 mutex::scoped_lock l(lock);
00636                 apps.clear();
00637                 inactiveApps.clear();
00638                 restartFileTimes.clear();
00639                 appInstanceCount.clear();
00640                 count = 0;
00641                 active = 0;
00642         }
00643         
00644         virtual void setMaxIdleTime(unsigned int seconds) {
00645                 mutex::scoped_lock l(lock);
00646                 maxIdleTime = seconds;
00647                 cleanerThreadSleeper.notify_one();
00648         }
00649         
00650         virtual void setMax(unsigned int max) {
00651                 mutex::scoped_lock l(lock);
00652                 this->max = max;
00653                 activeOrMaxChanged.notify_all();
00654         }
00655         
00656         virtual unsigned int getActive() const {
00657                 return active;
00658         }
00659         
00660         virtual unsigned int getCount() const {
00661                 return count;
00662         }
00663         
00664         virtual void setMaxPerApp(unsigned int maxPerApp) {
00665                 mutex::scoped_lock l(lock);
00666                 this->maxPerApp = maxPerApp;
00667                 activeOrMaxChanged.notify_all();
00668         }
00669         
00670         virtual pid_t getSpawnServerPid() const {
00671                 return spawnManager.getServerPid();
00672         }
00673         
00674         /**
00675          * Returns a textual description of the internal state of
00676          * the application pool.
00677          */
00678         virtual string toString(bool lockMutex = true) const {
00679                 if (lockMutex) {
00680                         return toString(boost::adopt_lock);
00681                 } else {
00682                         return toString(boost::defer_lock);
00683                 }
00684         }
00685 };
00686 
00687 } // namespace Passenger
00688 
00689 #endif /* _PASSENGER_STANDARD_APPLICATION_POOL_H_ */
00690 

Generated on Mon Jun 9 13:34:31 2008 for Passenger by  doxygen 1.5.3