passenger 3.9.2.beta → 4.0.0.rc4

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 (159) hide show
  1. data/.travis.yml +3 -0
  2. data/NEWS +77 -7
  3. data/README.md +3 -11
  4. data/bin/passenger-install-apache2-module +24 -20
  5. data/bin/passenger-install-nginx-module +25 -23
  6. data/build/agents.rb +11 -0
  7. data/build/apache2.rb +9 -5
  8. data/build/basics.rb +37 -30
  9. data/build/common_library.rb +4 -1
  10. data/build/cplusplus_support.rb +5 -5
  11. data/build/cxx_tests.rb +28 -8
  12. data/build/integration_tests.rb +6 -3
  13. data/build/nginx.rb +3 -3
  14. data/build/packaging.rb +95 -57
  15. data/build/ruby_extension.rb +34 -21
  16. data/build/ruby_tests.rb +4 -2
  17. data/build/test_basics.rb +1 -1
  18. data/dev/run_travis.sh +36 -1
  19. data/doc/Users guide Apache.html +425 -308
  20. data/doc/Users guide Apache.idmap.txt +78 -70
  21. data/doc/Users guide Apache.index.sqlite3 +0 -0
  22. data/doc/Users guide Apache.txt +33 -92
  23. data/doc/Users guide Nginx.html +519 -220
  24. data/doc/Users guide Nginx.idmap.txt +78 -60
  25. data/doc/Users guide Nginx.txt +115 -26
  26. data/doc/Users guide Standalone.html +8 -2
  27. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +1 -7
  28. data/doc/users_guide_snippets/installation.txt +167 -22
  29. data/doc/users_guide_snippets/rackup_specifications.txt +4 -0
  30. data/doc/users_guide_snippets/since_version.txt +1 -0
  31. data/doc/users_guide_snippets/support_information.txt +3 -7
  32. data/doc/users_guide_snippets/tips.txt +0 -24
  33. data/ext/apache2/Configuration.cpp +11 -33
  34. data/ext/apache2/Configuration.hpp +3 -18
  35. data/ext/apache2/DirectoryMapper.h +20 -70
  36. data/ext/apache2/Hooks.cpp +2 -2
  37. data/ext/common/AgentsStarter.cpp +0 -2
  38. data/ext/common/AgentsStarter.h +0 -1
  39. data/ext/common/AgentsStarter.hpp +1 -3
  40. data/ext/common/ApplicationPool2/AppTypes.cpp +74 -0
  41. data/ext/common/ApplicationPool2/AppTypes.h +202 -0
  42. data/ext/common/ApplicationPool2/Common.h +12 -10
  43. data/ext/common/ApplicationPool2/DirectSpawner.h +256 -0
  44. data/ext/common/ApplicationPool2/DummySpawner.h +90 -0
  45. data/ext/common/ApplicationPool2/Group.h +311 -94
  46. data/ext/common/ApplicationPool2/Implementation.cpp +405 -145
  47. data/ext/common/ApplicationPool2/Options.h +24 -26
  48. data/ext/common/ApplicationPool2/PipeWatcher.h +20 -13
  49. data/ext/common/ApplicationPool2/Pool.h +326 -183
  50. data/ext/common/ApplicationPool2/Process.h +205 -55
  51. data/ext/common/ApplicationPool2/README.md +1 -1
  52. data/ext/common/ApplicationPool2/Session.h +21 -10
  53. data/ext/common/ApplicationPool2/SmartSpawner.h +801 -0
  54. data/ext/common/ApplicationPool2/Spawner.h +141 -1149
  55. data/ext/common/ApplicationPool2/SpawnerFactory.h +132 -0
  56. data/ext/common/ApplicationPool2/SuperGroup.h +146 -223
  57. data/ext/common/Constants.h +4 -2
  58. data/ext/common/Exceptions.h +23 -1
  59. data/ext/common/Logging.cpp +17 -6
  60. data/ext/common/Logging.h +37 -7
  61. data/ext/common/ResourceLocator.h +1 -1
  62. data/ext/common/Utils.cpp +49 -1
  63. data/ext/common/Utils.h +13 -4
  64. data/ext/common/{AnsiColorConstants.h → Utils/AnsiColorConstants.h} +0 -0
  65. data/ext/common/{BCrypt.cpp → Utils/BCrypt.cpp} +0 -0
  66. data/ext/common/{BCrypt.h → Utils/BCrypt.h} +0 -0
  67. data/ext/common/{Blowfish.c → Utils/Blowfish.c} +0 -0
  68. data/ext/common/{Blowfish.h → Utils/Blowfish.h} +0 -0
  69. data/ext/common/Utils/CachedFileStat.hpp +27 -25
  70. data/ext/common/Utils/Curl.h +184 -0
  71. data/ext/common/{HttpConstants.h → Utils/HttpConstants.h} +3 -0
  72. data/ext/common/Utils/IOUtils.cpp +6 -2
  73. data/ext/common/{IniFile.h → Utils/IniFile.h} +0 -0
  74. data/ext/common/Utils/LargeFiles.cpp +30 -0
  75. data/ext/common/Utils/LargeFiles.h +40 -0
  76. data/ext/common/Utils/StrIntUtils.cpp +72 -8
  77. data/ext/common/Utils/StrIntUtils.h +24 -2
  78. data/ext/common/Utils/StringMap.h +12 -2
  79. data/ext/common/Utils/VariantMap.h +51 -2
  80. data/ext/common/Utils/jsoncpp.cpp +1 -1
  81. data/ext/common/agents/Base.cpp +147 -11
  82. data/ext/common/agents/HelperAgent/AgentOptions.h +14 -6
  83. data/ext/common/agents/HelperAgent/Main.cpp +79 -19
  84. data/ext/common/agents/HelperAgent/RequestHandler.h +36 -16
  85. data/ext/common/agents/LoggingAgent/LoggingServer.h +3 -5
  86. data/ext/common/agents/LoggingAgent/Main.cpp +2 -4
  87. data/ext/common/agents/LoggingAgent/RemoteSender.h +18 -24
  88. data/ext/common/agents/SpawnPreparer.cpp +7 -0
  89. data/ext/common/agents/Watchdog/Main.cpp +96 -38
  90. data/ext/nginx/Configuration.c +26 -22
  91. data/ext/nginx/Configuration.h +4 -2
  92. data/ext/nginx/ContentHandler.c +23 -52
  93. data/ext/nginx/ContentHandler.h +5 -11
  94. data/ext/nginx/config +10 -3
  95. data/ext/nginx/ngx_http_passenger_module.c +21 -6
  96. data/ext/nginx/ngx_http_passenger_module.h +4 -1
  97. data/ext/oxt/dynamic_thread_group.hpp +9 -1
  98. data/ext/oxt/system_calls.cpp +2 -2
  99. data/ext/ruby/extconf.rb +2 -1
  100. data/helper-scripts/backtrace-sanitizer.rb +2 -0
  101. data/helper-scripts/wsgi-loader.py +54 -21
  102. data/lib/phusion_passenger.rb +5 -3
  103. data/lib/phusion_passenger/abstract_installer.rb +18 -41
  104. data/lib/phusion_passenger/admin_tools/memory_stats.rb +2 -2
  105. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  106. data/lib/phusion_passenger/common_library.rb +23 -3
  107. data/lib/phusion_passenger/debug_logging.rb +10 -3
  108. data/lib/phusion_passenger/packaging.rb +1 -0
  109. data/lib/phusion_passenger/platform_info.rb +113 -115
  110. data/lib/phusion_passenger/platform_info/compiler.rb +224 -134
  111. data/lib/phusion_passenger/platform_info/cxx_portability.rb +143 -0
  112. data/lib/phusion_passenger/platform_info/depcheck.rb +371 -0
  113. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +124 -0
  114. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +97 -0
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +39 -0
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +118 -0
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +137 -0
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +15 -0
  119. data/lib/phusion_passenger/platform_info/operating_system.rb +6 -5
  120. data/lib/phusion_passenger/platform_info/ruby.rb +45 -34
  121. data/lib/phusion_passenger/request_handler.rb +35 -22
  122. data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -6
  123. data/lib/phusion_passenger/ruby_core_enhancements.rb +7 -1
  124. data/lib/phusion_passenger/standalone/runtime_installer.rb +43 -34
  125. data/lib/phusion_passenger/utils/robust_interruption.rb +34 -18
  126. data/passenger.gemspec +25 -0
  127. data/resources/templates/standalone/config.erb +3 -1
  128. data/test/config.json.travis +2 -2
  129. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +37 -5
  130. data/test/cxx/ApplicationPool2/PoolTest.cpp +143 -50
  131. data/test/cxx/ApplicationPool2/ProcessTest.cpp +8 -0
  132. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +28 -17
  133. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +31 -26
  134. data/test/cxx/RequestHandlerTest.cpp +17 -1
  135. data/test/cxx/UtilsTest.cpp +84 -10
  136. data/test/integration_tests/apache2_tests.rb +49 -163
  137. data/test/integration_tests/hello_world_wsgi_spec.rb +2 -2
  138. data/test/integration_tests/mycook_spec.rb +1 -1
  139. data/test/integration_tests/nginx_tests.rb +37 -19
  140. data/test/ruby/request_handler_spec.rb +1 -0
  141. data/test/ruby/spec_helper.rb +52 -1
  142. data/test/stub/nginx/nginx.conf.erb +2 -0
  143. data/test/stub/rack/start.rb +5 -0
  144. data/test/stub/rails3.0/Gemfile.lock +30 -30
  145. data/test/stub/rails3.1/Gemfile +1 -1
  146. data/test/stub/rails3.1/Gemfile.lock +3 -3
  147. data/test/stub/rails3.2/Gemfile +1 -1
  148. data/test/stub/rails3.2/Gemfile.lock +4 -4
  149. data/test/stub/rails_apps/2.3/mycook/app/controllers/welcome_controller.rb +1 -1
  150. data/test/stub/rails_apps/2.3/mycook/app/helpers/recipes_helper.rb +2 -0
  151. data/test/stub/rails_apps/2.3/mycook/app/helpers/test_helper.rb +2 -0
  152. data/test/stub/rails_apps/2.3/mycook/app/helpers/uploads_helper.rb +2 -0
  153. data/test/stub/rails_apps/2.3/mycook/app/helpers/welcome_helper.rb +2 -0
  154. data/test/support/nginx_controller.rb +2 -1
  155. metadata +160 -156
  156. data/build/gempackagetask.rb +0 -99
  157. data/build/packagetask.rb +0 -186
  158. data/ext/common/StringListCreator.h +0 -83
  159. data/lib/phusion_passenger/dependencies.rb +0 -657
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010, 2011, 2012 Phusion
3
+ * Copyright (c) 2010-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -29,6 +29,7 @@
29
29
  #include <vector>
