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
@@ -111,7 +111,7 @@ private:
111
111
  result.push_back(&options.loggingAgentAddress);
112
112
  result.push_back(&options.loggingAgentUsername);
113
113
  result.push_back(&options.loggingAgentPassword);
114
- result.push_back(&options.groupSecret);
114
+ result.push_back(&options.apiKey);
115
115
  result.push_back(&options.hostName);
116
116
  result.push_back(&options.uri);
117
117
  result.push_back(&options.unionStationKey);
@@ -417,7 +417,7 @@ public:
417
417
  unsigned long long currentTime;
418
418
 
419
419
  /** When true, Pool::get() and Pool::asyncGet() will create the necessary
420
- * SuperGroup and Group structures just as normally, and will even handle
420
+ * Group structure just as normally, and will even handle
421
421
  * restarting logic, but will not actually spawn any processes and will not
422
422
  * open a session with an existing process. Instead, a fake Session object
423
423
  * is returned which points to a Process object that isn't stored anywhere
@@ -436,8 +436,8 @@ public:
436
436
  * see these values.
437
437
  */
438
438
 
439
- /** The secret key of the pool group that the spawned process is to belong to. */
440
- StaticString groupSecret;
439
+ /** The API key of the pool group that the spawned process is to belong to. */
440
+ StaticString apiKey;
441
441
 
442
442
  /**
443
443
  * A UUID that's generated on Group initialization, and changes every time
@@ -603,7 +603,7 @@ public:
603
603
  appendKeyValue4(vec, "debugger", debugger);
604
604
  appendKeyValue4(vec, "analytics", analytics);
605
605
 
606
- appendKeyValue (vec, "group_secret", groupSecret);
606
+ appendKeyValue (vec, "api_key", apiKey);
607
607
 
608
608
  /*********************************/
609
609
  }
@@ -45,13 +45,14 @@
45
45
  #include <boost/date_time/posix_time/posix_time_types.hpp>
46
46
  #include <oxt/dynamic_thread_group.hpp>
47
47
  #include <oxt/backtrace.hpp>
48
+ #include <sys/types.h>
48
49
  #include <ApplicationPool2/Common.h>
50
+ #include <ApplicationPool2/Context.h>
49
51
  #include <ApplicationPool2/Process.h>
50
52
  #include <ApplicationPool2/Group.h>
51
- #include <ApplicationPool2/SuperGroup.h>
52
53
  #include <ApplicationPool2/Session.h>
53
- #include <ApplicationPool2/SpawnerFactory.h>
54
54
  #include <ApplicationPool2/Options.h>
55
+ #include <SpawningKit/Factory.h>
55
56
  #include <MemoryKit/palloc.h>
56
57
  #include <Logging.h>
57
58
  #include <Exceptions.h>
@@ -74,22 +75,93 @@ using namespace oxt;
74
75
 
