passenger 5.0.9 → 5.0.10

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 (106) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +15 -0
  5. data/CONTRIBUTORS +6 -0
  6. data/README.md +1 -1
  7. data/bin/passenger-install-apache2-module +24 -11
  8. data/bin/passenger-status +29 -14
  9. data/build/agents.rb +12 -10
  10. data/build/cxx_tests.rb +30 -30
  11. data/doc/Design and Architecture.html +1 -10
  12. data/doc/Design and Architecture.txt +1 -6
  13. data/doc/Users guide Apache.html +1 -19
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +2 -20
  16. data/doc/Users guide Nginx.txt +2 -2
  17. data/doc/users_guide_snippets/tips.txt +0 -9
  18. data/ext/common/ApplicationPool2/ApiKey.h +158 -0
  19. data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
  20. data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
  21. data/ext/common/ApplicationPool2/Common.h +5 -44
  22. data/ext/common/ApplicationPool2/Context.h +94 -0
  23. data/ext/common/ApplicationPool2/Group.h +130 -1205
  24. data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
  25. data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
  26. data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
  27. data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
  28. data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
  29. data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
  30. data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
  31. data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
  32. data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
  33. data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
  34. data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
  35. data/ext/common/ApplicationPool2/Options.h +5 -5
  36. data/ext/common/ApplicationPool2/Pool.h +260 -815
  37. data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
  38. data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
  39. data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
  40. data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
  41. data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
  42. data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
  43. data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
  44. data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
  45. data/ext/common/ApplicationPool2/Process.h +399 -205
  46. data/ext/common/ApplicationPool2/Session.h +70 -28
  47. data/ext/common/ApplicationPool2/Socket.h +1 -0
  48. data/ext/common/Constants.h +11 -3
  49. data/ext/common/Exceptions.h +1 -1
  50. data/ext/common/Logging.cpp +9 -4
  51. data/ext/common/Logging.h +6 -0
  52. data/ext/common/ServerKit/HttpServer.h +225 -215
  53. data/ext/common/ServerKit/Server.h +57 -57
  54. data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
  55. data/ext/common/SpawningKit/Config.h +107 -0
  56. data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
  57. data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
  58. data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
  59. data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
  60. data/ext/common/SpawningKit/PipeWatcher.h +148 -0
  61. data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
  62. data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
  63. data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
  64. data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
  65. data/ext/common/Utils/BufferedIO.h +24 -0
  66. data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
  67. data/ext/common/Utils/IOUtils.cpp +70 -0
  68. data/ext/common/Utils/IOUtils.h +19 -0
  69. data/ext/common/Utils/JsonUtils.h +113 -0
  70. data/ext/common/Utils/StrIntUtils.h +29 -0
  71. data/ext/common/Utils/json.h +1 -1
  72. data/ext/common/agents/ApiServerUtils.h +941 -0
  73. data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
  74. data/ext/common/agents/HelperAgent/Main.cpp +86 -88
  75. data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
  76. data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
  77. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
  78. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
  79. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
  80. data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
  81. data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
  82. data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
  83. data/ext/common/agents/Watchdog/ApiServer.h +311 -0
  84. data/ext/common/agents/Watchdog/Main.cpp +91 -65
  85. data/helper-scripts/prespawn +2 -0
  86. data/lib/phusion_passenger.rb +1 -1
  87. data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
  88. data/lib/phusion_passenger/common_library.rb +27 -14
  89. data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
  90. data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
  91. data/lib/phusion_passenger/config/main.rb +3 -5
  92. data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
  93. data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
  94. data/lib/phusion_passenger/config/utils.rb +15 -8
  95. data/lib/phusion_passenger/constants.rb +6 -2
  96. data/lib/phusion_passenger/platform_info/apache.rb +4 -0
  97. data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
  98. data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
  99. metadata +42 -21
  100. metadata.gz.asc +7 -7
  101. data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
  102. data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
  103. data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
  104. data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
  105. data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
  106. data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2011-2014 Phusion
3
+ * Copyright (c) 2011-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -38,6 +38,7 @@
38
38
  #include <UnionStation/Core.h>
39
39
  #include <UnionStation/Transaction.h>
40
40
  #include <ApplicationPool2/Options.h>
41
+ #include <SpawningKit/Config.h>
41
42
  #include <Utils/VariantMap.h>
42
43
 
43
44
  namespace tut {
@@ -52,7 +53,6 @@ using namespace boost;
52
53
  using namespace oxt;
53
54
 
54
55
  class Pool;
55
- class SuperGroup;
56
56
  class Group;
57
57
  class Process;
58
58
  class Socket;
@@ -161,12 +161,11 @@ enum RestartMethod {
161
161
  };
162
162
 
163
163
  typedef boost::shared_ptr<Pool> PoolPtr;
164
- typedef boost::shared_ptr<SuperGroup> SuperGroupPtr;
165
164
  typedef boost::shared_ptr<Group> GroupPtr;
166
- typedef boost::shared_ptr<Process> ProcessPtr;
165
+ typedef boost::intrusive_ptr<Process> ProcessPtr;
167
166
  typedef boost::intrusive_ptr<Session> SessionPtr;
168
167
  typedef boost::shared_ptr<tracable_exception> ExceptionPtr;
169
- typedef StringKeyTable<SuperGroupPtr> SuperGroupMap;
168
+ typedef StringKeyTable<GroupPtr> GroupMap;
170
169
  typedef boost::function<void (const ProcessPtr &process, DisableResult result)> DisableCallback;
171
170
  typedef boost::function<void ()> Callback;
172
171
 
@@ -202,48 +201,10 @@ struct Ticket {
202
201
  ExceptionPtr exception;
203
202
  };
204
203
 
205
- struct SpawnerConfig {
206
- // Used by error pages and hooks.
207
- ResourceLocator *resourceLocator;
208
- const VariantMap *agentsOptions;
209
-
210
- // Used for Union Station logging.
211
- UnionStation::CorePtr unionStationCore;
212
-
213
- // Used by SmartSpawner and DirectSpawner.
214
- RandomGeneratorPtr randomGenerator;
215
- string instanceDir;
216
-
217
- // Used by DummySpawner and SpawnerFactory.
218
- unsigned int concurrency;
219
- unsigned int spawnerCreationSleepTime;
220
- unsigned int spawnTime;
221
-
222
- SpawnerConfig()
223
- : resourceLocator(NULL),
224
- agentsOptions(NULL),
225
- concurrency(1),
226
- spawnerCreationSleepTime(0),
227
- spawnTime(0)
228
- { }
229
-
230
- void finalize() {
231
- TRACE_POINT();
232
- if (resourceLocator == NULL) {
233
- throw RuntimeException("ResourceLocator not initialized");
234
- }
235
- if (randomGenerator == NULL) {
236
- randomGenerator = boost::make_shared<RandomGenerator>();
237
- }
238
- }
239
- };
240
-
241
- typedef boost::shared_ptr<SpawnerConfig> SpawnerConfigPtr;
242
-
243
204
  ExceptionPtr copyException(const tracable_exception &e);
244
205
  void rethrowException(const ExceptionPtr &e);
245
206
  void processAndLogNewSpawnException(SpawnException &e, const Options &options,
246
- const SpawnerConfigPtr &config);
207
+ const SpawningKit::ConfigPtr &config);
247
208
  void recreateString(psg_pool_t *pool, StaticString &str);
248
209
 
249
210
  } // namespace ApplicationPool2
@@ -0,0 +1,94 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2014-2015 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_APPLICATION_POOL2_CONTEXT_H_
26
+ #define _PASSENGER_APPLICATION_POOL2_CONTEXT_H_
27
+
28
+ #include <boost/thread.hpp>
29
+ #include <boost/pool/object_pool.hpp>
30
+ #include <Exceptions.h>
31
+ #include <SpawningKit/Factory.h>
32
+ #include <Utils/ClassUtils.h>
33
+
34
+ namespace Passenger {
35
+ namespace ApplicationPool2 {
36
+
37
+
38
+ using namespace boost;
39
+
40
+ class Session;
41
+ class Process;
42
+
43
+
44
+ /**
45
+ * State shared by Pool, Group, Process and Session. It contains statistics
46
+ * and counters, memory management objects, configuration objects, etc.
47
+ * This struct was introduced so that Group, Process and Sessions don't have
48
+ * to depend on Pool (which introduces circular dependencies).
49
+ *
50
+ * The fields are separated in several groups. Each group may have its own mutex.
51
+ * If it does, then all operations on any of the fields in that group requires
52
+ * grabbing the mutex unless documented otherwise.
53
+ */
54
+ class Context {
55
+ private:
56
+ /****** Memory management objects *****/
57
+
58
+ P_RO_PROPERTY_REF(private, boost::mutex, MmSyncher);
59
+ P_RO_PROPERTY_REF(private, object_pool<Session>, SessionObjectPool);
60
+ P_RO_PROPERTY_REF(private, object_pool<Process>, ProcessObjectPool);
61
+
62
+
63
+ /****** Configuration objects ******/
64
+
65
+ P_PROPERTY_CONST_REF(private, SpawningKit::FactoryPtr, SpawningKitFactory);
66
+
67
+
68
+ public:
69
+ /****** Initialization ******/
70
+
71
+ Context()
72
+ : mSessionObjectPool(64, 1024),
73
+ mProcessObjectPool(4, 64)
74
+ { }
75
+
76
+ void finalize() {
77
+ if (mSpawningKitFactory == NULL) {
78
+ throw RuntimeException("spawningKitFactory must be set");
79
+ }
80
+ }
81
+
82
+
83
+ /****** Configuration objects ******/
84
+
85
+ const SpawningKit::ConfigPtr &getSpawningKitConfig() const {
86
+ return mSpawningKitFactory->getConfig();
87
+ }
88
+ };
89
+
90
+
91
+ } // namespace ApplicationPool2
92
+ } // namespace Passenger
93
+
94
+ #endif /* _PASSENGER_APPLICATION_POOL2_CONTEXT_H_ */
@@ -39,14 +39,17 @@
39
39
  #include <oxt/macros.hpp>