30
30
  #include <utility>
31
31
  #include <boost/shared_array.hpp>
32
+ #include <ApplicationPool2/AppTypes.h>
32
33
  #include <Account.h>
33
34
  #include <UnionStation.h>
34
35
  #include <Constants.h>
@@ -98,6 +99,7 @@ private:
98
99
  result.push_back(&postexecChroot);
99
100
 
100
101
  result.push_back(&ruby);
102
+ result.push_back(&python);
101
103
  result.push_back(&loggingAgentAddress);
102
104
  result.push_back(&loggingAgentUsername);
103
105
  result.push_back(&loggingAgentPassword);
@@ -163,10 +165,10 @@ public:
163
165
 
164
166
  /** The application's type, used for determining the command to invoke to
165
167
  * spawn an application process as well as determining the startup file's
166
- * filename. Either "classic-rails", "rack", "wsgi" or the empty string (default).
167
- * In case of the latter, 'startCommand' and 'startupFile' (which MUST
168
- * be set) will dictate the startup command and the startup file's
169
- * filename. */
168
+ * filename. It can be one of the app type names in AppType.cpp, or the
169
+ * empty string (default). In case of the latter, 'startCommand' and
170
+ * 'startupFile' (which MUST be set) will dictate the startup command
171
+ * and the startup file's filename. */
170
172
  StaticString appType;
171
173
 
172
174
  /** The command for spawning the application process. This is a list of
@@ -236,6 +238,12 @@ public:
236
238
  * is a Ruby app.
237
239
  */
238
240
  StaticString ruby;
241
+
242
+ /**
243
+ * Path to the Python interpreter to use, in case the application to spawn
244
+ * is a Python app.
245
+ */
246
+ StaticString python;
239
247
 