75
76
  class Pool: public boost::enable_shared_from_this<Pool> {
76
77
  public:
77
- typedef void (*AbortLongRunningConnectionsCallback)(const ProcessPtr &process);
78
+ struct AuthenticationOptions {
79
+ uid_t uid;
80
+ ApiKey apiKey;
81
+
82
+ AuthenticationOptions()
83
+ : uid(-1)
84
+ { }
85
+
86
+ static AuthenticationOptions makeAuthorized() {
87
+ AuthenticationOptions options;
88
+ options.apiKey = ApiKey::makeSuper();
89
+ return options;
90
+ }
91
+ };
92
+
93
+
94
+ /****** Group data structure utilities ******/
95
+
96
+ struct RestartOptions: public AuthenticationOptions {
97
+ RestartMethod method;
98
+
99
+ RestartOptions()
100
+ : method(RM_DEFAULT)
101
+ { }
102
+
103
+ static RestartOptions makeAuthorized() {
104
+ RestartOptions options;
105
+ options.apiKey = ApiKey::makeSuper();
106
+ return options;
107
+ }
108
+ };
109
+
110
+ /****** State inspection ******/
111
+
112
+ struct InspectOptions: public AuthenticationOptions {
113
+ bool colorize;
114
+ bool verbose;
115
+
116
+ InspectOptions()
117
+ : colorize(false),
118
+ verbose(false)
119
+ { }
120
+
121
+ InspectOptions(const VariantMap &options)
122
+ : colorize(options.getBool("colorize", false, false)),
123
+ verbose(options.getBool("verbose", false, false))
124
+ { }
125
+
126
+ static InspectOptions makeAuthorized() {
127
+ InspectOptions options;
128
+ options.apiKey = ApiKey::makeSuper();
129
+ return options;
130
+ }
131
+ };
132
+
133
+ struct ToXmlOptions: public AuthenticationOptions {
134
+ bool secrets;
135
+
136
+ ToXmlOptions()
137
+ : secrets(true)
138
+ { }
139
+
140
+ ToXmlOptions(const VariantMap &options)
141
+ : secrets(options.getBool("secrets", false, false))
142
+ { }
143
+
144
+ static ToXmlOptions makeAuthorized() {
145
+ ToXmlOptions options;
146
+ options.apiKey = ApiKey::makeSuper();
147
+ return options;
148
+ }
149
+ };
150
+
78
151
 
79
152
  // Actually private, but marked public so that unit tests can access the fields.
80
153
  public:
81
- friend class SuperGroup;
82
154
  friend class Group;
83
155
  friend class Process;
84
156
  friend struct tut::ApplicationPool2_PoolTest;
85
157
 
86
- SpawnerFactoryPtr spawnerFactory;
87
-
88
158
  mutable boost::mutex syncher;
89
159
  unsigned int max;
90
160
  unsigned long long maxIdleTime;
91
161
  bool selfchecking;
92
162
 
163
+ Context context;
164
+
93
165
  /**
94
166
  * Code can register background threads in one of these dynamic thread groups
95
167
  * to ensure that threads are interrupted and/or joined properly upon Pool
@@ -109,9 +181,7 @@ public:
109
181
  SHUT_DOWN
110
182
  } lifeStatus;
111
183
 
112
- mutable SuperGroupMap superGroups;
113
- boost::mutex sessionObjectPoolSyncher;
114
- object_pool<Session> sessionObjectPool;
184
+ mutable GroupMap groups;
115
185
  psg_pool_t *palloc;
116
186
 
117
187
  /**
@@ -132,16 +202,16 @@ public:
132
202
  * then be killed to free capacity.
133
203
  * - A process has failed to spawn, resulting in capacity to
134
204
  * become free.
135
- * - A SuperGroup failed to initialize, resulting in free capacity.
205
+ * - A Group failed to initialize, resulting in free capacity.
136
206
  * - Someone commanded Pool to detach a process, resulting in free
137
207
  * capacity.
138
- * - Someone commanded Pool to detach a SuperGroup, resulting in
208
+ * - Someone commanded Pool to detach a Group, resulting in
139
209
  * free capacity.
140
210
  * - The 'max' option has been increased, resulting in free capacity.
141
211
  *
142
212
  * Invariant 1:
143
213
  * for all options in getWaitlist:
144
- * options.getAppGroupName() is not in 'superGroups'.
214
+ * options.getAppGroupName() is not in 'groups'.
145
215
  *
146
216
  * Invariant 2:
147
217
  * if getWaitlist is non-empty:
@@ -154,215 +224,136 @@ public:
154
224
 
155
225
  const VariantMap *agentsOptions;
156
226
 
157
- #include <ApplicationPool2/Pool/AnalyticsCollection.h>
158
- #include <ApplicationPool2/Pool/GarbageCollection.h>
159
- #include <ApplicationPool2/Pool/GeneralUtils.h>
160
- #include <ApplicationPool2/Pool/ProcessUtils.h>
161
- #include <ApplicationPool2/Pool/Inspection.h>
162
- #include <ApplicationPool2/Pool/Debug.h>
163
-
164
227
  // Actually private, but marked public so that unit tests can access the fields.
165
228
  public:
166
-
167
- /** Process all waiters on the getWaitlist. Call when capacity has become free.
168
- * This function assigns sessions to them by calling get() on the corresponding
169
- * SuperGroups, or by creating more SuperGroups, in so far the new capacity allows.
170
- */
171
- void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions) {
172
- bool done = false;
173
- vector<GetWaiter>::iterator it, end = getWaitlist.end();
174
- vector<GetWaiter> newWaitlist;
175
-
176
- for (it = getWaitlist.begin(); it != end && !done; it++) {
177
- GetWaiter &waiter = *it;
178
-
179
- SuperGroup *superGroup = findMatchingSuperGroup(waiter.options);
180
- if (superGroup != NULL) {
181
- SessionPtr session = superGroup->get(waiter.options, waiter.callback,
182
- postLockActions);
183
- if (session != NULL) {
184
- postLockActions.push_back(boost::bind(GetCallback::call,
185
- waiter.callback, session, ExceptionPtr()));
186
- }
187
- /* else: the callback has now been put in
188
- * the group's get wait list.
189
- */
190
- } else if (!atFullCapacityUnlocked()) {
191
- createSuperGroupAndAsyncGetFromIt(waiter.options, waiter.callback,
192
- postLockActions);
193
- } else {
194
- /* Still cannot satisfy this get request. Keep it on the get
195
- * wait list and try again later.
196
- */
197
- newWaitlist.push_back(waiter);
198
- }
229
+ /****** Debugging support *******/
230
+
231
+ struct DebugSupport {
232
+ /** Mailbox for the unit tests to receive messages on. */
233
+ MessageBoxPtr debugger;
234
+ /** Mailbox for the ApplicationPool code to receive messages on. */
235
+ MessageBoxPtr messages;
236
+
237
+ // Choose aspects to debug.
238
+ bool restarting;
239
+ bool spawning;
240
+ bool oobw;
241
+ bool testOverflowRequestQueue;
242
+ bool detachedProcessesChecker;
243
+
244
+ // The following fields may only be accessed by Pool.
245
+ boost::mutex syncher;
246
+ unsigned int spawnLoopIteration;
247
+
248
+ DebugSupport() {
249
+ debugger = boost::make_shared<MessageBox>();
250
+ messages = boost::make_shared<MessageBox>();
251
+ restarting = true;
252
+ spawning = true;
253
+ oobw = false;
254
+ detachedProcessesChecker = false;
255
+ testOverflowRequestQueue = false;
256
+ spawnLoopIteration = 0;
199
257
  }
258
+ };
200
259
 
201
- std::swap(getWaitlist, newWaitlist);
202
- }
260
+ typedef boost::shared_ptr<DebugSupport> DebugSupportPtr;
203
261
 
204
- template<typename Queue>
205
- static void assignExceptionToGetWaiters(Queue &getWaitlist,
206
- const ExceptionPtr &exception,
207
- boost::container::vector<Callback> &postLockActions)
208
- {
209
- while (!getWaitlist.empty()) {
210
- postLockActions.push_back(boost::bind(GetCallback::call,
211
- getWaitlist.front().callback, SessionPtr(),
212
- exception));
213
- getWaitlist.pop_front();
214
- }
215
- }
216
-
217
- void possiblySpawnMoreProcessesForExistingGroups() {
218
- /* Looks for Groups that are waiting for capacity to become available,
219
- * and spawn processes in those groups.
220
- */
221
- SuperGroupMap::ConstIterator sg_it(superGroups);
222
- while (*sg_it != NULL) {
223
- const SuperGroupPtr &superGroup = sg_it.getValue();
224
- foreach (GroupPtr group, superGroup->groups) {
225
- if (group->isWaitingForCapacity()) {
226
- P_DEBUG("Group " << group->name << " is waiting for capacity");
227
- group->spawn();
228
- if (atFullCapacityUnlocked()) {
229
- return;
230
- }
231
- }
232
- }
233
- sg_it.next();
234
- }
235
- /* Now look for Groups that haven't maximized their allowed capacity
236
- * yet, and spawn processes in those groups.
237
- */
238
- sg_it = SuperGroupMap::ConstIterator(superGroups);
239
- while (*sg_it != NULL) {
240
- const SuperGroupPtr &superGroup = sg_it.getValue();
241
- foreach (GroupPtr group, superGroup->groups) {
242
- if (group->shouldSpawn()) {
243
- P_DEBUG("Group " << group->name << " requests more processes to be spawned");
244
- group->spawn();
245
- if (atFullCapacityUnlocked()) {
246
- return;
247
- }
248
- }
249
- }
250
- sg_it.next();
251
- }
252
- }
262
+ DebugSupportPtr debugSupport;
253
263
 
254
- void migrateSuperGroupGetWaitlistToPool(const SuperGroupPtr &superGroup) {
255
- getWaitlist.reserve(getWaitlist.size() + superGroup->getWaitlist.size());
256
- while (!superGroup->getWaitlist.empty()) {
257
- getWaitlist.push_back(superGroup->getWaitlist.front());
258
- superGroup->getWaitlist.pop_front();
259
- }
260
- }
261
-
262
- unsigned int capacityUsedUnlocked() const {
263
- if (superGroups.size() == 1) {
264
- SuperGroupPtr *superGroup;
265
- superGroups.lookupRandom(NULL, &superGroup);
266
- return (*superGroup)->capacityUsed();
267
- } else {
268
- SuperGroupMap::ConstIterator sg_it(superGroups);
269
- int result = 0;
270
- while (*sg_it != NULL) {
271
- const SuperGroupPtr &superGroup = sg_it.getValue();
272
- result += superGroup->capacityUsed();
273
- sg_it.next();
274
- }
275
- return result;
276
- }
277
- }
278
264
 
279
- bool atFullCapacityUnlocked() const {
280
- return capacityUsedUnlocked() >= max;
281
- }
265
+ /****** Analytics collection ******/
282
266
 
283
- /**
284
- * Calls Group::detach() so be sure to fix up the invariants afterwards.
285
- * See the comments for Group::detach() and the code for detachProcessUnlocked().
286
- */
287
- ProcessPtr forceFreeCapacity(const Group *exclude,
288
- boost::container::vector<Callback> &postLockActions)
289
- {
290
- ProcessPtr process = findOldestIdleProcess(exclude);
291
- if (process != NULL) {
292
- P_DEBUG("Forcefully detaching process " << process->inspect() <<
293
- " in order to free capacity in the pool");
294
-
295
- Group *group = process->getGroup();
296
- assert(group != NULL);
297
- assert(group->getWaitlist.empty());
298
-
299
- SuperGroup *superGroup = group->getSuperGroup();
300
- assert(superGroup != NULL);
301
- (void) superGroup;
302
-
303
- group->detach(process, postLockActions);
304
- }
305
- return process;
306
- }
267
+ struct UnionStationLogEntry {
268
+ string groupName;
269
+ const char *category;
270
+ string key;
271
+ string data;
272
+ };
307
273
 
308
- /**
309
- * Forcefully destroys and detaches the given SuperGroup. After detaching
310
- * the SuperGroup may have a non-empty getWaitlist so be sure to do
311
- * something with it.
312
- *
313
- * Also, one of the post lock actions can potentially perform a long-running
314
- * operation, so running them in a thread is advised.
315
- */
316
- void forceDetachSuperGroup(const SuperGroupPtr &superGroup,
317
- boost::container::vector<Callback> &postLockActions,
318
- const SuperGroup::ShutdownCallback &callback)
319
- {
320
- const SuperGroupPtr sp = superGroup; // Prevent premature destruction.
321
- bool removed = superGroups.erase(superGroup->name);
322
- assert(removed);
323
- (void) removed; // Shut up compiler warning.
324
- superGroup->destroy(false, postLockActions, callback);
325
- }
274
+ SystemMetricsCollector systemMetricsCollector;
275
+ SystemMetrics systemMetrics;
326
276
 
327
- bool detachProcessUnlocked(const ProcessPtr &process,
328
- boost::container::vector<Callback> &postLockActions)
329
- {
330
- if (OXT_LIKELY(process->isAlive())) {
331
- verifyInvariants();
332
-
333
- Group *group = process->getGroup();
334
- SuperGroup *superGroup = group->getSuperGroup();
335
- assert(superGroup->state != SuperGroup::INITIALIZING);
336
- assert(superGroup->getWaitlist.empty());
337
- (void) superGroup;
338
-
339
- group->detach(process, postLockActions);
340
- // 'process' may now be a stale pointer so don't use it anymore.
341
- assignSessionsToGetWaiters(postLockActions);
342
- possiblySpawnMoreProcessesForExistingGroups();
343
-
344
- group->verifyInvariants();
345
- superGroup->verifyInvariants();
346
- verifyInvariants();
347
- verifyExpensiveInvariants();
348
-
349
- return true;
350
- } else {
351
- return false;
352
- }
353
- }
277
+ void initializeAnalyticsCollection();
278
+ static void collectAnalytics(PoolPtr self);
279
+ static void collectPids(const ProcessList &processes, vector<pid_t> &pids);
280
+ static void updateProcessMetrics(const ProcessList &processes,
281
+ const ProcessMetricMap &allMetrics,
282
+ vector<ProcessPtr> &processesToDetach);
283
+ void prepareUnionStationProcessStateLogs(vector<UnionStationLogEntry> &logEntries,
284
+ const GroupPtr &group) const;
285
+ void prepareUnionStationSystemMetricsLogs(vector<UnionStationLogEntry> &logEntries,
286
+ const GroupPtr &group) const;
287
+ void realCollectAnalytics();
354
288
 
355
- struct DetachSuperGroupWaitTicket {
289
+
290
+ /****** Garbage collection ******/
291
+
292
+ struct GarbageCollectorState {
293
+ unsigned long long now;
294
+ unsigned long long nextGcRunTime;
295
+ boost::container::vector<Callback> actions;
296
+ };
297
+
298
+ boost::condition_variable garbageCollectionCond;
299
+
300
+ void initializeGarbageCollection();
301
+ static void garbageCollect(PoolPtr self);
302
+ void maybeUpdateNextGcRuntime(GarbageCollectorState &state, unsigned long candidate);
303
+ void checkWhetherProcessCanBeGarbageCollected(GarbageCollectorState &state,
304
+ const GroupPtr &group, const ProcessPtr &process, ProcessList &output);
305
+ void garbageCollectProcessesInGroup(GarbageCollectorState &state,
306
+ const GroupPtr &group);
307
+ void maybeCleanPreloader(GarbageCollectorState &state, const GroupPtr &group);
308
+ unsigned long long realGarbageCollect();
309
+ void wakeupGarbageCollector();
310
+
311
+
312
+ /****** General utilities ******/
313
+
314
+ static const char *maybeColorize(const InspectOptions &options, const char *color);
315
+ static const char *maybePluralize(unsigned int count, const char *singular, const char *plural);
316
+ static void runAllActions(const boost::container::vector<Callback> &actions);
317
+ static void runAllActionsWithCopy(boost::container::vector<Callback> actions);
318
+ bool runHookScripts(const char *name,
319
+ const boost::function<void (HookScriptOptions &)> &setup) const;
320
+ void verifyInvariants() const;
321
+ void verifyExpensiveInvariants() const;
322
+ void fullVerifyInvariants() const;
323
+ void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions);
324
+ template<typename Queue> static void assignExceptionToGetWaiters(Queue &getWaitlist,
325
+ const ExceptionPtr &exception,
326
+ boost::container::vector<Callback> &postLockActions);
327
+ static void syncGetCallback(const SessionPtr &session, const ExceptionPtr &e,
328
+ void *userData);
329
+
330
+
331
+ /****** Group data structure utilities ******/
332
+
333
+ struct DetachGroupWaitTicket {
356
334
  boost::mutex syncher;
357
335
  boost::condition_variable cond;
358
- SuperGroup::ShutdownResult result;
359
336
  bool done;
360
337
 
361
- DetachSuperGroupWaitTicket() {
338
+ DetachGroupWaitTicket() {
362
339
  done = false;
363
340
  }
364
341
  };
365
342
 
343
+ const GroupPtr getGroup(const char *name);
344
+ Group *findMatchingGroup(const Options &options);
345
+ GroupPtr createGroup(const Options &options);
346
+ GroupPtr createGroupAndAsyncGetFromIt(const Options &options,
347
+ const GetCallback &callback, boost::container::vector<Callback> &postLockActions);
348
+ void forceDetachGroup(const GroupPtr &group,
349
+ const Callback &callback,
350
+ boost::container::vector<Callback> &postLockActions);
351
+ static void syncDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket);
352
+ static void waitDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket);
353
+
354
+
355
+ /****** Process data structure utilities ******/
356
+
366
357
  struct DisableWaitTicket {
367
358
  boost::mutex syncher;
368
359
  boost::condition_variable cond;
@@ -374,641 +365,95 @@ public:
374
365
  }