40
40
  #include <oxt/thread.hpp>
41
41
  #include <oxt/dynamic_thread_group.hpp>
42
+ #include <sys/types.h>
42
43
  #include <sys/stat.h>
43
44
  #include <cstdlib>
44
45
  #include <cassert>
45
46
  #include <ApplicationPool2/Common.h>
46
- #include <ApplicationPool2/ComponentInfo.h>
47
- #include <ApplicationPool2/SpawnerFactory.h>
47
+ #include <ApplicationPool2/Context.h>
48
+ #include <ApplicationPool2/BasicGroupInfo.h>
48
49
  #include <ApplicationPool2/Process.h>
49
50
  #include <ApplicationPool2/Options.h>
51
+ #include <SpawningKit/Factory.h>
52
+ #include <SpawningKit/UserSwitchingRules.h>
50
53
  #include <MemoryKit/palloc.h>
51
54
  #include <Hooks.h>
52
55
  #include <Utils.h>
@@ -68,7 +71,6 @@ class Group: public boost::enable_shared_from_this<Group> {
68
71
  // Actually private, but marked public so that unit tests can access the fields.
69
72
  public:
70
73
  friend class Pool;
71
- friend class SuperGroup;
72
74
 
73
75
  struct GetAction {
74
76
  GetCallback callback;
@@ -98,7 +100,7 @@ public:
98
100
  enum LifeStatus {
99
101
  /** Up and operational. */
100
102
  ALIVE,
101
- /** Being shut down. The containing SuperGroup has issued the shutdown()
103
+ /** Being shut down. The containing Pool has issued the shutdown()
102
104
  * command, and this Group is now waiting for all detached processes to
103
105
  * exit. You cannot call `get()`, `restart()` and other mutating methods
104
106
  * anymore, and all threads created by this Group will exit as soon
@@ -112,13 +114,15 @@ public:
112
114
  SHUT_DOWN
113
115
  };
114
116
 
117
+ BasicGroupInfo info;
118
+
115
119
  /**
116
- * A back reference to the containing SuperGroup. Should never
117
- * be NULL because a SuperGroup should outlive all its containing
120
+ * A back reference to the containing Pool. Should never
121
+ * be NULL because a Pool should outlive all its containing
118
122
  * Groups.
119
123
  * Read-only; only set during initialization.
120
124
  */
121
- SuperGroup *superGroup;
125
+ Pool *pool;
122
126
  time_t lastRestartFileMtime;
123
127
  time_t lastRestartFileCheckTime;
124
128
 
@@ -185,16 +189,54 @@ public:
185
189
  GroupPtr selfPointer;
186
190
 
187
191
 
188
- static void generateSecret(const SuperGroup *superGroup, char *secret);
189
- static string generateUuid(const SuperGroup *superGroup);
192
+ /****** Initialization and shutdown ******/
193
+
194
+ static ApiKey generateApiKey(const Pool *pool);
195
+ static string generateUuid(const Pool *pool);
196
+
197
+ bool shutdownCanFinish() const;
198
+ void finishShutdown(boost::container::vector<Callback> &postLockActions);
199
+
200
+ /****** Session management ******/
201
+
202
+ RouteResult route(const Options &options) const;
203
+ SessionPtr newSession(Process *process, unsigned long long now = 0);
190
204
  static void _onSessionInitiateFailure(Session *session);
191
205
  static void _onSessionClose(Session *session);
192
206
  OXT_FORCE_INLINE void onSessionInitiateFailure(Process *process, Session *session);
193
207
  OXT_FORCE_INLINE void onSessionClose(Process *process, Session *session);
194
208
 
195
- /** Returns whether it is allowed to perform a new OOBW in this group. */
209
+ /****** Spawning and restarting ******/
210
+
211
+ void spawnThreadMain(GroupPtr self, SpawningKit::SpawnerPtr spawner, Options options,
212
+ unsigned int restartsInitiated);
213
+ void spawnThreadRealMain(const SpawningKit::SpawnerPtr &spawner, const Options &options,
214
+ unsigned int restartsInitiated);
215
+ void finalizeRestart(GroupPtr self, Options oldOptions, Options newOptions,
216
+ RestartMethod method, SpawningKit::FactoryPtr spawningKitFactory,
217
+ unsigned int restartsInitiated, boost::container::vector<Callback> postLockActions);
218
+
219
+ /****** Process list management ******/
220
+
221
+ Process *findProcessWithStickySessionId(unsigned int id) const;
222
+ Process *findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const;
223
+ Process *findProcessWithLowestBusyness(const ProcessList &processes) const;
224
+ Process *findEnabledProcessWithLowestBusyness() const;
225
+
226
+ void addProcessToList(const ProcessPtr &process, ProcessList &destination);
227
+ void removeProcessFromList(const ProcessPtr &process, ProcessList &source);
228
+ void removeFromDisableWaitlist(const ProcessPtr &p, DisableResult result,
229
+ boost::container::vector<Callback> &postLockActions);
230
+ void clearDisableWaitlist(DisableResult result,
231
+ boost::container::vector<Callback> &postLockActions);
232
+ void enableAllDisablingProcesses(boost::container::vector<Callback> &postLockActions);
233
+
234
+ void startCheckingDetachedProcesses(bool immediately);
235
+ void detachedProcessesCheckerMain(GroupPtr self);
236
+
237
+ /****** Out-of-band work ******/
238
+
196
239
  bool oobwAllowed() const;
197
- /** Returns whether a new OOBW should be initiated for this process. */
198
240
  bool shouldInitiateOobw(Process *process) const;
199
241
  void maybeInitiateOobw(Process *process);
200
242
  void lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self);
@@ -202,581 +244,50 @@ public:
202
244
  void spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process);
203
245
  void initiateNextOobwRequest();
204
246
 
205
- void spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options,
206
- unsigned int restartsInitiated);
207
- void spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options,
208
- unsigned int restartsInitiated);
209
- void finalizeRestart(GroupPtr self, Options oldOptions, Options newOptions,
210
- RestartMethod method, SpawnerFactoryPtr spawnerFactory,
211
- unsigned int restartsInitiated, boost::container::vector<Callback> postLockActions);
212
- void startCheckingDetachedProcesses(bool immediately);
213
- void detachedProcessesCheckerMain(GroupPtr self);
214
- void wakeUpGarbageCollector();
215
- bool selfCheckingEnabled() const;
247
+ /****** Internal utilities ******/
248
+
249
+ static void runAllActions(const boost::container::vector<Callback> &actions);
250
+ static void interruptAndJoinAllThreads(GroupPtr self);
251
+ static void doCleanupSpawner(SpawningKit::SpawnerPtr spawner);
252
+
253
+ void resetOptions(const Options &newOptions, Options *destination = NULL);
254
+ void mergeOptions(const Options &other);
255
+
256
+ bool prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name);
257
+ void runAttachHooks(const ProcessPtr process) const;
258
+ void runDetachHooks(const ProcessPtr process) const;
259
+ void setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const;
260
+
261
+ unsigned int generateStickySessionId();
262
+ ProcessPtr createProcessObject(const Json::Value &json);
216
263
  bool poolAtFullCapacity() const;
264
+ ProcessPtr poolForceFreeCapacity(const Group *exclude, boost::container::vector<Callback> &postLockActions);
265
+ void wakeUpGarbageCollector();
217
266
  bool anotherGroupIsWaitingForCapacity() const;
218
267
  Group *findOtherGroupWaitingForCapacity() const;