240
248
  /**
241
249
  * Any rights that the spawned application process may have. The SpawnManager
@@ -362,6 +370,7 @@ public:
362
370
  spawnMethod = "smart";
363
371
  defaultUser = "nobody";
364
372
  ruby = "ruby";
373
+ python = "python";
365
374
  rights = DEFAULT_BACKEND_ACCOUNT_RIGHTS;
366
375
  debugger = false;
367
376
  loadShellEnvvars = true;
@@ -498,6 +507,7 @@ public:
498
507
  appendKeyValue (vec, "preexec_chroot", preexecChroot);
499
508
  appendKeyValue (vec, "postexec_chroot", postexecChroot);
500
509
  appendKeyValue (vec, "ruby", ruby);
510
+ appendKeyValue (vec, "python", python);
501
511
  appendKeyValue (vec, "logging_agent_address", loggingAgentAddress);
502
512
  appendKeyValue (vec, "logging_agent_username", loggingAgentUsername);
503
513
  appendKeyValue (vec, "logging_agent_password", loggingAgentPassword);
@@ -528,39 +538,27 @@ public:
528
538
  } else if (appType == "rack") {
529
539
  return ruby + "\1" + resourceLocator.getHelperScriptsDir() + "/rack-loader.rb";
530
540
  } else if (appType == "wsgi") {
531
- return "python\1" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
532
- } else if (appType == "node") {
533
- return "node\1" + resourceLocator.getHelperScriptsDir() + "/node-loader.js";
541
+ return python + "\1" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
534
542
  } else {
535
543
  return startCommand;
536
544
  }
537
545
  }
538
546
 
539
547
  StaticString getStartupFile() const {
540
- if (appType == "classic-rails") {
541
- return "config/environment.rb";
542
- } else if (appType == "rack") {
543
- return "config.ru";
544
- } else if (appType == "wsgi") {
545
- return "passenger_wsgi.py";
546
- } else if (appType == "node") {
547
- return "passenger_node.js";
548
- } else {
548
+ const char *result = getAppTypeStartupFile(getAppType(appType));
549
+ if (result == NULL) {
549
550
  return startupFile;
551
+ } else {
552
+ return result;
550
553
  }
551
554
  }
552
555
 
553
556
  StaticString getProcessTitle() const {
554
- if (appType == "classic-rails") {
555
- return "Passenger RailsApp";
556
- } else if (appType == "rack") {
557
- return "Passenger RackApp";
558
- } else if (appType == "wsgi") {
559
- return "Passenger WsgiApp";
560
- } else if (appType == "node") {
561
- return "Passenger NodeJsApp";
562
- } else {
557
+ const char *result = getAppTypeProcessTitle(getAppType(appType));
558
+ if (result == NULL) {
563
559
  return processTitle;
560
+ } else {
561
+ return result;
564
562
  }
565
563
  }
566
564
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2012 Phusion
3
+ * Copyright (c) 2012-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -27,9 +27,10 @@
27
27
 
28
28
  #include <boost/shared_ptr.hpp>
29
29
  #include <boost/enable_shared_from_this.hpp>
30
- #include <SafeLibev.h>
30
+ #include <boost/thread.hpp>
31
+ #include <boost/function.hpp>
32
+ #include <sys/types.h>
31
33
  #include <FileDescriptor.h>
32
- #include <ev++.h>
33
34
 
34
35
  namespace Passenger {
35
36
  namespace ApplicationPool2 {
@@ -37,19 +38,25 @@ namespace ApplicationPool2 {
37
38
  using namespace boost;
38
39
 
39
40
 
41
+ /** A PipeWatcher lives until the file descriptor is closed. */
40
42
  struct PipeWatcher: public enable_shared_from_this<PipeWatcher> {
41
- SafeLibevPtr libev;
43
+ // For unit tests.
44
+ typedef function<void (const char *data, unsigned int size)> DataCallback;
45
+ static DataCallback onData;
46
+
42
47
  FileDescriptor fd;
43
- ev::io watcher;
44
- shared_ptr<PipeWatcher> selfPointer;
45
- int fdToForwardTo;
46
-
47
- PipeWatcher(const SafeLibevPtr &_libev,
48
- const FileDescriptor &_fd,
49
- int _fdToForwardTo);
50
- ~PipeWatcher();
48
+ const char *name;
49
+ pid_t pid;
50
+ bool print;
51
+ bool started;
52
+ boost::mutex startSyncher;
53
+ condition_variable startCond;
54
+
55
+ PipeWatcher(const FileDescriptor &_fd, const char *name, pid_t pid, bool _print);
56
+ void initialize();
51
57
  void start();
52
- void onReadable(ev::io &io, int revents);
58
+ static void threadMain(shared_ptr<PipeWatcher> self);
59
+ void threadMain();
53
60
  };
54
61
 
55
62
  typedef shared_ptr<PipeWatcher> PipeWatcherPtr;
@@ -36,6 +36,7 @@
36
36
  #include <boost/make_shared.hpp>
37
37
  #include <boost/function.hpp>
38
38
  #include <boost/foreach.hpp>
39
+ #include <boost/date_time/posix_time/posix_time_types.hpp>
39
40
  #include <oxt/dynamic_thread_group.hpp>
40
41
  #include <oxt/backtrace.hpp>
41
42
  #include <ApplicationPool2/Common.h>
@@ -43,15 +44,15 @@
43
44
  #include <ApplicationPool2/Group.h>
44
45
  #include <ApplicationPool2/SuperGroup.h>
45
46
  #include <ApplicationPool2/Session.h>
46
- #include <ApplicationPool2/Spawner.h>
47
+ #include <ApplicationPool2/SpawnerFactory.h>
47
48
  #include <ApplicationPool2/Options.h>
48
49
  #include <UnionStation.h>
49
50
  #include <Logging.h>
50
51
  #include <SafeLibev.h>
51
- #include <AnsiColorConstants.h>
52
52
  #include <Exceptions.h>
53
53
  #include <RandomGenerator.h>
54
54
  #include <Utils/Lock.h>
55
+ #include <Utils/AnsiColorConstants.h>
55
56
  #include <Utils/SystemTime.h>
56
57
  #include <Utils/MessagePassing.h>
57
58
  #include <Utils/VariantMap.h>
@@ -94,6 +95,11 @@ public:
94
95
  MessageBoxPtr debugger;
95
96
  MessageBoxPtr messages;
96
97
 
98
+ // Choose aspects to debug.
99
+ bool restarting;
100
+ bool spawning;
101
+ bool superGroup;
102
+
97
103
  // The following fields may only be accessed by Pool.
98
104
  boost::mutex syncher;
99
105
  unsigned int spawnLoopIteration;
@@ -101,6 +107,9 @@ public:
101
107
  DebugSupport() {
102
108
  debugger = make_shared<MessageBox>();
103
109
  messages = make_shared<MessageBox>();
110
+ restarting = true;
111
+ spawning = true;
112
+ superGroup = false;
104
113
  spawnLoopIteration = 0;
105
114
  }
106
115
  };
@@ -116,8 +125,7 @@ public:
116
125
  unsigned int max;
117
126
  unsigned long long maxIdleTime;
118
127
 
119
- ev::timer garbageCollectionTimer;
120
- ev::timer analyticsCollectionTimer;
128
+ condition_variable garbageCollectionCond;
121
129
 
122
130
  /**
123
131
  * Code can register background threads in one of these dynamic thread groups
@@ -130,6 +138,12 @@ public:
130
138
  */
131
139
  dynamic_thread_group interruptableThreads;
132
140
  dynamic_thread_group nonInterruptableThreads;
141
+
142
+ enum LifeStatus {
143
+ ALIVE,
144
+ SHUTTING_DOWN,
145
+ SHUT_DOWN
146
+ } lifeStatus;
133
147
 
134
148
  SuperGroupMap superGroups;
135
149
 
@@ -209,8 +223,11 @@ public:
209
223
  }