375
366
  };
376
367
 
377
- static void syncDetachSuperGroupCallback(SuperGroup::ShutdownResult result,
378
- boost::shared_ptr<DetachSuperGroupWaitTicket> ticket)
379
- {
380
- LockGuard l(ticket->syncher);
381
- ticket->done = true;
382
- ticket->result = result;
383
- ticket->cond.notify_one();
384
- }
385
-
386
- static void waitDetachSuperGroupCallback(boost::shared_ptr<DetachSuperGroupWaitTicket> ticket) {
387
- ScopedLock l(ticket->syncher);
388
- while (!ticket->done) {
389
- ticket->cond.wait(l);
390
- }
391
- }
392
-
368
+ ProcessPtr findOldestIdleProcess(const Group *exclude = NULL) const;
369
+ ProcessPtr findBestProcessToTrash() const;
370
+ ProcessPtr forceFreeCapacity(const Group *exclude,
371
+ boost::container::vector<Callback> &postLockActions);
372
+ bool detachProcessUnlocked(const ProcessPtr &process,
373
+ boost::container::vector<Callback> &postLockActions);
393
374
  static void syncDisableProcessCallback(const ProcessPtr &process, DisableResult result,
394
- boost::shared_ptr<DisableWaitTicket> ticket)
395
- {
396
- LockGuard l(ticket->syncher);
397
- ticket->done = true;
398
- ticket->result = result;
399
- ticket->cond.notify_one();
400
- }
375
+ boost::shared_ptr<DisableWaitTicket> ticket);
376
+ void possiblySpawnMoreProcessesForExistingGroups();
401
377
 