219
- ProcessPtr poolForceFreeCapacity(const Group *exclude, boost::container::vector<Callback> &postLockActions);
268
+ bool pushGetWaiter(const Options &newOptions, const GetCallback &callback,
269
+ boost::container::vector<Callback> &postLockActions);
270
+ template<typename Lock> void assignSessionsToGetWaitersQuickly(Lock &lock);
271
+ void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions);
220
272
  bool testOverflowRequestQueue() const;
221
273
  void callAbortLongRunningConnectionsCallback(const ProcessPtr &process);
222
- psg_pool_t *getPallocPool() const;
223
- const ResourceLocator &getResourceLocator() const;
224
- bool prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name);
225
- void runAttachHooks(const ProcessPtr process) const;
226
- void runDetachHooks(const ProcessPtr process) const;
227
- void setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const;
228
274
 
229
- void verifyInvariants() const {
230
- // !a || b: logical equivalent of a IMPLIES b.
231
- #ifndef NDEBUG
232
- if (!selfCheckingEnabled()) {
233
- return;
234
- }
235
-
236
- LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
237
-
238
- assert(enabledCount >= 0);
239
- assert(disablingCount >= 0);
240
- assert(disabledCount >= 0);
241
- assert(nEnabledProcessesTotallyBusy >= 0);
242
- assert(!( enabledCount == 0 && disablingCount > 0 ) || ( processesBeingSpawned > 0) );
243
- assert(!( !m_spawning ) || ( enabledCount > 0 || disablingCount == 0 ));
244
-
245
- assert((lifeStatus == ALIVE) == (spawner != NULL));
246
-
247
- // Verify getWaitlist invariants.
248
- assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || verifyNoRequestsOnGetWaitlistAreRoutable() ));
249
- assert(!( enabledProcesses.empty() && !m_spawning && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
250
- assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || m_spawning || restarting() || poolAtFullCapacity() ));
251
-
252
- // Verify disableWaitlist invariants.
253
- assert((int) disableWaitlist.size() >= disablingCount);
254
-
255
- // Verify processesBeingSpawned, m_spawning and m_restarting.
256
- assert(!( processesBeingSpawned > 0 ) || ( m_spawning ));
257
- assert(!( m_restarting ) || ( processesBeingSpawned == 0 ));
258
-
259
- // Verify lifeStatus.
260
- if (lifeStatus != ALIVE) {
261
- assert(enabledCount == 0);
262
- assert(disablingCount == 0);
263
- assert(disabledCount == 0);
264
- assert(nEnabledProcessesTotallyBusy == 0);
265
- }
266
-
267
- // Verify list sizes.
268
- assert((int) enabledProcesses.size() == enabledCount);
269
- assert((int) disablingProcesses.size() == disablingCount);
270
- assert((int) disabledProcesses.size() == disabledCount);
271
- assert(nEnabledProcessesTotallyBusy <= enabledCount);
272
- #endif
273
- }
274
-
275
- void verifyExpensiveInvariants() const {
276
- #ifndef NDEBUG
277
- // !a || b: logical equivalent of a IMPLIES b.
278
-
279
- if (!selfCheckingEnabled()) {
280
- return;
281
- }
282
-
283
- ProcessList::const_iterator it, end;
284
-
285
- end = enabledProcesses.end();
286
- for (it = enabledProcesses.begin(); it != end; it++) {
287
- const ProcessPtr &process = *it;
288
- assert(process->enabled == Process::ENABLED);
289
- assert(process->isAlive());
290
- assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
291
- || process->oobwStatus == Process::OOBW_REQUESTED);
292
- }
293
-
294
- end = disablingProcesses.end();
295
- for (it = disablingProcesses.begin(); it != end; it++) {
296
- const ProcessPtr &process = *it;
297
- assert(process->enabled == Process::DISABLING);
298
- assert(process->isAlive());
299
- assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
300
- || process->oobwStatus == Process::OOBW_IN_PROGRESS);
301
- }
302
-
303
- end = disabledProcesses.end();
304
- for (it = disabledProcesses.begin(); it != end; it++) {
305
- const ProcessPtr &process = *it;
306
- assert(process->enabled == Process::DISABLED);
307
- assert(process->isAlive());
308
- assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
309
- || process->oobwStatus == Process::OOBW_IN_PROGRESS);
310
- }
311
-
312
- foreach (const ProcessPtr &process, detachedProcesses) {
313
- assert(process->enabled == Process::DETACHED);
314
- }
315
- #endif
316
- }
275
+ /****** Correctness verification ******/
317
276
 
277
+ bool selfCheckingEnabled() const;
278
+ void verifyInvariants() const;
279
+ void verifyExpensiveInvariants() const;
318
280
  #ifndef NDEBUG
319
- bool verifyNoRequestsOnGetWaitlistAreRoutable() const {
320
- deque<GetWaiter>::const_iterator it, end = getWaitlist.end();
321
-
322
- for (it = getWaitlist.begin(); it != end; it++) {
323
- if (route(it->options).process != NULL) {
324
- return false;
325
- }
326
- }
327
- return true;
328
- }
281
+ bool verifyNoRequestsOnGetWaitlistAreRoutable() const;
329
282
  #endif
330
283
 