210
224
 
211
225
  void fullVerifyInvariants() const {
226
+ TRACE_POINT();
212
227
  verifyInvariants();
228
+ UPDATE_TRACE_POINT();
213
229
  verifyExpensiveInvariants();
230
+ UPDATE_TRACE_POINT();
214
231
  StringMap<SuperGroupPtr>::const_iterator sg_it, sg_end = superGroups.end();
215
232
  for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
216
233
  pair<StaticString, SuperGroupPtr> p = *sg_it;
@@ -308,6 +325,19 @@ public:
308
325
 
309
326
  getWaitlist = newWaitlist;
310
327
  }
328
+
329
+ template<typename Queue>
330
+ static void assignExceptionToGetWaiters(Queue &getWaitlist,
331
+ const ExceptionPtr &exception,
332
+ vector<Callback> &postLockActions)
333
+ {
334
+ while (!getWaitlist.empty()) {
335
+ postLockActions.push_back(boost::bind(
336
+ getWaitlist.front().callback, SessionPtr(),
337
+ exception));
338
+ getWaitlist.pop();
339
+ }
340
+ }
311
341
 
312
342
  void possiblySpawnMoreProcessesForExistingGroups() {
313
343
  StringMap<SuperGroupPtr>::const_iterator sg_it, sg_end = superGroups.end();
@@ -343,14 +373,6 @@ public:
343
373
  }
344
374
  }
345
375
 