402
- static void syncGetCallback(const SessionPtr &session, const ExceptionPtr &e,
403
- void *userData)
404
- {
405
- Ticket *ticket = static_cast<Ticket *>(userData);
406
- ScopedLock lock(ticket->syncher);
407
- if (OXT_LIKELY(session != NULL)) {
408
- ticket->session = session;
409
- } else {
410
- ticket->exception = e;
411
- }
412
- ticket->cond.notify_one();
413
- }
414
-
415
- SuperGroup *findMatchingSuperGroup(const Options &options) {
416
- SuperGroupPtr *superGroup;
417
- if (superGroups.lookup(options.getAppGroupName(), &superGroup)) {
418
- return superGroup->get();
419
- } else {
420
- return NULL;
421
- }
422
- }
423
-
424
- SuperGroupPtr createSuperGroup(const Options &options) {
425
- SuperGroupPtr superGroup = boost::make_shared<SuperGroup>(this,
426
- options);
427
- superGroup->initialize();
428
- superGroups.insert(options.getAppGroupName(), superGroup);
429
- wakeupGarbageCollector();
430
- return superGroup;
431
- }
432
-
433
- SuperGroupPtr createSuperGroupAndAsyncGetFromIt(const Options &options,
434
- const GetCallback &callback, boost::container::vector<Callback> &postLockActions)
435
- {
436
- SuperGroupPtr superGroup = createSuperGroup(options);
437
- SessionPtr session = superGroup->get(options, callback,
438
- postLockActions);
439
- /* Callback should now have been put on the wait list,
440
- * unless something has changed and we forgot to update
441
- * some code here...
442
- */
443
- assert(session == NULL);
444
- return superGroup;
445
- }
446
-
447
- // Debugging helper function, implemented in .cpp file so that GDB can access it.
448
- const SuperGroupPtr getSuperGroup(const char *name);
378
+
379
+ /****** State inspection ******/
380
+
381
+ unsigned int capacityUsedUnlocked() const;
382
+ bool atFullCapacityUnlocked() const;
383
+ void inspectProcessList(const InspectOptions &options, stringstream &result,
384
+ const Group *group, const ProcessList &processes) const;
449
385
 
