passenger 4.0.14 → 4.0.16

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 (121) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +69 -0
  3. data/bin/passenger-install-apache2-module +7 -1
  4. data/bin/passenger-install-nginx-module +18 -1
  5. data/build/apache2.rb +25 -1
  6. data/build/basics.rb +7 -4
  7. data/build/debian.rb +72 -25
  8. data/build/nginx.rb +24 -0
  9. data/build/packaging.rb +45 -3
  10. data/debian.template/{control → control.template} +17 -8
  11. data/debian.template/ruby-passenger.install.template +1 -0
  12. data/debian.template/rules.template +9 -3
  13. data/dev/run_travis.sh +14 -0
  14. data/doc/Users guide Apache.idmap.txt +64 -48
  15. data/doc/Users guide Apache.txt +127 -93
  16. data/doc/Users guide Nginx.idmap.txt +46 -28
  17. data/doc/Users guide Nginx.txt +100 -12
  18. data/doc/images/{conservative_spawning.png → direct_spawning.png} +0 -0
  19. data/doc/images/{conservative_spawning.svg → direct_spawning.svg} +0 -0
  20. data/doc/users_guide_snippets/installation.txt +107 -66
  21. data/doc/users_guide_snippets/passenger_spawn_method.txt +1 -1
  22. data/doc/users_guide_snippets/rvm_helper_tool.txt +0 -12
  23. data/doc/users_guide_snippets/tips.txt +70 -3
  24. data/doc/users_guide_snippets/under_the_hood/page_caching_support.txt +2 -0
  25. data/doc/users_guide_snippets/under_the_hood/relationship_with_ruby.txt +114 -0
  26. data/ext/apache2/Configuration.cpp +53 -101
  27. data/ext/apache2/Configuration.hpp +19 -41
  28. data/ext/apache2/ConfigurationCommands.cpp +95 -0
  29. data/ext/apache2/ConfigurationCommands.cpp.erb +91 -0
  30. data/ext/apache2/ConfigurationFields.hpp +59 -0
  31. data/ext/apache2/ConfigurationFields.hpp.erb +89 -0
  32. data/ext/apache2/ConfigurationSetters.cpp +223 -0
  33. data/ext/apache2/ConfigurationSetters.cpp.erb +126 -0
  34. data/ext/apache2/CreateDirConfig.cpp +50 -0
  35. data/ext/apache2/CreateDirConfig.cpp.erb +71 -0
  36. data/ext/apache2/Hooks.cpp +6 -0
  37. data/ext/apache2/MergeDirConfig.cpp +103 -0
  38. data/ext/apache2/MergeDirConfig.cpp.erb +81 -0
  39. data/ext/common/ApplicationPool2/AppTypes.cpp +2 -0
  40. data/ext/common/ApplicationPool2/AppTypes.h +2 -0
  41. data/ext/common/ApplicationPool2/Common.h +1 -1
  42. data/ext/common/ApplicationPool2/Group.h +56 -7
  43. data/ext/common/ApplicationPool2/Implementation.cpp +133 -31
  44. data/ext/common/ApplicationPool2/Options.h +23 -2
  45. data/ext/common/ApplicationPool2/Pool.h +8 -1
  46. data/ext/common/ApplicationPool2/Process.h +9 -0
  47. data/ext/common/ApplicationPool2/Session.h +1 -0
  48. data/ext/common/ApplicationPool2/Spawner.h +21 -19
  49. data/ext/common/ApplicationPool2/SuperGroup.h +1 -1
  50. data/ext/common/Constants.h +21 -17
  51. data/ext/common/Constants.h.erb +1 -1
  52. data/ext/common/Exceptions.h +19 -0
  53. data/ext/common/ServerInstanceDir.h +8 -4
  54. data/ext/common/Utils.cpp +33 -1
  55. data/ext/common/Utils.h +14 -0
  56. data/ext/common/Utils/StrIntUtils.cpp +16 -0
  57. data/ext/common/Utils/StrIntUtils.h +5 -0
  58. data/ext/common/agents/HelperAgent/Main.cpp +5 -5
  59. data/ext/common/agents/HelperAgent/RequestHandler.h +94 -45
  60. data/ext/common/agents/LoggingAgent/Main.cpp +10 -26
  61. data/ext/common/agents/Watchdog/Main.cpp +4 -15
  62. data/ext/nginx/CacheLocationConfig.c +501 -0
  63. data/ext/nginx/CacheLocationConfig.c.erb +140 -0
  64. data/ext/nginx/Configuration.c +29 -453
  65. data/ext/nginx/Configuration.h +3 -21
  66. data/ext/nginx/ConfigurationCommands.c +501 -0
  67. data/ext/nginx/ConfigurationCommands.c.erb +136 -0
  68. data/ext/nginx/ConfigurationFields.h +89 -0
  69. data/ext/nginx/ConfigurationFields.h.erb +85 -0
  70. data/ext/nginx/ContentHandler.c +3 -166
  71. data/ext/nginx/CreateLocationConfig.c +146 -0
  72. data/ext/nginx/CreateLocationConfig.c.erb +70 -0
  73. data/ext/nginx/MergeLocationConfig.c +166 -0
  74. data/ext/nginx/MergeLocationConfig.c.erb +72 -0
  75. data/ext/nginx/config +4 -0
  76. data/ext/oxt/detail/tracable_exception_disabled.hpp +21 -1
  77. data/ext/oxt/detail/tracable_exception_enabled.hpp +4 -1
  78. data/ext/oxt/implementation.cpp +7 -1
  79. data/ext/oxt/macros.hpp +9 -7
  80. data/helper-scripts/backtrace-sanitizer.rb +23 -0
  81. data/helper-scripts/classic-rails-loader.rb +23 -0
  82. data/helper-scripts/classic-rails-preloader.rb +23 -0
  83. data/helper-scripts/download_binaries/extconf.rb +10 -5
  84. data/helper-scripts/meteor-loader.rb +127 -0
  85. data/helper-scripts/node-loader.js +1 -1
  86. data/helper-scripts/rack-preloader.rb +23 -0
  87. data/helper-scripts/system-memory-stats.py +22 -0
  88. data/helper-scripts/touch-dir.sh +48 -0
  89. data/lib/phusion_passenger.rb +1 -1
  90. data/lib/phusion_passenger/apache2/config_options.rb +104 -0
  91. data/lib/phusion_passenger/constants.rb +8 -0
  92. data/lib/phusion_passenger/native_support.rb +9 -1
  93. data/lib/phusion_passenger/nginx/config_options.rb +328 -0
  94. data/lib/phusion_passenger/packaging.rb +2 -2
  95. data/lib/phusion_passenger/platform_info/apache.rb +8 -0
  96. data/lib/phusion_passenger/platform_info/compiler.rb +2 -2
  97. data/lib/phusion_passenger/rails3_extensions/init.rb +19 -4
  98. data/lib/phusion_passenger/ruby_core_enhancements.rb +4 -1
  99. data/lib/phusion_passenger/standalone/app_finder.rb +3 -2
  100. data/lib/phusion_passenger/standalone/command.rb +1 -0
  101. data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -6
  102. data/lib/phusion_passenger/standalone/runtime_locator.rb +9 -2
  103. data/lib/phusion_passenger/standalone/start_command.rb +45 -9
  104. data/lib/phusion_passenger/utils.rb +4 -1
  105. data/resources/templates/{nginx → installer_common}/run_installer_as_root.txt.erb +3 -1
  106. data/resources/templates/nginx/nginx_module_sources_not_available.txt.erb +6 -0
  107. data/resources/templates/standalone/config.erb +8 -8
  108. data/test/cxx/ApplicationPool2/PoolTest.cpp +120 -3
  109. data/test/cxx/RequestHandlerTest.cpp +66 -3
  110. data/test/integration_tests/native_packaging_spec.rb +41 -0
  111. data/test/integration_tests/nginx_tests.rb +1 -0
  112. data/test/integration_tests/standalone_tests.rb +4 -0
  113. data/test/ruby/shared/rails/analytics_logging_extensions_sharedspec.rb +22 -0
  114. data/test/stub/rails3.0/config/initializers/passenger.rb +2 -1
  115. data/test/stub/rails3.1/config/initializers/passenger.rb +2 -1
  116. data/test/stub/rails3.2/config/initializers/passenger.rb +2 -1
  117. data/test/stub/rails4.0/config/initializers/passenger.rb +5 -1
  118. data/test/stub/wsgi/passenger_wsgi.py +5 -0
  119. metadata +32 -7
  120. metadata.gz.asc +7 -7
  121. data/resources/templates/apache2/run_installer_as_root.txt.erb +0 -8
