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
@@ -28,8 +28,8 @@
|
|
28
28
|
#include <string>
|
29
29
|
#include <vector>
|
30
30
|
#include <algorithm>
|
31
|
-
#include <boost/
|
32
|
-
#include <boost/
|
31
|
+
#include <boost/intrusive_ptr.hpp>
|
32
|
+
#include <boost/move/core.hpp>
|
33
33
|
#include <boost/container/vector.hpp>
|
34
34
|
#include <oxt/system_calls.hpp>
|
35
35
|
#include <oxt/spin_lock.hpp>
|
@@ -42,12 +42,14 @@
|
|
42
42
|
#include <ApplicationPool2/Common.h>
|
43
43
|
#include <ApplicationPool2/Socket.h>
|
44
44
|
#include <ApplicationPool2/Session.h>
|
45
|
-
#include <
|
45
|
+
#include <SpawningKit/PipeWatcher.h>
|
46
|
+
#include <SpawningKit/Result.h>
|
46
47
|
#include <Constants.h>
|
47
48
|
#include <FileDescriptor.h>
|
48
49
|
#include <Logging.h>
|
49
50
|
#include <Utils/SystemTime.h>
|
50
51
|
#include <Utils/StrIntUtils.h>
|
52
|
+
#include <Utils/Lock.h>
|
51
53
|
#include <Utils/ProcessMetricsCollector.h>
|
52
54
|
|
53
55
|
namespace Passenger {
|
@@ -60,9 +62,9 @@ using namespace boost;
|
|
60
62
|
typedef boost::container::vector<ProcessPtr> ProcessList;
|
61
63
|
|
62
64
|
/**
|
63
|
-
* Represents an application process, as spawned by a Spawner. Every
|
64
|
-
* a PID, an admin socket and a list of sockets on which it listens for
|
65
|
-
* connections. A Process is
|
65
|
+
* Represents an application process, as spawned by a SpawningKit::Spawner. Every
|
66
|
+
* Process has a PID, an admin socket and a list of sockets on which it listens for
|
67
|
+
* connections. A Process object is contained inside a Group.
|
66
68
|
*
|
67
69
|
* The admin socket, an anonymous Unix domain socket, is mapped to the process's
|
68
70
|
* STDIN and STDOUT and has two functions.
|
@@ -96,14 +98,86 @@ typedef boost::container::vector<ProcessPtr> ProcessList;
|
|
96
98
|
* This means that a Group outlives all its Processes, a Process outlives all
|
97
99
|
* its Sessions, and a Process also outlives the OS process.
|
98
100
|
*/
|
99
|
-
class Process
|
101
|
+
class Process {
|
100
102
|
public:
|
101
103
|
static const unsigned int MAX_SESSION_SOCKETS = 3;
|
102
|
-
static const unsigned int GUPID_MAX_SIZE = 20;
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
private:
|
106
|
+
/*************************************************************
|
107
|
+
* Read-only fields, set once during initialization and never
|
108
|
+
* written to again. Reading is thread-safe.
|
109
|
+
*************************************************************/
|
110
|
+
|
111
|
+
BasicProcessInfo info;
|
112
|
+
DynamicBuffer stringBuffer;
|
113
|
+
SocketList sockets;
|
114
|
+
|
115
|
+
/**
|
116
|
+
* The maximum amount of concurrent sessions this process can handle.
|
117
|
+
* 0 means unlimited. Automatically inferred from the sockets.
|
118
|
+
*/
|
119
|
+
int concurrency;
|
120
|
+
|
121
|
+
/**
|
122
|
+
* A subset of 'sockets': all sockets that speak the
|
123
|
+
* "session" or "http_session" protocol.
|
124
|
+
*/
|
125
|
+
unsigned int sessionSocketCount;
|
126
|
+
Socket *sessionSockets[MAX_SESSION_SOCKETS];
|
127
|
+
|
128
|
+
/** Admin socket. See Process class description. */
|
129
|
+
FileDescriptor adminSocket;
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Pipe on which this process outputs errors. Mapped to the process's STDERR.
|
133
|
+
* Only Processes spawned by DirectSpawner have this set.
|
134
|
+
* SmartSpawner-spawned Processes use the same STDERR as their parent preloader processes.
|
135
|
+
*/
|
136
|
+
FileDescriptor errorPipe;
|
137
|
+
|
138
|
+
/**
|
139
|
+
* The code revision of the application, inferred through various means.
|
140
|
+
* See Spawner::prepareSpawn() to learn how this is determined.
|
141
|
+
* May be an empty string if no code revision has been inferred.
|
142
|
+
*/
|
143
|
+
StaticString codeRevision;
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Time at which the Spawner that created this process was created.
|
147
|
+
* Microseconds resolution.
|
148
|
+
*/
|
149
|
+
unsigned long long spawnerCreationTime;
|
150
|
+
|
151
|
+
/** Time at which we started spawning this process. Microseconds resolution. */
|
152
|
+
unsigned long long spawnStartTime;
|
153
|
+
|
154
|
+
/**
|
155
|
+
* Time at which we finished spawning this process, i.e. when this
|
156
|
+
* process was finished initializing. Microseconds resolution.
|
157
|
+
*/
|
158
|
+
unsigned long long spawnEndTime;
|
159
|
+
|
160
|
+
/**
|
161
|
+
* If true, then indicates that this Process does not refer to a real OS
|
162
|
+
* process. The sockets in the socket list are fake and need not be deleted,
|
163
|
+
* the admin socket need not be closed, etc.
|
164
|
+
*/
|
165
|
+
bool dummy;
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Whether it is required that triggerShutdown() and cleanup() must be called
|
169
|
+
* before destroying this Process. Normally true, except for dummy Process
|
170
|
+
* objects created by Pool::asyncGet() with options.noop == true, because those
|
171
|
+
* processes are never added to Group.enabledProcesses.
|
172
|
+
*/
|
173
|
+
bool requiresShutdown;
|
174
|
+
|
175
|
+
|
176
|
+
/*************************************************************
|
177
|
+
* Read-write fields.
|
178
|
+
*************************************************************/
|
179
|
+
|
180
|
+
mutable boost::atomic<int> refcount;
|
107
181
|
|
108
182
|
/** A mutex to protect access to `lifeStatus`. */
|
109
183
|
mutable oxt::spin_lock lifetimeSyncher;
|
@@ -111,42 +185,100 @@ public:
|
|
111
185
|
/** The index inside the associated Group's process list. */
|
112
186
|
unsigned int index;
|
113
187
|
|
114
|
-
/** Group inside the Pool that this Process belongs to.
|
115
|
-
* Should never be NULL because a Group should outlive all of its Processes.
|
116
|
-
* Read-only; only set once during initialization.
|
117
|
-
*/
|
118
|
-
Group *group;
|
119
188
|
|
120
|
-
|
121
|
-
*
|
122
|
-
|
189
|
+
/*************************************************************
|
190
|
+
* Methods
|
191
|
+
*************************************************************/
|
123
192
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
193
|
+
/****** Initialization and destruction ******/
|
194
|
+
|
195
|
+
struct InitializationLog {
|
196
|
+
struct String {
|
197
|
+
unsigned int offset;
|
198
|
+
unsigned int size;
|
199
|
+
};
|
200
|
+
|
201
|
+
struct SocketStringOffsets {
|
202
|
+
String name;
|
203
|
+
String address;
|
204
|
+
String protocol;
|
205
|
+
};
|
206
|
+
|
207
|
+
vector<SocketStringOffsets> socketStringOffsets;
|
208
|
+
String codeRevision;
|
209
|
+
};
|
210
|
+
|
211
|
+
void appendJsonFieldToBuffer(std::string &buffer, const Json::Value &json,
|
212
|
+
const char *key, InitializationLog::String &str) const
|
213
|
+
{
|
214
|
+
StaticString value = getJsonStaticStringField(json, key);
|
215
|
+
str.offset = buffer.size();
|
216
|
+
str.size = value.size();
|
217
|
+
buffer.append(value.data(), value.size());
|
218
|
+
buffer.append(1, '\0');
|
219
|
+
}
|
220
|
+
|
221
|
+
void initializeSocketsAndStringFields(const Json::Value &json) {
|
222
|
+
InitializationLog log;
|
223
|
+
string buffer;
|
224
|
+
|
225
|
+
|
226
|
+
// Step 1: append strings to temporary buffer and take note of their
|
227
|
+
// offsets within the temporary buffer.
|
228
|
+
|
229
|
+
Json::Value sockets = getJsonField(json, "sockets");
|
230
|
+
// The const_cast here works around a jsoncpp bug.
|
231
|
+
Json::Value::const_iterator it = const_cast<const Json::Value &>(sockets).begin();
|
232
|
+
Json::Value::const_iterator end = const_cast<const Json::Value &>(sockets).end();
|
233
|
+
buffer.reserve(1024);
|
234
|
+
|
235
|
+
for (it = sockets.begin(); it != end; it++) {
|
236
|
+
const Json::Value &socket = *it;
|
237
|
+
InitializationLog::SocketStringOffsets offsets;
|
238
|
+
|
239
|
+
appendJsonFieldToBuffer(buffer, socket, "name", offsets.name);
|
240
|
+
appendJsonFieldToBuffer(buffer, socket, "address", offsets.address);
|
241
|
+
appendJsonFieldToBuffer(buffer, socket, "protocol", offsets.protocol);
|
242
|
+
|
243
|
+
log.socketStringOffsets.push_back(offsets);
|
131
244
|
}
|
132
245
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
const char *line;
|
246
|
+
if (json.isMember("code_revision")) {
|
247
|
+
appendJsonFieldToBuffer(buffer, json, "code_revision", log.codeRevision);
|
248
|
+
}
|
137
249
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
250
|
+
|
251
|
+
// Step 2: allocate the real buffer.
|
252
|
+
|
253
|
+
this->stringBuffer = DynamicBuffer(buffer.size());
|
254
|
+
memcpy(this->stringBuffer.data, buffer.data(), buffer.size());
|
255
|
+
|
256
|
+
|
257
|
+
// Step 3: initialize the string fields and point them to
|
258
|
+
// addresses within the real buffer.
|
259
|
+
|
260
|
+
unsigned int i;
|
261
|
+
const char *base = this->stringBuffer.data;
|
262
|
+
|
263
|
+
it = const_cast<const Json::Value &>(sockets).begin();
|
264
|
+
for (i = 0; it != end; it++, i++) {
|
265
|
+
const Json::Value &socket = *it;
|
266
|
+
this->sockets.add(
|
267
|
+
info.pid,
|
268
|
+
StaticString(base + log.socketStringOffsets[i].name.offset,
|
269
|
+
log.socketStringOffsets[i].name.size),
|
270
|
+
StaticString(base + log.socketStringOffsets[i].address.offset,
|
271
|
+
log.socketStringOffsets[i].address.size),
|
272
|
+
StaticString(base + log.socketStringOffsets[i].protocol.offset,
|
273
|
+
log.socketStringOffsets[i].protocol.size),
|
274
|
+
getJsonIntField(socket, "concurrency")
|
275
|
+
);
|
276
|
+
}
|
277
|
+
|
278
|
+
if (json.isMember("code_revision")) {
|
279
|
+
codeRevision = StaticString(base + log.codeRevision.offset,
|
280
|
+
log.codeRevision.size);
|
147
281
|
}
|
148
|
-
fclose(f);
|
149
|
-
return result;
|
150
282
|
}
|
151
283
|
|
152
284
|
void indexSessionSockets() {
|
@@ -183,60 +315,49 @@ public:
|
|
183
315
|
}
|
184
316
|
}
|
185
317
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
318
|
+
void destroySelf() const {
|
319
|
+
this->~Process();
|
320
|
+
LockGuard l(getContext()->getMmSyncher());
|
321
|
+
getContext()->getProcessObjectPool().free(const_cast<Process *>(this));
|
322
|
+
}
|
191
323
|
|
192
|
-
/** Process PID. */
|
193
|
-
pid_t pid;
|
194
|
-
/** An ID that uniquely identifies this Process in the Group, for
|
195
|
-
* use in implementing sticky sessions. Set by Group::attach(). */
|
196
|
-
unsigned int stickySessionId;
|
197
|
-
/** UUID for this process, randomly generated and extremely unlikely to ever
|
198
|
-
* appear again in this universe. */
|
199
|
-
char gupid[GUPID_MAX_SIZE];
|
200
|
-
unsigned int gupidSize;
|
201
|
-
/** Admin socket, see class description. */
|
202
|
-
FileDescriptor adminSocket;
|
203
|
-
/** The sockets that this Process listens on for connections. */
|
204
|
-
SocketList sockets;
|
205
|
-
/** The code revision of the application, inferred through various means.
|
206
|
-
* See Spawner::prepareSpawn() to learn how this is determined.
|
207
|
-
* May be an empty string.
|
208
|
-
*/
|
209
|
-
StaticString codeRevision;
|
210
|
-
/** Time at which the Spawner that created this process was created.
|
211
|
-
* Microseconds resolution. */
|
212
|
-
unsigned long long spawnerCreationTime;
|
213
|
-
/** Time at which we started spawning this process. Microseconds resolution. */
|
214
|
-
unsigned long long spawnStartTime;
|
215
|
-
/** The maximum amount of concurrent sessions this process can handle.
|
216
|
-
* 0 means unlimited. */
|
217
|
-
int concurrency;
|
218
|
-
/** If true, then indicates that this Process does not refer to a real OS
|
219
|
-
* process. The sockets in the socket list are fake and need not be deleted,
|
220
|
-
* the admin socket need not be closed, etc.
|
221
|
-
*/
|
222
|
-
bool dummy;
|
223
|
-
/** Whether it is required that triggerShutdown() and cleanup() must be called
|
224
|
-
* before destroying this Process. Normally true, except for dummy Process
|
225
|
-
* objects created by Pool::asyncGet() with options.noop == true, because those
|
226
|
-
* processes are never added to Group.enabledProcesses.
|
227
|
-
*/
|
228
|
-
bool requiresShutdown;
|
229
324
|
|
325
|
+
/****** Miscellaneous ******/
|
326
|
+
|
327
|
+
static bool isZombie(pid_t pid) {
|
328
|
+
string filename = "/proc/" + toString(pid) + "/status";
|
329
|
+
FILE *f = fopen(filename.c_str(), "r");
|
330
|
+
if (f == NULL) {
|
331
|
+
// Don't know.
|
332
|
+
return false;
|
333
|
+
}
|
334
|
+
|
335
|
+
bool result = false;
|
336
|
+
while (!feof(f)) {
|
337
|
+
char buf[512];
|
338
|
+
const char *line;
|
339
|
+
|
340
|
+
line = fgets(buf, sizeof(buf), f);
|
341
|
+
if (line == NULL) {
|
342
|
+
break;
|
343
|
+
}
|
344
|
+
if (strcmp(line, "State: Z (zombie)\n") == 0) {
|
345
|
+
// Is a zombie.
|
346
|
+
result = true;
|
347
|
+
break;
|
348
|
+
}
|
349
|
+
}
|
350
|
+
fclose(f);
|
351
|
+
return result;
|
352
|
+
}
|
353
|
+
|
354
|
+
public:
|
230
355
|
/*************************************************************
|
231
356
|
* Information used by Pool. Do not write to these from
|
232
357
|
* outside the Pool. If you read these make sure the Pool
|
233
358
|
* isn't concurrently modifying.
|
234
359
|
*************************************************************/
|
235
360
|
|
236
|
-
/** Time at which we finished spawning this process, i.e. when this
|
237
|
-
* process was finished initializing. Microseconds resolution.
|
238
|
-
*/
|
239
|
-
unsigned long long spawnEndTime;
|
240
361
|
/** Last time when a session was opened for this Process. */
|
241
362
|
unsigned long long lastUsed;
|
242
363
|
/** Number of sessions currently open.
|
@@ -260,7 +381,7 @@ public:
|
|
260
381
|
* this object is no longer usable.
|
261
382
|
*/
|
262
383
|
DEAD
|
263
|
-
} lifeStatus
|
384
|
+
} lifeStatus;
|
264
385
|
enum EnabledStatus {
|
265
386
|
/** Up and operational. */
|
266
387
|
ENABLED,
|
@@ -281,7 +402,7 @@ public:
|
|
281
402
|
* eligible for new requests.
|
282
403
|
*/
|
283
404
|
DETACHED
|
284
|
-
} enabled
|
405
|
+
} enabled;
|
285
406
|
enum OobwStatus {
|
286
407
|
/** Process is not using out-of-band work. */
|
287
408
|
OOBW_NOT_ACTIVE,
|
@@ -292,41 +413,27 @@ public:
|
|
292
413
|
* sessions have ended and the process has been disabled before the
|
293
414
|
* out-of-band work can be performed. */
|
294
415
|
OOBW_IN_PROGRESS,
|
295
|
-
} oobwStatus
|
416
|
+
} oobwStatus;
|
296
417
|
/** Caches whether or not the OS process still exists. */
|
297
418
|
mutable bool m_osProcessExists: 1;
|
298
419
|
bool longRunningConnectionsAborted: 1;
|
299
|
-
/** Number of items in `sessionSockets`. Private field, but put here
|
300
|
-
* for alignment optimization.
|
301
|
-
*/
|
302
|
-
unsigned int sessionSocketCount: 8;
|
303
420
|
/** Time at which shutdown began. */
|
304
421
|
time_t shutdownStartTime;
|
305
422
|
/** Collected by Pool::collectAnalytics(). */
|
306
423
|
ProcessMetrics metrics;
|
307
424
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
group(NULL),
|
321
|
-
pid(_pid),
|
322
|
-
stickySessionId(0),
|
323
|
-
adminSocket(_adminSocket),
|
324
|
-
sockets(_sockets),
|
325
|
-
spawnerCreationTime(_spawnerCreationTime),
|
326
|
-
spawnStartTime(_spawnStartTime),
|
327
|
-
concurrency(0),
|
328
|
-
dummy(false),
|
329
|
-
requiresShutdown(true),
|
425
|
+
|
426
|
+
Process(const BasicGroupInfo *groupInfo, const Json::Value &json)
|
427
|
+
: info(this, groupInfo, json),
|
428
|
+
sessionSocketCount(0),
|
429
|
+
spawnerCreationTime(getJsonUint64Field(json, "spawner_creation_time")),
|
430
|
+
spawnStartTime(getJsonUint64Field(json, "spawn_start_time")),
|
431
|
+
spawnEndTime(SystemTime::getUsec()),
|
432
|
+
dummy(json["type"] == "dummy"),
|
433
|
+
requiresShutdown(false),
|
434
|
+
refcount(1),
|
435
|
+
index(-1),
|
436
|
+
lastUsed(spawnEndTime),
|
330
437
|
sessions(0),
|
331
438
|
processed(0),
|
332
439
|
lifeStatus(ALIVE),
|
@@ -334,39 +441,65 @@ public:
|
|
334
441
|
oobwStatus(OOBW_NOT_ACTIVE),
|
335
442
|
m_osProcessExists(true),
|
336
443
|
longRunningConnectionsAborted(false),
|
337
|
-
sessionSocketCount(0),
|
338
444
|
shutdownStartTime(0)
|
339
445
|
{
|
340
|
-
|
446
|
+
initializeSocketsAndStringFields(json);
|
447
|
+
indexSessionSockets();
|
341
448
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
watcher->start();
|
347
|
-
}
|
348
|
-
if (_errorPipe != -1) {
|
349
|
-
PipeWatcherPtr watcher = boost::make_shared<PipeWatcher>(_errorPipe,
|
350
|
-
"stderr", pid);
|
351
|
-
watcher->initialize();
|
352
|
-
watcher->start();
|
353
|
-
}
|
449
|
+
const SpawningKit::Result *skResult = dynamic_cast<const SpawningKit::Result *>(&json);
|
450
|
+
if (skResult != NULL) {
|
451
|
+
adminSocket = skResult->adminSocket;
|
452
|
+
errorPipe = skResult->errorPipe;
|
354
453
|
|
355
|
-
|
454
|
+
if (adminSocket != -1) {
|
455
|
+
SpawningKit::PipeWatcherPtr watcher = boost::make_shared<SpawningKit::PipeWatcher>(
|
456
|
+
getContext()->getSpawningKitConfig(), adminSocket, "stdout", info.pid);
|
457
|
+
watcher->initialize();
|
458
|
+
watcher->start();
|
459
|
+
}
|
356
460
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
461
|
+
if (errorPipe != -1) {
|
462
|
+
SpawningKit::PipeWatcherPtr watcher = boost::make_shared<SpawningKit::PipeWatcher>(
|
463
|
+
getContext()->getSpawningKitConfig(), errorPipe, "stderr", info.pid);
|
464
|
+
watcher->initialize();
|
465
|
+
watcher->start();
|
466
|
+
}
|
467
|
+
}
|
361
468
|
}
|
362
469
|
|
363
470
|
~Process() {
|
364
|
-
if (OXT_UNLIKELY(!isDead()
|
471
|
+
if (OXT_UNLIKELY(requiresShutdown && !isDead())) {
|
365
472
|
P_BUG("You must call Process::triggerShutdown() and Process::cleanup() before actually "
|
366
473
|
"destroying the Process object.");
|
367
474
|
}
|
368
475
|
}
|
369
476
|
|
477
|
+
void initializeStickySessionId(unsigned int value) {
|
478
|
+
info.stickySessionId = value;
|
479
|
+
}
|
480
|
+
|
481
|
+
void shutdownNotRequired() {
|
482
|
+
requiresShutdown = false;
|
483
|
+
}
|
484
|
+
|
485
|
+
|
486
|
+
/****** Memory and life time management ******/
|
487
|
+
|
488
|
+
void ref() const {
|
489
|
+
refcount.fetch_add(1, boost::memory_order_relaxed);
|
490
|
+
}
|
491
|
+
|
492
|
+
void unref() const {
|
493
|
+
if (refcount.fetch_sub(1, boost::memory_order_release) == 1) {
|
494
|
+
boost::atomic_thread_fence(boost::memory_order_acquire);
|
495
|
+
destroySelf();
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
ProcessPtr shared_from_this() {
|
500
|
+
return ProcessPtr(this);
|
501
|
+
}
|
502
|
+
|
370
503
|
static void forceTriggerShutdownAndCleanup(ProcessPtr process) {
|
371
504
|
if (process != NULL) {
|
372
505
|
process->triggerShutdown();
|
@@ -377,35 +510,6 @@ public:
|
|
377
510
|
}
|
378
511
|
}
|
379
512
|
|
380
|
-
/**
|
381
|
-
* Thread-safe.
|
382
|
-
* @pre getLifeState() != SHUT_DOWN
|
383
|
-
* @post result != NULL
|
384
|
-
*/
|
385
|
-
Group *getGroup() const {
|
386
|
-
assert(!isDead());
|
387
|
-
return group;
|
388
|
-
}
|
389
|
-
|
390
|
-
void setGroup(Group *group) {
|
391
|
-
assert(this->group == NULL || this->group == group);
|
392
|
-
this->group = group;
|
393
|
-
}
|
394
|
-
|
395
|
-
/**
|
396
|
-
* Thread-safe.
|
397
|
-
* @pre getLifeState() != DEAD
|
398
|
-
* @post result != NULL
|
399
|
-
*/
|
400
|
-
Pool *getPool() const;
|
401
|
-
|
402
|
-
/**
|
403
|
-
* Thread-safe.
|
404
|
-
* @pre getLifeState() != DEAD
|
405
|
-
* @post result != NULL
|
406
|
-
*/
|
407
|
-
SuperGroup *getSuperGroup() const;
|
408
|
-
|
409
513
|
// Thread-safe.
|
410
514
|
bool isAlive() const {
|
411
515
|
oxt::spin_lock::scoped_lock lock(lifetimeSyncher);
|
@@ -430,29 +534,6 @@ public:
|
|
430
534
|
return lifeStatus;
|
431
535
|
}
|
432
536
|
|
433
|
-
// Thread-safe.
|
434
|
-
StaticString getGroupSecret() const;
|
435
|
-
|
436
|
-
Socket *findSessionSocketWithLowestBusyness() const {
|
437
|
-
if (OXT_UNLIKELY(sessionSocketCount == 0)) {
|
438
|
-
return NULL;
|
439
|
-
} else if (sessionSocketCount == 1) {
|
440
|
-
return sessionSockets[0];
|
441
|
-
} else {
|
442
|
-
int leastBusySessionSocketIndex = 0;
|
443
|
-
int lowestBusyness = sessionSockets[0]->busyness();
|
444
|
-
|
445
|
-
for (unsigned i = 1; i < sessionSocketCount; i++) {
|
446
|
-
if (sessionSockets[i]->busyness() < lowestBusyness) {
|
447
|
-
leastBusySessionSocketIndex = i;
|
448
|
-
lowestBusyness = sessionSockets[i]->busyness();
|
449
|
-
}
|
450
|
-
}
|
451
|
-
|
452
|
-
return sessionSockets[leastBusySessionSocketIndex];
|
453
|
-
}
|
454
|
-
}
|
455
|
-
|
456
537
|
bool canTriggerShutdown() const {
|
457
538
|
return getLifeStatus() == ALIVE && sessions == 0;
|
458
539
|
}
|
@@ -498,6 +579,85 @@ public:
|
|
498
579
|
lifeStatus = DEAD;
|
499
580
|
}
|
500
581
|
|
582
|
+
|
583
|
+
/****** Basic information queries ******/
|
584
|
+
|
585
|
+
OXT_FORCE_INLINE
|
586
|
+
Context *getContext() const {
|
587
|
+
return info.groupInfo->context;
|
588
|
+
}
|
589
|
+
|
590
|
+
Group *getGroup() const {
|
591
|
+
return info.groupInfo->group;
|
592
|
+
}
|
593
|
+
|
594
|
+
StaticString getGroupName() const {
|
595
|
+
return info.groupInfo->name;
|
596
|
+
}
|
597
|
+
|
598
|
+
const ApiKey &getApiKey() const {
|
599
|
+
return info.groupInfo->apiKey;
|
600
|
+
}
|
601
|
+
|
602
|
+
const BasicProcessInfo &getInfo() const {
|
603
|
+
return info;
|
604
|
+
}
|
605
|
+
|
606
|
+
pid_t getPid() const {
|
607
|
+
return info.pid;
|
608
|
+
}
|
609
|
+
|
610
|
+
StaticString getGupid() const {
|
611
|
+
return StaticString(info.gupid, info.gupidSize);
|
612
|
+
}
|
613
|
+
|
614
|
+
unsigned int getStickySessionId() const {
|
615
|
+
return info.stickySessionId;
|
616
|
+
}
|
617
|
+
|
618
|
+
unsigned long long getSpawnerCreationTime() const {
|
619
|
+
return spawnerCreationTime;
|
620
|
+
}
|
621
|
+
|
622
|
+
bool isDummy() const {
|
623
|
+
return dummy;
|
624
|
+
}
|
625
|
+
|
626
|
+
|
627
|
+
/****** Miscellaneous ******/
|
628
|
+
|
629
|
+
unsigned int getIndex() const {
|
630
|
+
return index;
|
631
|
+
}
|
632
|
+
|
633
|
+
void setIndex(unsigned int i) {
|
634
|
+
index = i;
|
635
|
+
}
|
636
|
+
|
637
|
+
const SocketList &getSockets() const {
|
638
|
+
return sockets;
|
639
|
+
}
|
640
|
+
|
641
|
+
Socket *findSessionSocketWithLowestBusyness() const {
|
642
|
+
if (OXT_UNLIKELY(sessionSocketCount == 0)) {
|
643
|
+
return NULL;
|
644
|
+
} else if (sessionSocketCount == 1) {
|
645
|
+
return sessionSockets[0];
|
646
|
+
} else {
|
647
|
+
int leastBusySessionSocketIndex = 0;
|
648
|
+
int lowestBusyness = sessionSockets[0]->busyness();
|
649
|
+
|
650
|
+
for (unsigned i = 1; i < sessionSocketCount; i++) {
|
651
|
+
if (sessionSockets[i]->busyness() < lowestBusyness) {
|
652
|
+
leastBusySessionSocketIndex = i;
|
653
|
+
lowestBusyness = sessionSockets[i]->busyness();
|
654
|
+
}
|
655
|
+
}
|
656
|
+
|
657
|
+
return sessionSockets[leastBusySessionSocketIndex];
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
501
661
|
/** Checks whether the OS process exists.
|
502
662
|
* Once it has been detected that it doesn't, that event is remembered
|
503
663
|
* so that we don't accidentally ping any new processes that have the
|
@@ -505,13 +665,13 @@ public:
|
|
505
665
|
*/
|
506
666
|
bool osProcessExists() const {
|
507
667
|
if (!dummy && m_osProcessExists) {
|
508
|
-
if (syscalls::kill(
|
668
|
+
if (syscalls::kill(getPid(), 0) == 0) {
|
509
669
|
/* On some environments, e.g. Heroku, the init process does
|
510
670
|
* not properly reap adopted zombie processes, which can interfere
|
511
671
|
* with our process existance check. To work around this, we
|
512
672
|
* explicitly check whether or not the process has become a zombie.
|
513
673
|
*/
|
514
|
-
m_osProcessExists = !isZombie(
|
674
|
+
m_osProcessExists = !isZombie(getPid());
|
515
675
|
} else {
|
516
676
|
m_osProcessExists = errno != ESRCH;
|
517
677
|
}
|
@@ -524,7 +684,7 @@ public:
|
|
524
684
|
/** Kill the OS process with the given signal. */
|
525
685
|
int kill(int signo) {
|
526
686
|
if (osProcessExists()) {
|
527
|
-
return syscalls::kill(
|
687
|
+
return syscalls::kill(getPid(), signo);
|
528
688
|
} else {
|
529
689
|
return 0;
|
530
690
|
}
|
@@ -594,7 +754,35 @@ public:
|
|
594
754
|
}
|
595
755
|
}
|
596
756
|
|
597
|
-
SessionPtr createSessionObject(Socket *socket)
|
757
|
+
SessionPtr createSessionObject(Socket *socket) {
|
758
|
+
struct Guard {
|
759
|
+
Context *context;
|
760
|
+
Session *session;
|
761
|
+
|
762
|
+
Guard(Context *c, Session *s)
|
763
|
+
: context(c),
|
764
|
+
session(s)
|
765
|
+
{ }
|
766
|
+
|
767
|
+
~Guard() {
|
768
|
+
if (session != NULL) {
|
769
|
+
context->getSessionObjectPool().free(session);
|
770
|
+
}
|
771
|
+
}
|
772
|
+
|
773
|
+
void clear() {
|
774
|
+
session = NULL;
|
775
|
+
}
|
776
|
+
};
|
777
|
+
|
778
|
+
Context *context = getContext();
|
779
|
+
LockGuard l(context->getMmSyncher());
|
780
|
+
Session *session = context->getSessionObjectPool().malloc();
|
781
|
+
Guard guard(context, session);
|
782
|
+
session = new (session) Session(context, &info, socket);
|
783
|
+
guard.clear();
|
784
|
+
return SessionPtr(session, false);
|
785
|
+
}
|
598
786
|
|
599
787
|
void sessionClosed(Session *session) {
|
600
788
|
Socket *socket = session->getSocket();
|
@@ -615,23 +803,18 @@ public:
|
|
615
803
|
return distanceOfTimeInWords(spawnEndTime / 1000000);
|
616
804
|
}
|
617
805
|
|
618
|
-
string inspect() const
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
recreateString(pool, codeRevision);
|
624
|
-
|
625
|
-
for (it = sockets.begin(); it != sockets.end(); it++) {
|
626
|
-
it->recreateStrings(pool);
|
627
|
-
}
|
806
|
+
string inspect() const {
|
807
|
+
assert(getLifeStatus() != DEAD);
|
808
|
+
stringstream result;
|
809
|
+
result << "(pid=" << getPid() << ", group=" << getGroupName() << ")";
|
810
|
+
return result.str();
|
628
811
|
}
|
629
812
|
|
630
813
|
template<typename Stream>
|
631
814
|
void inspectXml(Stream &stream, bool includeSockets = true) const {
|
632
|
-
stream << "<pid>" <<
|
633
|
-
stream << "<sticky_session_id>" <<
|
634
|
-
stream << "<gupid>" <<
|
815
|
+
stream << "<pid>" << getPid() << "</pid>";
|
816
|
+
stream << "<sticky_session_id>" << getStickySessionId() << "</sticky_session_id>";
|
817
|
+
stream << "<gupid>" << getGupid() << "</gupid>";
|
635
818
|
stream << "<concurrency>" << concurrency << "</concurrency>";
|
636
819
|
stream << "<sessions>" << sessions << "</sessions>";
|
637
820
|
stream << "<busyness>" << busyness() << "</busyness>";
|
@@ -706,6 +889,17 @@ public:
|
|
706
889
|
};
|
707
890
|
|
708
891
|
|
892
|
+
inline void
|
893
|
+
intrusive_ptr_add_ref(const Process *process) {
|
894
|
+
process->ref();
|
895
|
+
}
|
896
|
+
|
897
|
+
inline void
|
898
|
+
intrusive_ptr_release(const Process *process) {
|
899
|
+
process->unref();
|
900
|
+
}
|
901
|
+
|
902
|
+
|
709
903
|
} // namespace ApplicationPool2
|
710
904
|
} // namespace Passenger
|
711
905
|
|