331
- /**
332
- * Persists options into this Group. Called at creation time and at restart time.
333
- * Values will be persisted into `destination`. Or if it's NULL, into `this->options`.
334
- */
335
- void resetOptions(const Options &newOptions, Options *destination = NULL) {
336
- if (destination == NULL) {
337
- destination = &this->options;
338
- }
339
- *destination = newOptions;
340
- destination->persist(newOptions);
341
- destination->clearPerRequestFields();
342
- destination->groupSecret = StaticString(secret, SECRET_SIZE);
343
- destination->groupUuid = uuid;
344
- }
345
-
346
- /**
347
- * Merges some of the new options from the latest get() request into this Group.
348
- */
349
- void mergeOptions(const Options &other) {
350
- options.maxRequests = other.maxRequests;
351
- options.minProcesses = other.minProcesses;
352
- options.statThrottleRate = other.statThrottleRate;
353
- options.maxPreloaderIdleTime = other.maxPreloaderIdleTime;
354
- }
355
-
356
- static void runAllActions(const boost::container::vector<Callback> &actions) {
357
- boost::container::vector<Callback>::const_iterator it, end = actions.end();
358
- for (it = actions.begin(); it != end; it++) {
359
- (*it)();
360
- }
361
- }
362
-
363
- static void doCleanupSpawner(SpawnerPtr spawner) {
364
- spawner->cleanup();
365
- }
366
-
367
- unsigned int generateStickySessionId() {
368
- unsigned int result;
369
-
370
- while (true) {
371
- result = (unsigned int) rand();
372
- if (findProcessWithStickySessionId(result) == NULL) {
373
- return result;
374
- }
375
- }
376
- // Never reached; shut up compiler warning.
377
- return 0;
378
- }
379
-
380
- /* Determines which process to route a get() action to. The returned process
381
- * is guaranteed to be `canBeRoutedTo()`, i.e. not totally busy.
382
- *
383
- * A request is routed to an enabled processes, or if there are none,
384
- * from a disabling process. The rationale is as follows:
385
- * If there are no enabled process, then waiting for one to spawn is too
386
- * expensive. The next best thing is to route to disabling processes
387
- * until more processes have been spawned.
388
- */
389
- RouteResult route(const Options &options) const {
390
- if (OXT_LIKELY(enabledCount > 0)) {
391
- if (options.stickySessionId == 0) {
392
- Process *process = findEnabledProcessWithLowestBusyness();
393
- if (process->canBeRoutedTo()) {
394
- return RouteResult(process);
395
- } else {
396
- return RouteResult(NULL, true);
397
- }
398
- } else {
399
- Process *process = findProcessWithStickySessionIdOrLowestBusyness(
400
- options.stickySessionId);
401
- if (process != NULL) {
402
- if (process->canBeRoutedTo()) {
403
- return RouteResult(process);
404
- } else {
405
- return RouteResult(NULL, false);
406
- }
407
- } else {
408
- return RouteResult(NULL, true);
409
- }
410
- }
411
- } else {
412
- Process *process = findProcessWithLowestBusyness(disablingProcesses);
413
- if (process->canBeRoutedTo()) {
414
- return RouteResult(process);
415
- } else {
416
- return RouteResult(NULL, true);
417
- }
418
- }
419
- }
420
-
421
- SessionPtr newSession(Process *process, unsigned long long now = 0) {
422
- bool wasTotallyBusy = process->isTotallyBusy();
423
- SessionPtr session = process->newSession(now);
424
- session->onInitiateFailure = _onSessionInitiateFailure;
425
- session->onClose = _onSessionClose;
426
- if (process->enabled == Process::ENABLED) {
427
- enabledProcessBusynessLevels[process->index] = process->busyness();
428
- if (!wasTotallyBusy && process->isTotallyBusy()) {
429
- nEnabledProcessesTotallyBusy++;
430
- }
431
- }
432
- return session;
433
- }
434
-
435
- bool pushGetWaiter(const Options &newOptions, const GetCallback &callback,
436
- boost::container::vector<Callback> &postLockActions)
437
- {
438
- if (OXT_LIKELY(!testOverflowRequestQueue()
439
- && (newOptions.maxRequestQueueSize == 0
440
- || getWaitlist.size() < newOptions.maxRequestQueueSize)))
441
- {
442
- getWaitlist.push_back(GetWaiter(
443
- newOptions.copyAndPersist().detachFromUnionStationTransaction(),
444
- callback));
445
- return true;
446
- } else {
447
- postLockActions.push_back(boost::bind(GetCallback::call,
448
- callback, SessionPtr(), boost::make_shared<RequestQueueFullException>(newOptions.maxRequestQueueSize)));
449
-
450
- HookScriptOptions hsOptions;
451
- if (prepareHookScriptOptions(hsOptions, "queue_full_error")) {
452
- // TODO <Feb 17, 2015] DK> should probably rate limit this, since we are already at heavy load
453
- postLockActions.push_back(boost::bind(runHookScripts, hsOptions));
454
- }
455
-
456
- return false;
457
- }
458
- }
459
-
460
- Process *findProcessWithStickySessionId(unsigned int id) const {
461
- ProcessList::const_iterator it, end = enabledProcesses.end();
462
- for (it = enabledProcesses.begin(); it != end; it++) {
463
- Process *process = it->get();
464
- if (process->stickySessionId == id) {
465
- return process;
466
- }
467
- }
468
- return NULL;
469
- }
470
-
471
- Process *findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const {
472
- int leastBusyProcessIndex = -1;
473
- int lowestBusyness = 0;
474
- unsigned int i, size = enabledProcessBusynessLevels.size();
475
- const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];
476
-
477
- for (i = 0; i < size; i++) {
478
- Process *process = enabledProcesses[i].get();
479
- if (process->stickySessionId == id) {
480
- return process;
481
- } else if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
482
- leastBusyProcessIndex = i;
483
- lowestBusyness = enabledProcessBusynessLevels[i];
484
- }
485
- }
486
-
487
- if (leastBusyProcessIndex == -1) {
488
- return NULL;
489
- } else {
490
- return enabledProcesses[leastBusyProcessIndex].get();
491
- }
492
- }
493
-
494
- Process *findProcessWithLowestBusyness(const ProcessList &processes) const {
495
- if (processes.empty()) {
496
- return NULL;
497
- }
498
-
499
- int lowestBusyness = -1;
500
- Process *leastBusyProcess = NULL;
501
- ProcessList::const_iterator it;
502
- ProcessList::const_iterator end = processes.end();
503
- for (it = processes.begin(); it != end; it++) {
504
- Process *process = (*it).get();
505
- int busyness = process->busyness();
506
- if (lowestBusyness == -1 || lowestBusyness > busyness) {
507
- lowestBusyness = busyness;
508
- leastBusyProcess = process;
509
- }
510
- }
511
- return leastBusyProcess;
512
- }
513
-
514
- /**
515
- * Cache-optimized version of findProcessWithLowestBusyness() for the common case.
516
- */
517
- Process *findEnabledProcessWithLowestBusyness() const {
518
- if (enabledProcesses.empty()) {
519
- return NULL;
520
- }
521
-
522
- int leastBusyProcessIndex = -1;
523
- int lowestBusyness = 0;
524
- unsigned int i, size = enabledProcessBusynessLevels.size();
525
- const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];
526
-
527
- for (i = 0; i < size; i++) {
528
- if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
529
- leastBusyProcessIndex = i;
530
- lowestBusyness = enabledProcessBusynessLevels[i];
531
- }
532
- }
533
- return enabledProcesses[leastBusyProcessIndex].get();
534
- }
535
-
536
- /**
537
- * Removes a process to the given list (enabledProcess, disablingProcesses, disabledProcesses).
538
- * This function does not fix getWaitlist invariants or other stuff.
539
- */
540
- void removeProcessFromList(const ProcessPtr &process, ProcessList &source) {
541
- ProcessPtr p = process; // Keep an extra reference count just in case.
542
-
543
- source.erase(source.begin() + process->index);
544
- process->index = -1;
545
-
546
- switch (process->enabled) {
547
- case Process::ENABLED:
548
- assert(&source == &enabledProcesses);
549
- enabledCount--;
550
- if (process->isTotallyBusy()) {
551
- nEnabledProcessesTotallyBusy--;
552
- }
553
- break;
554
- case Process::DISABLING:
555
- assert(&source == &disablingProcesses);
556
- disablingCount--;
557
- break;
558
- case Process::DISABLED:
559
- assert(&source == &disabledProcesses);
560
- disabledCount--;
561
- break;
562
- case Process::DETACHED:
563
- assert(&source == &detachedProcesses);
564
- break;
565
- default:
566
- P_BUG("Unknown 'enabled' state " << (int) process->enabled);
567
- }
568
-
569
- // Rebuild indices
570
- ProcessList::iterator it, end = source.end();
571
- unsigned int i = 0;
572
- for (it = source.begin(); it != end; it++, i++) {
573
- const ProcessPtr &process = *it;
574
- process->index = i;
575
- }
576
-
577
- // Rebuild enabledProcessBusynessLevels
578
- if (&source == &enabledProcesses) {
579
- enabledProcessBusynessLevels.clear();
580
- for (it = source.begin(); it != end; it++, i++) {
581
- const ProcessPtr &process = *it;
582
- enabledProcessBusynessLevels.push_back(process->busyness());
583
- }
584
- enabledProcessBusynessLevels.shrink_to_fit();
585
- }
586
- }
587
-
588
- /**
589
- * Adds a process to the given list (enabledProcess, disablingProcesses, disabledProcesses)
590
- * and sets the process->enabled flag accordingly.
591
- * The process must currently not be in any list. This function does not fix
592
- * getWaitlist invariants or other stuff.
593
- */
594
- void addProcessToList(const ProcessPtr &process, ProcessList &destination) {
595
- destination.push_back(process);
596
- process->index = destination.size() - 1;
597
- if (&destination == &enabledProcesses) {
598
- process->enabled = Process::ENABLED;
599
- enabledCount++;
600
- enabledProcessBusynessLevels.push_back(process->busyness());
601
- if (process->isTotallyBusy()) {
602
- nEnabledProcessesTotallyBusy++;
603
- }
604
- } else if (&destination == &disablingProcesses) {
605
- process->enabled = Process::DISABLING;
606
- disablingCount++;
607
- } else if (&destination == &disabledProcesses) {
608
- assert(process->sessions == 0);
609
- process->enabled = Process::DISABLED;
610
- disabledCount++;
611
- } else if (&destination == &detachedProcesses) {
612
- assert(process->isAlive());
613
- process->enabled = Process::DETACHED;
614
- callAbortLongRunningConnectionsCallback(process);
615
- } else {
616
- P_BUG("Unknown destination list");
617
- }
618
- }
619
-
620
- template<typename Lock>
621
- void assignSessionsToGetWaitersQuickly(Lock &lock) {
622
- if (getWaitlist.empty()) {
623
- verifyInvariants();
624
- lock.unlock();
625
- return;
626
- }
627
-
628
- SmallVector<GetAction, 8> actions;
629
- unsigned int i = 0;
630
- bool done = false;
631
-
632
- actions.reserve(getWaitlist.size());
633
-
634
- while (!done && i < getWaitlist.size()) {
635
- const GetWaiter &waiter = getWaitlist[i];
636
- RouteResult result = route(waiter.options);
637
- if (result.process != NULL) {
638
- GetAction action;
639
- action.callback = waiter.callback;
640
- action.session = newSession(result.process);
641
- getWaitlist.erase(getWaitlist.begin() + i);
642
- actions.push_back(action);
643
- } else {
644
- done = result.finished;
645
- if (!result.finished) {
646
- i++;
647
- }
648
- }
649
- }
650
-
651
- verifyInvariants();
652
- lock.unlock();
653
- SmallVector<GetAction, 50>::const_iterator it, end = actions.end();
654
- for (it = actions.begin(); it != end; it++) {
655
- it->callback(it->session, ExceptionPtr());
656
- }
657
- }
658
-
659
- void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions) {
660
- unsigned int i = 0;
661
- bool done = false;
662
-
663
- while (!done && i < getWaitlist.size()) {
664
- const GetWaiter &waiter = getWaitlist[i];
665
- RouteResult result = route(waiter.options);
666
- if (result.process != NULL) {
667
- postLockActions.push_back(boost::bind(
668
- GetCallback::call,
669
- waiter.callback,
670
- newSession(result.process),
671
- ExceptionPtr()));
672
- getWaitlist.erase(getWaitlist.begin() + i);
673
- } else {
674
- done = result.finished;
675
- if (!result.finished) {
676
- i++;
677
- }
678
- }
679
- }
680
- }
681
-
682
- void enableAllDisablingProcesses(boost::container::vector<Callback> &postLockActions) {
683
- P_DEBUG("Enabling all DISABLING processes with result DR_ERROR");
684
- deque<DisableWaiter>::iterator it, end = disableWaitlist.end();
685
- for (it = disableWaitlist.begin(); it != end; it++) {
686
- const DisableWaiter &waiter = *it;
687
- const ProcessPtr process = waiter.process;
688
- // A process can appear multiple times in disableWaitlist.
689
- assert(process->enabled == Process::DISABLING
690
- || process->enabled == Process::ENABLED);
691
- if (process->enabled == Process::DISABLING) {
692
- removeProcessFromList(process, disablingProcesses);
693
- addProcessToList(process, enabledProcesses);
694
- P_DEBUG("Enabled process " << process->inspect());
695
- }
696
- }
697
- clearDisableWaitlist(DR_ERROR, postLockActions);
698
- }
699
-
700
- void removeFromDisableWaitlist(const ProcessPtr &p, DisableResult result,
701
- boost::container::vector<Callback> &postLockActions)
702
- {
703
- deque<DisableWaiter>::const_iterator it, end = disableWaitlist.end();
704
- deque<DisableWaiter> newList;
705
- for (it = disableWaitlist.begin(); it != end; it++) {
706
- const DisableWaiter &waiter = *it;
707
- const ProcessPtr process = waiter.process;
708
- if (process == p) {
709
- postLockActions.push_back(boost::bind(waiter.callback, p, result));
710
- } else {
711
- newList.push_back(waiter);
712
- }
713
- }
714
- disableWaitlist = newList;
715
- }
716
-
717
- void clearDisableWaitlist(DisableResult result,
718
- boost::container::vector<Callback> &postLockActions)
719
- {
720
- // This function may be called after processes in the disableWaitlist
721
- // have been disabled or enabled, so do not assume any value for
722
- // waiter.process->enabled in this function.
723
- postLockActions.reserve(postLockActions.size() + disableWaitlist.size());
724
- while (!disableWaitlist.empty()) {
725
- const DisableWaiter &waiter = disableWaitlist.front();
726
- postLockActions.push_back(boost::bind(waiter.callback, waiter.process, result));
727
- disableWaitlist.pop_front();
728
- }
729
- }
730
-
731
- bool shutdownCanFinish() const {
732
- LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
733
- return lifeStatus == SHUTTING_DOWN
734
- && enabledCount == 0
735
- && disablingCount == 0
736
- && disabledCount == 0
737
- && detachedProcesses.empty();
738
- }
739
-
740
- static void interruptAndJoinAllThreads(GroupPtr self) {
741
- self->interruptableThreads.interrupt_and_join_all();
742
- }
743
-
744
- /** One of the post lock actions can potentially perform a long-running
745
- * operation, so running them in a thread is advised.
746
- */
747
- void finishShutdown(boost::container::vector<Callback> &postLockActions) {
748
- TRACE_POINT();
749
- #ifndef NDEBUG
750
- LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
751
- P_ASSERT_EQ(lifeStatus, SHUTTING_DOWN);
752
- #endif
753
- P_DEBUG("Finishing shutdown of group " << name);
754
- if (shutdownCallback) {
755
- postLockActions.push_back(shutdownCallback);
756
- shutdownCallback = Callback();
757
- }
758
- postLockActions.push_back(boost::bind(interruptAndJoinAllThreads,
759
- shared_from_this()));
760
- this->lifeStatus.store(SHUT_DOWN, boost::memory_order_release);
761
- selfPointer.reset();
762
- }
763
-
764
284
  public:
