passenger 4.0.0.rc4 → 4.0.0.rc6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- data.tar.gz.asc +12 -0
- data/.travis.yml +4 -4
- data/NEWS +46 -0
- data/bin/passenger-config +31 -1
- data/bin/passenger-install-apache2-module +1 -1
- data/bin/passenger-install-nginx-module +1 -0
- data/build/common_library.rb +4 -0
- data/build/cplusplus_support.rb +27 -6
- data/build/cxx_tests.rb +1 -1
- data/build/misc.rb +28 -6
- data/build/packaging.rb +72 -65
- data/build/test_basics.rb +1 -1
- data/dev/googlecode_upload.py +265 -0
- data/dev/run_travis.sh +9 -0
- data/doc/Users guide Apache.html +376 -193
- data/doc/Users guide Apache.idmap.txt +80 -62
- data/doc/Users guide Apache.txt +61 -35
- data/doc/Users guide Nginx.html +278 -83
- data/doc/Users guide Nginx.idmap.txt +26 -10
- data/doc/Users guide Nginx.txt +59 -31
- data/doc/Users guide Standalone.html +1 -1
- data/doc/users_guide_snippets/installation.txt +121 -11
- data/doc/users_guide_snippets/rvm_helper_tool.txt +56 -0
- data/ext/apache2/Bucket.cpp +1 -1
- data/ext/apache2/Configuration.cpp +7 -1
- data/ext/apache2/Configuration.hpp +4 -0
- data/ext/apache2/Hooks.cpp +2 -2
- data/ext/common/AgentsStarter.cpp +2 -2
- data/ext/common/AgentsStarter.h +1 -1
- data/ext/common/AgentsStarter.hpp +2 -2
- data/ext/common/ApplicationPool2/DirectSpawner.h +4 -8
- data/ext/common/ApplicationPool2/Group.h +17 -11
- data/ext/common/ApplicationPool2/Implementation.cpp +39 -11
- data/ext/common/ApplicationPool2/Pool.h +23 -4
- data/ext/common/ApplicationPool2/Process.h +30 -11
- data/ext/common/ApplicationPool2/SmartSpawner.h +3 -1
- data/ext/common/Constants.h +1 -1
- data/ext/common/EventedBufferedInput.h +4 -0
- data/ext/common/Utils.cpp +21 -3
- data/ext/common/Utils.h +8 -1
- data/ext/common/Utils/HttpHeaderBufferer.h +1 -1
- data/ext/common/Utils/IOUtils.cpp +5 -4
- data/ext/common/Utils/IOUtils.h +32 -14
- data/ext/common/Utils/MessagePassing.h +2 -2
- data/ext/common/Utils/ProcessMetricsCollector.h +47 -15
- data/ext/common/Utils/ScopeGuard.h +20 -3
- data/ext/common/Utils/StrIntUtils.h +14 -5
- data/ext/common/agents/Base.cpp +161 -50
- data/ext/common/agents/HelperAgent/AgentOptions.h +2 -2
- data/ext/common/agents/HelperAgent/Main.cpp +1 -0
- data/ext/common/agents/HelperAgent/RequestHandler.h +166 -52
- data/ext/common/agents/LoggingAgent/Main.cpp +1 -1
- data/ext/common/agents/Watchdog/Main.cpp +2 -2
- data/ext/nginx/Configuration.c +31 -4
- data/ext/nginx/Configuration.h +1 -0
- data/ext/nginx/ContentHandler.c +148 -34
- data/ext/nginx/ngx_http_passenger_module.c +4 -1
- data/ext/oxt/detail/spin_lock_pthreads.hpp +4 -4
- data/ext/oxt/macros.hpp +30 -8
- data/lib/phusion_passenger.rb +2 -2
- data/lib/phusion_passenger/classic_rails/thread_handler_extension.rb +1 -1
- data/lib/phusion_passenger/native_support.rb +19 -1
- data/lib/phusion_passenger/platform_info/compiler.rb +6 -0
- data/lib/phusion_passenger/platform_info/ruby.rb +54 -5
- data/lib/phusion_passenger/preloader_shared_helpers.rb +8 -1
- data/lib/phusion_passenger/rack/out_of_band_gc.rb +3 -1
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +32 -5
- data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -8
- data/lib/phusion_passenger/ruby_core_enhancements.rb +9 -1
- data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -0
- data/lib/phusion_passenger/utils/unseekable_socket.rb +50 -5
- data/passenger.gemspec +1 -1
- data/resources/templates/apache2/config_snippets.txt.erb +1 -1
- data/test/cxx/ApplicationPool2/PoolTest.cpp +4 -9
- data/test/cxx/RequestHandlerTest.cpp +5 -5
- data/test/ruby/classic_rails/loader_spec.rb +1 -1
- data/test/ruby/classic_rails/preloader_spec.rb +1 -1
- data/test/ruby/request_handler_spec.rb +207 -1
- data/test/ruby/shared/loader_sharedspec.rb +1 -0
- data/test/ruby/spec_helper.rb +11 -1
- data/test/stub/apache2/httpd.conf.erb +1 -1
- metadata +5 -3
- metadata.gz.asc +12 -0
@@ -337,6 +337,9 @@ struct DirConfig {
|
|
337
337
|
struct ServerConfig {
|
338
338
|
/** The Passenger root folder. */
|
339
339
|
const char *root;
|
340
|
+
|
341
|
+
/** The default Ruby interpreter to use. */
|
342
|
+
const char *defaultRuby;
|
340
343
|
|
341
344
|
/** The log verbosity. */
|
342
345
|
int logLevel;
|
@@ -380,6 +383,7 @@ struct ServerConfig {
|
|
380
383
|
|
381
384
|
ServerConfig() {
|
382
385
|
root = NULL;
|
386
|
+
defaultRuby = DEFAULT_RUBY;
|
383
387
|
logLevel = DEFAULT_LOG_LEVEL;
|
384
388
|
debugLogFile = NULL;
|
385
389
|
maxPoolSize = DEFAULT_MAX_POOL_SIZE;
|
data/ext/apache2/Hooks.cpp
CHANGED
@@ -955,7 +955,7 @@ private:
|
|
955
955
|
addHeader(output, "PASSENGER_STATUS_LINE", "false");
|
956
956
|
addHeader(output, "PASSENGER_APP_ROOT", appRoot);
|
957
957
|
addHeader(output, "PASSENGER_APP_GROUP_NAME", config->getAppGroupName(appRoot));
|
958
|
-
addHeader(output, "PASSENGER_RUBY", config->ruby);
|
958
|
+
addHeader(output, "PASSENGER_RUBY", config->ruby ? config->ruby : serverConfig.defaultRuby);
|
959
959
|
addHeader(output, "PASSENGER_PYTHON", config->python);
|
960
960
|
addHeader(output, "PASSENGER_ENV", config->getEnvironment());
|
961
961
|
addHeader(output, "PASSENGER_SPAWN_METHOD", config->getSpawnMethodString());
|
@@ -1265,7 +1265,7 @@ public:
|
|
1265
1265
|
serverConfig.userSwitching,
|
1266
1266
|
serverConfig.defaultUser, serverConfig.defaultGroup,
|
1267
1267
|
unixd_config.user_id, unixd_config.group_id,
|
1268
|
-
serverConfig.root,
|
1268
|
+
serverConfig.root, serverConfig.defaultRuby, serverConfig.maxPoolSize,
|
1269
1269
|
serverConfig.maxInstancesPerApp, serverConfig.poolIdleTime,
|
1270
1270
|
"",
|
1271
1271
|
serverConfig.analyticsLogUser,
|
@@ -52,7 +52,7 @@ agents_starter_start(AgentsStarter *as,
|
|
52
52
|
const char *defaultUser, const char *defaultGroup,
|
53
53
|
uid_t webServerWorkerUid, gid_t webServerWorkerGid,
|
54
54
|
const char *passengerRoot,
|
55
|
-
const char *
|
55
|
+
const char *defaultRubyCommand, unsigned int maxPoolSize,
|
56
56
|
unsigned int maxInstancesPerApp,
|
57
57
|
unsigned int poolIdleTime,
|
58
58
|
const char *analyticsServer,
|
@@ -84,7 +84,7 @@ agents_starter_start(AgentsStarter *as,
|
|
84
84
|
webServerPid, tempDir, userSwitching,
|
85
85
|
defaultUser, defaultGroup,
|
86
86
|
webServerWorkerUid, webServerWorkerGid,
|
87
|
-
passengerRoot,
|
87
|
+
passengerRoot, defaultRubyCommand,
|
88
88
|
maxPoolSize, maxInstancesPerApp, poolIdleTime,
|
89
89
|
analyticsServer,
|
90
90
|
analyticsLogUser, analyticsLogGroup,
|
data/ext/common/AgentsStarter.h
CHANGED
@@ -52,7 +52,7 @@ int agents_starter_start(AgentsStarter *as,
|
|
52
52
|
const char *defaultUser, const char *defaultGroup,
|
53
53
|
uid_t webServerWorkerUid, gid_t webServerWorkerGid,
|
54
54
|
const char *passengerRoot,
|
55
|
-
const char *
|
55
|
+
const char *defaultRubyCommand, unsigned int maxPoolSize,
|
56
56
|
unsigned int maxInstancesPerApp,
|
57
57
|
unsigned int poolIdleTime,
|
58
58
|
const char *analyticsServer,
|
@@ -375,7 +375,7 @@ public:
|
|
375
375
|
pid_t webServerPid, const string &tempDir,
|
376
376
|
bool userSwitching, const string &defaultUser, const string &defaultGroup,
|
377
377
|
uid_t webServerWorkerUid, gid_t webServerWorkerGid,
|
378
|
-
const string &passengerRoot, const string &
|
378
|
+
const string &passengerRoot, const string &defaultRubyCommand,
|
379
379
|
unsigned int maxPoolSize, unsigned int maxInstancesPerApp,
|
380
380
|
unsigned int poolIdleTime,
|
381
381
|
const string &analyticsServer,
|
@@ -414,7 +414,7 @@ public:
|
|
414
414
|
.setUid ("web_server_worker_uid", webServerWorkerUid)
|
415
415
|
.setGid ("web_server_worker_gid", webServerWorkerGid)
|
416
416
|
.set ("passenger_root", passengerRoot)
|
417
|
-
.set ("
|
417
|
+
.set ("default_ruby", defaultRubyCommand)
|
418
418
|
.setInt ("max_pool_size", maxPoolSize)
|
419
419
|
.setInt ("max_instances_per_app", maxInstancesPerApp)
|
420
420
|
.setInt ("pool_idle_time", poolIdleTime)
|
@@ -111,7 +111,6 @@ private:
|
|
111
111
|
|
112
112
|
vector<string> createCommand(const Options &options, shared_array<const char *> &args) const {
|
113
113
|
vector<string> startCommandArgs;
|
114
|
-
string processTitle;
|
115
114
|
string agentsDir = resourceLocator.getAgentsDir();
|
116
115
|
vector<string> command;
|
117
116
|
|
@@ -119,12 +118,7 @@ private:
|
|
119
118
|
if (startCommandArgs.empty()) {
|
120
119
|
throw RuntimeException("No startCommand given");
|
121
120
|
}
|
122
|
-
|
123
|
-
processTitle = startCommandArgs[0];
|
124
|
-
} else {
|
125
|
-
processTitle = options.getProcessTitle() + ": " + options.appRoot;
|
126
|
-
}
|
127
|
-
|
121
|
+
|
128
122
|
if (options.loadShellEnvvars) {
|
129
123
|
command.push_back("bash");
|
130
124
|
command.push_back("bash");
|
@@ -137,7 +131,9 @@ private:
|
|
137
131
|
command.push_back(agentsDir + "/SpawnPreparer");
|
138
132
|
command.push_back(serializeEnvvarsFromPoolOptions(options));
|
139
133
|
command.push_back(startCommandArgs[0]);
|
140
|
-
|
134
|
+
// Note: do not try to set a process title here.
|
135
|
+
// https://code.google.com/p/phusion-passenger/issues/detail?id=855
|
136
|
+
command.push_back(startCommandArgs[0]);
|
141
137
|
for (unsigned int i = 1; i < startCommandArgs.size(); i++) {
|
142
138
|
command.push_back(startCommandArgs[i]);
|
143
139
|
}
|
@@ -181,6 +181,7 @@ private:
|
|
181
181
|
vector<Callback> postLockActions);
|
182
182
|
void startCheckingDetachedProcesses(bool immediately);
|
183
183
|
void detachedProcessesCheckerMain(GroupPtr self);
|
184
|
+
void wakeUpGarbageCollector();
|
184
185
|
bool poolAtFullCapacity() const;
|
185
186
|
bool anotherGroupIsWaitingForCapacity() const;
|
186
187
|
|
@@ -197,8 +198,8 @@ private:
|
|
197
198
|
assert((lifeStatus == ALIVE) == (spawner != NULL));
|
198
199
|
|
199
200
|
// Verify getWaitlist invariants.
|
200
|
-
assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || pqueue.top()->
|
201
|
-
assert(!( !enabledProcesses.empty() && !pqueue.top()->
|
201
|
+
assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || pqueue.top()->atFullUtilization() ));
|
202
|
+
assert(!( !enabledProcesses.empty() && !pqueue.top()->atFullUtilization() ) || ( getWaitlist.empty() ));
|
202
203
|
assert(!( enabledProcesses.empty() && !spawning() && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
|
203
204
|
assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || spawning() || restarting() || poolAtFullCapacity() ));
|
204
205
|
|
@@ -378,7 +379,7 @@ private:
|
|
378
379
|
// Checkout sessions from enabled processes, or if there are none,
|
379
380
|
// from disabling processes.
|
380
381
|
if (enabledCount > 0) {
|
381
|
-
while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->
|
382
|
+
while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->atFullUtilization()) {
|
382
383
|
GetAction action;
|
383
384
|
action.callback = getWaitlist.front().callback;
|
384
385
|
action.session = newSession();
|
@@ -413,7 +414,7 @@ private:
|
|
413
414
|
|
414
415
|
void assignSessionsToGetWaiters(vector<Callback> &postLockActions) {
|
415
416
|
if (enabledCount > 0) {
|
416
|
-
while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->
|
417
|
+
while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->atFullUtilization()) {
|
417
418
|
postLockActions.push_back(boost::bind(
|
418
419
|
getWaitlist.front().callback, newSession(),
|
419
420
|
ExceptionPtr()));
|
@@ -438,6 +439,7 @@ private:
|
|
438
439
|
}
|
439
440
|
|
440
441
|
void enableAllDisablingProcesses(vector<Callback> &postLockActions) {
|
442
|
+
P_DEBUG("Enabling all DISABLING processes with result DR_ERROR");
|
441
443
|
deque<DisableWaiter>::iterator it, end = disableWaitlist.end();
|
442
444
|
for (it = disableWaitlist.begin(); it != end; it++) {
|
443
445
|
const DisableWaiter &waiter = *it;
|
@@ -448,6 +450,7 @@ private:
|
|
448
450
|
if (process->enabled == Process::DISABLING) {
|
449
451
|
removeProcessFromList(process, disablingProcesses);
|
450
452
|
addProcessToList(process, enabledProcesses);
|
453
|
+
P_DEBUG("Enabled process " << process->inspect());
|
451
454
|
}
|
452
455
|
}
|
453
456
|
clearDisableWaitlist(DR_ERROR, postLockActions);
|
@@ -571,8 +574,8 @@ public:
|
|
571
574
|
* if !spawning():
|
572
575
|
* (enabledCount > 0) or (disablingCount == 0)
|
573
576
|
*
|
574
|
-
* if pqueue.top().
|
575
|
-
* All enabled processes are at full
|
577
|
+
* if pqueue.top().atFullUtilization():
|
578
|
+
* All enabled processes are at full utilization.
|
576
579
|
*
|
577
580
|
* for all process in enabledProcesses:
|
578
581
|
* process.enabled == Process::ENABLED
|
@@ -735,8 +738,8 @@ public:
|
|
735
738
|
} else {
|
736
739
|
Process *process = pqueue.top();
|
737
740
|
assert(process != NULL);
|
738
|
-
if (process->
|
739
|
-
/* Looks like all processes are at full
|
741
|
+
if (process->atFullUtilization()) {
|
742
|
+
/* Looks like all processes are at full utilization.
|
740
743
|
* Wait until a new one has been spawned or until
|
741
744
|
* resources have become free.
|
742
745
|
*/
|
@@ -827,6 +830,9 @@ public:
|
|
827
830
|
}
|
828
831
|
}
|
829
832
|
disableWaitlist = newDisableWaitlist;
|
833
|
+
|
834
|
+
// Update GC sleep timer.
|
835
|
+
wakeUpGarbageCollector();
|
830
836
|
}
|
831
837
|
|
832
838
|
/**
|
@@ -1080,13 +1086,13 @@ public:
|
|
1080
1086
|
}
|
1081
1087
|
switch (lifeStatus) {
|
1082
1088
|
case ALIVE:
|
1083
|
-
stream << "<life_status>
|
1089
|
+
stream << "<life_status>ALIVE</life_status>";
|
1084
1090
|
break;
|
1085
1091
|
case SHUTTING_DOWN:
|
1086
|
-
stream << "<life_status>
|
1092
|
+
stream << "<life_status>SHUTTING_DOWN</life_status>";
|
1087
1093
|
break;
|
1088
1094
|
case SHUT_DOWN:
|
1089
|
-
stream << "<life_status>
|
1095
|
+
stream << "<life_status>SHUT_DOWN</life_status>";
|
1090
1096
|
break;
|
1091
1097
|
default:
|
1092
1098
|
P_BUG("Unknown 'lifeStatus' state " << (int) lifeStatus);
|
@@ -483,8 +483,8 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
|
|
483
483
|
* become available then call them now.
|
484
484
|
*/
|
485
485
|
UPDATE_TRACE_POINT();
|
486
|
+
// Already calls verifyInvariants().
|
486
487
|
assignSessionsToGetWaitersQuickly(lock);
|
487
|
-
verifyInvariants();
|
488
488
|
}
|
489
489
|
}
|
490
490
|
}
|
@@ -494,9 +494,9 @@ Group::requestOOBW(const ProcessPtr &process) {
|
|
494
494
|
// Standard resource management boilerplate stuff...
|
495
495
|
PoolPtr pool = getPool();
|
496
496
|
unique_lock<boost::mutex> lock(pool->syncher);
|
497
|
-
|
498
|
-
|
499
|
-
|
497
|
+
if (isAlive() && process->isAlive() && process->oobwStatus == Process::OOBW_NOT_ACTIVE) {
|
498
|
+
process->oobwStatus = Process::OOBW_REQUESTED;
|
499
|
+
}
|
500
500
|
}
|
501
501
|
|
502
502
|
// The 'self' parameter is for keeping the current Group object alive
|
@@ -515,18 +515,20 @@ Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult
|
|
515
515
|
return;
|
516
516
|
}
|
517
517
|
|
518
|
+
P_DEBUG("Process " << process->inspect() << " disabled; proceeding with OOBW");
|
518
519
|
asyncOOBWRequestIfNeeded(process);
|
519
520
|
}
|
520
521
|
|
521
522
|
void
|
522
523
|
Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
|
523
|
-
if (
|
524
|
+
if (process->oobwStatus != Process::OOBW_REQUESTED || !process->isAlive()) {
|
524
525
|
return;
|
525
526
|
}
|
526
527
|
if (process->enabled == Process::ENABLED) {
|
527
528
|
// We want the process to be disabled. However, disabling a process is potentially
|
528
529
|
// asynchronous, so we pass a callback which will re-aquire the lock and call this
|
529
530
|
// method again.
|
531
|
+
P_DEBUG("Disabling process " << process->inspect() << " in preparation for OOBW");
|
530
532
|
DisableResult result = disable(process,
|
531
533
|
boost::bind(&Group::lockAndAsyncOOBWRequestIfNeeded, this,
|
532
534
|
_1, _2, shared_from_this()));
|
@@ -540,6 +542,7 @@ Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
|
|
540
542
|
assert(process->enabled == Process::DISABLED);
|
541
543
|
assert(process->sessions == 0);
|
542
544
|
|
545
|
+
P_DEBUG("Initiating OOBW request for process " << process->inspect());
|
543
546
|
interruptableThreads.create_thread(
|
544
547
|
boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
|
545
548
|
"OOB request thread for process " + process->inspect(),
|
@@ -555,25 +558,34 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
555
558
|
|
556
559
|
Socket *socket;
|
557
560
|
Connection connection;
|
561
|
+
PoolPtr pool = getPool();
|
562
|
+
Pool::DebugSupportPtr debug = pool->debugSupport;
|
563
|
+
|
564
|
+
UPDATE_TRACE_POINT();
|
565
|
+
P_DEBUG("Performing OOBW request for process " << process->inspect());
|
566
|
+
if (debug != NULL && debug->oobw) {
|
567
|
+
debug->debugger->send("OOBW request about to start");
|
568
|
+
debug->messages->recv("Proceed with OOBW request");
|
569
|
+
}
|
558
570
|
|
571
|
+
UPDATE_TRACE_POINT();
|
559
572
|
{
|
560
573
|
// Standard resource management boilerplate stuff...
|
561
|
-
PoolPtr pool = getPool();
|
562
574
|
unique_lock<boost::mutex> lock(pool->syncher);
|
563
575
|
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
564
576
|
return;
|
565
577
|
}
|
566
578
|
|
567
|
-
assert(process->
|
579
|
+
assert(process->oobwStatus = Process::OOBW_IN_PROGRESS);
|
568
580
|
assert(process->sessions == 0);
|
569
581
|
assert(process->enabled == Process::DISABLED);
|
570
582
|
socket = process->sessionSockets.top();
|
571
583
|
assert(socket != NULL);
|
572
584
|
}
|
573
585
|
|
586
|
+
UPDATE_TRACE_POINT();
|
574
587
|
unsigned long long timeout = 1000 * 1000 * 60; // 1 min
|
575
588
|
try {
|
576
|
-
ScopeGuard guard(boost::bind(&Socket::checkinConnection, socket, connection));
|
577
589
|
this_thread::restore_interruption ri(di);
|
578
590
|
this_thread::restore_syscall_interruption rsi(dsi);
|
579
591
|
|
@@ -582,7 +594,7 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
582
594
|
// need to completely read the response).
|
583
595
|
connection = socket->checkoutConnection();
|
584
596
|
connection.fail = true;
|
585
|
-
|
597
|
+
ScopeGuard guard(boost::bind(&Socket::checkinConnection, socket, connection));
|
586
598
|
|
587
599
|
// This is copied from RequestHandler when it is sending data using the
|
588
600
|
// "session" protocol.
|
@@ -605,6 +617,7 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
605
617
|
gatheredWrite(connection.fd, &data[0], data.size(), &timeout);
|
606
618
|
|
607
619
|
// We do not care what the actual response is ... just wait for it.
|
620
|
+
UPDATE_TRACE_POINT();
|
608
621
|
waitUntilReadable(connection.fd, &timeout);
|
609
622
|
} catch (const SystemException &e) {
|
610
623
|
P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
|
@@ -612,6 +625,7 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
612
625
|
P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
|
613
626
|
}
|
614
627
|
|
628
|
+
UPDATE_TRACE_POINT();
|
615
629
|
vector<Callback> actions;
|
616
630
|
{
|
617
631
|
// Standard resource management boilerplate stuff...
|
@@ -621,7 +635,7 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
621
635
|
return;
|
622
636
|
}
|
623
637
|
|
624
|
-
process->
|
638
|
+
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
625
639
|
if (process->enabled == Process::DISABLED) {
|
626
640
|
enable(process, actions);
|
627
641
|
assignSessionsToGetWaiters(actions);
|
@@ -629,7 +643,15 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
629
643
|
|
630
644
|
pool->fullVerifyInvariants();
|
631
645
|
}
|
646
|
+
UPDATE_TRACE_POINT();
|
632
647
|
runAllActions(actions);
|
648
|
+
actions.clear();
|
649
|
+
|
650
|
+
UPDATE_TRACE_POINT();
|
651
|
+
P_DEBUG("Finished OOBW request for process " << process->inspect());
|
652
|
+
if (debug != NULL && debug->oobw) {
|
653
|
+
debug->debugger->send("OOBW request finished");
|
654
|
+
}
|
633
655
|
}
|
634
656
|
|
635
657
|
// The 'self' parameter is for keeping the current Group object alive while this thread is running.
|
@@ -741,7 +763,8 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, un
|
|
741
763
|
// TODO: sure this is the best thing? if there are
|
742
764
|
// processes currently alive we should just use them.
|
743
765
|
P_ERROR("Could not spawn process for group " << name <<
|
744
|
-
": " << exception->what()
|
766
|
+
": " << exception->what() << "\n" <<
|
767
|
+
exception->backtrace());
|
745
768
|
if (enabledCount == 0) {
|
746
769
|
enableAllDisablingProcesses(actions);
|
747
770
|
}
|
@@ -953,6 +976,11 @@ Group::detachedProcessesCheckerMain(GroupPtr self) {
|
|
953
976
|
}
|
954
977
|
}
|
955
978
|
|
979
|
+
void
|
980
|
+
Group::wakeUpGarbageCollector() {
|
981
|
+
getPool()->garbageCollectionCond.notify_all();
|
982
|
+
}
|
983
|
+
|
956
984
|
bool
|
957
985
|
Group::poolAtFullCapacity() const {
|
958
986
|
return getPool()->atFullCapacity(false);
|
@@ -99,6 +99,7 @@ public:
|
|
99
99
|
bool restarting;
|
100
100
|
bool spawning;
|
101
101
|
bool superGroup;
|
102
|
+
bool oobw;
|
102
103
|
|
103
104
|
// The following fields may only be accessed by Pool.
|
104
105
|
boost::mutex syncher;
|
@@ -110,6 +111,7 @@ public:
|
|
110
111
|
restarting = true;
|
111
112
|
spawning = true;
|
112
113
|
superGroup = false;
|
114
|
+
oobw = false;
|
113
115
|
spawnLoopIteration = 0;
|
114
116
|
}
|
115
117
|
};
|
@@ -432,20 +434,29 @@ public:
|
|
432
434
|
for (p_it = processes.begin(); p_it != processes.end(); p_it++) {
|
433
435
|
const ProcessPtr &process = *p_it;
|
434
436
|
char buf[128];
|
437
|
+
char membuf[10];
|
435
438
|
|
439
|
+
snprintf(membuf, sizeof(membuf), "%ldM",
|
440
|
+
(unsigned long) (process->metrics.realMemory() / 1024));
|
436
441
|
snprintf(buf, sizeof(buf),
|
437
|
-
"* PID: %-5lu Sessions: %-2u Processed: %-5u Uptime: %s"
|
442
|
+
" * PID : %-5lu Sessions : %-2u Processed: %-5u Uptime: %s\n"
|
443
|
+
" Memory: %-5s Last used: %s ago",
|
438
444
|
(unsigned long) process->pid,
|
439
445
|
process->sessions,
|
440
446
|
process->processed,
|
441
|
-
process->uptime().c_str()
|
442
|
-
|
447
|
+
process->uptime().c_str(),
|
448
|
+
membuf,
|
449
|
+
distanceOfTimeInWords(process->lastUsed / 1000000).c_str());
|
450
|
+
result << buf << endl;
|
443
451
|
|
444
452
|
if (process->enabled == Process::DISABLING) {
|
445
453
|
result << " Disabling..." << endl;
|
446
454
|
} else if (process->enabled == Process::DISABLED) {
|
447
455
|
result << " DISABLED" << endl;
|
448
456
|
}
|
457
|
+
if (process->getLifeStatus() == Process::SHUTTING_DOWN) {
|
458
|
+
result << " Shutting down...";
|
459
|
+
}
|
449
460
|
|
450
461
|
const Socket *socket;
|
451
462
|
if (options.verbose && (socket = process->sockets->findSocketWithName("http")) != NULL) {
|
@@ -609,7 +620,11 @@ public:
|
|
609
620
|
// Schedule next garbage collection run.
|
610
621
|
unsigned long long sleepTime;
|
611
622
|
if (nextGcRunTime == 0 || nextGcRunTime <= now) {
|
612
|
-
|
623
|
+
if (maxIdleTime == 0) {
|
624
|
+
sleepTime = 10 * 60 * 1000000;
|
625
|
+
} else {
|
626
|
+
sleepTime = maxIdleTime;
|
627
|
+
}
|
613
628
|
} else {
|
614
629
|
sleepTime = nextGcRunTime - now;
|
615
630
|
}
|
@@ -747,6 +762,7 @@ public:
|
|
747
762
|
xml << "Group: <group>";
|
748
763
|
group->inspectXml(xml, false);
|
749
764
|
xml << "</group>";
|
765
|
+
logEntries.push_back(entry);
|
750
766
|
}
|
751
767
|
}
|
752
768
|
}
|
@@ -792,6 +808,7 @@ public:
|
|
792
808
|
options);
|
793
809
|
superGroup->initialize();
|
794
810
|
superGroups.set(options.getAppGroupName(), superGroup);
|
811
|
+
garbageCollectionCond.notify_all();
|
795
812
|
return superGroup;
|
796
813
|
}
|
797
814
|
|
@@ -977,6 +994,7 @@ public:
|
|
977
994
|
superGroup = make_shared<SuperGroup>(shared_from_this(), options);
|
978
995
|
superGroup->initialize();
|
979
996
|
superGroups.set(options.getAppGroupName(), superGroup);
|
997
|
+
garbageCollectionCond.notify_all();
|
980
998
|
SessionPtr session = superGroup->get(options, callback);
|
981
999
|
/* The SuperGroup is still initializing so the callback
|
982
1000
|
* should now have been put on the wait list,
|
@@ -1373,6 +1391,7 @@ public:
|
|
1373
1391
|
inspectProcessList(options, result, group->enabledProcesses);
|
1374
1392
|
inspectProcessList(options, result, group->disablingProcesses);
|
1375
1393
|
inspectProcessList(options, result, group->disabledProcesses);
|
1394
|
+
inspectProcessList(options, result, group->detachedProcesses);
|
1376
1395
|
result << endl;
|
1377
1396
|
}
|
1378
1397
|
}
|