@@ -32,6 +32,8 @@ const AppTypeDefinition appTypeDefinitions[] = {
32
32
  { PAT_RACK, "rack", "config.ru", "Passenger RackApp" },
33
33
  { PAT_WSGI, "wsgi", "passenger_wsgi.py", "Passenger WsgiApp" },
34
34
  { PAT_CLASSIC_RAILS, "classic-rails", "config/environment.rb", "Passenger ClassicRailsApp" },
35
+ { PAT_NODE, "node", "passenger_node.js", "Passenger NodeApp" },
36
+ { PAT_METEOR, "meteor", ".meteor", "Passenger MeteorApp" },
35
37
  { PAT_NONE, NULL, NULL, NULL }
36
38
  };
37
39
 
@@ -42,6 +42,8 @@ typedef enum {
42
42
  PAT_RACK,
43
43
  PAT_WSGI,
44
44
  PAT_CLASSIC_RAILS,
45
+ PAT_NODE,
46
+ PAT_METEOR,
45
47
  PAT_NONE
46
48
  } PassengerAppType;
47
49
 
@@ -70,7 +70,7 @@ enum DisableResult {
70
70
  DR_NOOP,
71
71
 
72
72
  // The disabling of the process failed: an error occurred.
73
- // Only passed to the callback.
73
+ // Returned by functions and passed to the callback.
74
74
  DR_ERROR,
75
75
 
76
76
  // Indicates that the process cannot be disabled immediately
@@ -170,9 +170,17 @@ private:
170
170
  static string generateSecret(const SuperGroupPtr &superGroup);
171
171
  void onSessionInitiateFailure(const ProcessPtr &process, Session *session);
172
172
  void onSessionClose(const ProcessPtr &process, Session *session);
173
- void lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult result, GroupPtr self);
174
- void asyncOOBWRequestIfNeeded(const ProcessPtr &process);
173
+
174
+ /** Returns whether it is allowed to perform a new OOBW in this group. */
175
+ bool oobwAllowed() const;
176
+ /** Returns whether a new OOBW should be initiated for this process. */
177
+ bool shouldInitiateOobw(const ProcessPtr &process) const;
178
+ void maybeInitiateOobw(const ProcessPtr &process);
179
+ void lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self);
180
+ void initiateOobw(const ProcessPtr &process);
175
181
  void spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process);