450
386
  public:
387
+ typedef void (*AbortLongRunningConnectionsCallback)(const ProcessPtr &process);
451
388
  AbortLongRunningConnectionsCallback abortLongRunningConnectionsCallback;
452
389
 
453
- Pool(const SpawnerFactoryPtr &spawnerFactory,
454
- const VariantMap *agentsOptions = NULL)
455
- : sessionObjectPool(64, 1024),
456
- abortLongRunningConnectionsCallback(NULL)
457
- {
458
- this->spawnerFactory = spawnerFactory;
459
- this->agentsOptions = agentsOptions;
460
-
461
- try {
462
- systemMetricsCollector.collect(systemMetrics);
463
- } catch (const RuntimeException &e) {
464
- P_WARN("Unable to collect system metrics: " << e.what());
465
- }
466
390
 
467
- lifeStatus = ALIVE;
468
- max = 6;
469
- maxIdleTime = 60 * 1000000;
470
- selfchecking = true;
471
- palloc = psg_create_pool(PSG_DEFAULT_POOL_SIZE);
472
-
473
- // The following code only serve to instantiate certain inline methods
474
- // so that they can be invoked from gdb.
475
- (void) SuperGroupPtr().get();
476
- (void) GroupPtr().get();
477
- (void) ProcessPtr().get();
478
- (void) SessionPtr().get();
479
- }
480
-
481
- ~Pool() {
482
- if (lifeStatus != SHUT_DOWN) {
483
- P_BUG("You must call Pool::destroy() before actually destroying the Pool object!");
484
- }
485
- psg_destroy_pool(palloc);
486
- }
487
-
488
- /** Must be called right after construction. */
489
- void initialize() {
490
- LockGuard l(syncher);
491
- initializeAnalyticsCollection();
492
- initializeGarbageCollection();
493
- }
494
-
495
- void initDebugging() {
496
- LockGuard l(syncher);
497
- debugSupport = boost::make_shared<DebugSupport>();
498
- }
499
-
500
- /** Should be called right after the agent has received
501
- * the message to exit gracefully. This will tell processes to
502
- * abort any long-running connections, e.g. WebSocket connections,
503
- * because the RequestHandler has to wait until all connections are
504
- * finished before proceeding with shutdown.
505
- */
506
- void prepareForShutdown() {
507
- TRACE_POINT();
508
- ScopedLock lock(syncher);
509
- assert(lifeStatus == ALIVE);
510
- lifeStatus = PREPARED_FOR_SHUTDOWN;
511
- if (abortLongRunningConnectionsCallback != NULL) {
512
- vector<ProcessPtr> processes = getProcesses(false);
513
- foreach (ProcessPtr process, processes) {
514
- // Ensure that the process is not immediately respawned.
515
- process->getGroup()->options.minProcesses = 0;
516
- abortLongRunningConnectionsCallback(process);
517
- }
518
- }
519
- }
520
-
521
- /** Must be called right before destruction. */
522
- void destroy() {
523
- TRACE_POINT();
524
- ScopedLock lock(syncher);
525
- assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
526
-
527
- lifeStatus = SHUTTING_DOWN;
528
-
529
- while (!superGroups.empty()) {
530
- SuperGroupPtr *superGroup;
531
- superGroups.lookupRandom(NULL, &superGroup);
532
- string name = superGroup->get()->name;
533
- lock.unlock();
534
- detachSuperGroupByName(name);
535
- lock.lock();
536
- }
391
+ /****** Initialization and shutdown ******/
537
392
 
538
- UPDATE_TRACE_POINT();
539
- lock.unlock();
540
- interruptableThreads.interrupt_and_join_all();
541
- nonInterruptableThreads.join_all();
542
- lock.lock();
393
+ Pool(const SpawningKit::FactoryPtr &spawningKitFactory,
394
+ const VariantMap *agentsOptions = NULL);
395
+ ~Pool();
396
+ void initialize();
397
+ void initDebugging();
398
+ void prepareForShutdown();
399
+ void destroy();
543
400
 
544
- lifeStatus = SHUT_DOWN;
545
401
 
546
- UPDATE_TRACE_POINT();
547
- verifyInvariants();
548
- verifyExpensiveInvariants();
549
- }
402
+ /****** General utilities ******/
550
403
 