765
- static const unsigned int SECRET_SIZE = 16;
766
-
767
285
  Options options;
768
- /** This name uniquely identifies this Group within its Pool. It can also be used as the display name. */
769
- const string name;
770
- /** A secret token that may be known among all processes in this Group. Used for securing
771
- * intra-group process communication.
772
- */
773
- char secret[SECRET_SIZE];
774
286
  /** A UUID that's generated on Group initialization, and changes every time
775
287
  * the Group receives a restart command. Allows Union Station to track app
776
288
  * restarts. This information is public.
777
289
  */
778
290
  string uuid;
779
- ComponentInfo componentInfo;
780
291
 
781
292
  /**
782
293
  * Processes are categorized as enabled, disabling or disabled.
@@ -899,668 +410,82 @@ public:
899
410
  * Invariant:
900
411
  * (lifeStatus == ALIVE) == (spawner != NULL)
901
412
  */
902
- SpawnerPtr spawner;
413
+ SpawningKit::SpawnerPtr spawner;
903
414
 
904
415
 
905
- /********************************************
906
- * Constructors and destructors
907
- ********************************************/
416
+ /****** Initialization and shutdown ******/
908
417
 
909
- Group(SuperGroup *superGroup, const Options &options, const ComponentInfo &info);
418
+ Group(Pool *pool, const Options &options);
910
419
  ~Group();
911
- void initialize();
912
-
913
- /**
914
- * Must be called before destroying a Group. You can optionally provide a
915
- * callback so that you are notified when shutdown has finished.
916
- *
917
- * The caller is responsible for migrating waiters on the getWaitlist.
918
- *
919
- * One of the post lock actions can potentially perform a long-running
920
- * operation, so running them in a thread is advised.
921
- */
420
+ bool initialize();
922
421
  void shutdown(const Callback &callback,
923
- boost::container::vector<Callback> &postLockActions)
924
- {
925
- assert(isAlive());
926
-
927
- P_DEBUG("Begin shutting down group " << name);
928
- shutdownCallback = callback;
929
- detachAll(postLockActions);
930
- startCheckingDetachedProcesses(true);
931
- interruptableThreads.interrupt_all();
932
- postLockActions.push_back(boost::bind(doCleanupSpawner, spawner));
933
- spawner.reset();
934
- selfPointer = shared_from_this();
935
- assert(disableWaitlist.empty());
936
- lifeStatus.store(SHUTTING_DOWN, boost::memory_order_release);
937
- }
938
-
939
-
940
- /********************************************
941
- * Life time and back-reference methods
942
- ********************************************/
422
+ boost::container::vector<Callback> &postLockActions);
943
423
 
944
- /**
945
- * Thread-safe.
946
- * @pre getLifeState() != SHUT_DOWN
947
- * @post result != NULL
948
- */
949
- SuperGroup *getSuperGroup() const {
950
- return superGroup;
951
- }
952
-
953
- void setSuperGroup(SuperGroup *superGroup) {
954
- assert(this->superGroup == NULL);
955
- this->superGroup = superGroup;
956
- }
957
-
958
- /**
959
- * Thread-safe.
960
- * @pre getLifeState() != SHUT_DOWN
961
- * @post result != NULL
962
- */
963
- OXT_FORCE_INLINE Pool *getPool() const;
424
+ /****** Life time, basic info, backreferences and related objects ******/
964
425
 
965
- // Thread-safe.
966
- bool isAlive() const {
967
- return getLifeStatus() == ALIVE;
968
- }
426
+ bool isAlive() const;
427
+ OXT_FORCE_INLINE LifeStatus getLifeStatus() const;
969
428
 
970
- // Thread-safe.
971
- OXT_FORCE_INLINE
972
- LifeStatus getLifeStatus() const {
973
- return (LifeStatus) lifeStatus.load(boost::memory_order_acquire);
974
- }
429
+ StaticString getName() const;
430
+ const BasicGroupInfo &getInfo();
431
+ const ApiKey &getApiKey() const;
975
432
 
433
+ OXT_FORCE_INLINE Pool *getPool() const;
434
+ Context *getContext() const;
435
+ psg_pool_t *getPallocPool() const;
436
+ const ResourceLocator &getResourceLocator() const;
976
437
 
977
- /********************************************
978
- * Core methods
979
- ********************************************/
438
+ /****** Session management ******/
980
439
 