182
+ void initiateNextOobwRequest();
183
+
176
184
  void spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options,
177
185
  unsigned int restartsInitiated);
178
186
  void spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options,
@@ -184,6 +192,7 @@ private:
184
192
  void wakeUpGarbageCollector();
185
193
  bool poolAtFullCapacity() const;
186
194
  bool anotherGroupIsWaitingForCapacity() const;
195
+ bool testOverflowRequestQueue() const;
187
196
  const ResourceLocator &getResourceLocator() const;
188
197
 
189
198
  void verifyInvariants() const {
@@ -233,6 +242,8 @@ private:
233
242
  assert(process->enabled == Process::ENABLED);
234
243
  assert(process->pqHandle != NULL);
235
244
  assert(process->isAlive());
245
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
246
+ || process->oobwStatus == Process::OOBW_REQUESTED);
236
247
  }
237
248
 
238
249
  end = disablingProcesses.end();
@@ -241,6 +252,8 @@ private:
241
252
  assert(process->enabled == Process::DISABLING);
242
253
  assert(process->pqHandle == NULL);
243
254
  assert(process->isAlive());
255
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
256
+ || process->oobwStatus == Process::OOBW_IN_PROGRESS);
244
257
  }
245
258
 
246
259
  end = disabledProcesses.end();
@@ -249,6 +262,8 @@ private:
249
262
  assert(process->enabled == Process::DISABLED);
250
263
  assert(process->pqHandle == NULL);
251
264
  assert(process->isAlive());
265
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
266
+ || process->oobwStatus == Process::OOBW_IN_PROGRESS);
252
267
  }