346
- void migrateGroupGetWaitlistToPool(const GroupPtr &group) {
347
- getWaitlist.reserve(getWaitlist.size() + group->getWaitlist.size());
348
- while (!group->getWaitlist.empty()) {
349
- getWaitlist.push_back(group->getWaitlist.front());
350
- group->getWaitlist.pop();
351
- }
352
- }
353
-
354
376
  void migrateSuperGroupGetWaitlistToPool(const SuperGroupPtr &superGroup) {
355
377
  getWaitlist.reserve(getWaitlist.size() + superGroup->getWaitlist.size());
356
378
  while (!superGroup->getWaitlist.empty()) {
@@ -364,24 +386,26 @@ public:
364
386
  * the SuperGroup may have a non-empty getWaitlist so be sure to do
365
387
  * something with it.
366
388
  *
367
- * 'superGroup' is a non-const non-reference smart pointer so that
368
- * it does not get destroy immediately after the 'superGroups.remove()'
369
- * call.
389
+ * Also, one of the post lock actions can potentially perform a long-running
390
+ * operation, so running them in a thread is advised.
370
391
  */
371
- void forceDetachSuperGroup(SuperGroupPtr superGroup, vector<Callback> &postLockActions) {
392
+ void forceDetachSuperGroup(const SuperGroupPtr &superGroup,
393
+ vector<Callback> &postLockActions,
394
+ const SuperGroup::ShutdownCallback &callback)
395
+ {
396
+ const SuperGroupPtr sp = superGroup; // Prevent premature destruction.
372
397
  bool removed = superGroups.remove(superGroup->name);
373
398
  assert(removed);
374
399
  (void) removed; // Shut up compiler warning.
375
- superGroup->destroy(postLockActions, false);
376
- superGroup->setPool(PoolPtr());
400
+ superGroup->destroy(false, postLockActions, callback);
377
401
  }
378
402
 
379
403
  bool detachProcessUnlocked(const ProcessPtr &process, vector<Callback> &postLockActions) {
380
- GroupPtr group = process->getGroup();
381
- if (group != NULL && group->getPool().get() == this) {
404
+ if (OXT_LIKELY(process->isAlive())) {
382
405
  verifyInvariants();
383
406
 
384
- SuperGroupPtr superGroup = group->getSuperGroup();
407
+ const GroupPtr group = process->getGroup();
408
+ const SuperGroupPtr superGroup = group->getSuperGroup();
385
409
  assert(superGroup->state != SuperGroup::INITIALIZING);
386
410
  assert(superGroup->getWaitlist.empty());
387
411
 
@@ -431,6 +455,17 @@ public:
431
455
  }
432
456
  }
433
457
 
458
+ struct DetachSuperGroupWaitTicket {
459
+ boost::mutex syncher;
460
+ condition_variable cond;
461
+ SuperGroup::ShutdownResult result;
462
+ bool done;
463
+
464
+ DetachSuperGroupWaitTicket() {
465
+ done = false;
466
+ }
467
+ };
468
+
434
469
  struct DisableWaitTicket {
435
470
  boost::mutex syncher;
436
471
  condition_variable cond;
@@ -442,10 +477,26 @@ public:
442
477
  }
443
478
  };
444
479
 
480
+ static void syncDetachSuperGroupCallback(SuperGroup::ShutdownResult result,
481
+ shared_ptr<DetachSuperGroupWaitTicket> ticket)
482
+ {
483
+ LockGuard l(ticket->syncher);
484
+ ticket->done = true;
485
+ ticket->result = result;
486
+ ticket->cond.notify_one();
487
+ }
488
+
489
+ static void waitDetachSuperGroupCallback(shared_ptr<DetachSuperGroupWaitTicket> ticket) {
490
+ ScopedLock l(ticket->syncher);
491
+ while (!ticket->done) {
492
+ ticket->cond.wait(l);
493
+ }
494
+ }
495
+
445
496
  static void syncDisableProcessCallback(const ProcessPtr &process, DisableResult result,
446
497
  shared_ptr<DisableWaitTicket> ticket)
447
498
  {
448
- ScopedLock l(ticket->syncher);
499
+ LockGuard l(ticket->syncher);
449
500
  ticket->done = true;
450
501
  ticket->result = result;
451
502
  ticket->cond.notify_one();
@@ -465,17 +516,37 @@ public:
465
516
  return superGroups.get(options.getAppGroupName()).get();
466
517
  }
467
518
 
468
- void garbageCollect(ev::timer &timer, int revents) {
469
- PoolPtr self = shared_from_this(); // Keep pool object alive.
519
+ static void garbageCollect(PoolPtr self) {
520
+ TRACE_POINT();
521
+ {
522
+ ScopedLock lock(self->syncher);
523
+ self->garbageCollectionCond.timed_wait(lock,
524
+ posix_time::seconds(5));
525
+ }
526
+ while (!this_thread::interruption_requested()) {
527
+ try {
528
+ UPDATE_TRACE_POINT();
529
+ unsigned long long sleepTime = self->realGarbageCollect();
530
+ ScopedLock lock(self->syncher);
531
+ self->garbageCollectionCond.timed_wait(lock,
532
+ posix_time::microseconds(sleepTime));
533
+ } catch (const thread_interrupted &) {
534
+ break;
535
+ } catch (const tracable_exception &e) {
536
+ P_WARN("ERROR: " << e.what() << "\n Backtrace:\n" << e.backtrace());
537
+ }
538
+ }
539
+ }
540
+
541
+ unsigned long long realGarbageCollect() {
470
542
  TRACE_POINT();
471
543
  ScopedLock lock(syncher);
472
544
  SuperGroupMap::iterator it, end = superGroups.end();
473
- vector<SuperGroupPtr> superGroupsToDetach;
474
545
  vector<Callback> actions;
475
546
  unsigned long long now = SystemTime::getUsec();
476
547
  unsigned long long nextGcRunTime = 0;
477
548
 
478
- P_DEBUG("Garbage collection time");
549
+ P_DEBUG("Garbage collection time...");
479
550
  verifyInvariants();
480
551
 
481
552
  // For all supergroups and groups...
@@ -521,7 +592,7 @@ public:
521
592
  group->options.getMaxPreloaderIdleTime() * 1000000;
522
593
  if (now >= spawnerGcTime) {
523
594
  P_DEBUG("Garbage collect idle spawner: group=" << group->name);
524
- group->asyncCleanupSpawner();
595
+ group->cleanupSpawner(actions);
525
596
  } else if (nextGcRunTime == 0
526
597
  || spawnerGcTime < nextGcRunTime) {
527
598
  nextGcRunTime = spawnerGcTime;
@@ -529,36 +600,27 @@ public:
529
600
  }
530
601
  }
531
602
 
532
- // ...remove entire supergroup if it has become garbage
533
- // collectable after detaching idle processes.
534
- if (superGroup->garbageCollectable(now)) {
535
- superGroupsToDetach.push_back(superGroup);
536
- }
537
-
538
603
  superGroup->verifyInvariants();
539
604
  }
540
605
 
541
- vector<SuperGroupPtr>::const_iterator it2;
542
- for (it2 = superGroupsToDetach.begin(); it2 != superGroupsToDetach.end(); it2++) {
543
- P_DEBUG("Garbage collect SuperGroup: " << (*it2)->inspect());
544
- detachSuperGroup(*it2, false, &actions);
545
- }
546
-
547
606
  verifyInvariants();
607
+ lock.unlock();
548
608
 
549
609
  // Schedule next garbage collection run.
550
- ev_tstamp tstamp;
610
+ unsigned long long sleepTime;
551
611
  if (nextGcRunTime == 0 || nextGcRunTime <= now) {
552
- tstamp = maxIdleTime / 1000000.0;
612
+ sleepTime = maxIdleTime;
553
613
  } else {
554
- tstamp = (nextGcRunTime - now) / 1000000.0;
614
+ sleepTime = nextGcRunTime - now;
555
615
  }
556
616
  P_DEBUG("Garbage collection done; next garbage collect in " <<
557
- std::fixed << std::setprecision(3) << tstamp << " sec");
617
+ std::fixed << std::setprecision(3) << (sleepTime / 1000000.0) << " sec");
558
618
 
559
- lock.unlock();
619
+ UPDATE_TRACE_POINT();
560
620
  runAllActions(actions);
561
- timer.start(tstamp, 0.0);
621
+ UPDATE_TRACE_POINT();
622
+ actions.clear();
623
+ return sleepTime;
562
624
  }
563
625
 
564
626
  struct ProcessAnalyticsLogEntry {
@@ -569,22 +631,56 @@ public:
569
631
 
570
632
  typedef shared_ptr<ProcessAnalyticsLogEntry> ProcessAnalyticsLogEntryPtr;
571
633
 
572
- void collectAnalytics(ev::timer &timer, int revents) {
573
- try {
574
- realCollectAnalytics(timer);
575
- } catch (const tracable_exception &e) {
576
- P_WARN("ERROR: " << e.what() << "\n Backtrace:\n" << e.backtrace());
634
+ static void collectAnalytics(PoolPtr self) {
635
+ TRACE_POINT();
636
+ syscalls::usleep(3000000);
637
+ while (!this_thread::interruption_requested()) {
638
+ try {
639
+ UPDATE_TRACE_POINT();
640
+ unsigned long long sleepTime = self->realCollectAnalytics();
641
+ syscalls::usleep(sleepTime);
642
+ } catch (const thread_interrupted &) {
643
+ break;
644
+ } catch (const tracable_exception &e) {
645
+ P_WARN("ERROR: " << e.what() << "\n Backtrace:\n" << e.backtrace());
646
+ }
647
+ }
648
+ }
649
+
650
+ static void collectPids(const ProcessList &processes, vector<pid_t> &pids) {
651
+ foreach (const ProcessPtr &process, processes) {
652
+ pids.push_back(process->pid);
653
+ }
654
+ }
655
+
656
+ static void updateProcessMetrics(const ProcessList &processes,
657
+ const ProcessMetricMap &allMetrics,
658
+ vector<ProcessPtr> &processesToDetach)
659
+ {
660
+ foreach (const ProcessPtr &process, processes) {
661
+ ProcessMetricMap::const_iterator metrics_it =
662
+ allMetrics.find(process->pid);
663
+ if (metrics_it != allMetrics.end()) {
664
+ process->metrics = metrics_it->second;
665
+ // If the process is missing from 'allMetrics' then either 'ps'
666
+ // failed or the process really is gone. We double check by sending
667
+ // it a signal.
668
+ } else if (!process->dummy && !process->osProcessExists()) {
669
+ P_WARN("Process " << process->inspect() << " no longer exists! "
670
+ "Detaching it from the pool.");
671
+ processesToDetach.push_back(process);
672
+ }
577
673
  }
578
674
  }
579
675
 
580
- void realCollectAnalytics(ev::timer &timer) {
581
- PoolPtr self = shared_from_this(); // Keep pool object alive.
676
+ unsigned long long realCollectAnalytics() {
582
677
  TRACE_POINT();
583
678
  this_thread::disable_interruption di;
584
679
  this_thread::disable_syscall_interruption dsi;
585
680
  vector<pid_t> pids;
586
681
  unsigned int max;
587
682
 
683
+ P_DEBUG("Analytics collection time...");
588
684
  // Collect all the PIDs.
589
685
  {
590
686
  UPDATE_TRACE_POINT();
@@ -603,24 +699,9 @@ public:
603
699
 
604
700
  for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
605
701
  const GroupPtr &group = *g_it;
606
- ProcessList::const_iterator p_it, p_end = group->enabledProcesses.end();
607
-
608
- for (p_it = group->enabledProcesses.begin(); p_it != p_end; p_it++) {
609
- const ProcessPtr &process = *p_it;
610
- pids.push_back(process->pid);
611
- }
612
-
613
- p_end = group->disablingProcesses.end();
614
- for (p_it = group->disablingProcesses.begin(); p_it != p_end; p_it++) {
615
- const ProcessPtr &process = *p_it;
616
- pids.push_back(process->pid);
617
- }
618
-
619
- p_end = group->disabledProcesses.end();
620
- for (p_it = group->disabledProcesses.begin(); p_it != p_end; p_it++) {
621
- const ProcessPtr &process = *p_it;
622
- pids.push_back(process->pid);
623
- }
702
+ collectPids(group->enabledProcesses, pids);
703
+ collectPids(group->disablingProcesses, pids);
704
+ collectPids(group->disabledProcesses, pids);
624
705
  }
625
706
  }
626
707
  }
@@ -639,6 +720,8 @@ public:
639
720
  {
640
721
  UPDATE_TRACE_POINT();
641
722
  vector<ProcessAnalyticsLogEntryPtr> logEntries;
723
+ vector<ProcessPtr> processesToDetach;
724
+ vector<Callback> actions;
642
725
  ScopedLock l(syncher);
643
726
  SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
644
727
 
@@ -649,36 +732,10 @@ public:
649
732
 
650
733
  for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
651
734
  const GroupPtr &group = *g_it;
652
- ProcessList::iterator p_it, p_end = group->enabledProcesses.end();
653
-
654
- for (p_it = group->enabledProcesses.begin(); p_it != p_end; p_it++) {
655
- ProcessPtr &process = *p_it;
656
- ProcessMetricMap::const_iterator metrics_it =
657
- allMetrics.find(process->pid);
658
- if (metrics_it != allMetrics.end()) {
659
- process->metrics = metrics_it->second;
660
- }
661
- }
662
735
 
663
- p_end = group->disablingProcesses.end();
664
- for (p_it = group->disablingProcesses.begin(); p_it != p_end; p_it++) {
665
- ProcessPtr &process = *p_it;
666
- ProcessMetricMap::const_iterator metrics_it =
667
- allMetrics.find(process->pid);
668
- if (metrics_it != allMetrics.end()) {
669
- process->metrics = metrics_it->second;
670
- }
671
- }
672
-
673
- p_end = group->disabledProcesses.end();
674
- for (p_it = group->disabledProcesses.begin(); p_it != p_end; p_it++) {
675
- ProcessPtr &process = *p_it;
676
- ProcessMetricMap::const_iterator metrics_it =
677
- allMetrics.find(process->pid);
678
- if (metrics_it != allMetrics.end()) {
679
- process->metrics = metrics_it->second;
680
- }
681
- }
736
+ updateProcessMetrics(group->enabledProcesses, allMetrics, processesToDetach);
737
+ updateProcessMetrics(group->disablingProcesses, allMetrics, processesToDetach);
738
+ updateProcessMetrics(group->disabledProcesses, allMetrics, processesToDetach);
682
739
 
683
740
  // Log to Union Station.
684
741
  if (group->options.analytics && loggerFactory != NULL) {
@@ -694,7 +751,15 @@ public:
694
751
  }
695
752
  }
696
753
 
754
+ UPDATE_TRACE_POINT();
755
+ foreach (const ProcessPtr process, processesToDetach) {
756
+ detachProcessUnlocked(process, actions);
757
+ }
758
+ UPDATE_TRACE_POINT();
759
+ processesToDetach.clear();
760
+
697
761
  l.unlock();
762
+ UPDATE_TRACE_POINT();
698
763
  while (!logEntries.empty()) {
699
764
  ProcessAnalyticsLogEntryPtr entry = logEntries.back();
700
765
  logEntries.pop_back();
@@ -702,16 +767,24 @@ public:
702
767
  "processes", entry->key);
703
768
  logger->message(entry->data.str());
704
769
  }
770
+
771
+ UPDATE_TRACE_POINT();
772
+ runAllActions(actions);
773
+ UPDATE_TRACE_POINT();
774
+ // Run destructors with updated trace point.
775
+ actions.clear();
705
776
  }
706
777
 
707
778
  end:
708
779
  // Sleep for about 4 seconds, aligned to seconds boundary
709
780
  // for saving power on laptops.
710
- ev_now_update(libev->getLoop());
711
781
  unsigned long long currentTime = SystemTime::getUsec();
712
782
  unsigned long long deadline =
713
783
  roundUp<unsigned long long>(currentTime, 1000000) + 4000000;
714
- timer.start((deadline - currentTime) / 1000000.0, 0.0);
784
+ P_DEBUG("Analytics collection done; next analytics collection in " <<
785
+ std::fixed << std::setprecision(3) << ((deadline - currentTime) / 1000000.0) <<
786
+ " sec");
787
+ return deadline - currentTime;
715
788
  }
716
789
 
717
790
  SuperGroupPtr createSuperGroup(const Options &options) {
@@ -752,16 +825,10 @@ public:
752
825
  this->randomGenerator = make_shared<RandomGenerator>();
753
826
  }
754
827
 
828
+ lifeStatus = ALIVE;
755
829
  max = 6;
756
830
  maxIdleTime = 60 * 1000000;
757
831
 
758
- garbageCollectionTimer.set<Pool, &Pool::garbageCollect>(this);
759
- garbageCollectionTimer.set(maxIdleTime / 1000000.0, 0.0);
760
- libev->start(garbageCollectionTimer);
761
- analyticsCollectionTimer.set<Pool, &Pool::collectAnalytics>(this);
762
- analyticsCollectionTimer.set(3.0, 0.0);
763
- libev->start(analyticsCollectionTimer);
764
-
765
832
  // The following code only serve to instantiate certain inline methods
766
833
  // so that they can be invoked from gdb.
767
834
  (void) SuperGroupPtr().get();
@@ -771,8 +838,23 @@ public:
771
838
  }