551
- // 'lockNow == false' may only be used during unit tests. Normally we
552
- // should never call the callback while holding the lock.
553
- void asyncGet(const Options &options, const GetCallback &callback, bool lockNow = true) {
554
- DynamicScopedLock lock(syncher, lockNow);
404
+ Context *getContext();
405
+ const SpawningKit::ConfigPtr &getSpawningKitConfig() const;
406
+ const UnionStation::CorePtr &getUnionStationCore() const;
407
+ const RandomGeneratorPtr &getRandomGenerator() const;
555
408
 
556
- assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
557
- verifyInvariants();
558
- P_TRACE(2, "asyncGet(appGroupName=" << options.getAppGroupName() << ")");
559
- boost::container::vector<Callback> actions;
560
409
 
561
- SuperGroup *existingSuperGroup = findMatchingSuperGroup(options);
562
- if (OXT_LIKELY(existingSuperGroup != NULL)) {
563
- /* Best case: the app super group is already in the pool. Let's use it. */
564
- P_TRACE(2, "Found existing SuperGroup");
565
- existingSuperGroup->verifyInvariants();
566
- SessionPtr session = existingSuperGroup->get(options, callback, actions);
567
- existingSuperGroup->verifyInvariants();
568
- verifyInvariants();
569
- P_TRACE(2, "asyncGet() finished");
570
- if (lockNow) {
571
- lock.unlock();
572
- }
573
- if (session != NULL) {
574
- callback(session, ExceptionPtr());
575
- }
576
-
577
- } else if (!atFullCapacityUnlocked()) {
578
- /* The app super group isn't in the pool and we have enough free
579
- * resources to make a new one.
580
- */
581
- P_DEBUG("Spawning new SuperGroup");
582
- SuperGroupPtr superGroup = createSuperGroupAndAsyncGetFromIt(options,
583
- callback, actions);
584
- superGroup->verifyInvariants();
585
- verifyInvariants();
586
- P_DEBUG("asyncGet() finished");
587
-
588
- } else {
589
- /* Uh oh, the app super group isn't in the pool but we don't
590
- * have the resources to make a new one. The sysadmin should
591
- * configure the system to let something like this happen
592
- * as least as possible, but let's try to handle it as well
593
- * as we can.
594
- */
595
- ProcessPtr freedProcess = forceFreeCapacity(NULL, actions);
596
- if (freedProcess == NULL) {
597
- /* No process is eligible for killing. This could happen if, for example,
598
- * all (super)groups are currently initializing/restarting/spawning/etc.
599
- * We have no choice but to satisfy this get() action later when resources
600
- * become available.
601
- */
602
- P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
603
- getWaitlist.push_back(GetWaiter(
604
- options.copyAndPersist().detachFromUnionStationTransaction(),
605
- callback));
606
- } else {
607
- /* Now that a process has been trashed we can create
608
- * the missing SuperGroup.
609
- */
610
- P_DEBUG("Creating new SuperGroup");
611
- SuperGroupPtr superGroup;
612
- superGroup = boost::make_shared<SuperGroup>(this, options);
613
- superGroup->initialize();
614
- superGroups.insert(options.getAppGroupName(), superGroup);
615
- wakeupGarbageCollector();
616
- SessionPtr session = superGroup->get(options, callback,
617
- actions);
618
- /* The SuperGroup is still initializing so the callback
619
- * should now have been put on the wait list,
620
- * unless something has changed and we forgot to update
621
- * some code here...
622
- */
623
- assert(session == NULL);
624
- freedProcess->getGroup()->verifyInvariants();
625
- superGroup->verifyInvariants();
626
- }
627
-
628
- assert(atFullCapacityUnlocked());
629
- verifyInvariants();
630
- verifyExpensiveInvariants();
631
- P_TRACE(2, "asyncGet() finished");
632
- }
410
+ /****** Group manipulation ******/
633
411
 
634
- if (!actions.empty()) {
635
- if (lockNow) {
636
- if (lock.owns_lock()) {
637
- lock.unlock();
638
- }
639
- runAllActions(actions);
640
- } else {
641
- // This state is not allowed. If we reach
642
- // here then it probably indicates a bug in
643
- // the test suite.
644
- abort();
645
- }
646
- }
647
- }
412
+ GroupPtr findOrCreateGroup(const Options &options);
413
+ GroupPtr findGroupByApiKey(const StaticString &value, bool lock = true) const;
414
+ bool detachGroupByName(const HashedStaticString &name);
415
+ bool detachGroupByApiKey(const StaticString &value);
416
+ bool restartGroupByName(const StaticString &name,
417
+ const RestartOptions &options = RestartOptions::makeAuthorized());
418
+ unsigned int restartGroupsByAppRoot(const StaticString &appRoot,
419
+ const RestartOptions &options = RestartOptions::makeAuthorized());
648
420
 
649
- // TODO: 'ticket' should be a boost::shared_ptr for interruption-safety.
650
- SessionPtr get(const Options &options, Ticket *ticket) {
651
- ticket->session.reset();
652
- ticket->exception.reset();
653
421
 
654
- GetCallback callback;
655
- callback.func = syncGetCallback;
656
- callback.userData = ticket;
657
- asyncGet(options, callback);
422
+ /***** Process manipulation ******/
658
423
 
659
- ScopedLock lock(ticket->syncher);
660
- while (ticket->session == NULL && ticket->exception == NULL) {
661
- ticket->cond.wait(lock);
662
- }
663
- lock.unlock();
664
-
665
- if (OXT_LIKELY(ticket->session != NULL)) {
666
- SessionPtr session = ticket->session;
667
- ticket->session.reset();
668
- return session;
669
- } else {
670
- rethrowException(ticket->exception);
671
- return SessionPtr(); // Shut up compiler warning.
672
- }
673
- }
674
-
675
- GroupPtr findOrCreateGroup(const Options &options) {
676
- Options options2 = options;
677
- options2.noop = true;
678
-
679
- Ticket ticket;
680
- {
681
- LockGuard l(syncher);
682
- SuperGroupPtr *superGroup;
683
- if (!superGroups.lookup(options.getAppGroupName(), &superGroup)) {
684
- // Forcefully create SuperGroup, don't care whether resource limits
685
- // actually allow it.
686
- createSuperGroup(options);
687
- }
688
- }
689
- return get(options2, &ticket)->getGroup()->shared_from_this();
690
- }
691
-
692
- void setMax(unsigned int max) {
693
- ScopedLock l(syncher);
694
- assert(max > 0);
695
- fullVerifyInvariants();
696
- bool bigger = max > this->max;
697
- this->max = max;
698
- if (bigger) {
699
- /* If there are clients waiting for resources
700
- * to become free, spawn more processes now that
701
- * we have the capacity.
702
- *
703
- * We favor waiters on the pool over waiters on the
704
- * the groups because the latter already have the
705
- * resources to eventually complete. Favoring waiters
706
- * on the pool should be fairer.
707
- */
708
- boost::container::vector<Callback> actions;
709
- assignSessionsToGetWaiters(actions);
710
- possiblySpawnMoreProcessesForExistingGroups();
711
-
712
- fullVerifyInvariants();
713
- l.unlock();
714
- runAllActions(actions);
715
- } else {
716
- fullVerifyInvariants();
717
- }
718
- }
719
-
720
- void setMaxIdleTime(unsigned long long value) {
721
- LockGuard l(syncher);
722
- maxIdleTime = value;
723
- wakeupGarbageCollector();
724
- }
725
-
726
- void enableSelfChecking(bool enabled) {
727
- LockGuard l(syncher);
728
- selfchecking = enabled;
729
- }
730
-
731
- unsigned int capacityUsed() const {
732
- LockGuard l(syncher);
733
- return capacityUsedUnlocked();
734
- }
735
-
736
- bool atFullCapacity() const {
737
- LockGuard l(syncher);
738
- return atFullCapacityUnlocked();
739
- }
740
-
741
- vector<ProcessPtr> getProcesses(bool lock = true) const {
742
- DynamicScopedLock l(syncher, lock);
743
- vector<ProcessPtr> result;
744
- SuperGroupMap::ConstIterator sg_it(superGroups);
745
- while (*sg_it != NULL) {
746
- const SuperGroupPtr &superGroup = sg_it.getValue();
747
- SuperGroup::GroupList &groups = superGroup->groups;
748
- SuperGroup::GroupList::const_iterator g_it, g_end = groups.end();
749
- for (g_it = groups.begin(); g_it != g_end; g_it++) {
750
- const GroupPtr &group = *g_it;
751
- ProcessList::const_iterator p_it;
752
-
753
- for (p_it = group->enabledProcesses.begin(); p_it != group->enabledProcesses.end(); p_it++) {
754
- result.push_back(*p_it);
755
- }
756
- for (p_it = group->disablingProcesses.begin(); p_it != group->disablingProcesses.end(); p_it++) {
757
- result.push_back(*p_it);
758
- }
759
- for (p_it = group->disabledProcesses.begin(); p_it != group->disabledProcesses.end(); p_it++) {
760
- result.push_back(*p_it);
761
- }
762
- }
763
- sg_it.next();
764
- }
765
- return result;
766
- }
424
+ vector<ProcessPtr> getProcesses(bool lock = true) const;
425
+ ProcessPtr findProcessByGupid(const StaticString &gupid, bool lock = true) const;
426
+ ProcessPtr findProcessByPid(pid_t pid, bool lock = true) const;
427
+ bool detachProcess(const ProcessPtr &process);
428
+ bool detachProcess(pid_t pid,
429
+ const AuthenticationOptions &options = AuthenticationOptions::makeAuthorized());
430
+ bool detachProcess(const string &gupid,
431
+ const AuthenticationOptions &options = AuthenticationOptions::makeAuthorized());
432
+ DisableResult disableProcess(const StaticString &gupid);
767
433
 
