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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- 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.
|
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
|
-
*
|
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
|
440
|
-
StaticString
|
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, "
|
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
|
-
|
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
|
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
|
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
|
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 '
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
202
|
-
}
|
260
|
+
typedef boost::shared_ptr<DebugSupport> DebugSupportPtr;
|
203
261
|
|
204
|
-
|
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
|
-
|
280
|
-
return capacityUsedUnlocked() >= max;
|
281
|
-
}
|
265
|
+
/****** Analytics collection ******/
|
282
266
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
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
|
-
|
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
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
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
|
-
|
547
|
-
verifyInvariants();
|
548
|
-
verifyExpensiveInvariants();
|
549
|
-
}
|
402
|
+
/****** General utilities ******/
|
550
403
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
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
|
-
|
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
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
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
|
-
|
655
|
-
callback.func = syncGetCallback;
|
656
|
-
callback.userData = ticket;
|
657
|
-
asyncGet(options, callback);
|
422
|
+
/***** Process manipulation ******/
|
658
423
|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
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
|
-
|
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
|
-
|
976
|
-
|
977
|
-
|
978
|
-
unsigned int
|
979
|
-
|
980
|
-
|
981
|
-
|
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
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
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
|
|