772
839
 
773
840
  ~Pool() {
774
- TRACE_POINT();
775
- destroy();
841
+ if (lifeStatus != SHUT_DOWN) {
842
+ P_BUG("You must call Pool::destroy() before actually destroying the Pool object!");
843
+ }
844
+ }
845
+
846
+ void initialize() {
847
+ LockGuard l(syncher);
848
+ interruptableThreads.create_thread(
849
+ boost::bind(collectAnalytics, shared_from_this()),
850
+ "Pool analytics collector",
851
+ POOL_HELPER_THREAD_STACK_SIZE
852
+ );
853
+ interruptableThreads.create_thread(
854
+ boost::bind(garbageCollect, shared_from_this()),
855
+ "Pool garbage collector",
856
+ POOL_HELPER_THREAD_STACK_SIZE
857
+ );
776
858
  }
777
859
 
778
860
  void initDebugging() {
@@ -782,34 +864,29 @@ public:
782
864
 
783
865
  void destroy() {
784
866
  TRACE_POINT();
785
- libev->stop(garbageCollectionTimer);
786
- libev->stop(analyticsCollectionTimer);
867
+ ScopedLock lock(syncher);
868
+ assert(lifeStatus == ALIVE);
869
+
870
+ lifeStatus = SHUTTING_DOWN;
871
+
872
+ while (!superGroups.empty()) {
873
+ string name = superGroups.begin()->second->name;
874
+ lock.unlock();
875
+ detachSuperGroupByName(name);
876
+ lock.lock();
877
+ }
787
878
 
788
879
  UPDATE_TRACE_POINT();
880
+ lock.unlock();
789
881
  interruptableThreads.interrupt_and_join_all();
790
882
  nonInterruptableThreads.join_all();
883
+ lock.lock();
884
+
885
+ lifeStatus = SHUT_DOWN;
791
886
 
792
887
  UPDATE_TRACE_POINT();
793
- ScopedLock l(syncher);
794
- SuperGroupMap::iterator it;
795
- vector<SuperGroupPtr>::iterator it2;
796
- vector<SuperGroupPtr> superGroupsToDetach;
797
- vector<Callback> actions;
798
- for (it = superGroups.begin(); it != superGroups.end(); it++) {
799
- superGroupsToDetach.push_back(it->second);
800
- }
801
- for (it2 = superGroupsToDetach.begin(); it2 != superGroupsToDetach.end(); it2++) {
802
- detachSuperGroup(*it2, false, &actions);
803
- }
804
-
805
888
  verifyInvariants();
806
889
  verifyExpensiveInvariants();
807
- l.unlock();
808
- runAllActions(actions);
809
-
810
- // detachSuperGroup() may launch additional threads, so wait for them.
811
- interruptableThreads.interrupt_and_join_all();
812
- nonInterruptableThreads.join_all();
813
890
  }
814
891
 
815
892
  // 'lockNow == false' may only be used during unit tests. Normally we
@@ -817,6 +894,7 @@ public:
817
894
  void asyncGet(const Options &options, const GetCallback &callback, bool lockNow = true) {
818
895
  DynamicScopedLock lock(syncher, lockNow);
819
896
 
897
+ assert(lifeStatus == ALIVE);
820
898
  verifyInvariants();
821
899
  P_TRACE(2, "asyncGet(appRoot=" << options.appRoot << ")");
822
900
 
@@ -965,7 +1043,7 @@ public:
965
1043
  createSuperGroup(options);
966
1044
  }
967
1045
  }