768
- /**
769
- * Returns the total number of processes in the pool, including all disabling and
770
- * disabled processes, but excluding processes that are shutting down and excluding
771
- * processes that are being spawned.
772
- */
773
- unsigned int getProcessCount(bool lock = true) const {
774
- DynamicScopedLock l(syncher, lock);
775
- unsigned int result = 0;
776
- SuperGroupMap::ConstIterator sg_it(superGroups);
777
- while (*sg_it != NULL) {
778
- const SuperGroupPtr &superGroup = sg_it.getValue();
779
- result += superGroup->getProcessCount();
780
- sg_it.next();
781
- }
782
- return result;
783
- }
784
-
785
- unsigned int getSuperGroupCount() const {
786
- LockGuard l(syncher);
787
- return superGroups.size();
788
- }
789
-
790
- SuperGroupPtr findSuperGroupBySecret(const string &secret, bool lock = true) const {
791
- DynamicScopedLock l(syncher, lock);
792
- SuperGroupMap::ConstIterator sg_it(superGroups);
793
- while (*sg_it != NULL) {
794
- const SuperGroupPtr &superGroup = sg_it.getValue();
795
- if (superGroup->secret == secret) {
796
- return superGroup;
797
- }
798
- sg_it.next();
799
- }
800
- return SuperGroupPtr();
801
- }
802
-
803
- ProcessPtr findProcessByGupid(const StaticString &gupid, bool lock = true) const {
804
- vector<ProcessPtr> processes = getProcesses(lock);
805
- vector<ProcessPtr>::const_iterator it, end = processes.end();
806
- for (it = processes.begin(); it != end; it++) {
807
- const ProcessPtr &process = *it;
808
- if (StaticString(process->gupid, process->gupidSize) == gupid) {
809
- return process;
810
- }
811
- }
812
- return ProcessPtr();
813
- }
814
-
815
- ProcessPtr findProcessByPid(pid_t pid, bool lock = true) const {
816
- vector<ProcessPtr> processes = getProcesses(lock);
817
- vector<ProcessPtr>::const_iterator it, end = processes.end();
818
- for (it = processes.begin(); it != end; it++) {
819
- const ProcessPtr &process = *it;
820
- if (process->pid == pid) {
821
- return process;
822
- }
823
- }
824
- return ProcessPtr();
825
- }
826
-
827
- bool detachSuperGroupByName(const string &name) {
828
- TRACE_POINT();
829
- ScopedLock l(syncher);
830
- SuperGroupPtr superGroup = superGroups.lookupCopy(name);
831
-
832
- if (OXT_LIKELY(superGroup != NULL)) {
833
- P_ASSERT_EQ(superGroup->name, name);
834
- UPDATE_TRACE_POINT();
835
- verifyInvariants();
836
- verifyExpensiveInvariants();
837
-
838
- boost::container::vector<Callback> actions;
839
- boost::shared_ptr<DetachSuperGroupWaitTicket> ticket =
840
- boost::make_shared<DetachSuperGroupWaitTicket>();
841
- ExceptionPtr exception = copyException(
842
- GetAbortedException("The containing SuperGroup was detached."));
843
-
844
- forceDetachSuperGroup(superGroup, actions,
845
- boost::bind(syncDetachSuperGroupCallback, _1, ticket));
846
- assignExceptionToGetWaiters(superGroup->getWaitlist,
847
- exception, actions);
848
- #if 0
849
- /* If this SuperGroup had get waiters, either
850
- * on itself or in one of its groups, then we must
851
- * reprocess them immediately. Detaching such a
852
- * SuperGroup is essentially the same as restarting it.
853
- */
854
- migrateSuperGroupGetWaitlistToPool(superGroup);
855
-
856
- UPDATE_TRACE_POINT();
857
- assignSessionsToGetWaiters(actions);
858
- #endif
859
- possiblySpawnMoreProcessesForExistingGroups();
860
-
861
- verifyInvariants();
862
- verifyExpensiveInvariants();
863
-
864
- l.unlock();
865
- UPDATE_TRACE_POINT();
866
- runAllActions(actions);
867
- actions.clear();
868
-
869
- UPDATE_TRACE_POINT();
870
- ScopedLock l2(ticket->syncher);
871
- while (!ticket->done) {
872
- ticket->cond.wait(l2);
873
- }
874
- return ticket->result == SuperGroup::SUCCESS;
875
- } else {
876
- return false;
877
- }
878
- }
879
-
880
- bool detachSuperGroupBySecret(const string &superGroupSecret) {
881
- ScopedLock l(syncher);
882
- SuperGroupPtr superGroup = findSuperGroupBySecret(superGroupSecret, false);
883
- if (superGroup != NULL) {
884
- string name = superGroup->name;
885
- superGroup.reset();
886
- l.unlock();
887
- return detachSuperGroupByName(name);
888
- } else {
889
- return false;
890
- }
891
- }
892
434
 