981
440
  SessionPtr get(const Options &newOptions, const GetCallback &callback,
982
- boost::container::vector<Callback> &postLockActions)
983
- {
984
- assert(isAlive());
985
-
986
- if (OXT_LIKELY(!restarting())) {
987
- if (OXT_UNLIKELY(needsRestart(newOptions))) {
988
- restart(newOptions);
989
- } else {
990
- mergeOptions(newOptions);
991
- }
992
- if (OXT_UNLIKELY(!newOptions.noop && shouldSpawnForGetAction())) {
993
- // If we're trying to spawn the first process for this group, and
994
- // spawning failed because the pool is at full capacity, then we
995
- // try to kill some random idle process in the pool and try again.
996
- if (spawn() == SR_ERR_POOL_AT_FULL_CAPACITY && enabledCount == 0) {
997
- P_INFO("Unable to spawn the the sole process for group " << name <<
998
- " because the max pool size has been reached. Trying " <<
999
- "to shutdown another idle process to free capacity...");
1000
- if (poolForceFreeCapacity(this, postLockActions) != NULL) {
1001
- SpawnResult result = spawn();
1002
- assert(result == SR_OK);
1003
- (void) result;
1004
- } else {
1005
- P_INFO("There are no processes right now that are eligible "
1006
- "for shutdown. Will try again later.");
1007
- }
1008
- }
1009
- }
1010
- }
1011
-
1012
- if (OXT_UNLIKELY(newOptions.noop)) {
1013
- return nullProcess->createSessionObject((Socket *) NULL);
1014
- }
1015
-
1016
- if (OXT_UNLIKELY(enabledCount == 0)) {
1017
- /* We don't have any processes yet, but they're on the way.
1018
- *
1019
- * We have some choices here. If there are disabling processes
1020
- * then we generally want to use them, except:
1021
- * - When non-rolling restarting because those disabling processes
1022
- * are from the old version.
1023
- * - When all disabling processes are totally busy.
1024
- *
1025
- * Whenever a disabling process cannot be used, call the callback
1026
- * after a process has been spawned or has failed to spawn, or
1027
- * when a disabling process becomes available.
1028
- */
1029
- assert(m_spawning || restarting() || poolAtFullCapacity());
1030
-
1031
- if (disablingCount > 0 && !restarting()) {
1032
- Process *process = findProcessWithLowestBusyness(disablingProcesses);
1033
- assert(process != NULL);
1034
- if (!process->isTotallyBusy()) {
1035
- return newSession(process, newOptions.currentTime);
1036
- }
1037
- }
1038
-
1039
- if (pushGetWaiter(newOptions, callback, postLockActions)) {
1040
- P_DEBUG("No session checked out yet: group is spawning or restarting");
1041
- }
1042
- return SessionPtr();
1043
- } else {
1044
- RouteResult result = route(newOptions);
1045
- if (result.process == NULL) {
1046
- /* Looks like all processes are totally busy.
1047
- * Wait until a new one has been spawned or until
1048
- * resources have become free.
1049
- */
1050
- if (pushGetWaiter(newOptions, callback, postLockActions)) {
1051
- P_DEBUG("No session checked out yet: all processes are at full capacity");
1052
- }
1053
- return SessionPtr();
1054
- } else {
1055
- P_DEBUG("Session checked out from process " << result.process->inspect());
1056
- return newSession(result.process, newOptions.currentTime);
1057
- }
1058
- }
1059
- }
1060
-
1061
-
1062
- /********************************************
1063
- * State mutation methods
1064
- ********************************************/
1065
-
1066
- // Thread-safe, but only call outside the pool lock!
1067
- void requestOOBW(const ProcessPtr &process);
1068
-
1069
- /**
1070
- * Attaches the given process to this Group and mark it as enabled. This
1071
- * function doesn't touch `getWaitlist` so be sure to fix its invariants
1072
- * afterwards if necessary, e.g. by calling `assignSessionsToGetWaiters()`.
1073
- */
1074
- AttachResult attach(const SpawnObject &spawnObject,
1075
- boost::container::vector<Callback> &postLockActions)
1076
- {
1077
- TRACE_POINT();
1078
- const ProcessPtr &process = spawnObject.process;
1079
- assert(process->getGroup() == NULL || process->getGroup() == this);
1080
- assert(process->isAlive());
1081
- assert(isAlive());
1082
-
1083
- if (processUpperLimitsReached()) {
1084
- return AR_GROUP_UPPER_LIMITS_REACHED;
1085
- } else if (poolAtFullCapacity()) {
1086
- return AR_POOL_AT_FULL_CAPACITY;
1087
- } else if (!isWaitingForCapacity() && anotherGroupIsWaitingForCapacity()) {
1088
- return AR_ANOTHER_GROUP_IS_WAITING_FOR_CAPACITY;
1089
- }
1090
-
1091
- process->setGroup(this);
1092
- process->stickySessionId = generateStickySessionId();
1093
- P_DEBUG("Attaching process " << process->inspect());
1094
- addProcessToList(process, enabledProcesses);
1095
-
1096
- if (spawnObject.pool != getPallocPool()) {
1097
- process->recreateStrings(getPallocPool());
1098
- }
1099
-
1100
- /* Now that there are enough resources, relevant processes in
1101
- * 'disableWaitlist' can be disabled.
1102
- */
1103
- deque<DisableWaiter>::const_iterator it, end = disableWaitlist.end();
1104
- deque<DisableWaiter> newDisableWaitlist;
1105
- for (it = disableWaitlist.begin(); it != end; it++) {
1106
- const DisableWaiter &waiter = *it;
1107
- const ProcessPtr process2 = waiter.process;
1108
- // The same process can appear multiple times in disableWaitlist.
1109
- assert(process2->enabled == Process::DISABLING
1110
- || process2->enabled == Process::DISABLED);
1111
- if (process2->sessions == 0) {
1112
- if (process2->enabled == Process::DISABLING) {
1113
- P_DEBUG("Disabling DISABLING process " << process2->inspect() <<
1114
- "; disable command succeeded immediately");
1115
- removeProcessFromList(process2, disablingProcesses);
1116
- addProcessToList(process2, disabledProcesses);
1117
- } else {
1118
- P_DEBUG("Disabling (already disabled) DISABLING process " <<
1119
- process2->inspect() << "; disable command succeeded immediately");
1120
- }
1121
- postLockActions.push_back(boost::bind(waiter.callback, process2, DR_SUCCESS));
1122
- } else {
1123
- newDisableWaitlist.push_back(waiter);
1124
- }
1125
- }
1126
- disableWaitlist = newDisableWaitlist;
1127
-
1128
- // Update GC sleep timer.
1129
- wakeUpGarbageCollector();
1130
-
1131
- postLockActions.push_back(boost::bind(&Group::runAttachHooks, this, process));
1132
-
1133
- return AR_OK;
1134
- }
441
+ boost::container::vector<Callback> &postLockActions);
1135
442
 
1136
- /**
1137
- * Detaches the given process from this Group. This function doesn't touch
1138
- * getWaitlist so be sure to fix its invariants afterwards if necessary.
1139
- * `pool->detachProcessUnlocked()` does that so you should usually use
1140
- * that method over this one.
1141
- */
1142
- void detach(const ProcessPtr &process, boost::container::vector<Callback> &postLockActions) {
1143
- TRACE_POINT();
1144
- assert(process->getGroup() == this);
1145
- assert(process->isAlive());
1146
- assert(isAlive());
1147
-
1148
- if (process->enabled == Process::DETACHED) {
1149
- P_DEBUG("Detaching process " << process->inspect() << ", which was already being detached");
1150
- return;
1151
- }
1152
-
1153
- const ProcessPtr p = process; // Keep an extra reference just in case.
1154
- P_DEBUG("Detaching process " << process->inspect());
1155
-
1156
- if (process->enabled == Process::ENABLED || process->enabled == Process::DISABLING) {
1157
- assert(enabledCount > 0 || disablingCount > 0);
1158
- if (process->enabled == Process::ENABLED) {
1159
- removeProcessFromList(process, enabledProcesses);
1160
- } else {
1161
- removeProcessFromList(process, disablingProcesses);
1162
- removeFromDisableWaitlist(process, DR_NOOP, postLockActions);
1163
- }
1164
- } else {
1165
- assert(process->enabled == Process::DISABLED);
1166
- assert(!disabledProcesses.empty());
1167
- removeProcessFromList(process, disabledProcesses);
1168
- }
1169
-
1170
- addProcessToList(process, detachedProcesses);
1171
- startCheckingDetachedProcesses(false);
1172
-
1173
- postLockActions.push_back(boost::bind(&Group::runDetachHooks, this, process));
1174
- }
443
+ /****** Spawning and restarting ******/
1175
444
 
1176
- /**
1177
- * Detaches all processes from this Group. This function doesn't touch
1178
- * getWaitlist so be sure to fix its invariants afterwards if necessary.
1179
- */
1180
- void detachAll(boost::container::vector<Callback> &postLockActions) {
1181
- assert(isAlive());
1182
- P_DEBUG("Detaching all processes in group " << name);
1183
-
1184
- foreach (ProcessPtr process, enabledProcesses) {
1185
- addProcessToList(process, detachedProcesses);
1186
- }
1187
- foreach (ProcessPtr process, disablingProcesses) {
1188
- addProcessToList(process, detachedProcesses);
1189
- }
1190
- foreach (ProcessPtr process, disabledProcesses) {
1191
- addProcessToList(process, detachedProcesses);
1192
- }
1193
-
1194
- enabledProcesses.clear();
1195
- disablingProcesses.clear();
1196
- disabledProcesses.clear();
1197
- enabledProcessBusynessLevels.clear();
1198
- enabledCount = 0;
1199
- disablingCount = 0;
1200
- disabledCount = 0;
1201
- nEnabledProcessesTotallyBusy = 0;
1202
- clearDisableWaitlist(DR_NOOP, postLockActions);
1203
- startCheckingDetachedProcesses(false);
1204
- }
445
+ void restart(const Options &options, RestartMethod method = RM_DEFAULT);
446
+ bool restarting() const;
447
+ bool needsRestart(const Options &options);
1205
448
 
