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) 2011, 2012 Phusion
3
+ * Copyright (c) 2011-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -23,7 +23,9 @@
23
23
  * THE SOFTWARE.
24
24
  */
25
25
  #include <typeinfo>
26
+ #include <algorithm>
26
27
  #include <boost/make_shared.hpp>
28
+ #include <boost/date_time/posix_time/posix_time_types.hpp>
27
29
  #include <oxt/backtrace.hpp>
28
30
  #include <ApplicationPool2/Pool.h>
29
31
  #include <ApplicationPool2/SuperGroup.h>
@@ -31,6 +33,7 @@
31
33
  #include <ApplicationPool2/PipeWatcher.h>
32
34
  #include <Exceptions.h>
33
35
  #include <MessageReadersWriters.h>
36
+ #include <Utils/ScopeGuard.h>
34
37
 
35
38
  namespace Passenger {
36
39
  namespace ApplicationPool2 {
@@ -71,6 +74,7 @@ copyException(const tracable_exception &e) {
71
74
  TRY_COPY_EXCEPTION(ConfigurationException);
72
75
 
73
76
  TRY_COPY_EXCEPTION(SpawnException);
77
+ TRY_COPY_EXCEPTION(GetAbortedException);
74
78
 
75
79
  TRY_COPY_EXCEPTION(InvalidModeStringException);
76
80
  TRY_COPY_EXCEPTION(ArgumentException);
@@ -110,6 +114,7 @@ rethrowException(const ExceptionPtr &e) {
110
114
  TRY_RETHROW_EXCEPTION(ConfigurationException);
111
115
 
112
116
  TRY_RETHROW_EXCEPTION(SpawnException);
117
+ TRY_RETHROW_EXCEPTION(GetAbortedException);
113
118
 
114
119
  TRY_RETHROW_EXCEPTION(InvalidModeStringException);
115
120
  TRY_RETHROW_EXCEPTION(ArgumentException);
@@ -167,10 +172,153 @@ SuperGroup::createInterruptableThread(const function<void ()> &func, const strin
167
172
  }
168
173
 
169
174
  void
170
- SuperGroup::createNonInterruptableThread(const function<void ()> &func, const string &name,
171
- unsigned int stackSize)
172
- {
173
- getPool()->nonInterruptableThreads.create_thread(func, name, stackSize);
175
+ SuperGroup::realDoInitialize(const Options &options, unsigned int generation) {
176
+ vector<ComponentInfo> componentInfos;
177
+ vector<ComponentInfo>::const_iterator it;
178
+ ExceptionPtr exception;
179
+
180
+ P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background...");
181
+ try {
182
+ componentInfos = loadComponentInfos(options);
183
+ } catch (const tracable_exception &e) {
184
+ exception = copyException(e);
185
+ }
186
+ if (componentInfos.empty() && exception == NULL) {
187
+ string message = "The directory " +
188
+ options.appRoot +
189
+ " does not seem to contain a web application.";
190
+ exception = make_shared<SpawnException>(
191
+ message, message, false);
192
+ }
193
+
194
+ PoolPtr pool = getPool();
195
+ Pool::DebugSupportPtr debug = pool->debugSupport;
196
+
197
+ vector<Callback> actions;
198
+ {
199
+ if (debug != NULL && debug->superGroup) {
200
+ debug->debugger->send("About to finish SuperGroup initialization");
201
+ debug->messages->recv("Proceed with initializing SuperGroup");
202
+ }
203
+
204
+ unique_lock<boost::mutex> lock(getPoolSyncher(pool));
205
+ this_thread::disable_interruption di;
206
+ this_thread::disable_syscall_interruption dsi;
207
+ NOT_EXPECTING_EXCEPTIONS();
208
+ if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) {
209
+ return;
210
+ }
211
+ P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock");
212
+ assert(state == INITIALIZING);
213
+ verifyInvariants();
214
+
215
+ if (componentInfos.empty()) {
216
+ /* Somehow initialization failed. Maybe something has deleted
217
+ * the supergroup files while we're working.
218
+ */
219
+ assert(exception != NULL);
220
+ setState(DESTROYED);
221
+
222
+ actions.reserve(getWaitlist.size());
223
+ while (!getWaitlist.empty()) {
224
+ const GetWaiter &waiter = getWaitlist.front();
225
+ actions.push_back(boost::bind(waiter.callback,
226
+ SessionPtr(), exception));
227
+ getWaitlist.pop();
228
+ }
229
+ } else {
230
+ for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
231
+ const ComponentInfo &info = *it;
232
+ GroupPtr group = make_shared<Group>(shared_from_this(),
233
+ options, info);
234
+ groups.push_back(group);
235
+ if (info.isDefault) {
236
+ defaultGroup = group.get();
237
+ }
238
+ }
239
+
240
+ setState(READY);
241
+ assignGetWaitlistToGroups(actions);
242
+ }
243
+
244
+ verifyInvariants();
245
+ P_TRACE(2, "Done initializing SuperGroup " << inspect());
246
+ }
247
+ this_thread::disable_interruption di;
248
+ this_thread::disable_syscall_interruption dsi;
249
+ runAllActions(actions);
250
+ }
251
+
252
+ void
253
+ SuperGroup::realDoRestart(const Options &options, unsigned int generation) {
254
+ TRACE_POINT();
255
+ vector<ComponentInfo> componentInfos = loadComponentInfos(options);
256
+ vector<ComponentInfo>::const_iterator it;
257
+
258
+ PoolPtr pool = getPool();
259
+ Pool::DebugSupportPtr debug = pool->debugSupport;
260
+ if (debug != NULL && debug->superGroup) {
261
+ debug->debugger->send("About to finish SuperGroup restart");
262
+ debug->messages->recv("Proceed with restarting SuperGroup");
263
+ }
264
+
265
+ unique_lock<boost::mutex> lock(getPoolSyncher(pool));
266
+ if (OXT_UNLIKELY(this->generation != generation)) {
267
+ return;
268
+ }
269
+
270
+ assert(state == RESTARTING);
271
+ verifyInvariants();
272
+
273
+ vector<GroupPtr> allGroups;
274
+ vector<GroupPtr> updatedGroups;
275
+ vector<GroupPtr> newGroups;
276
+ vector<GroupPtr>::const_iterator g_it;
277
+ vector<Callback> actions;
278
+ this->options = options;
279
+
280
+ // Update the component information for existing groups.
281
+ UPDATE_TRACE_POINT();
282
+ for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
283
+ const ComponentInfo &info = *it;
284
+ pair<GroupPtr, unsigned int> result =
285
+ findGroupCorrespondingToComponent(groups, info);
286
+ GroupPtr group = result.first;
287
+ if (group != NULL) {
288
+ unsigned int index = result.second;
289
+ group->componentInfo = info;
290
+ updatedGroups.push_back(group);
291
+ groups[index].reset();
292
+ } else {
293
+ // This is not an existing group but a new one,
294
+ // so create it.
295
+ group = make_shared<Group>(shared_from_this(),
296
+ options, info);
297
+ newGroups.push_back(group);
298
+ }
299
+ // allGroups must be in the same order as componentInfos.
300
+ allGroups.push_back(group);
301
+ }
302
+
303
+ // Some components might have been deleted, so delete the
304
+ // corresponding groups.
305
+ detachAllGroups(groups, actions);
306
+
307
+ // Tell all previous existing groups to restart.
308
+ for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) {
309
+ GroupPtr group = *g_it;
310
+ group->restart(options);
311
+ }
312
+
313
+ groups = allGroups;
314
+ defaultGroup = findDefaultGroup(allGroups);
315
+ setState(READY);
316
+ assignGetWaitlistToGroups(actions);
317
+
318
+ UPDATE_TRACE_POINT();
319
+ verifyInvariants();
320
+ lock.unlock();
321
+ runAllActions(actions);
174
322
  }
175
323
 
176
324
 
@@ -184,8 +332,10 @@ Group::Group(const SuperGroupPtr &_superGroup, const Options &options, const Com
184
332
  disablingCount = 0;
185
333
  disabledCount = 0;
186
334
  spawner = getPool()->spawnerFactory->create(options);
335
+ restartsInitiated = 0;
187
336
  m_spawning = false;
188
337
  m_restarting = false;
338
+ lifeStatus = ALIVE;
189
339
  if (options.restartDir.empty()) {
190
340
  restartFile = options.appRoot + "/tmp/restart.txt";
191
341
  alwaysRestartFile = options.appRoot + "/always_restart.txt";
@@ -194,23 +344,22 @@ Group::Group(const SuperGroupPtr &_superGroup, const Options &options, const Com
194
344
  alwaysRestartFile = options.restartDir + "/always_restart.txt";
195
345
  }
196
346
  resetOptions(options);
347
+
348
+ detachedProcessesCheckerActive = false;
197
349
  }
198
350
 
199
- PoolPtr
200
- Group::getPool() const {
201
- SuperGroupPtr superGroup = getSuperGroup();
202
- if (superGroup != NULL) {
203
- return superGroup->getPool();
204
- } else {
205
- return PoolPtr();
351
+ Group::~Group() {
352
+ LifeStatus lifeStatus = getLifeStatus();
353
+ if (OXT_UNLIKELY(lifeStatus == ALIVE)) {
354
+ P_BUG("You must call Group::shutdown() before destroying a Group.");
206
355
  }
356
+ assert(lifeStatus == SHUT_DOWN);
357
+ assert(!detachedProcessesCheckerActive);
207
358
  }
208
359
 
209
- void
210
- Group::createInterruptableThread(const function<void ()> &func, const string &name,
211
- unsigned int stackSize)
212
- {
213
- getPool()->interruptableThreads.create_thread(func, name, stackSize);
360
+ PoolPtr
361
+ Group::getPool() const {
362
+ return getSuperGroup()->getPool();
214
363
  }
215
364
 
216
365
  void
@@ -220,14 +369,9 @@ Group::onSessionInitiateFailure(const ProcessPtr &process, Session *session) {
220
369
  TRACE_POINT();
221
370
  // Standard resource management boilerplate stuff...
222
371
  PoolPtr pool = getPool();
223
- if (OXT_UNLIKELY(pool == NULL)) {
224
- return;
225
- }
226
372
  unique_lock<boost::mutex> lock(pool->syncher);
227
- pool = getPool();
228
- if (OXT_UNLIKELY(pool == NULL) || process->detached()) {
229
- return;
230
- }
373
+ assert(!process->isShutDown());
374
+ assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
231
375
 
232
376
  UPDATE_TRACE_POINT();
233
377
  P_DEBUG("Could not initiate a session with process " <<
@@ -245,23 +389,19 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
245
389
  TRACE_POINT();
246
390
  // Standard resource management boilerplate stuff...
247
391
  PoolPtr pool = getPool();
248
- if (OXT_UNLIKELY(pool == NULL)) {
249
- return;
250
- }
251
392
  unique_lock<boost::mutex> lock(pool->syncher);
252
- pool = getPool();
253
- if (OXT_UNLIKELY(pool == NULL) || process->detached()) {
254
- return;
255
- }
256
-
393
+ assert(!process->isShutDown());
394
+ assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
395
+
257
396
  P_TRACE(2, "Session closed for process " << process->inspect());
258
397
  verifyInvariants();
259
398
  UPDATE_TRACE_POINT();
260
399
 
261
400
  /* Update statistics. */
262
401
  process->sessionClosed(session);
402
+ Process::LifeStatus lifeStatus = process->getLifeStatus();
263
403
  assert(process->enabled == Process::ENABLED || process->enabled == Process::DISABLING);
264
- if (process->enabled == Process::ENABLED) {
404
+ if (process->enabled == Process::ENABLED && lifeStatus == Process::ALIVE) {
265
405
  pqueue.decrease(process->pqHandle, process->utilization());
266
406
  }
267
407
 
@@ -270,6 +410,16 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
270
410
  */
271
411
  assert(!process->atFullUtilization());
272
412
 
413
+ if (lifeStatus == Process::SHUTTING_DOWN) {
414
+ UPDATE_TRACE_POINT();
415
+ if (process->canBeShutDown()) {
416
+ shutdownAndRemoveProcess(process);
417
+ }
418
+ verifyInvariants();
419
+ verifyExpensiveInvariants();
420
+ return;
421
+ }
422
+
273
423
  bool detachingBecauseOfMaxRequests = false;
274
424
  bool detachingBecauseCapacityNeeded = false;
275
425
  bool shouldDetach =
@@ -343,15 +493,9 @@ void
343
493
  Group::requestOOBW(const ProcessPtr &process) {
344
494
  // Standard resource management boilerplate stuff...
345
495
  PoolPtr pool = getPool();
346
- if (OXT_UNLIKELY(pool == NULL)) {
347
- return;
348
- }
349
496
  unique_lock<boost::mutex> lock(pool->syncher);
350
- pool = getPool();
351
- if (OXT_UNLIKELY(pool == NULL || process->detached())) {
352
- return;
353
- }
354
-
497
+ assert(isAlive());
498
+
355
499
  process->oobwRequested = true;
356
500
  }
357
501
 
@@ -366,12 +510,8 @@ Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult
366
510
 
367
511
  // Standard resource management boilerplate stuff...
368
512
  PoolPtr pool = getPool();
369
- if (OXT_UNLIKELY(pool == NULL)) {
370
- return;
371
- }
372
513
  unique_lock<boost::mutex> lock(pool->syncher);
373
- pool = getPool();
374
- if (OXT_UNLIKELY(pool == NULL || process->detached())) {
514
+ if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
375
515
  return;
376
516
  }
377
517
 
@@ -380,11 +520,7 @@ Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult
380
520
 
381
521
  void
382
522
  Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
383
- if (process->detached()) {
384
- return;
385
- }
386
- if (!process->oobwRequested) {
387
- // The process has not requested oobw, so nothing to do here.
523
+ if (!process->oobwRequested || !process->isAlive()) {
388
524
  return;
389
525
  }
390
526
  if (process->enabled == Process::ENABLED) {
@@ -404,7 +540,7 @@ Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
404
540
  assert(process->enabled == Process::DISABLED);
405
541
  assert(process->sessions == 0);
406
542
 
407
- createInterruptableThread(
543
+ interruptableThreads.create_thread(
408
544
  boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
409
545
  "OOB request thread for process " + process->inspect(),
410
546
  POOL_HELPER_THREAD_STACK_SIZE);
@@ -423,16 +559,11 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
423
559
  {
424
560
  // Standard resource management boilerplate stuff...
425
561
  PoolPtr pool = getPool();
426
- if (OXT_UNLIKELY(pool == NULL)) {
427
- return;
428
- }
429
562
  unique_lock<boost::mutex> lock(pool->syncher);
430
- pool = getPool();
431
- if (OXT_UNLIKELY(pool == NULL || process->detached())) {
563
+ if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
432
564
  return;
433
565
  }
434
566
 
435
- assert(!process->detached());
436
567
  assert(process->oobwRequested);
437
568
  assert(process->sessions == 0);
438
569
  assert(process->enabled == Process::DISABLED);
@@ -485,12 +616,8 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
485
616
  {
486
617
  // Standard resource management boilerplate stuff...
487
618
  PoolPtr pool = getPool();
488
- if (OXT_UNLIKELY(pool == NULL)) {
489
- return;
490
- }
491
619
  unique_lock<boost::mutex> lock(pool->syncher);
492
- pool = getPool();
493
- if (OXT_UNLIKELY(pool == NULL || process->detached())) {
620
+ if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
494
621
  return;
495
622
  }
496
623
 
@@ -507,30 +634,23 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
507
634
 
508
635
  // The 'self' parameter is for keeping the current Group object alive while this thread is running.
509
636
  void
510
- Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options) {
511
- try {
512
- spawnThreadRealMain(spawner, options);
513
- } catch (const thread_interrupted &) {
514
- // Return.
515
- }
637
+ Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options, unsigned int restartsInitiated) {
638
+ spawnThreadRealMain(spawner, options, restartsInitiated);
516
639
  }
517
640
 
518
641
  void
519
- Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
642
+ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, unsigned int restartsInitiated) {
520
643
  TRACE_POINT();
521
644
  this_thread::disable_interruption di;
522
645
  this_thread::disable_syscall_interruption dsi;
523
646
 
524
647
  PoolPtr pool = getPool();
525
- if (pool == NULL) {
526
- return;
527
- }
528
648
  Pool::DebugSupportPtr debug = pool->debugSupport;
529
649
 
530
650
  bool done = false;
531
651
  while (!done) {
532
652
  bool shouldFail = false;
533
- if (debug != NULL) {
653
+ if (debug != NULL && debug->spawning) {
534
654
  UPDATE_TRACE_POINT();
535
655
  this_thread::restore_interruption ri(di);
536
656
  this_thread::restore_syscall_interruption rsi(dsi);
@@ -562,6 +682,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
562
682
  throw SpawnException("Simulated failure");
563
683
  } else {
564
684
  process = spawner->spawn(options);
685
+ process->setGroup(shared_from_this());
565
686
  }
566
687
  } catch (const thread_interrupted &) {
567
688
  break;
@@ -572,23 +693,43 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
572
693
  }
573
694
 
574
695
  UPDATE_TRACE_POINT();
575
- pool = getPool();
576
- if (pool == NULL) {
577
- break;
578
- }
696
+ ScopeGuard guard(boost::bind(Process::maybeShutdown, process));
579
697
  unique_lock<boost::mutex> lock(pool->syncher);
580
- pool = getPool();
581
- if (pool == NULL) {
698
+
699
+ if (!isAlive()) {
700
+ if (process != NULL) {
701
+ P_DEBUG("Group is being shut down so dropping process " <<
702
+ process->inspect() << " which we just spawned and exiting spawn loop");
703
+ } else {
704
+ P_DEBUG("The group is being shut down. A process failed "
705
+ "to be spawned anyway, so ignoring this error and exiting "
706
+ "spawn loop");
707
+ }
708
+ // We stop immediately because any previously assumed invariants
709
+ // may have been violated.
710
+ break;
711
+ } else if (restartsInitiated != this->restartsInitiated) {
712
+ if (process != NULL) {
713
+ P_DEBUG("A restart was issued for the group, so dropping process " <<
714
+ process->inspect() << " which we just spawned and exiting spawn loop");
715
+ } else {
716
+ P_DEBUG("A restart was issued for the group. A process failed "
717
+ "to be spawned anyway, so ignoring this error and exiting "
718
+ "spawn loop");
719
+ }
720
+ // We stop immediately because any previously assumed invariants
721
+ // may have been violated.
582
722
  break;
583
723
  }
584
724
 
585
725
  verifyInvariants();
586
- assert(m_spawning || m_restarting);
587
-
726
+ assert(m_spawning);
727
+
588
728
  UPDATE_TRACE_POINT();
589
729
  vector<Callback> actions;
590
730
  if (process != NULL) {
591
731
  attach(process, actions);
732
+ guard.clear();
592
733
  if (getWaitlist.empty()) {
593
734
  pool->assignSessionsToGetWaiters(actions);
594
735
  } else {
@@ -604,7 +745,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
604
745
  if (enabledCount == 0) {
605
746
  enableAllDisablingProcesses(actions);
606
747
  }
607
- assignExceptionToGetWaiters(exception, actions);
748
+ Pool::assignExceptionToGetWaiters(getWaitlist, exception, actions);
608
749
  pool->assignSessionsToGetWaiters(actions);
609
750
  done = true;
610
751
  }
@@ -616,15 +757,10 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
616
757
 
617
758
  done = done
618
759
  || ((unsigned long) enabledCount >= options.minProcesses && getWaitlist.empty())
619
- || pool->atFullCapacity(false)
620
- || m_restarting;
760
+ || pool->atFullCapacity(false);
621
761
  m_spawning = !done;
622
762
  if (done) {
623
- if (m_restarting) {
624
- P_DEBUG("Spawn loop aborted because the group is being restarted");
625
- } else {
626
- P_DEBUG("Spawn loop done");
627
- }
763
+ P_DEBUG("Spawn loop done");
628
764
  } else {
629
765
  P_DEBUG("Continue spawning");
630
766
  }
@@ -637,7 +773,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
637
773
  UPDATE_TRACE_POINT();
638
774
  }
639
775
 
640
- if (debug != NULL) {
776
+ if (debug != NULL && debug->spawning) {
641
777
  debug->debugger->send("Spawn loop done");
642
778
  }
643
779
  }
@@ -649,6 +785,7 @@ Group::shouldSpawn() const {
649
785
  (unsigned long) enabledCount < options.minProcesses
650
786
  || (enabledCount > 0 && pqueue.top()->atFullCapacity())
651
787
  )
788
+ && isAlive()
652
789
  && !poolAtFullCapacity();
653
790
  }
654
791
 
@@ -661,8 +798,11 @@ void
661
798
  Group::restart(const Options &options) {
662
799
  vector<Callback> actions;
663
800
 
801
+ assert(isAlive());
664
802
  assert(!m_restarting);
665
803
  P_DEBUG("Restarting group " << name);
804
+ // Tell the restarter thread to exit as soon as possible.
805
+ restartsInitiated++;
666
806
  m_spawning = false;
667
807
  m_restarting = true;
668
808
  detachAll(actions);
@@ -692,15 +832,11 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
692
832
  SpawnerPtr newSpawner = spawnerFactory->create(options);
693
833
  SpawnerPtr oldSpawner;
694
834
 
695
- // Standard resource management boilerplate stuff...
696
835
  UPDATE_TRACE_POINT();
697
836
  PoolPtr pool = getPool();
698
- if (OXT_UNLIKELY(pool == NULL)) {
699
- return;
700
- }
701
837
 
702
838
  Pool::DebugSupportPtr debug = pool->debugSupport;
703
- if (debug != NULL) {
839
+ if (debug != NULL && debug->restarting) {
704
840
  this_thread::restore_interruption ri(di);
705
841
  this_thread::restore_syscall_interruption rsi(dsi);
706
842
  this_thread::interruption_point();
@@ -708,9 +844,9 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
708
844
  debug->messages->recv("Finish restarting");
709
845
  }
710
846
 
711
- LockGuard l(pool->syncher);
712
- pool = getPool();
713
- if (OXT_UNLIKELY(pool == NULL)) {
847
+ ScopedLock l(pool->syncher);
848
+ if (!isAlive()) {
849
+ P_DEBUG("Group " << name << " is shutting down, so aborting restart");
714
850
  return;
715
851
  }
716
852
 
@@ -728,9 +864,93 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
728
864
  if (!getWaitlist.empty()) {
729
865
  spawn();
730
866
  }
731
- P_DEBUG("Restart of group " << name << " done");
732
867
  verifyInvariants();
733
- // oldSpawner will now be destroyed, outside the lock.
868
+
869
+ l.unlock();
870
+ oldSpawner.reset();
871
+ P_DEBUG("Restart of group " << name << " done");
872
+ if (debug != NULL && debug->restarting) {
873
+ debug->debugger->send("Restarting done");
874
+ }
875
+ }
876
+
877
+ void
878
+ Group::startCheckingDetachedProcesses(bool immediately) {
879
+ if (!detachedProcessesCheckerActive && !detachedProcesses.empty()) {
880
+ P_DEBUG("Starting detached processes checker");
881
+ getPool()->nonInterruptableThreads.create_thread(
882
+ boost::bind(&Group::detachedProcessesCheckerMain, this, shared_from_this()),
883
+ "Detached processes checker: " + name,
884
+ POOL_HELPER_THREAD_STACK_SIZE
885
+ );
886
+ detachedProcessesCheckerActive = true;
887
+ } else if (detachedProcessesCheckerActive && immediately) {
888
+ detachedProcessesCheckerCond.notify_all();
889
+ }
890
+ }
891
+
892
+ void
893
+ Group::detachedProcessesCheckerMain(GroupPtr self) {
894
+ TRACE_POINT();
895
+ PoolPtr pool = getPool();
896
+
897
+ while (!this_thread::interruption_requested()) {
898
+ unique_lock<boost::mutex> lock(pool->syncher);
899
+ assert(detachedProcessesCheckerActive);
900
+
901
+ if (getLifeStatus() == SHUT_DOWN) {
902
+ P_DEBUG("Stopping detached processes checker");
903
+ detachedProcessesCheckerActive = false;
904
+ break;
905
+ }
906
+
907
+ UPDATE_TRACE_POINT();
908
+ if (!detachedProcesses.empty()) {
909
+ P_TRACE(2, "Checking whether any detached processes have exited...");
910
+ ProcessList::iterator it = detachedProcesses.begin();
911
+ ProcessList::iterator end = detachedProcesses.end();
912
+ while (it != end) {
913
+ const ProcessPtr process = *it;
914
+ if (process->canBeShutDown()) {
915
+ it++;
916
+ P_DEBUG("Detached process " << process->inspect() << " has exited.");
917
+ shutdownAndRemoveProcess(process);
918
+ } else {
919
+ P_DEBUG("Detached process " << process->inspect() << " not yet exited. "
920
+ "sessions = " << process->sessions);
921
+ it++;
922
+ }
923
+ }
924
+ }
925
+
926
+ UPDATE_TRACE_POINT();
927
+ if (detachedProcesses.empty()) {
928
+ UPDATE_TRACE_POINT();
929
+ P_DEBUG("Stopping detached processes checker");
930
+ detachedProcessesCheckerActive = false;
931
+
932
+ vector<Callback> actions;
933
+ if (shutdownCanFinish()) {
934
+ UPDATE_TRACE_POINT();
935
+ finishShutdown(actions);
936
+ }
937
+
938
+ verifyInvariants();
939
+ verifyExpensiveInvariants();
940
+ lock.unlock();
941
+ UPDATE_TRACE_POINT();
942
+ runAllActions(actions);
943
+ break;
944
+ } else {
945
+ UPDATE_TRACE_POINT();
946
+ verifyInvariants();
947
+ verifyExpensiveInvariants();
948
+ }
949
+
950
+ UPDATE_TRACE_POINT();
951
+ detachedProcessesCheckerCond.timed_wait(lock,
952
+ posix_time::milliseconds(10));
953
+ }
734
954
  }
735
955
 
736
956
  bool
@@ -763,26 +983,21 @@ Group::generateSecret(const SuperGroupPtr &superGroup) {
763
983
  }
764
984
 
765
985
 
766
- // Thread-safe
767
986
  SuperGroupPtr
768
987
  Process::getSuperGroup() const {
769
- GroupPtr group = getGroup();
770
- if (group != NULL) {
771
- return group->getSuperGroup();
772
- } else {
773
- return SuperGroupPtr();
774
- }
988
+ assert(getLifeStatus() != SHUT_DOWN);
989
+ return getGroup()->getSuperGroup();
775
990
  }
776
991
 
777
992
  string
778
993
  Process::inspect() const {
994
+ assert(getLifeStatus() != SHUT_DOWN);
779
995
  stringstream result;
780
- result << "(pid=";
781
- result << pid;
996
+ result << "(pid=" << pid;
782
997
  GroupPtr group = getGroup();
783
998
  if (group != NULL) {
784
- result << ", group=";
785
- result << group->name;
999
+ // This Process hasn't been attached to a Group yet.
1000
+ result << ", group=" << group->name;
786
1001
  }
787
1002
  result << ")";
788
1003
  return result.str();
@@ -791,68 +1006,113 @@ Process::inspect() const {
791
1006
 
792
1007
  const string &
793
1008
  Session::getConnectPassword() const {
794
- return process->connectPassword;
1009
+ return getProcess()->connectPassword;
795
1010
  }
796
1011
 
797
1012
  pid_t
798
1013
  Session::getPid() const {
799
- return process->pid;
1014
+ return getProcess()->pid;
800
1015
  }
801
1016
 
802
1017
  const string &
803
1018
  Session::getGupid() const {
804
- return process->gupid;
1019
+ return getProcess()->gupid;
805
1020
  }
806
1021
 
807
1022
  const GroupPtr
808
1023
  Session::getGroup() const {
809
- return process->getGroup();
1024
+ return getProcess()->getGroup();
810
1025
  }
811
1026
 
812
1027
  void
813
1028
  Session::requestOOBW() {
814
- GroupPtr group = process->getGroup();
815
- if (OXT_UNLIKELY(group != NULL)) {
816
- group->requestOOBW(process);
817
- }
1029
+ ProcessPtr process = getProcess();
1030
+ assert(!process->isShutDown());
1031
+ process->getGroup()->requestOOBW(process);
818
1032
  }
819
1033
 
820
- PipeWatcher::PipeWatcher(
821
- const SafeLibevPtr &_libev,
822
- const FileDescriptor &_fd,
823
- int _fdToForwardTo)
824
- : libev(_libev),
825
- fd(_fd),
826
- fdToForwardTo(_fdToForwardTo)
1034
+
1035
+ PipeWatcher::DataCallback PipeWatcher::onData;
1036
+
1037
+ PipeWatcher::PipeWatcher(const FileDescriptor &_fd, const char *_name, pid_t _pid, bool _print)
1038
+ : fd(_fd),
1039
+ name(_name),
1040
+ pid(_pid),
1041
+ print(_print)
827
1042
  {
828
- watcher.set(fd, ev::READ);
829
- watcher.set<PipeWatcher, &PipeWatcher::onReadable>(this);
1043
+ started = false;
830
1044
  }
831
1045
 
832
- PipeWatcher::~PipeWatcher() {
833
- libev->stop(watcher);
1046
+ void
1047
+ PipeWatcher::initialize() {
1048
+ oxt::thread(boost::bind(threadMain, shared_from_this()),
1049
+ "PipeWatcher: PID " + toString(pid) + " " + name + ", fd " + toString(fd),
1050
+ POOL_HELPER_THREAD_STACK_SIZE);
834
1051
  }
835
1052
 
836
1053
  void
837
1054
  PipeWatcher::start() {
838
- selfPointer = shared_from_this();
839
- libev->start(watcher);
1055
+ lock_guard<boost::mutex> lock(startSyncher);
1056
+ started = true;
1057
+ startCond.notify_all();
1058
+ }
1059
+
1060
+ void
1061
+ PipeWatcher::threadMain(shared_ptr<PipeWatcher> self) {
1062
+ TRACE_POINT();
1063
+ self->threadMain();
840
1064
  }
841
1065
 
842
1066
  void
843
- PipeWatcher::onReadable(ev::io &io, int revents) {
844
- char buf[1024 * 8];
845
- ssize_t ret;
846
-
847
- ret = read(fd, buf, sizeof(buf));
848
- if (ret <= 0) {
849
- if (ret != -1 || errno != EAGAIN) {
850
- libev->stop(watcher);
851
- selfPointer.reset();
1067
+ PipeWatcher::threadMain() {
1068
+ TRACE_POINT();
1069
+ {
1070
+ unique_lock<boost::mutex> lock(startSyncher);
1071
+ while (!started) {
1072
+ startCond.wait(lock);
1073
+ }
1074
+ }
1075
+
1076
+ UPDATE_TRACE_POINT();
1077
+ while (!this_thread::interruption_requested()) {
1078
+ char buf[1024 * 8];
1079
+ ssize_t ret;
1080
+
1081
+ UPDATE_TRACE_POINT();
1082
+ ret = syscalls::read(fd, buf, sizeof(buf));
1083
+ if (ret == 0) {
1084
+ break;
1085
+ } else if (ret == -1) {
1086
+ UPDATE_TRACE_POINT();
1087
+ if (errno == ECONNRESET) {
1088
+ break;
1089
+ } else if (errno != EAGAIN) {
1090
+ int e = errno;
1091
+ P_WARN("Cannot read from process " << pid << " " << name <<
1092
+ ": " << strerror(e) << " (errno=" << e << ")");
1093
+ break;
1094
+ }
1095
+ } else if (ret == 1 && buf[0] == '\n') {
1096
+ UPDATE_TRACE_POINT();
1097
+ P_LOG(print ? LVL_INFO : LVL_DEBUG,
1098
+ "[App " << pid << " " << name << "] ");
1099
+ } else {
1100
+ UPDATE_TRACE_POINT();
1101
+ vector<StaticString> lines;
1102
+ ssize_t ret2 = ret;
1103
+ if (ret2 > 0 && buf[ret2 - 1] == '\n') {
1104
+ ret2--;
1105
+ }
1106
+ split(StaticString(buf, ret2), '\n', lines);
1107
+ foreach (const StaticString line, lines) {
1108
+ P_LOG(print ? LVL_INFO : LVL_DEBUG,
1109
+ "[App " << pid << " " << name << "] " << line);
1110
+ }
1111
+ }
1112
+
1113
+ if (onData != NULL) {
1114
+ onData(buf, ret);
852
1115
  }
853
- } else if (fdToForwardTo != -1) {
854
- // Don't care about errors.
855
- write(fdToForwardTo, buf, ret);
856
1116
  }
857
1117
  }
858
1118