893
- bool detachProcess(const ProcessPtr &process) {
894
- ScopedLock l(syncher);
895
- boost::container::vector<Callback> actions;
896
- bool result = detachProcessUnlocked(process, actions);
897
- fullVerifyInvariants();
898
- l.unlock();
899
- runAllActions(actions);
900
- return result;
901
- }
902
-
903
- bool detachProcess(pid_t pid) {
904
- ScopedLock l(syncher);
905
- ProcessPtr process = findProcessByPid(pid, false);
906
- if (process != NULL) {
907
- boost::container::vector<Callback> actions;
908
- bool result = detachProcessUnlocked(process, actions);
909
- fullVerifyInvariants();
910
- l.unlock();
911
- runAllActions(actions);
912
- return result;
913
- } else {
914
- return false;
915
- }
916
- }
917
-
918
- bool detachProcess(const string &gupid) {
919
- ScopedLock l(syncher);
920
- ProcessPtr process = findProcessByGupid(gupid, false);
921
- if (process != NULL) {
922
- boost::container::vector<Callback> actions;
923
- bool result = detachProcessUnlocked(process, actions);
924
- fullVerifyInvariants();
925
- l.unlock();
926
- runAllActions(actions);
927
- return result;
928
- } else {
929
- return false;
930
- }
931
- }
932
-
933
- DisableResult disableProcess(const StaticString &gupid) {
934
- ScopedLock l(syncher);
935
- ProcessPtr process = findProcessByGupid(gupid, false);
936
- if (process != NULL) {
937
- Group *group = process->getGroup();
938
- // Must be a boost::shared_ptr to be interruption-safe.
939
- boost::shared_ptr<DisableWaitTicket> ticket = boost::make_shared<DisableWaitTicket>();
940
- DisableResult result = group->disable(process,
941
- boost::bind(syncDisableProcessCallback, _1, _2, ticket));
942
- group->verifyInvariants();
943
- group->verifyExpensiveInvariants();
944
- if (result == DR_DEFERRED) {
945
- l.unlock();
946
- ScopedLock l2(ticket->syncher);
947
- while (!ticket->done) {
948
- ticket->cond.wait(l2);
949
- }
950
- return ticket->result;
951
- } else {
952
- return result;
953
- }
954
- } else {
955
- return DR_NOOP;
956
- }
957
- }
958
-
959
- bool restartGroupByName(const StaticString &name, RestartMethod method = RM_DEFAULT) {
960
- ScopedLock l(syncher);
961
- SuperGroupMap::ConstIterator sg_it(superGroups);
962
- while (*sg_it != NULL) {
963
- const SuperGroupPtr &superGroup = sg_it.getValue();
964
- foreach (const GroupPtr &group, superGroup->groups) {
965
- if (name == group->name) {
966
- if (!group->restarting()) {
967
- group->restart(group->options, method);
968
- }
969
- return true;
970
- }
971
- }
972
- sg_it.next();
973
- }
435
+ /****** State inspection ******/
974
436
 
975
- return false;
976
- }
977
-
978
- unsigned int restartSuperGroupsByAppRoot(const StaticString &appRoot) {
979
- ScopedLock l(syncher);
980
- SuperGroupMap::ConstIterator sg_it(superGroups);
981
- unsigned int result = 0;
982
-
983
- while (*sg_it != NULL) {
984
- const SuperGroupPtr &superGroup = sg_it.getValue();
985
- if (appRoot == superGroup->options.appRoot) {
986
- result++;
987
- superGroup->restart(superGroup->options);
988
- }
989
- sg_it.next();
990
- }
437
+ unsigned int capacityUsed() const;
438
+ bool atFullCapacity() const;
439
+ unsigned int getProcessCount(bool lock = true) const;
440
+ unsigned int getGroupCount() const;
441
+ string inspect(const InspectOptions &options = InspectOptions::makeAuthorized(),
442
+ bool lock = true) const;
443
+ string toXml(const ToXmlOptions &options = ToXmlOptions::makeAuthorized(),
444
+ bool lock = true) const;
991
445
 
992
- return result;
993
- }
994
446
 
995
- /**
996
- * Checks whether at least one process is being spawned.
997
- */
998
- bool isSpawning(bool lock = true) const {
999
- DynamicScopedLock l(syncher, lock);
1000
- SuperGroupMap::ConstIterator sg_it(superGroups);
1001
- while (*sg_it != NULL) {
1002
- const SuperGroupPtr &superGroup = sg_it.getValue();
1003
- foreach (GroupPtr group, superGroup->groups) {
1004
- if (group->spawning()) {
1005
- return true;
1006
- }
1007
- }
1008
- sg_it.next();
1009
- }
1010
- return false;
1011
- }
447
+ /****** Miscellaneous ******/
448
+
449
+ void asyncGet(const Options &options, const GetCallback &callback, bool lockNow = true);
450
+ SessionPtr get(const Options &options, Ticket *ticket);
451
+ void setMax(unsigned int max);
452
+ void setMaxIdleTime(unsigned long long value);
453
+ void enableSelfChecking(bool enabled);
454
+ bool isSpawning(bool lock = true) const;
455
+ bool authorizeByApiKey(const ApiKey &key, bool lock = true) const;
456
+ bool authorizeByUid(uid_t uid, bool lock = true) const;
1012
457
  };
1013
458
 
1014
459