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
@@ -0,0 +1,276 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#include <ApplicationPool2/Pool.h>
|
26
|
+
|
27
|
+
/*************************************************************************
|
28
|
+
*
|
29
|
+
* Group data structure utility functions for ApplicationPool2::Pool
|
30
|
+
*
|
31
|
+
*************************************************************************/
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ApplicationPool2 {
|
35
|
+
|
36
|
+
using namespace std;
|
37
|
+
using namespace boost;
|
38
|
+
|
39
|
+
|
40
|
+
/****************************
|
41
|
+
*
|
42
|
+
* Private methods
|
43
|
+
*
|
44
|
+
****************************/
|
45
|
+
|
46
|
+
|
47
|
+
const GroupPtr
|
48
|
+
Pool::getGroup(const char *name) {
|
49
|
+
GroupPtr *group;
|
50
|
+
if (groups.lookup(name, &group)) {
|
51
|
+
return *group;
|
52
|
+
} else {
|
53
|
+
return GroupPtr();
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
Group *
|
58
|
+
Pool::findMatchingGroup(const Options &options) {
|
59
|
+
GroupPtr *group;
|
60
|
+
if (groups.lookup(options.getAppGroupName(), &group)) {
|
61
|
+
return group->get();
|
62
|
+
} else {
|
63
|
+
return NULL;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
GroupPtr
|
68
|
+
Pool::createGroup(const Options &options) {
|
69
|
+
GroupPtr group = boost::make_shared<Group>(this, options);
|
70
|
+
group->initialize();
|
71
|
+
groups.insert(options.getAppGroupName(), group);
|
72
|
+
wakeupGarbageCollector();
|
73
|
+
return group;
|
74
|
+
}
|
75
|
+
|
76
|
+
GroupPtr
|
77
|
+
Pool::createGroupAndAsyncGetFromIt(const Options &options,
|
78
|
+
const GetCallback &callback, boost::container::vector<Callback> &postLockActions)
|
79
|
+
{
|
80
|
+
GroupPtr group = createGroup(options);
|
81
|
+
SessionPtr session = group->get(options, callback,
|
82
|
+
postLockActions);
|
83
|
+
/* If !options.noop, then the callback should now have been put on the
|
84
|
+
* wait list, unless something has changed and we forgot to update
|
85
|
+
* some code here...
|
86
|
+
*/
|
87
|
+
if (session != NULL) {
|
88
|
+
assert(options.noop);
|
89
|
+
postLockActions.push_back(boost::bind(GetCallback::call,
|
90
|
+
callback, session, ExceptionPtr()));
|
91
|
+
}
|
92
|
+
return group;
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Forcefully destroys and detaches the given Group. After detaching
|
97
|
+
* the Group may have a non-empty getWaitlist so be sure to do
|
98
|
+
* something with it.
|
99
|
+
*
|
100
|
+
* Also, one of the post lock actions can potentially perform a long-running
|
101
|
+
* operation, so running them in a thread is advised.
|
102
|
+
*/
|
103
|
+
void
|
104
|
+
Pool::forceDetachGroup(const GroupPtr &group,
|
105
|
+
const Callback &callback,
|
106
|
+
boost::container::vector<Callback> &postLockActions)
|
107
|
+
{
|
108
|
+
assert(group->getWaitlist.empty());
|
109
|
+
const GroupPtr p = group; // Prevent premature destruction.
|
110
|
+
bool removed = groups.erase(group->getName());
|
111
|
+
assert(removed);
|
112
|
+
(void) removed; // Shut up compiler warning.
|
113
|
+
group->shutdown(callback, postLockActions);
|
114
|
+
}
|
115
|
+
|
116
|
+
void
|
117
|
+
Pool::syncDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket) {
|
118
|
+
LockGuard l(ticket->syncher);
|
119
|
+
ticket->done = true;
|
120
|
+
ticket->cond.notify_one();
|
121
|
+
}
|
122
|
+
|
123
|
+
void
|
124
|
+
Pool::waitDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket) {
|
125
|
+
ScopedLock l(ticket->syncher);
|
126
|
+
while (!ticket->done) {
|
127
|
+
ticket->cond.wait(l);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
|
132
|
+
/****************************
|
133
|
+
*
|
134
|
+
* Public methods
|
135
|
+
*
|
136
|
+
****************************/
|
137
|
+
|
138
|
+
|
139
|
+
GroupPtr
|
140
|
+
Pool::findOrCreateGroup(const Options &options) {
|
141
|
+
Options options2 = options;
|
142
|
+
options2.noop = true;
|
143
|
+
|
144
|
+
Ticket ticket;
|
145
|
+
{
|
146
|
+
LockGuard l(syncher);
|
147
|
+
GroupPtr *group;
|
148
|
+
if (!groups.lookup(options.getAppGroupName(), &group)) {
|
149
|
+
// Forcefully create Group, don't care whether resource limits
|
150
|
+
// actually allow it.
|
151
|
+
createGroup(options);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
return get(options2, &ticket)->getGroup()->shared_from_this();
|
155
|
+
}
|
156
|
+
|
157
|
+
GroupPtr
|
158
|
+
Pool::findGroupByApiKey(const StaticString &value, bool lock) const {
|
159
|
+
DynamicScopedLock l(syncher, lock);
|
160
|
+
GroupMap::ConstIterator g_it(groups);
|
161
|
+
while (*g_it != NULL) {
|
162
|
+
const GroupPtr &group = g_it.getValue();
|
163
|
+
if (group->getApiKey() == value) {
|
164
|
+
return group;
|
165
|
+
}
|
166
|
+
g_it.next();
|
167
|
+
}
|
168
|
+
return GroupPtr();
|
169
|
+
}
|
170
|
+
|
171
|
+
bool
|
172
|
+
Pool::detachGroupByName(const HashedStaticString &name) {
|
173
|
+
TRACE_POINT();
|
174
|
+
ScopedLock l(syncher);
|
175
|
+
GroupPtr group = groups.lookupCopy(name);
|
176
|
+
|
177
|
+
if (OXT_LIKELY(group != NULL)) {
|
178
|
+
P_ASSERT_EQ(group->getName(), name);
|
179
|
+
UPDATE_TRACE_POINT();
|
180
|
+
verifyInvariants();
|
181
|
+
verifyExpensiveInvariants();
|
182
|
+
|
183
|
+
boost::container::vector<Callback> actions;
|
184
|
+
boost::shared_ptr<DetachGroupWaitTicket> ticket =
|
185
|
+
boost::make_shared<DetachGroupWaitTicket>();
|
186
|
+
ExceptionPtr exception = copyException(
|
187
|
+
GetAbortedException("The containing Group was detached."));
|
188
|
+
|
189
|
+
assignExceptionToGetWaiters(group->getWaitlist,
|
190
|
+
exception, actions);
|
191
|
+
forceDetachGroup(group,
|
192
|
+
boost::bind(syncDetachGroupCallback, ticket),
|
193
|
+
actions);
|
194
|
+
possiblySpawnMoreProcessesForExistingGroups();
|
195
|
+
|
196
|
+
verifyInvariants();
|
197
|
+
verifyExpensiveInvariants();
|
198
|
+
|
199
|
+
l.unlock();
|
200
|
+
UPDATE_TRACE_POINT();
|
201
|
+
runAllActions(actions);
|
202
|
+
actions.clear();
|
203
|
+
|
204
|
+
UPDATE_TRACE_POINT();
|
205
|
+
ScopedLock l2(ticket->syncher);
|
206
|
+
while (!ticket->done) {
|
207
|
+
ticket->cond.wait(l2);
|
208
|
+
}
|
209
|
+
return true;
|
210
|
+
} else {
|
211
|
+
return false;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
bool
|
216
|
+
Pool::detachGroupByApiKey(const StaticString &value) {
|
217
|
+
ScopedLock l(syncher);
|
218
|
+
GroupPtr group = findGroupByApiKey(value, false);
|
219
|
+
if (group != NULL) {
|
220
|
+
string name = group->getName();
|
221
|
+
group.reset();
|
222
|
+
l.unlock();
|
223
|
+
return detachGroupByName(name);
|
224
|
+
} else {
|
225
|
+
return false;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
bool
|
230
|
+
Pool::restartGroupByName(const StaticString &name, const RestartOptions &options) {
|
231
|
+
ScopedLock l(syncher);
|
232
|
+
GroupMap::ConstIterator g_it(groups);
|
233
|
+
while (*g_it != NULL) {
|
234
|
+
const GroupPtr &group = g_it.getValue();
|
235
|
+
if (name == group->getName()) {
|
236
|
+
if (!group->authorizeByUid(options.uid)
|
237
|
+
&& !group->authorizeByApiKey(options.apiKey))
|
238
|
+
{
|
239
|
+
throw SecurityException("Operation unauthorized");
|
240
|
+
}
|
241
|
+
if (!group->restarting()) {
|
242
|
+
group->restart(group->options, options.method);
|
243
|
+
}
|
244
|
+
return true;
|
245
|
+
}
|
246
|
+
g_it.next();
|
247
|
+
}
|
248
|
+
|
249
|
+
return false;
|
250
|
+
}
|
251
|
+
|
252
|
+
unsigned int
|
253
|
+
Pool::restartGroupsByAppRoot(const StaticString &appRoot, const RestartOptions &options) {
|
254
|
+
ScopedLock l(syncher);
|
255
|
+
GroupMap::ConstIterator g_it(groups);
|
256
|
+
unsigned int result = 0;
|
257
|
+
|
258
|
+
while (*g_it != NULL) {
|
259
|
+
const GroupPtr &group = g_it.getValue();
|
260
|
+
if (appRoot == group->options.appRoot) {
|
261
|
+
if (group->authorizeByUid(options.uid)
|
262
|
+
|| group->authorizeByApiKey(options.apiKey))
|
263
|
+
{
|
264
|
+
result++;
|
265
|
+
group->restart(group->options, options.method);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
g_it.next();
|
269
|
+
}
|
270
|
+
|
271
|
+
return result;
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
} // namespace ApplicationPool2
|
276
|
+
} // namespace Passenger
|
@@ -0,0 +1,145 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#include <ApplicationPool2/Pool.h>
|
26
|
+
|
27
|
+
/*************************************************************************
|
28
|
+
*
|
29
|
+
* Initialization and shutdown-related code for ApplicationPool2::Pool
|
30
|
+
*
|
31
|
+
*************************************************************************/
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ApplicationPool2 {
|
35
|
+
|
36
|
+
using namespace std;
|
37
|
+
using namespace boost;
|
38
|
+
|
39
|
+
|
40
|
+
Pool::Pool(const SpawningKit::FactoryPtr &spawningKitFactory,
|
41
|
+
const VariantMap *agentsOptions)
|
42
|
+
: abortLongRunningConnectionsCallback(NULL)
|
43
|
+
{
|
44
|
+
context.setSpawningKitFactory(spawningKitFactory);
|
45
|
+
context.finalize();
|
46
|
+
|
47
|
+
this->agentsOptions = agentsOptions;
|
48
|
+
|
49
|
+
try {
|
50
|
+
systemMetricsCollector.collect(systemMetrics);
|
51
|
+
} catch (const RuntimeException &e) {
|
52
|
+
P_WARN("Unable to collect system metrics: " << e.what());
|
53
|
+
}
|
54
|
+
|
55
|
+
lifeStatus = ALIVE;
|
56
|
+
max = 6;
|
57
|
+
maxIdleTime = 60 * 1000000;
|
58
|
+
selfchecking = true;
|
59
|
+
palloc = psg_create_pool(PSG_DEFAULT_POOL_SIZE);
|
60
|
+
|
61
|
+
// The following code only serve to instantiate certain inline methods
|
62
|
+
// so that they can be invoked from gdb.
|
63
|
+
(void) GroupPtr().get();
|
64
|
+
(void) ProcessPtr().get();
|
65
|
+
(void) SessionPtr().get();
|
66
|
+
}
|
67
|
+
|
68
|
+
Pool::~Pool() {
|
69
|
+
if (lifeStatus != SHUT_DOWN) {
|
70
|
+
P_BUG("You must call Pool::destroy() before actually destroying the Pool object!");
|
71
|
+
}
|
72
|
+
psg_destroy_pool(palloc);
|
73
|
+
}
|
74
|
+
|
75
|
+
/** Must be called right after construction. */
|
76
|
+
void
|
77
|
+
Pool::initialize() {
|
78
|
+
LockGuard l(syncher);
|
79
|
+
initializeAnalyticsCollection();
|
80
|
+
initializeGarbageCollection();
|
81
|
+
}
|
82
|
+
|
83
|
+
void
|
84
|
+
Pool::initDebugging() {
|
85
|
+
LockGuard l(syncher);
|
86
|
+
debugSupport = boost::make_shared<DebugSupport>();
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Should be called right after the agent has received
|
91
|
+
* the message to exit gracefully. This will tell processes to
|
92
|
+
* abort any long-running connections, e.g. WebSocket connections,
|
93
|
+
* because the RequestHandler has to wait until all connections are
|
94
|
+
* finished before proceeding with shutdown.
|
95
|
+
*/
|
96
|
+
void
|
97
|
+
Pool::prepareForShutdown() {
|
98
|
+
TRACE_POINT();
|
99
|
+
ScopedLock lock(syncher);
|
100
|
+
assert(lifeStatus == ALIVE);
|
101
|
+
lifeStatus = PREPARED_FOR_SHUTDOWN;
|
102
|
+
if (abortLongRunningConnectionsCallback != NULL) {
|
103
|
+
vector<ProcessPtr> processes = getProcesses(false);
|
104
|
+
foreach (ProcessPtr process, processes) {
|
105
|
+
// Ensure that the process is not immediately respawned.
|
106
|
+
process->getGroup()->options.minProcesses = 0;
|
107
|
+
abortLongRunningConnectionsCallback(process);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
/** Must be called right before destruction. */
|
113
|
+
void
|
114
|
+
Pool::destroy() {
|
115
|
+
TRACE_POINT();
|
116
|
+
ScopedLock lock(syncher);
|
117
|
+
assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
|
118
|
+
|
119
|
+
lifeStatus = SHUTTING_DOWN;
|
120
|
+
|
121
|
+
while (!groups.empty()) {
|
122
|
+
GroupPtr *group;
|
123
|
+
groups.lookupRandom(NULL, &group);
|
124
|
+
string name = group->get()->getName().toString();
|
125
|
+
lock.unlock();
|
126
|
+
detachGroupByName(name);
|
127
|
+
lock.lock();
|
128
|
+
}
|
129
|
+
|
130
|
+
UPDATE_TRACE_POINT();
|
131
|
+
lock.unlock();
|
132
|
+
interruptableThreads.interrupt_and_join_all();
|
133
|
+
nonInterruptableThreads.join_all();
|
134
|
+
lock.lock();
|
135
|
+
|
136
|
+
lifeStatus = SHUT_DOWN;
|
137
|
+
|
138
|
+
UPDATE_TRACE_POINT();
|
139
|
+
verifyInvariants();
|
140
|
+
verifyExpensiveInvariants();
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
} // namespace ApplicationPool2
|
145
|
+
} // namespace Passenger
|
@@ -0,0 +1,244 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#include <ApplicationPool2/Pool.h>
|
26
|
+
|
27
|
+
namespace Passenger {
|
28
|
+
namespace ApplicationPool2 {
|
29
|
+
|
30
|
+
using namespace std;
|
31
|
+
using namespace boost;
|
32
|
+
|
33
|
+
|
34
|
+
// 'lockNow == false' may only be used during unit tests. Normally we
|
35
|
+
// should never call the callback while holding the lock.
|
36
|
+
void
|
37
|
+
Pool::asyncGet(const Options &options, const GetCallback &callback, bool lockNow) {
|
38
|
+
DynamicScopedLock lock(syncher, lockNow);
|
39
|
+
|
40
|
+
assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
|
41
|
+
verifyInvariants();
|
42
|
+
P_TRACE(2, "asyncGet(appGroupName=" << options.getAppGroupName() << ")");
|
43
|
+
boost::container::vector<Callback> actions;
|
44
|
+
|
45
|
+
Group *existingGroup = findMatchingGroup(options);
|
46
|
+
if (OXT_LIKELY(existingGroup != NULL)) {
|
47
|
+
/* Best case: the app group is already in the pool. Let's use it. */
|
48
|
+
P_TRACE(2, "Found existing Group");
|
49
|
+
existingGroup->verifyInvariants();
|
50
|
+
SessionPtr session = existingGroup->get(options, callback, actions);
|
51
|
+
existingGroup->verifyInvariants();
|
52
|
+
verifyInvariants();
|
53
|
+
P_TRACE(2, "asyncGet() finished");
|
54
|
+
if (lockNow) {
|
55
|
+
lock.unlock();
|
56
|
+
}
|
57
|
+
if (session != NULL) {
|
58
|
+
callback(session, ExceptionPtr());
|
59
|
+
}
|
60
|
+
|
61
|
+
} else if (!atFullCapacityUnlocked()) {
|
62
|
+
/* The app super group isn't in the pool and we have enough free
|
63
|
+
* resources to make a new one.
|
64
|
+
*/
|
65
|
+
P_DEBUG("Spawning new Group");
|
66
|
+
GroupPtr group = createGroupAndAsyncGetFromIt(options,
|
67
|
+
callback, actions);
|
68
|
+
group->verifyInvariants();
|
69
|
+
verifyInvariants();
|
70
|
+
P_DEBUG("asyncGet() finished");
|
71
|
+
|
72
|
+
} else {
|
73
|
+
/* Uh oh, the app super group isn't in the pool but we don't
|
74
|
+
* have the resources to make a new one. The sysadmin should
|
75
|
+
* configure the system to let something like this happen
|
76
|
+
* as least as possible, but let's try to handle it as well
|
77
|
+
* as we can.
|
78
|
+
*/
|
79
|
+
ProcessPtr freedProcess = forceFreeCapacity(NULL, actions);
|
80
|
+
if (freedProcess == NULL) {
|
81
|
+
/* No process is eligible for killing. This could happen if, for example,
|
82
|
+
* all (super)groups are currently initializing/restarting/spawning/etc.
|
83
|
+
* We have no choice but to satisfy this get() action later when resources
|
84
|
+
* become available.
|
85
|
+
*/
|
86
|
+
P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
|
87
|
+
getWaitlist.push_back(GetWaiter(
|
88
|
+
options.copyAndPersist().detachFromUnionStationTransaction(),
|
89
|
+
callback));
|
90
|
+
} else {
|
91
|
+
/* Now that a process has been trashed we can create
|
92
|
+
* the missing Group.
|
93
|
+
*/
|
94
|
+
P_DEBUG("Creating new Group");
|
95
|
+
GroupPtr group = createGroup(options);
|
96
|
+
SessionPtr session = group->get(options, callback,
|
97
|
+
actions);
|
98
|
+
/* The Group is now spawning a process so the callback
|
99
|
+
* should now have been put on the wait list,
|
100
|
+
* unless something has changed and we forgot to update
|
101
|
+
* some code here or if options.noop...
|
102
|
+
*/
|
103
|
+
if (session != NULL) {
|
104
|
+
assert(options.noop);
|
105
|
+
actions.push_back(boost::bind(GetCallback::call,
|
106
|
+
callback, session, ExceptionPtr()));
|
107
|
+
}
|
108
|
+
freedProcess->getGroup()->verifyInvariants();
|
109
|
+
group->verifyInvariants();
|
110
|
+
}
|
111
|
+
|
112
|
+
assert(atFullCapacityUnlocked());
|
113
|
+
verifyInvariants();
|
114
|
+
verifyExpensiveInvariants();
|
115
|
+
P_TRACE(2, "asyncGet() finished");
|
116
|
+
}
|
117
|
+
|
118
|
+
if (!actions.empty()) {
|
119
|
+
if (lockNow) {
|
120
|
+
if (lock.owns_lock()) {
|
121
|
+
lock.unlock();
|
122
|
+
}
|
123
|
+
runAllActions(actions);
|
124
|
+
} else {
|
125
|
+
// This state is not allowed. If we reach
|
126
|
+
// here then it probably indicates a bug in
|
127
|
+
// the test suite.
|
128
|
+
abort();
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
// TODO: 'ticket' should be a boost::shared_ptr for interruption-safety.
|
134
|
+
SessionPtr
|
135
|
+
Pool::get(const Options &options, Ticket *ticket) {
|
136
|
+
ticket->session.reset();
|
137
|
+
ticket->exception.reset();
|
138
|
+
|
139
|
+
GetCallback callback;
|
140
|
+
callback.func = syncGetCallback;
|
141
|
+
callback.userData = ticket;
|
142
|
+
asyncGet(options, callback);
|
143
|
+
|
144
|
+
ScopedLock lock(ticket->syncher);
|
145
|
+
while (ticket->session == NULL && ticket->exception == NULL) {
|
146
|
+
ticket->cond.wait(lock);
|
147
|
+
}
|
148
|
+
lock.unlock();
|
149
|
+
|
150
|
+
if (OXT_LIKELY(ticket->session != NULL)) {
|
151
|
+
SessionPtr session = ticket->session;
|
152
|
+
ticket->session.reset();
|
153
|
+
return session;
|
154
|
+
} else {
|
155
|
+
rethrowException(ticket->exception);
|
156
|
+
return SessionPtr(); // Shut up compiler warning.
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
void
|
161
|
+
Pool::setMax(unsigned int max) {
|
162
|
+
ScopedLock l(syncher);
|
163
|
+
assert(max > 0);
|
164
|
+
fullVerifyInvariants();
|
165
|
+
bool bigger = max > this->max;
|
166
|
+
this->max = max;
|
167
|
+
if (bigger) {
|
168
|
+
/* If there are clients waiting for resources
|
169
|
+
* to become free, spawn more processes now that
|
170
|
+
* we have the capacity.
|
171
|
+
*
|
172
|
+
* We favor waiters on the pool over waiters on the
|
173
|
+
* the groups because the latter already have the
|
174
|
+
* resources to eventually complete. Favoring waiters
|
175
|
+
* on the pool should be fairer.
|
176
|
+
*/
|
177
|
+
boost::container::vector<Callback> actions;
|
178
|
+
assignSessionsToGetWaiters(actions);
|
179
|
+
possiblySpawnMoreProcessesForExistingGroups();
|
180
|
+
|
181
|
+
fullVerifyInvariants();
|
182
|
+
l.unlock();
|
183
|
+
runAllActions(actions);
|
184
|
+
} else {
|
185
|
+
fullVerifyInvariants();
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
void
|
190
|
+
Pool::setMaxIdleTime(unsigned long long value) {
|
191
|
+
LockGuard l(syncher);
|
192
|
+
maxIdleTime = value;
|
193
|
+
wakeupGarbageCollector();
|
194
|
+
}
|
195
|
+
|
196
|
+
void
|
197
|
+
Pool::enableSelfChecking(bool enabled) {
|
198
|
+
LockGuard l(syncher);
|
199
|
+
selfchecking = enabled;
|
200
|
+
}
|
201
|
+
|
202
|
+
/**
|
203
|
+
* Checks whether at least one process is being spawned.
|
204
|
+
*/
|
205
|
+
bool
|
206
|
+
Pool::isSpawning(bool lock) const {
|
207
|
+
DynamicScopedLock l(syncher, lock);
|
208
|
+
GroupMap::ConstIterator g_it(groups);
|
209
|
+
while (*g_it != NULL) {
|
210
|
+
const GroupPtr &group = g_it.getValue();
|
211
|
+
if (group->spawning()) {
|
212
|
+
return true;
|
213
|
+
}
|
214
|
+
g_it.next();
|
215
|
+
}
|
216
|
+
return false;
|
217
|
+
}
|
218
|
+
|
219
|
+
bool
|
220
|
+
Pool::authorizeByApiKey(const ApiKey &key, bool lock) const {
|
221
|
+
return key.isSuper() || findGroupByApiKey(key.toStaticString(), lock) != NULL;
|
222
|
+
}
|
223
|
+
|
224
|
+
bool
|
225
|
+
Pool::authorizeByUid(uid_t uid, bool lock) const {
|
226
|
+
if (uid == 0 || uid == geteuid()) {
|
227
|
+
return true;
|
228
|
+
}
|
229
|
+
|
230
|
+
DynamicScopedLock l(syncher, lock);
|
231
|
+
GroupMap::ConstIterator g_it(groups);
|
232
|
+
while (*g_it != NULL) {
|
233
|
+
const GroupPtr &group = g_it.getValue();
|
234
|
+
if (group->authorizeByUid(uid)) {
|
235
|
+
return true;
|
236
|
+
}
|
237
|
+
g_it.next();
|
238
|
+
}
|
239
|
+
return false;
|
240
|
+
}
|
241
|
+
|
242
|
+
|
243
|
+
} // namespace ApplicationPool2
|
244
|
+
} // namespace Passenger
|