968
- return get(options2, &ticket)->getProcess()->getGroup();
1046
+ return get(options2, &ticket)->getGroup();
969
1047
  }
970
1048
 
971
1049
  void setMax(unsigned int max) {
@@ -996,18 +1074,10 @@ public:
996
1074
  }
997
1075
  }
998
1076
 
999
- void activateNewMaxIdleTime() {
1000
- LockGuard l(syncher);
1001
- garbageCollectionTimer.stop();
1002
- garbageCollectionTimer.start(maxIdleTime / 1000000.0, 0.0);
1003
- }
1004
-
1005
1077
  void setMaxIdleTime(unsigned long long value) {
1006
- {
1007
- LockGuard l(syncher);
1008
- maxIdleTime = value;
1009
- }
1010
- libev->run(boost::bind(&Pool::activateNewMaxIdleTime, this));
1078
+ LockGuard l(syncher);
1079
+ maxIdleTime = value;
1080
+ garbageCollectionCond.notify_all();
1011
1081
  }
1012
1082
 
1013
1083
  unsigned int utilization(bool lock = true) const {
@@ -1062,6 +1132,11 @@ public:
1062
1132
  }
1063
1133
  return result;
1064
1134
  }
1135
+
1136
+ unsigned int getSuperGroupCount() const {
1137
+ LockGuard l(syncher);
1138
+ return superGroups.size();
1139
+ }
1065
1140
 
1066
1141
  SuperGroupPtr findSuperGroupBySecret(const string &secret, bool lock = true) const {
1067
1142
  DynamicScopedLock l(syncher, lock);
@@ -1086,21 +1161,29 @@ public:
1086
1161
  }
1087
1162
  return ProcessPtr();
1088
1163
  }
