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.

Files changed (83) hide show
  1. data.tar.gz.asc +12 -0
  2. data/.travis.yml +4 -4
  3. data/NEWS +46 -0
  4. data/bin/passenger-config +31 -1
  5. data/bin/passenger-install-apache2-module +1 -1
  6. data/bin/passenger-install-nginx-module +1 -0
  7. data/build/common_library.rb +4 -0
  8. data/build/cplusplus_support.rb +27 -6
  9. data/build/cxx_tests.rb +1 -1
  10. data/build/misc.rb +28 -6
  11. data/build/packaging.rb +72 -65
  12. data/build/test_basics.rb +1 -1
  13. data/dev/googlecode_upload.py +265 -0
  14. data/dev/run_travis.sh +9 -0
  15. data/doc/Users guide Apache.html +376 -193
  16. data/doc/Users guide Apache.idmap.txt +80 -62
  17. data/doc/Users guide Apache.txt +61 -35
  18. data/doc/Users guide Nginx.html +278 -83
  19. data/doc/Users guide Nginx.idmap.txt +26 -10
  20. data/doc/Users guide Nginx.txt +59 -31
  21. data/doc/Users guide Standalone.html +1 -1
  22. data/doc/users_guide_snippets/installation.txt +121 -11
  23. data/doc/users_guide_snippets/rvm_helper_tool.txt +56 -0
  24. data/ext/apache2/Bucket.cpp +1 -1
  25. data/ext/apache2/Configuration.cpp +7 -1
  26. data/ext/apache2/Configuration.hpp +4 -0
  27. data/ext/apache2/Hooks.cpp +2 -2
  28. data/ext/common/AgentsStarter.cpp +2 -2
  29. data/ext/common/AgentsStarter.h +1 -1
  30. data/ext/common/AgentsStarter.hpp +2 -2
  31. data/ext/common/ApplicationPool2/DirectSpawner.h +4 -8
  32. data/ext/common/ApplicationPool2/Group.h +17 -11
  33. data/ext/common/ApplicationPool2/Implementation.cpp +39 -11
  34. data/ext/common/ApplicationPool2/Pool.h +23 -4
  35. data/ext/common/ApplicationPool2/Process.h +30 -11
  36. data/ext/common/ApplicationPool2/SmartSpawner.h +3 -1
  37. data/ext/common/Constants.h +1 -1
  38. data/ext/common/EventedBufferedInput.h +4 -0
  39. data/ext/common/Utils.cpp +21 -3
  40. data/ext/common/Utils.h +8 -1
  41. data/ext/common/Utils/HttpHeaderBufferer.h +1 -1
  42. data/ext/common/Utils/IOUtils.cpp +5 -4
  43. data/ext/common/Utils/IOUtils.h +32 -14
  44. data/ext/common/Utils/MessagePassing.h +2 -2
  45. data/ext/common/Utils/ProcessMetricsCollector.h +47 -15
  46. data/ext/common/Utils/ScopeGuard.h +20 -3
  47. data/ext/common/Utils/StrIntUtils.h +14 -5
  48. data/ext/common/agents/Base.cpp +161 -50
  49. data/ext/common/agents/HelperAgent/AgentOptions.h +2 -2
  50. data/ext/common/agents/HelperAgent/Main.cpp +1 -0
  51. data/ext/common/agents/HelperAgent/RequestHandler.h +166 -52
  52. data/ext/common/agents/LoggingAgent/Main.cpp +1 -1
  53. data/ext/common/agents/Watchdog/Main.cpp +2 -2
  54. data/ext/nginx/Configuration.c +31 -4
  55. data/ext/nginx/Configuration.h +1 -0
  56. data/ext/nginx/ContentHandler.c +148 -34
  57. data/ext/nginx/ngx_http_passenger_module.c +4 -1
  58. data/ext/oxt/detail/spin_lock_pthreads.hpp +4 -4
  59. data/ext/oxt/macros.hpp +30 -8
  60. data/lib/phusion_passenger.rb +2 -2
  61. data/lib/phusion_passenger/classic_rails/thread_handler_extension.rb +1 -1
  62. data/lib/phusion_passenger/native_support.rb +19 -1
  63. data/lib/phusion_passenger/platform_info/compiler.rb +6 -0
  64. data/lib/phusion_passenger/platform_info/ruby.rb +54 -5
  65. data/lib/phusion_passenger/preloader_shared_helpers.rb +8 -1
  66. data/lib/phusion_passenger/rack/out_of_band_gc.rb +3 -1
  67. data/lib/phusion_passenger/rack/thread_handler_extension.rb +32 -5
  68. data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -8
  69. data/lib/phusion_passenger/ruby_core_enhancements.rb +9 -1
  70. data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -0
  71. data/lib/phusion_passenger/utils/unseekable_socket.rb +50 -5
  72. data/passenger.gemspec +1 -1
  73. data/resources/templates/apache2/config_snippets.txt.erb +1 -1
  74. data/test/cxx/ApplicationPool2/PoolTest.cpp +4 -9
  75. data/test/cxx/RequestHandlerTest.cpp +5 -5
  76. data/test/ruby/classic_rails/loader_spec.rb +1 -1
  77. data/test/ruby/classic_rails/preloader_spec.rb +1 -1
  78. data/test/ruby/request_handler_spec.rb +207 -1
  79. data/test/ruby/shared/loader_sharedspec.rb +1 -0
  80. data/test/ruby/spec_helper.rb +11 -1
  81. data/test/stub/apache2/httpd.conf.erb +1 -1
  82. metadata +5 -3
  83. 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;
@@ -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, "ruby", serverConfig.maxPoolSize,
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 *rubyCommand, unsigned int maxPoolSize,
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, rubyCommand,
87
+ passengerRoot, defaultRubyCommand,
88
88
  maxPoolSize, maxInstancesPerApp, poolIdleTime,
89
89
  analyticsServer,
90
90
  analyticsLogUser, analyticsLogGroup,
@@ -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 *rubyCommand, unsigned int maxPoolSize,
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 &rubyCommand,
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 ("ruby", rubyCommand)
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
- if (options.getProcessTitle().empty()) {
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
- command.push_back(processTitle);
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()->atFullCapacity() ));
201
- assert(!( !enabledProcesses.empty() && !pqueue.top()->atFullCapacity() ) || ( getWaitlist.empty() ));
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()->atFullCapacity()) {
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()->atFullCapacity()) {
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().atFullCapacity():
575
- * All enabled processes are at full capacity.
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->atFullCapacity()) {
739
- /* Looks like all processes are at full capacity.
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>alive</life_status>";
1089
+ stream << "<life_status>ALIVE</life_status>";
1084
1090
  break;
1085
1091
  case SHUTTING_DOWN:
1086
- stream << "<life_status>shutting_down</life_status>";
1092
+ stream << "<life_status>SHUTTING_DOWN</life_status>";
1087
1093
  break;
1088
1094
  case SHUT_DOWN:
1089
- stream << "<life_status>shut_down</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
- assert(isAlive());
498
-
499
- process->oobwRequested = true;
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 (!process->oobwRequested || !process->isAlive()) {
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->oobwRequested);
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->oobwRequested = false;
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
- result << " " << buf << endl;
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
- sleepTime = maxIdleTime;
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
  }