253
268
 
254
269
  foreach (const ProcessPtr &process, detachedProcesses) {
@@ -305,6 +320,20 @@ private:
305
320
  return session;
306
321
  }
307
322
 
323
+ bool pushGetWaiter(const Options &newOptions, const GetCallback &callback) {
324
+ if (OXT_LIKELY(!testOverflowRequestQueue()
325
+ && (newOptions.maxRequestQueueSize == 0
326
+ || getWaitlist.size() < newOptions.maxRequestQueueSize)))
327
+ {
328
+ getWaitlist.push(GetWaiter(newOptions.copyAndPersist().clearLogger(), callback));
329
+ return true;
330
+ } else {
331
+ P_WARN("Request queue is full. Returning an error");
332
+ callback(SessionPtr(), make_shared<RequestQueueFullException>());
333
+ return false;
334
+ }
335
+ }
336
+
308
337
  Process *findProcessWithLowestUtilization(const ProcessList &processes) const {
309
338
  Process *result = NULL;
310
339
  ProcessList::const_iterator it, end = processes.end();
@@ -577,14 +606,17 @@ public:
577
606
  * process.enabled == Process::ENABLED
578
607
  * process.pqHandle != NULL
579
608
  * process.isAlive()
609
+ * process.oobwStatus == Process::OOBW_NOT_ACTIVE || process.oobwStatus == Process::OOBW_REQUESTED
580
610
  * for all processes in disablingProcesses:
581
611
  * process.enabled == Process::DISABLING
582
612
  * process.pqHandle == NULL
583
613
  * process.isAlive()
614
+ * process.oobwStatus == Process::OOBW_NOT_ACTIVE || process.oobwStatus == Process::OOBW_IN_PROGRESS
584
615
  * for all process in disabledProcesses:
585
616
  * process.enabled == Process::DISABLED
586
617
  * process.pqHandle == NULL
587
618
  * process.isAlive()
619
+ * process.oobwStatus == Process::OOBW_NOT_ACTIVE || process.oobwStatus == Process::OOBW_IN_PROGRESS
588
620
  */
589
621
  int enabledCount;
590
622
  int disablingCount;
@@ -723,8 +755,9 @@ public:
723
755
  }
724
756
  }
725
757
 
726
- getWaitlist.push(GetWaiter(newOptions.copyAndPersist().clearLogger(), callback));
727
- P_DEBUG("No session checked out yet: group is spawning or restarting");
758
+ if (pushGetWaiter(newOptions, callback)) {
759
+ P_DEBUG("No session checked out yet: group is spawning or restarting");
760
+ }
728
761
  return SessionPtr();