1089
-
1090
- bool detachSuperGroup(const SuperGroupPtr &superGroup, bool lock = true,
1091
- vector<Callback> *postLockActions = NULL)
1092
- {
1093
- assert(lock || postLockActions != NULL);
1094
- DynamicScopedLock l(syncher, lock);
1164
+
1165
+ bool detachSuperGroupByName(const string &name) {
1166
+ TRACE_POINT();
1167
+ ScopedLock l(syncher);
1095
1168
 
1096
- if (OXT_LIKELY(superGroup->getPool().get() == this)) {
1169
+ SuperGroupPtr superGroup = superGroups.get(name);
1170
+ if (OXT_LIKELY(superGroup != NULL)) {
1097
1171
  if (OXT_LIKELY(superGroups.get(superGroup->name) != NULL)) {
1172
+ UPDATE_TRACE_POINT();
1098
1173
  verifyInvariants();
1099
1174
  verifyExpensiveInvariants();
1100
1175
 
1101
1176
  vector<Callback> actions;
1102
-
1103
- forceDetachSuperGroup(superGroup, actions);
1177
+ shared_ptr<DetachSuperGroupWaitTicket> ticket =
1178
+ make_shared<DetachSuperGroupWaitTicket>();
1179
+ ExceptionPtr exception = copyException(
1180
+ GetAbortedException("The containg SuperGroup was detached."));
1181
+
1182
+ forceDetachSuperGroup(superGroup, actions,
1183
+ boost::bind(syncDetachSuperGroupCallback, _1, ticket));
1184
+ assignExceptionToGetWaiters(superGroup->getWaitlist,
1185
+ exception, actions);
1186
+ #if 0
1104
1187
  /* If this SuperGroup had get waiters, either
1105
1188
  * on itself or in one of its groups, then we must
1106
1189
  * reprocess them immediately. Detaching such a
@@ -1108,26 +1191,25 @@ public:
1108
1191
  */
1109
1192
  migrateSuperGroupGetWaitlistToPool(superGroup);
1110
1193
 
1194
+ UPDATE_TRACE_POINT();
1111
1195
  assignSessionsToGetWaiters(actions);
1196
+ #endif
1112
1197
  possiblySpawnMoreProcessesForExistingGroups();
1113
1198
 
1114
1199
  verifyInvariants();
1115
1200
  verifyExpensiveInvariants();
1116
1201
 
1117
- if (lock) {
1118
- l.unlock();
1119
- runAllActions(actions);
1120
- } else if (postLockActions->empty()) {
1121
- *postLockActions = actions;
1122
- } else {
1123
- postLockActions->reserve(postLockActions->size() +
1124
- actions.size());
1125
- for (unsigned int i = 0; i < actions.size(); i++) {
1126
- postLockActions->push_back(actions[i]);
1127
- }
1202
+ l.unlock();
1203
+ UPDATE_TRACE_POINT();
1204
+ runAllActions(actions);
1205
+ actions.clear();
1206
+
1207
+ UPDATE_TRACE_POINT();
1208
+ ScopedLock l2(ticket->syncher);
1209
+ while (!ticket->done) {
1210
+ ticket->cond.wait(l2);
1128
1211
  }
1129
-
1130
- return true;
1212
+ return ticket->result == SuperGroup::SUCCESS;
1131
1213
  } else {
1132
1214
  return false;
1133
1215
  }
@@ -1136,11 +1218,14 @@ public:
1136
1218
  }
1137
1219
  }
1138
1220
 
1139
- bool detachSuperGroup(const string &superGroupSecret) {
1140
- LockGuard l(syncher);
1221
+ bool detachSuperGroupBySecret(const string &superGroupSecret) {
1222
+ ScopedLock l(syncher);
1141
1223
  SuperGroupPtr superGroup = findSuperGroupBySecret(superGroupSecret, false);
1142
1224
  if (superGroup != NULL) {
1143
- return detachSuperGroup(superGroup, false);
1225
+ string name = superGroup->name;
1226
+ superGroup.reset();
1227
+ l.unlock();
1228
+ return detachSuperGroupByName(name);
1144
1229
  } else {
1145
1230
  return false;
1146
1231
  }
@@ -1174,23 +1259,63 @@ public:
1174
1259
  DisableResult disableProcess(const string &gupid) {
1175
1260
  ScopedLock l(syncher);
1176
1261
  ProcessPtr process = findProcessByGupid(gupid, false);
1177
- GroupPtr group = process->getGroup();
1178
- // Must be a shared_ptr to be interruption-safe.
1179
- shared_ptr<DisableWaitTicket> ticket = make_shared<DisableWaitTicket>();
1180
- DisableResult result = group->disable(process,
1181
- boost::bind(syncDisableProcessCallback, _1, _2, ticket));
1182
- group->verifyInvariants();
1183
- group->verifyExpensiveInvariants();
1184
- if (result == DR_DEFERRED) {
1185
- l.unlock();
1186
- ScopedLock l2(ticket->syncher);
1187
- while (!ticket->done) {
1188
- ticket->cond.wait(l2);
1262
+ if (process != NULL) {
1263
+ GroupPtr group = process->getGroup();
1264
+ // Must be a shared_ptr to be interruption-safe.
1265
+ shared_ptr<DisableWaitTicket> ticket = make_shared<DisableWaitTicket>();
1266
+ DisableResult result = group->disable(process,
1267
+ boost::bind(syncDisableProcessCallback, _1, _2, ticket));
1268
+ group->verifyInvariants();
1269
+ group->verifyExpensiveInvariants();
1270
+ if (result == DR_DEFERRED) {
1271
+ l.unlock();
1272
+ ScopedLock l2(ticket->syncher);
1273
+ while (!ticket->done) {
1274
+ ticket->cond.wait(l2);
1275
+ }
1276
+ return ticket->result;
1277
+ } else {
1278
+ return result;
1189
1279
  }
1190
- return ticket->result;
1191
1280
  } else {
1192
- return result;
1281
+ return DR_NOOP;
1282
+ }
1283
+ }
1284
+
1285
+ unsigned int restartGroupsByAppRoot(const string &appRoot) {
1286
+ ScopedLock l(syncher);
1287
+ SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
1288
+ unsigned int result = 0;
1289
+
1290
+ for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
1291
+ const SuperGroupPtr &superGroup = sg_it->second;
1292
+ foreach (const GroupPtr &group, superGroup->groups) {
1293
+ if (group->options.appRoot == appRoot) {
1294
+ result++;
1295
+ if (!group->restarting()) {
1296
+ group->restart(group->options);
1297
+ }
1298
+ }
1299
+ }
1193
1300
  }
1301
+
1302
+ return result;
1303
+ }
1304
+
1305
+ unsigned int restartSuperGroupsByAppRoot(const string &appRoot) {
1306
+ ScopedLock l(syncher);
1307
+ SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
1308
+ unsigned int result = 0;
1309
+
1310
+ for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
1311
+ const SuperGroupPtr &superGroup = sg_it->second;
1312
+ if (superGroup->options.appRoot == appRoot) {
1313
+ result++;
1314
+ superGroup->restart(superGroup->options);
1315
+ }
1316
+ }
1317
+
1318
+ return result;
1194
1319
  }
1195
1320
 
1196
1321
  /**
@@ -1238,6 +1363,9 @@ public:
1238
1363
  if (group != NULL) {
1239
1364
  result << group->name << ":" << endl;
1240
1365
  result << " App root: " << group->options.appRoot << endl;
1366
+ if (group->restarting()) {
1367
+ result << " (restarting...)" << endl;
1368
+ }
1241
1369
  if (group->spawning()) {
1242
1370
  result << " (spawning new process...)" << endl;
1243
1371
  }
@@ -1251,8 +1379,8 @@ public:
1251
1379
  return result.str();
1252
1380
  }
1253
1381
 
1254
- string toXml(bool includeSecrets = true) const {
1255
- LockGuard l(syncher);
1382
+ string toXml(bool includeSecrets = true, bool lock = true) const {
1383
+ DynamicScopedLock l(syncher, lock);
1256
1384
  stringstream result;
1257
1385
  SuperGroupMap::const_iterator sg_it;
1258
1386
  vector<GroupPtr>::const_iterator g_it;
@@ -1265,11 +1393,25 @@ public:
1265
1393
  result << "<max>" << max << "</max>";
1266
1394
  result << "<utilization>" << utilization(false) << "</utilization>";
1267
1395
  result << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
1396
+
1397
+ if (includeSecrets) {
1398
+ vector<GetWaiter>::const_iterator w_it, w_end = getWaitlist.end();
1399
+
1400
+ result << "<get_wait_list>";
1401
+ for (w_it = getWaitlist.begin(); w_it != w_end; w_it++) {
1402
+ const GetWaiter &waiter = *w_it;
1403
+ result << "<item>";
1404
+ result << "<app_group_name>" << escapeForXml(waiter.options.getAppGroupName()) << "</app_group_name>";
1405
+ result << "</item>";
1406
+ }
1407
+ result << "</get_wait_list>";
1408
+ }
1268
1409
 
1269
1410
  result << "<supergroups>";
1270
1411
  for (sg_it = superGroups.begin(); sg_it != superGroups.end(); sg_it++) {
1271
1412
  const SuperGroupPtr &superGroup = sg_it->second;
1272
1413
 
1414
+ result << "<supergroup>";
1273
1415
  result << "<name>" << escapeForXml(superGroup->name) << "</name>";
1274
1416
  result << "<state>" << superGroup->getStateName() << "</state>";
1275
1417
  result << "<get_wait_list_size>" << superGroup->getWaitlist.size() << "</get_wait_list_size>";
@@ -1289,6 +1431,7 @@ public:
1289
1431
  group->inspectXml(result, includeSecrets);
1290
1432
  result << "</group>";
1291
1433
  }
1434
+ result << "</supergroup>";
1292
1435
  }
1293
1436
  result << "</supergroups>";
1294
1437