1206
- /**
1207
- * Marks the given process as enabled. This function doesn't touch getWaitlist
1208
- * so be sure to fix its invariants afterwards if necessary.
1209
- */
1210
- void enable(const ProcessPtr &process, boost::container::vector<Callback> &postLockActions) {
1211
- assert(process->getGroup() == this);
1212
- assert(process->isAlive());
1213
- assert(isAlive());
1214
-
1215
- if (process->enabled == Process::DISABLING) {
1216
- P_DEBUG("Enabling DISABLING process " << process->inspect());
1217
- removeProcessFromList(process, disablingProcesses);
1218
- addProcessToList(process, enabledProcesses);
1219
- removeFromDisableWaitlist(process, DR_CANCELED, postLockActions);
1220
- } else if (process->enabled == Process::DISABLED) {
1221
- P_DEBUG("Enabling DISABLED process " << process->inspect());
1222
- removeProcessFromList(process, disabledProcesses);
1223
- addProcessToList(process, enabledProcesses);
1224
- } else {
1225
- P_DEBUG("Enabling ENABLED process " << process->inspect());
1226
- }
1227
- }
449
+ SpawnResult spawn();
450
+ bool spawning() const;
451
+ bool shouldSpawn() const;
452
+ bool shouldSpawnForGetAction() const;
453
+ bool allowSpawn() const;
1228
454
 
1229
- /**
1230
- * Marks the given process as disabled. Returns DR_SUCCESS, DR_DEFERRED
1231
- * or DR_NOOP. If the result is DR_DEFERRED, then the callback will be
1232
- * called later with the result of this action.
1233
- */
1234
- DisableResult disable(const ProcessPtr &process, const DisableCallback &callback) {
1235
- assert(process->getGroup() == this);
1236
- assert(process->isAlive());
1237
- assert(isAlive());
1238
-
1239
- if (process->enabled == Process::ENABLED) {
1240
- P_DEBUG("Disabling ENABLED process " << process->inspect() <<
1241
- "; enabledCount=" << enabledCount << ", process.sessions=" << process->sessions);
1242
- assert(enabledCount >= 0);
1243
- if (enabledCount == 1 && !allowSpawn()) {
1244
- P_WARN("Cannot disable sole enabled process in group " << name <<
1245
- " because spawning is not allowed according to the current" <<
1246
- " configuration options");
1247
- return DR_ERROR;
1248
- } else if (enabledCount <= 1 || process->sessions > 0) {
1249
- removeProcessFromList(process, enabledProcesses);
1250
- addProcessToList(process, disablingProcesses);
1251
- disableWaitlist.push_back(DisableWaiter(process, callback));
1252
- if (enabledCount == 0) {
1253
- /* All processes are going to be disabled, so in order
1254
- * to avoid blocking requests we first spawn a new process
1255
- * and disable this process after the other one is done
1256
- * spawning. We do this irregardless of resource limits
1257
- * because this is an exceptional situation.
1258
- */
1259
- P_DEBUG("Spawning a new process to avoid the disable action from blocking requests");
1260
- spawn();
1261
- }
1262
- P_DEBUG("Deferring disable command completion");
1263
- return DR_DEFERRED;
1264
- } else {
1265
- removeProcessFromList(process, enabledProcesses);
1266
- addProcessToList(process, disabledProcesses);
1267
- P_DEBUG("Disable command succeeded immediately");
1268
- return DR_SUCCESS;
1269
- }
1270
- } else if (process->enabled == Process::DISABLING) {
1271
- assert(disablingCount > 0);
1272
- disableWaitlist.push_back(DisableWaiter(process, callback));
1273
- P_DEBUG("Disabling DISABLING process " << process->inspect() <<
1274
- name << "; command queued, deferring disable command completion");
1275
- return DR_DEFERRED;
1276
- } else {
1277
- assert(disabledCount > 0);
1278
- P_DEBUG("Disabling DISABLED process " << process->inspect() <<
1279
- name << "; disable command succeeded immediately");
1280
- return DR_NOOP;
1281
- }
1282
- }
455
+ /****** Process list management ******/
1283
456
 
1284
- /**
1285
- * Attempts to increase the number of processes by one, while respecting the
1286
- * resource limits. That is, this method will ensure that there are at least
1287
- * `minProcesses` processes, but no more than `maxProcesses` processes, and no
1288
- * more than `pool->max` processes in the entire pool.
1289
- */
1290
- SpawnResult spawn() {
1291
- assert(isAlive());
1292
- if (m_spawning) {
1293
- return SR_IN_PROGRESS;
1294
- } else if (restarting()) {
1295
- return SR_ERR_RESTARTING;
1296
- } else if (processUpperLimitsReached()) {
1297
- return SR_ERR_GROUP_UPPER_LIMITS_REACHED;
1298
- } else if (poolAtFullCapacity()) {
1299
- return SR_ERR_POOL_AT_FULL_CAPACITY;
1300
- } else {
1301
- P_DEBUG("Requested spawning of new process for group " << name);
1302
- interruptableThreads.create_thread(
1303
- boost::bind(&Group::spawnThreadMain,
1304
- this, shared_from_this(), spawner,
1305
- options.copyAndPersist().clearPerRequestFields(),
1306
- restartsInitiated),
1307
- "Group process spawner: " + name,
1308
- POOL_HELPER_THREAD_STACK_SIZE);
1309
- m_spawning = true;
1310
- processesBeingSpawned++;
1311
- return SR_OK;
1312
- }
1313
- }
1314
-
1315
- void cleanupSpawner(boost::container::vector<Callback> &postLockActions) {
1316
- assert(isAlive());
1317
- postLockActions.push_back(boost::bind(doCleanupSpawner, spawner));
1318
- }
457
+ AttachResult attach(const ProcessPtr &process,
458
+ boost::container::vector<Callback> &postLockActions);
459
+ void detach(const ProcessPtr &process,
460
+ boost::container::vector<Callback> &postLockActions);
461
+ void detachAll(boost::container::vector<Callback> &postLockActions);
1319
462
 
1320
- void restart(const Options &options, RestartMethod method = RM_DEFAULT);
463
+ void enable(const ProcessPtr &process,
464
+ boost::container::vector<Callback> &postLockActions);
465
+ DisableResult disable(const ProcessPtr &process, const DisableCallback &callback);
1321
466
 
467
+ /****** State inspection ******/
1322
468
 
1323
- /********************************************
1324
- * Queries
1325
- ********************************************/
469
+ unsigned int getProcessCount() const;
470
+ bool processLowerLimitsSatisfied() const;
471
+ bool processUpperLimitsReached() const;
472
+ bool allEnabledProcessesAreTotallyBusy() const;
1326
473
 
1327
- unsigned int getProcessCount() const {
1328
- return enabledCount + disablingCount + disabledCount;
1329
- }
474
+ unsigned int capacityUsed() const;
475
+ bool isWaitingForCapacity() const;
476
+ bool garbageCollectable(unsigned long long now = 0) const;
1330
477
 
1331
- /**
1332
- * Returns the number of processes in this group that should be part of the
1333
- * ApplicationPool process limits calculations.
1334
- */
1335
- unsigned int capacityUsed() const {
1336
- return enabledCount + disablingCount + disabledCount + processesBeingSpawned;
1337
- }
478
+ void inspectXml(std::ostream &stream, bool includeSecrets = true) const;
1338
479
 
1339
- /**
1340
- * Returns whether the lower bound of the group-specific process limits
1341
- * have been satisfied. Note that even if the result is false, the pool limits
1342
- * may not allow spawning, so you should check `pool->atFullCapacity()` too.
1343
- */
1344
- bool processLowerLimitsSatisfied() const {
1345
- return capacityUsed() >= options.minProcesses;
1346
- }
480
+ /****** Out-of-band work ******/
1347
481
 
1348
- /**
1349
- * Returns whether the upper bound of the group-specific process limits have
1350
- * been reached, or surpassed. Does not check whether pool limits have been
1351
- * reached. Use `pool->atFullCapacity()` to check for that.
1352
- */
1353
- bool processUpperLimitsReached() const {
1354
- return options.maxProcesses != 0 && capacityUsed() >= options.maxProcesses;
1355
- }
482
+ void requestOOBW(const ProcessPtr &process);
1356
483
 