729
762
  } else {
730
763
  Process *process = pqueue.top();
@@ -734,8 +767,9 @@ public:
734
767
  * Wait until a new one has been spawned or until
735
768
  * resources have become free.
736
769
  */
737
- getWaitlist.push(GetWaiter(newOptions.copyAndPersist().clearLogger(), callback));
738
- P_DEBUG("No session checked out yet: all processes are at full capacity");
770
+ if (pushGetWaiter(newOptions, callback)) {
771
+ P_DEBUG("No session checked out yet: all processes are at full capacity");
772
+ }
739
773
  return SessionPtr();
740
774
  } else {
741
775
  P_DEBUG("Session checked out from process " << process->inspect());
@@ -924,7 +958,12 @@ public:
924
958
  P_DEBUG("Disabling ENABLED process " << process->inspect() <<
925
959
  "; enabledCount=" << enabledCount << ", process.sessions=" << process->sessions);
926
960
  assert(enabledCount >= 0);
927
- if (enabledCount <= 1 || process->sessions > 0) {
961
+ if (enabledCount == 1 && !allowSpawn()) {
962
+ P_WARN("Cannot disable sole enabled process in group " << name <<
963
+ " because spawning is not allowed according to the current" <<
964
+ " configuration options");
965
+ return DR_ERROR;
966
+ } else if (enabledCount <= 1 || process->sessions > 0) {
928
967
  removeProcessFromList(process, enabledProcesses);
929
968
  addProcessToList(process, disablingProcesses);
930
969
  disableWaitlist.push_back(DisableWaiter(process, callback));
@@ -992,6 +1031,8 @@ public:
992
1031
  * specific case that another get action is to be performed.
993
1032
  */
994
1033
  bool shouldSpawnForGetAction() const;
1034
+ /** Whether a new process is allowed to be spawned for this group. */
1035
+ bool allowSpawn() const;
995
1036
 
996
1037
  /** Start spawning a new process in the background, in case this
997
1038
  * isn't already happening and the group isn't being restarted.
@@ -1032,6 +1073,14 @@ public:
1032
1073
  return m_restarting;
1033
1074
  }
1034
1075
 
1076
+ /**
1077
+ * Returns the number of processes in this group that should be part for the
1078
+ * MaxPoolSize constraint calculation.
1079
+ */
1080
+ unsigned int getProcessCount() const {
1081
+ return enabledCount + disablingCount + disabledCount;
1082
+ }
1083
+
1035
1084
  /**
1036
1085
  * Checks whether this group is waiting for capacity on the pool to
1037
1086
  * become available before it can continue processing requests.
@@ -62,8 +62,9 @@ copyException(const tracable_exception &e) {
62
62
 
63
63
  TRY_COPY_EXCEPTION(ConfigurationException);
64
64
 
65
- TRY_COPY_EXCEPTION(SpawnException);
65
+ TRY_COPY_EXCEPTION(RequestQueueFullException);
66
66
  TRY_COPY_EXCEPTION(GetAbortedException);
67
+ TRY_COPY_EXCEPTION(SpawnException);
67
68
 
68
69
  TRY_COPY_EXCEPTION(InvalidModeStringException);
69
70
  TRY_COPY_EXCEPTION(ArgumentException);
@@ -104,6 +105,7 @@ rethrowException(const ExceptionPtr &e) {
104
105
  TRY_RETHROW_EXCEPTION(ConfigurationException);
105
106
 
106
107
  TRY_RETHROW_EXCEPTION(SpawnException);
108
+ TRY_RETHROW_EXCEPTION(RequestQueueFullException);
107
109
  TRY_RETHROW_EXCEPTION(GetAbortedException);
108
110
 
109
111
  TRY_RETHROW_EXCEPTION(InvalidModeStringException);
@@ -452,7 +454,7 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
452
454
  removeProcessFromList(process, disablingProcesses);
453
455
  addProcessToList(process, disabledProcesses);
454
456
  removeFromDisableWaitlist(process, DR_SUCCESS, actions);
455
- asyncOOBWRequestIfNeeded(process);
457
+ maybeInitiateOobw(process);
456
458
  }
457
459
 
458
460
  pool->fullVerifyInvariants();
@@ -461,7 +463,7 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
461
463
 
462
464
  } else {
463
465
  // This could change process->enabled.
464
- asyncOOBWRequestIfNeeded(process);
466
+ maybeInitiateOobw(process);
465
467
 
466
468
  if (!getWaitlist.empty() && process->enabled == Process::ENABLED) {
467
469
  /* If there are clients on this group waiting for a process to
@@ -484,47 +486,111 @@ Group::requestOOBW(const ProcessPtr &process) {
484
486
  }
485
487
  }
486
488
 
489
+ bool
490
+ Group::oobwAllowed() const {
491
+ unsigned int oobwInstances = 0;
492
+ foreach (const ProcessPtr &process, disablingProcesses) {
493
+ if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
494
+ oobwInstances += 1;
495
+ }
496
+ }
497
+ foreach (const ProcessPtr &process, disabledProcesses) {
498
+ if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
499
+ oobwInstances += 1;
500
+ }
501
+ }
502
+ return oobwInstances < options.maxOutOfBandWorkInstances;
503
+ }
504
+
505
+ bool
506
+ Group::shouldInitiateOobw(const ProcessPtr &process) const {
507
+ return process->oobwStatus == Process::OOBW_REQUESTED
508
+ && process->enabled != Process::DETACHED
509
+ && process->isAlive()
510
+ && oobwAllowed();
511
+ }
512
+
513
+ void
514
+ Group::maybeInitiateOobw(const ProcessPtr &process) {
515
+ if (shouldInitiateOobw(process)) {
516
+ initiateOobw(process);
517
+ }
518
+ }
519
+
487
520
  // The 'self' parameter is for keeping the current Group object alive
488
521
  void
489
- Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult result, GroupPtr self) {
522
+ Group::lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self) {
490
523
  TRACE_POINT();
491
524
 
492
- if (result != DR_SUCCESS && result != DR_CANCELED) {
493
- return;
494
- }
495
-
496
525
  // Standard resource management boilerplate stuff...
497
526
  PoolPtr pool = getPool();
498
527
  unique_lock<boost::mutex> lock(pool->syncher);
499
528
  if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
500
529
  return;
501
530
  }
502
-
503
- P_DEBUG("Process " << process->inspect() << " disabled; proceeding with OOBW");
504
- asyncOOBWRequestIfNeeded(process);
531
+
532
+ assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
533
+
534
+ if (result == DR_SUCCESS) {
535
+ if (process->enabled == Process::DISABLED) {
536
+ P_DEBUG("Process " << process->inspect() << " disabled; proceeding " <<
537
+ "with out-of-band work");
538
+ process->oobwStatus = Process::OOBW_REQUESTED;
539
+ if (shouldInitiateOobw(process)) {
540
+ initiateOobw(process);
541
+ } else {
542
+ // We do not re-enable the process because it's likely that the
543
+ // administrator has explicitly changed the state.
544
+ P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
545
+ "because the process no longer requests out-of-band work");
546
+ process->oobwStatus = Process::OOBW_NOT_ACTIVE;
547
+ }
548
+ } else {
549
+ // We do not re-enable the process because it's likely that the
550
+ // administrator has explicitly changed the state.
551
+ P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
552
+ "because the process was reenabled after disabling");
553
+ process->oobwStatus = Process::OOBW_NOT_ACTIVE;
554
+ }
555
+ } else {
556
+ P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
557
+ "because the process could not be disabled");
558
+ process->oobwStatus = Process::OOBW_NOT_ACTIVE;
559
+ }
505
560
  }
506
561
 
507
562
  void
508
- Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
509
- if (process->oobwStatus != Process::OOBW_REQUESTED
510
- || process->enabled == Process::DETACHED
511
- || !process->isAlive())
563
+ Group::initiateOobw(const ProcessPtr &process) {
564
+ assert(process->oobwStatus == Process::OOBW_REQUESTED);
565
+
566
+ process->oobwStatus = Process::OOBW_IN_PROGRESS;
567
+
568
+ if (process->enabled == Process::ENABLED
569
+ || process->enabled == Process::DISABLING)
512
570
  {
513
- return;
514
- }
515
- if (process->enabled == Process::ENABLED) {
516
571
  // We want the process to be disabled. However, disabling a process is potentially
517
572
  // asynchronous, so we pass a callback which will re-aquire the lock and call this
518
573
  // method again.
519
574
  P_DEBUG("Disabling process " << process->inspect() << " in preparation for OOBW");
520
575
  DisableResult result = disable(process,
521
- boost::bind(&Group::lockAndAsyncOOBWRequestIfNeeded, this,
576
+ boost::bind(&Group::lockAndMaybeInitiateOobw, this,
522
577
  _1, _2, shared_from_this()));
523
- if (result == DR_DEFERRED) {
578
+ switch (result) {
579
+ case DR_SUCCESS:
580
+ // Continue code flow.
581
+ break;
582
+ case DR_DEFERRED:
583
+ // lockAndMaybeInitateOobw() will eventually be called.
584
+ return;
585
+ case DR_ERROR:
586
+ case DR_NOOP:
587
+ P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
588
+ "because the process could not be disabled");
589
+ process->oobwStatus = Process::OOBW_NOT_ACTIVE;
524
590
  return;
591
+ default:
592
+ P_BUG("Unexpected disable() result " << result);
525
593
  }
526
- } else if (process->enabled == Process::DISABLING) {
527
- return;
528
594
  }
529
595
 
530
596
  assert(process->enabled == Process::DISABLED);
@@ -533,7 +599,7 @@ Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
533
599
  P_DEBUG("Initiating OOBW request for process " << process->inspect());
534
600
  interruptableThreads.create_thread(
535
601
  boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
536
- "OOB request thread for process " + process->inspect(),
602
+ "OOBW request thread for process " + process->inspect(),
537
603
  POOL_HELPER_THREAD_STACK_SIZE);
538
604
  }
539
605
 
@@ -577,7 +643,7 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
577
643
  return;
578
644
  }
579
645
 
580
- assert(process->oobwStatus = Process::OOBW_IN_PROGRESS);
646
+ assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
581
647
  assert(process->sessions == 0);
582
648
  socket = process->sessionSockets.top();
583
649
  assert(socket != NULL);
@@ -640,8 +706,10 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
640
706
  enable(process, actions);
641
707
  assignSessionsToGetWaiters(actions);
642
708
  }
643
-
709
+
644
710
  pool->fullVerifyInvariants();
711
+
712
+ initiateNextOobwRequest();
645
713
  }
646
714
  UPDATE_TRACE_POINT();
647
715
  runAllActions(actions);
@@ -654,6 +722,20 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
654
722
  }
655
723
  }
656
724
 
725
+ void
726
+ Group::initiateNextOobwRequest() {
727
+ ProcessList::const_iterator it, end = enabledProcesses.end();
728
+ for (it = enabledProcesses.begin(); it != end; it++) {
729
+ const ProcessPtr &process = *it;
730
+ if (shouldInitiateOobw(process)) {
731
+ // We keep an extra reference to processes to prevent premature destruction.
732
+ ProcessPtr p = process;
733
+ initiateOobw(p);
734
+ return;
735
+ }
736
+ }
737
+ }
738
+
657
739
  // The 'self' parameter is for keeping the current Group object alive while this thread is running.
658
740
  void
659
741
  Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options, unsigned int restartsInitiated) {
@@ -779,7 +861,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, un
779
861
  m_spawning = false;
780
862
 
781
863
  done = done
782
- || ((unsigned long) enabledCount >= options.minProcesses && getWaitlist.empty())
864
+ || ((unsigned long) getProcessCount() >= options.minProcesses && getWaitlist.empty())
783
865
  || pool->atFullCapacity(false);
784
866
  m_spawning = !done;
785
867
  if (done) {
@@ -803,13 +885,11 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, un
803
885
 
804
886
  bool
805
887
  Group::shouldSpawn() const {
806
- return !m_spawning
888
+ return allowSpawn()
807
889
  && (
808
- (unsigned long) enabledCount < options.minProcesses
890
+ (unsigned long) getProcessCount() < options.minProcesses
809
891
  || (enabledCount > 0 && pqueue.top()->atFullCapacity())
810
- )
811
- && isAlive()
812
- && !poolAtFullCapacity();
892
+ );
813
893
  }
814
894
 
815
895
  bool
@@ -817,6 +897,11 @@ Group::shouldSpawnForGetAction() const {
817
897
  return enabledCount == 0 || shouldSpawn();
818
898
  }
819
899
 
900
+ bool
901
+ Group::allowSpawn() const {
902
+ return isAlive() && !poolAtFullCapacity();
903
+ }
904
+
820
905
  void
821
906
  Group::restart(const Options &options) {
822
907
  vector<Callback> actions;
@@ -1035,6 +1120,18 @@ Group::anotherGroupIsWaitingForCapacity() const {
1035
1120
  return false;
1036
1121
  }
1037
1122
 
1123
+ bool
1124
+ Group::testOverflowRequestQueue() const {
1125
+ // This has a performance penalty, although I'm not sure whether the penalty is
1126
+ // any greater than a hash table lookup if I were to implement it in Options.
1127
+ Pool::DebugSupportPtr debug = getPool()->debugSupport;
1128
+ if (debug) {
1129
+ return debug->testOverflowRequestQueue;
1130
+ } else {
1131
+ return false;
1132
+ }
1133
+ }
1134
+
1038
1135
  const ResourceLocator &
1039
1136
  Group::getResourceLocator() const {
1040
1137
  return getPool()->spawnerFactory->getResourceLocator();
@@ -1094,6 +1191,11 @@ Session::requestOOBW() {
1094
1191
  process->getGroup()->requestOOBW(process);
1095
1192
  }
1096
1193
 
1194
+ int
1195
+ Session::kill(int signo) {
1196
+ return getProcess()->kill(signo);
1197
+ }
1198
+
1097
1199
 
1098
1200
  PipeWatcher::DataCallback PipeWatcher::onData;
1099
1201