1357
- /**
1358
- * Returns whether all enabled processes are totally busy. If so, another
1359
- * process should be spawned, if allowed by the process limits.
1360
- * Returns false if there are no enabled processes.
1361
- */
1362
- bool allEnabledProcessesAreTotallyBusy() const {
1363
- return nEnabledProcessesTotallyBusy == enabledCount;
1364
- }
484
+ /****** Miscellaneous ******/
1365
485
 
1366
- /**
1367
- * Checks whether this group is waiting for capacity on the pool to
1368
- * become available before it can continue processing requests.
1369
- */
1370
- bool isWaitingForCapacity() const {
1371
- return enabledProcesses.empty()
1372
- && processesBeingSpawned == 0
1373
- && !m_restarting
1374
- && !getWaitlist.empty();
1375
- }
1376
-
1377
- bool garbageCollectable(unsigned long long now = 0) const {
1378
- /* if (now == 0) {
1379
- now = SystemTime::getUsec();
1380
- }
1381
- return busyness() == 0
1382
- && getWaitlist.empty()
1383
- && disabledProcesses.empty()
1384
- && options.getMaxPreloaderIdleTime() != 0
1385
- && now - spawner->lastUsed() >
1386
- (unsigned long long) options.getMaxPreloaderIdleTime() * 1000000; */
1387
- return false;
1388
- }
1389
-
1390
- /** Whether a new process should be spawned for this group. */
1391
- bool shouldSpawn() const;
1392
- /** Whether a new process should be spawned for this group in the
1393
- * specific case that another get action is to be performed.
1394
- */
1395
- bool shouldSpawnForGetAction() const;
1396
-
1397
- /**
1398
- * Whether a new process is allowed to be spawned for this group,
1399
- * i.e. whether the upper processes limits have not been reached.
1400
- */
1401
- bool allowSpawn() const {
1402
- return isAlive()
1403
- && !processUpperLimitsReached()
1404
- && !poolAtFullCapacity();
1405
- }
1406
-
1407
- bool needsRestart(const Options &options) {
1408
- if (m_restarting) {
1409
- return false;
1410
- } else {
1411
- time_t now;
1412
- struct stat buf;
1413
-
1414
- if (options.currentTime != 0) {
1415
- now = options.currentTime / 1000000;
1416
- } else {
1417
- now = SystemTime::get();
1418
- }
1419
-
1420
- if (lastRestartFileCheckTime == 0) {
1421
- // First time we call needsRestart() for this group.
1422
- if (syscalls::stat(restartFile.c_str(), &buf) == 0) {
1423
- lastRestartFileMtime = buf.st_mtime;
1424
- } else {
1425
- lastRestartFileMtime = 0;
1426
- }
1427
- lastRestartFileCheckTime = now;
1428
- return false;
1429
-
1430
- } else if (lastRestartFileCheckTime <= now - (time_t) options.statThrottleRate) {
1431
- // Not first time we call needsRestart() for this group.
1432
- // Stat throttle time has passed.
1433
- bool restart;
1434
-
1435
- lastRestartFileCheckTime = now;
1436
-
1437
- if (lastRestartFileMtime > 0) {
1438
- // restart.txt existed before
1439
- if (syscalls::stat(restartFile.c_str(), &buf) == -1) {
1440
- // restart.txt no longer exists
1441
- lastRestartFileMtime = buf.st_mtime;
1442
- restart = false;
1443
- } else if (buf.st_mtime != lastRestartFileMtime) {
1444
- // restart.txt's mtime has changed
1445
- lastRestartFileMtime = buf.st_mtime;
1446
- restart = true;
1447
- } else {
1448
- restart = false;
1449
- }
1450
- } else {
1451
- // restart.txt didn't exist before
1452
- if (syscalls::stat(restartFile.c_str(), &buf) == 0) {
1453
- // restart.txt now exists
1454
- lastRestartFileMtime = buf.st_mtime;
1455
- restart = true;
1456
- } else {
1457
- // restart.txt still doesn't exist
1458
- lastRestartFileMtime = 0;
1459
- restart = false;
1460
- }
1461
- }
1462
-
1463
- if (!restart) {
1464
- alwaysRestartFileExists = restart =
1465
- syscalls::stat(alwaysRestartFile.c_str(), &buf) == 0;
1466
- }
1467
-
1468
- return restart;
1469
-
1470
- } else {
1471
- // Not first time we call needsRestart() for this group.
1472
- // Still within stat throttling window.
1473
- if (alwaysRestartFileExists) {
1474
- // always_restart.txt existed before
1475
- alwaysRestartFileExists = syscalls::stat(
1476
- alwaysRestartFile.c_str(), &buf) == 0;
1477
- return alwaysRestartFileExists;
1478
- } else {
1479
- // Don't check until stat throttling window is over
1480
- return false;
1481
- }
1482
- }
1483
- }
1484
- }
1485
-
1486
- bool spawning() const {
1487
- return m_spawning;
1488
- }
1489
-
1490
- bool restarting() const {
1491
- return m_restarting;
1492
- }
1493
-
1494
- template<typename Stream>
1495
- void inspectXml(Stream &stream, bool includeSecrets = true) const {
1496
- ProcessList::const_iterator it;
1497
-
1498
- stream << "<name>" << escapeForXml(name) << "</name>";
1499
- stream << "<component_name>" << escapeForXml(componentInfo.name) << "</component_name>";
1500
- stream << "<app_root>" << escapeForXml(options.appRoot) << "</app_root>";
1501
- stream << "<app_type>" << escapeForXml(options.appType) << "</app_type>";
1502
- stream << "<environment>" << escapeForXml(options.environment) << "</environment>";
1503
- stream << "<uuid>" << toString(uuid) << "</uuid>";
1504
- stream << "<enabled_process_count>" << enabledCount << "</enabled_process_count>";
1505
- stream << "<disabling_process_count>" << disablingCount << "</disabling_process_count>";
1506
- stream << "<disabled_process_count>" << disabledCount << "</disabled_process_count>";
1507
- stream << "<capacity_used>" << capacityUsed() << "</capacity_used>";
1508
- stream << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
1509
- stream << "<disable_wait_list_size>" << disableWaitlist.size() << "</disable_wait_list_size>";
1510
- stream << "<processes_being_spawned>" << processesBeingSpawned << "</processes_being_spawned>";
1511
- if (m_spawning) {
1512
- stream << "<spawning/>";
1513
- }
1514
- if (restarting()) {
1515
- stream << "<restarting/>";
1516
- }
1517
- if (includeSecrets) {
1518
- stream << "<secret>" << escapeForXml(StaticString(secret, SECRET_SIZE)) << "</secret>";
1519
- }
1520
- LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
1521
- switch (lifeStatus) {
1522
- case ALIVE:
1523
- stream << "<life_status>ALIVE</life_status>";
1524
- break;
1525
- case SHUTTING_DOWN:
1526
- stream << "<life_status>SHUTTING_DOWN</life_status>";
1527
- break;
1528
- case SHUT_DOWN:
1529
- stream << "<life_status>SHUT_DOWN</life_status>";
1530
- break;
1531
- default:
1532
- P_BUG("Unknown 'lifeStatus' state " << lifeStatus);
1533
- }
1534
-
1535
- stream << "<options>";
1536
- options.toXml(stream, getResourceLocator());
1537
- stream << "</options>";
1538
-
1539
- stream << "<processes>";
1540
-
1541
- for (it = enabledProcesses.begin(); it != enabledProcesses.end(); it++) {
1542
- stream << "<process>";
1543
- (*it)->inspectXml(stream, includeSecrets);
1544
- stream << "</process>";
1545
- }
1546
- for (it = disablingProcesses.begin(); it != disablingProcesses.end(); it++) {
1547
- stream << "<process>";
1548
- (*it)->inspectXml(stream, includeSecrets);
1549
- stream << "</process>";
1550
- }
1551
- for (it = disabledProcesses.begin(); it != disabledProcesses.end(); it++) {
1552
- stream << "<process>";
1553
- (*it)->inspectXml(stream, includeSecrets);
1554
- stream << "</process>";
1555
- }
1556
- for (it = detachedProcesses.begin(); it != detachedProcesses.end(); it++) {
1557
- stream << "<process>";
1558
- (*it)->inspectXml(stream, includeSecrets);
1559
- stream << "</process>";
1560
- }
1561
-
1562
- stream << "</processes>";
1563
- }
486
+ void cleanupSpawner(boost::container::vector<Callback> &postLockActions);
487
+ bool authorizeByUid(uid_t uid) const;
488
+ bool authorizeByApiKey(const ApiKey &key) const;
1564
489
  };
1565
490
 
1566
491