passenger 3.9.2.beta → 4.0.0.rc4
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.
- data/.travis.yml +3 -0
- data/NEWS +77 -7
- data/README.md +3 -11
- data/bin/passenger-install-apache2-module +24 -20
- data/bin/passenger-install-nginx-module +25 -23
- data/build/agents.rb +11 -0
- data/build/apache2.rb +9 -5
- data/build/basics.rb +37 -30
- data/build/common_library.rb +4 -1
- data/build/cplusplus_support.rb +5 -5
- data/build/cxx_tests.rb +28 -8
- data/build/integration_tests.rb +6 -3
- data/build/nginx.rb +3 -3
- data/build/packaging.rb +95 -57
- data/build/ruby_extension.rb +34 -21
- data/build/ruby_tests.rb +4 -2
- data/build/test_basics.rb +1 -1
- data/dev/run_travis.sh +36 -1
- data/doc/Users guide Apache.html +425 -308
- data/doc/Users guide Apache.idmap.txt +78 -70
- data/doc/Users guide Apache.index.sqlite3 +0 -0
- data/doc/Users guide Apache.txt +33 -92
- data/doc/Users guide Nginx.html +519 -220
- data/doc/Users guide Nginx.idmap.txt +78 -60
- data/doc/Users guide Nginx.txt +115 -26
- data/doc/Users guide Standalone.html +8 -2
- data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +1 -7
- data/doc/users_guide_snippets/installation.txt +167 -22
- data/doc/users_guide_snippets/rackup_specifications.txt +4 -0
- data/doc/users_guide_snippets/since_version.txt +1 -0
- data/doc/users_guide_snippets/support_information.txt +3 -7
- data/doc/users_guide_snippets/tips.txt +0 -24
- data/ext/apache2/Configuration.cpp +11 -33
- data/ext/apache2/Configuration.hpp +3 -18
- data/ext/apache2/DirectoryMapper.h +20 -70
- data/ext/apache2/Hooks.cpp +2 -2
- data/ext/common/AgentsStarter.cpp +0 -2
- data/ext/common/AgentsStarter.h +0 -1
- data/ext/common/AgentsStarter.hpp +1 -3
- data/ext/common/ApplicationPool2/AppTypes.cpp +74 -0
- data/ext/common/ApplicationPool2/AppTypes.h +202 -0
- data/ext/common/ApplicationPool2/Common.h +12 -10
- data/ext/common/ApplicationPool2/DirectSpawner.h +256 -0
- data/ext/common/ApplicationPool2/DummySpawner.h +90 -0
- data/ext/common/ApplicationPool2/Group.h +311 -94
- data/ext/common/ApplicationPool2/Implementation.cpp +405 -145
- data/ext/common/ApplicationPool2/Options.h +24 -26
- data/ext/common/ApplicationPool2/PipeWatcher.h +20 -13
- data/ext/common/ApplicationPool2/Pool.h +326 -183
- data/ext/common/ApplicationPool2/Process.h +205 -55
- data/ext/common/ApplicationPool2/README.md +1 -1
- data/ext/common/ApplicationPool2/Session.h +21 -10
- data/ext/common/ApplicationPool2/SmartSpawner.h +801 -0
- data/ext/common/ApplicationPool2/Spawner.h +141 -1149
- data/ext/common/ApplicationPool2/SpawnerFactory.h +132 -0
- data/ext/common/ApplicationPool2/SuperGroup.h +146 -223
- data/ext/common/Constants.h +4 -2
- data/ext/common/Exceptions.h +23 -1
- data/ext/common/Logging.cpp +17 -6
- data/ext/common/Logging.h +37 -7
- data/ext/common/ResourceLocator.h +1 -1
- data/ext/common/Utils.cpp +49 -1
- data/ext/common/Utils.h +13 -4
- data/ext/common/{AnsiColorConstants.h → Utils/AnsiColorConstants.h} +0 -0
- data/ext/common/{BCrypt.cpp → Utils/BCrypt.cpp} +0 -0
- data/ext/common/{BCrypt.h → Utils/BCrypt.h} +0 -0
- data/ext/common/{Blowfish.c → Utils/Blowfish.c} +0 -0
- data/ext/common/{Blowfish.h → Utils/Blowfish.h} +0 -0
- data/ext/common/Utils/CachedFileStat.hpp +27 -25
- data/ext/common/Utils/Curl.h +184 -0
- data/ext/common/{HttpConstants.h → Utils/HttpConstants.h} +3 -0
- data/ext/common/Utils/IOUtils.cpp +6 -2
- data/ext/common/{IniFile.h → Utils/IniFile.h} +0 -0
- data/ext/common/Utils/LargeFiles.cpp +30 -0
- data/ext/common/Utils/LargeFiles.h +40 -0
- data/ext/common/Utils/StrIntUtils.cpp +72 -8
- data/ext/common/Utils/StrIntUtils.h +24 -2
- data/ext/common/Utils/StringMap.h +12 -2
- data/ext/common/Utils/VariantMap.h +51 -2
- data/ext/common/Utils/jsoncpp.cpp +1 -1
- data/ext/common/agents/Base.cpp +147 -11
- data/ext/common/agents/HelperAgent/AgentOptions.h +14 -6
- data/ext/common/agents/HelperAgent/Main.cpp +79 -19
- data/ext/common/agents/HelperAgent/RequestHandler.h +36 -16
- data/ext/common/agents/LoggingAgent/LoggingServer.h +3 -5
- data/ext/common/agents/LoggingAgent/Main.cpp +2 -4
- data/ext/common/agents/LoggingAgent/RemoteSender.h +18 -24
- data/ext/common/agents/SpawnPreparer.cpp +7 -0
- data/ext/common/agents/Watchdog/Main.cpp +96 -38
- data/ext/nginx/Configuration.c +26 -22
- data/ext/nginx/Configuration.h +4 -2
- data/ext/nginx/ContentHandler.c +23 -52
- data/ext/nginx/ContentHandler.h +5 -11
- data/ext/nginx/config +10 -3
- data/ext/nginx/ngx_http_passenger_module.c +21 -6
- data/ext/nginx/ngx_http_passenger_module.h +4 -1
- data/ext/oxt/dynamic_thread_group.hpp +9 -1
- data/ext/oxt/system_calls.cpp +2 -2
- data/ext/ruby/extconf.rb +2 -1
- data/helper-scripts/backtrace-sanitizer.rb +2 -0
- data/helper-scripts/wsgi-loader.py +54 -21
- data/lib/phusion_passenger.rb +5 -3
- data/lib/phusion_passenger/abstract_installer.rb +18 -41
- data/lib/phusion_passenger/admin_tools/memory_stats.rb +2 -2
- data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
- data/lib/phusion_passenger/common_library.rb +23 -3
- data/lib/phusion_passenger/debug_logging.rb +10 -3
- data/lib/phusion_passenger/packaging.rb +1 -0
- data/lib/phusion_passenger/platform_info.rb +113 -115
- data/lib/phusion_passenger/platform_info/compiler.rb +224 -134
- data/lib/phusion_passenger/platform_info/cxx_portability.rb +143 -0
- data/lib/phusion_passenger/platform_info/depcheck.rb +371 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +124 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +97 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +39 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +118 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +137 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +15 -0
- data/lib/phusion_passenger/platform_info/operating_system.rb +6 -5
- data/lib/phusion_passenger/platform_info/ruby.rb +45 -34
- data/lib/phusion_passenger/request_handler.rb +35 -22
- data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -6
- data/lib/phusion_passenger/ruby_core_enhancements.rb +7 -1
- data/lib/phusion_passenger/standalone/runtime_installer.rb +43 -34
- data/lib/phusion_passenger/utils/robust_interruption.rb +34 -18
- data/passenger.gemspec +25 -0
- data/resources/templates/standalone/config.erb +3 -1
- data/test/config.json.travis +2 -2
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +37 -5
- data/test/cxx/ApplicationPool2/PoolTest.cpp +143 -50
- data/test/cxx/ApplicationPool2/ProcessTest.cpp +8 -0
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +28 -17
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +31 -26
- data/test/cxx/RequestHandlerTest.cpp +17 -1
- data/test/cxx/UtilsTest.cpp +84 -10
- data/test/integration_tests/apache2_tests.rb +49 -163
- data/test/integration_tests/hello_world_wsgi_spec.rb +2 -2
- data/test/integration_tests/mycook_spec.rb +1 -1
- data/test/integration_tests/nginx_tests.rb +37 -19
- data/test/ruby/request_handler_spec.rb +1 -0
- data/test/ruby/spec_helper.rb +52 -1
- data/test/stub/nginx/nginx.conf.erb +2 -0
- data/test/stub/rack/start.rb +5 -0
- data/test/stub/rails3.0/Gemfile.lock +30 -30
- data/test/stub/rails3.1/Gemfile +1 -1
- data/test/stub/rails3.1/Gemfile.lock +3 -3
- data/test/stub/rails3.2/Gemfile +1 -1
- data/test/stub/rails3.2/Gemfile.lock +4 -4
- data/test/stub/rails_apps/2.3/mycook/app/controllers/welcome_controller.rb +1 -1
- data/test/stub/rails_apps/2.3/mycook/app/helpers/recipes_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/test_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/uploads_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/welcome_helper.rb +2 -0
- data/test/support/nginx_controller.rb +2 -1
- metadata +160 -156
- data/build/gempackagetask.rb +0 -99
- data/build/packagetask.rb +0 -186
- data/ext/common/StringListCreator.h +0 -83
- data/lib/phusion_passenger/dependencies.rb +0 -657
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2011
|
3
|
+
* Copyright (c) 2011-2013 Phusion
|
4
4
|
*
|
5
5
|
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
6
|
*
|
@@ -23,7 +23,9 @@
|
|
23
23
|
* THE SOFTWARE.
|
24
24
|
*/
|
25
25
|
#include <typeinfo>
|
26
|
+
#include <algorithm>
|
26
27
|
#include <boost/make_shared.hpp>
|
28
|
+
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
27
29
|
#include <oxt/backtrace.hpp>
|
28
30
|
#include <ApplicationPool2/Pool.h>
|
29
31
|
#include <ApplicationPool2/SuperGroup.h>
|
@@ -31,6 +33,7 @@
|
|
31
33
|
#include <ApplicationPool2/PipeWatcher.h>
|
32
34
|
#include <Exceptions.h>
|
33
35
|
#include <MessageReadersWriters.h>
|
36
|
+
#include <Utils/ScopeGuard.h>
|
34
37
|
|
35
38
|
namespace Passenger {
|
36
39
|
namespace ApplicationPool2 {
|
@@ -71,6 +74,7 @@ copyException(const tracable_exception &e) {
|
|
71
74
|
TRY_COPY_EXCEPTION(ConfigurationException);
|
72
75
|
|
73
76
|
TRY_COPY_EXCEPTION(SpawnException);
|
77
|
+
TRY_COPY_EXCEPTION(GetAbortedException);
|
74
78
|
|
75
79
|
TRY_COPY_EXCEPTION(InvalidModeStringException);
|
76
80
|
TRY_COPY_EXCEPTION(ArgumentException);
|
@@ -110,6 +114,7 @@ rethrowException(const ExceptionPtr &e) {
|
|
110
114
|
TRY_RETHROW_EXCEPTION(ConfigurationException);
|
111
115
|
|
112
116
|
TRY_RETHROW_EXCEPTION(SpawnException);
|
117
|
+
TRY_RETHROW_EXCEPTION(GetAbortedException);
|
113
118
|
|
114
119
|
TRY_RETHROW_EXCEPTION(InvalidModeStringException);
|
115
120
|
TRY_RETHROW_EXCEPTION(ArgumentException);
|
@@ -167,10 +172,153 @@ SuperGroup::createInterruptableThread(const function<void ()> &func, const strin
|
|
167
172
|
}
|
168
173
|
|
169
174
|
void
|
170
|
-
SuperGroup::
|
171
|
-
|
172
|
-
|
173
|
-
|
175
|
+
SuperGroup::realDoInitialize(const Options &options, unsigned int generation) {
|
176
|
+
vector<ComponentInfo> componentInfos;
|
177
|
+
vector<ComponentInfo>::const_iterator it;
|
178
|
+
ExceptionPtr exception;
|
179
|
+
|
180
|
+
P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background...");
|
181
|
+
try {
|
182
|
+
componentInfos = loadComponentInfos(options);
|
183
|
+
} catch (const tracable_exception &e) {
|
184
|
+
exception = copyException(e);
|
185
|
+
}
|
186
|
+
if (componentInfos.empty() && exception == NULL) {
|
187
|
+
string message = "The directory " +
|
188
|
+
options.appRoot +
|
189
|
+
" does not seem to contain a web application.";
|
190
|
+
exception = make_shared<SpawnException>(
|
191
|
+
message, message, false);
|
192
|
+
}
|
193
|
+
|
194
|
+
PoolPtr pool = getPool();
|
195
|
+
Pool::DebugSupportPtr debug = pool->debugSupport;
|
196
|
+
|
197
|
+
vector<Callback> actions;
|
198
|
+
{
|
199
|
+
if (debug != NULL && debug->superGroup) {
|
200
|
+
debug->debugger->send("About to finish SuperGroup initialization");
|
201
|
+
debug->messages->recv("Proceed with initializing SuperGroup");
|
202
|
+
}
|
203
|
+
|
204
|
+
unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
205
|
+
this_thread::disable_interruption di;
|
206
|
+
this_thread::disable_syscall_interruption dsi;
|
207
|
+
NOT_EXPECTING_EXCEPTIONS();
|
208
|
+
if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) {
|
209
|
+
return;
|
210
|
+
}
|
211
|
+
P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock");
|
212
|
+
assert(state == INITIALIZING);
|
213
|
+
verifyInvariants();
|
214
|
+
|
215
|
+
if (componentInfos.empty()) {
|
216
|
+
/* Somehow initialization failed. Maybe something has deleted
|
217
|
+
* the supergroup files while we're working.
|
218
|
+
*/
|
219
|
+
assert(exception != NULL);
|
220
|
+
setState(DESTROYED);
|
221
|
+
|
222
|
+
actions.reserve(getWaitlist.size());
|
223
|
+
while (!getWaitlist.empty()) {
|
224
|
+
const GetWaiter &waiter = getWaitlist.front();
|
225
|
+
actions.push_back(boost::bind(waiter.callback,
|
226
|
+
SessionPtr(), exception));
|
227
|
+
getWaitlist.pop();
|
228
|
+
}
|
229
|
+
} else {
|
230
|
+
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
231
|
+
const ComponentInfo &info = *it;
|
232
|
+
GroupPtr group = make_shared<Group>(shared_from_this(),
|
233
|
+
options, info);
|
234
|
+
groups.push_back(group);
|
235
|
+
if (info.isDefault) {
|
236
|
+
defaultGroup = group.get();
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
setState(READY);
|
241
|
+
assignGetWaitlistToGroups(actions);
|
242
|
+
}
|
243
|
+
|
244
|
+
verifyInvariants();
|
245
|
+
P_TRACE(2, "Done initializing SuperGroup " << inspect());
|
246
|
+
}
|
247
|
+
this_thread::disable_interruption di;
|
248
|
+
this_thread::disable_syscall_interruption dsi;
|
249
|
+
runAllActions(actions);
|
250
|
+
}
|
251
|
+
|
252
|
+
void
|
253
|
+
SuperGroup::realDoRestart(const Options &options, unsigned int generation) {
|
254
|
+
TRACE_POINT();
|
255
|
+
vector<ComponentInfo> componentInfos = loadComponentInfos(options);
|
256
|
+
vector<ComponentInfo>::const_iterator it;
|
257
|
+
|
258
|
+
PoolPtr pool = getPool();
|
259
|
+
Pool::DebugSupportPtr debug = pool->debugSupport;
|
260
|
+
if (debug != NULL && debug->superGroup) {
|
261
|
+
debug->debugger->send("About to finish SuperGroup restart");
|
262
|
+
debug->messages->recv("Proceed with restarting SuperGroup");
|
263
|
+
}
|
264
|
+
|
265
|
+
unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
266
|
+
if (OXT_UNLIKELY(this->generation != generation)) {
|
267
|
+
return;
|
268
|
+
}
|
269
|
+
|
270
|
+
assert(state == RESTARTING);
|
271
|
+
verifyInvariants();
|
272
|
+
|
273
|
+
vector<GroupPtr> allGroups;
|
274
|
+
vector<GroupPtr> updatedGroups;
|
275
|
+
vector<GroupPtr> newGroups;
|
276
|
+
vector<GroupPtr>::const_iterator g_it;
|
277
|
+
vector<Callback> actions;
|
278
|
+
this->options = options;
|
279
|
+
|
280
|
+
// Update the component information for existing groups.
|
281
|
+
UPDATE_TRACE_POINT();
|
282
|
+
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
283
|
+
const ComponentInfo &info = *it;
|
284
|
+
pair<GroupPtr, unsigned int> result =
|
285
|
+
findGroupCorrespondingToComponent(groups, info);
|
286
|
+
GroupPtr group = result.first;
|
287
|
+
if (group != NULL) {
|
288
|
+
unsigned int index = result.second;
|
289
|
+
group->componentInfo = info;
|
290
|
+
updatedGroups.push_back(group);
|
291
|
+
groups[index].reset();
|
292
|
+
} else {
|
293
|
+
// This is not an existing group but a new one,
|
294
|
+
// so create it.
|
295
|
+
group = make_shared<Group>(shared_from_this(),
|
296
|
+
options, info);
|
297
|
+
newGroups.push_back(group);
|
298
|
+
}
|
299
|
+
// allGroups must be in the same order as componentInfos.
|
300
|
+
allGroups.push_back(group);
|
301
|
+
}
|
302
|
+
|
303
|
+
// Some components might have been deleted, so delete the
|
304
|
+
// corresponding groups.
|
305
|
+
detachAllGroups(groups, actions);
|
306
|
+
|
307
|
+
// Tell all previous existing groups to restart.
|
308
|
+
for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) {
|
309
|
+
GroupPtr group = *g_it;
|
310
|
+
group->restart(options);
|
311
|
+
}
|
312
|
+
|
313
|
+
groups = allGroups;
|
314
|
+
defaultGroup = findDefaultGroup(allGroups);
|
315
|
+
setState(READY);
|
316
|
+
assignGetWaitlistToGroups(actions);
|
317
|
+
|
318
|
+
UPDATE_TRACE_POINT();
|
319
|
+
verifyInvariants();
|
320
|
+
lock.unlock();
|
321
|
+
runAllActions(actions);
|
174
322
|
}
|
175
323
|
|
176
324
|
|
@@ -184,8 +332,10 @@ Group::Group(const SuperGroupPtr &_superGroup, const Options &options, const Com
|
|
184
332
|
disablingCount = 0;
|
185
333
|
disabledCount = 0;
|
186
334
|
spawner = getPool()->spawnerFactory->create(options);
|
335
|
+
restartsInitiated = 0;
|
187
336
|
m_spawning = false;
|
188
337
|
m_restarting = false;
|
338
|
+
lifeStatus = ALIVE;
|
189
339
|
if (options.restartDir.empty()) {
|
190
340
|
restartFile = options.appRoot + "/tmp/restart.txt";
|
191
341
|
alwaysRestartFile = options.appRoot + "/always_restart.txt";
|
@@ -194,23 +344,22 @@ Group::Group(const SuperGroupPtr &_superGroup, const Options &options, const Com
|
|
194
344
|
alwaysRestartFile = options.restartDir + "/always_restart.txt";
|
195
345
|
}
|
196
346
|
resetOptions(options);
|
347
|
+
|
348
|
+
detachedProcessesCheckerActive = false;
|
197
349
|
}
|
198
350
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
return superGroup->getPool();
|
204
|
-
} else {
|
205
|
-
return PoolPtr();
|
351
|
+
Group::~Group() {
|
352
|
+
LifeStatus lifeStatus = getLifeStatus();
|
353
|
+
if (OXT_UNLIKELY(lifeStatus == ALIVE)) {
|
354
|
+
P_BUG("You must call Group::shutdown() before destroying a Group.");
|
206
355
|
}
|
356
|
+
assert(lifeStatus == SHUT_DOWN);
|
357
|
+
assert(!detachedProcessesCheckerActive);
|
207
358
|
}
|
208
359
|
|
209
|
-
|
210
|
-
Group::
|
211
|
-
|
212
|
-
{
|
213
|
-
getPool()->interruptableThreads.create_thread(func, name, stackSize);
|
360
|
+
PoolPtr
|
361
|
+
Group::getPool() const {
|
362
|
+
return getSuperGroup()->getPool();
|
214
363
|
}
|
215
364
|
|
216
365
|
void
|
@@ -220,14 +369,9 @@ Group::onSessionInitiateFailure(const ProcessPtr &process, Session *session) {
|
|
220
369
|
TRACE_POINT();
|
221
370
|
// Standard resource management boilerplate stuff...
|
222
371
|
PoolPtr pool = getPool();
|
223
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
224
|
-
return;
|
225
|
-
}
|
226
372
|
unique_lock<boost::mutex> lock(pool->syncher);
|
227
|
-
|
228
|
-
|
229
|
-
return;
|
230
|
-
}
|
373
|
+
assert(!process->isShutDown());
|
374
|
+
assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
|
231
375
|
|
232
376
|
UPDATE_TRACE_POINT();
|
233
377
|
P_DEBUG("Could not initiate a session with process " <<
|
@@ -245,23 +389,19 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
|
|
245
389
|
TRACE_POINT();
|
246
390
|
// Standard resource management boilerplate stuff...
|
247
391
|
PoolPtr pool = getPool();
|
248
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
249
|
-
return;
|
250
|
-
}
|
251
392
|
unique_lock<boost::mutex> lock(pool->syncher);
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
}
|
256
|
-
|
393
|
+
assert(!process->isShutDown());
|
394
|
+
assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
|
395
|
+
|
257
396
|
P_TRACE(2, "Session closed for process " << process->inspect());
|
258
397
|
verifyInvariants();
|
259
398
|
UPDATE_TRACE_POINT();
|
260
399
|
|
261
400
|
/* Update statistics. */
|
262
401
|
process->sessionClosed(session);
|
402
|
+
Process::LifeStatus lifeStatus = process->getLifeStatus();
|
263
403
|
assert(process->enabled == Process::ENABLED || process->enabled == Process::DISABLING);
|
264
|
-
if (process->enabled == Process::ENABLED) {
|
404
|
+
if (process->enabled == Process::ENABLED && lifeStatus == Process::ALIVE) {
|
265
405
|
pqueue.decrease(process->pqHandle, process->utilization());
|
266
406
|
}
|
267
407
|
|
@@ -270,6 +410,16 @@ Group::onSessionClose(const ProcessPtr &process, Session *session) {
|
|
270
410
|
*/
|
271
411
|
assert(!process->atFullUtilization());
|
272
412
|
|
413
|
+
if (lifeStatus == Process::SHUTTING_DOWN) {
|
414
|
+
UPDATE_TRACE_POINT();
|
415
|
+
if (process->canBeShutDown()) {
|
416
|
+
shutdownAndRemoveProcess(process);
|
417
|
+
}
|
418
|
+
verifyInvariants();
|
419
|
+
verifyExpensiveInvariants();
|
420
|
+
return;
|
421
|
+
}
|
422
|
+
|
273
423
|
bool detachingBecauseOfMaxRequests = false;
|
274
424
|
bool detachingBecauseCapacityNeeded = false;
|
275
425
|
bool shouldDetach =
|
@@ -343,15 +493,9 @@ void
|
|
343
493
|
Group::requestOOBW(const ProcessPtr &process) {
|
344
494
|
// Standard resource management boilerplate stuff...
|
345
495
|
PoolPtr pool = getPool();
|
346
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
347
|
-
return;
|
348
|
-
}
|
349
496
|
unique_lock<boost::mutex> lock(pool->syncher);
|
350
|
-
|
351
|
-
|
352
|
-
return;
|
353
|
-
}
|
354
|
-
|
497
|
+
assert(isAlive());
|
498
|
+
|
355
499
|
process->oobwRequested = true;
|
356
500
|
}
|
357
501
|
|
@@ -366,12 +510,8 @@ Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult
|
|
366
510
|
|
367
511
|
// Standard resource management boilerplate stuff...
|
368
512
|
PoolPtr pool = getPool();
|
369
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
370
|
-
return;
|
371
|
-
}
|
372
513
|
unique_lock<boost::mutex> lock(pool->syncher);
|
373
|
-
|
374
|
-
if (OXT_UNLIKELY(pool == NULL || process->detached())) {
|
514
|
+
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
375
515
|
return;
|
376
516
|
}
|
377
517
|
|
@@ -380,11 +520,7 @@ Group::lockAndAsyncOOBWRequestIfNeeded(const ProcessPtr &process, DisableResult
|
|
380
520
|
|
381
521
|
void
|
382
522
|
Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
|
383
|
-
if (process->
|
384
|
-
return;
|
385
|
-
}
|
386
|
-
if (!process->oobwRequested) {
|
387
|
-
// The process has not requested oobw, so nothing to do here.
|
523
|
+
if (!process->oobwRequested || !process->isAlive()) {
|
388
524
|
return;
|
389
525
|
}
|
390
526
|
if (process->enabled == Process::ENABLED) {
|
@@ -404,7 +540,7 @@ Group::asyncOOBWRequestIfNeeded(const ProcessPtr &process) {
|
|
404
540
|
assert(process->enabled == Process::DISABLED);
|
405
541
|
assert(process->sessions == 0);
|
406
542
|
|
407
|
-
|
543
|
+
interruptableThreads.create_thread(
|
408
544
|
boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
|
409
545
|
"OOB request thread for process " + process->inspect(),
|
410
546
|
POOL_HELPER_THREAD_STACK_SIZE);
|
@@ -423,16 +559,11 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
423
559
|
{
|
424
560
|
// Standard resource management boilerplate stuff...
|
425
561
|
PoolPtr pool = getPool();
|
426
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
427
|
-
return;
|
428
|
-
}
|
429
562
|
unique_lock<boost::mutex> lock(pool->syncher);
|
430
|
-
|
431
|
-
if (OXT_UNLIKELY(pool == NULL || process->detached())) {
|
563
|
+
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
432
564
|
return;
|
433
565
|
}
|
434
566
|
|
435
|
-
assert(!process->detached());
|
436
567
|
assert(process->oobwRequested);
|
437
568
|
assert(process->sessions == 0);
|
438
569
|
assert(process->enabled == Process::DISABLED);
|
@@ -485,12 +616,8 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
485
616
|
{
|
486
617
|
// Standard resource management boilerplate stuff...
|
487
618
|
PoolPtr pool = getPool();
|
488
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
489
|
-
return;
|
490
|
-
}
|
491
619
|
unique_lock<boost::mutex> lock(pool->syncher);
|
492
|
-
|
493
|
-
if (OXT_UNLIKELY(pool == NULL || process->detached())) {
|
620
|
+
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
494
621
|
return;
|
495
622
|
}
|
496
623
|
|
@@ -507,30 +634,23 @@ Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
|
507
634
|
|
508
635
|
// The 'self' parameter is for keeping the current Group object alive while this thread is running.
|
509
636
|
void
|
510
|
-
Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options) {
|
511
|
-
|
512
|
-
spawnThreadRealMain(spawner, options);
|
513
|
-
} catch (const thread_interrupted &) {
|
514
|
-
// Return.
|
515
|
-
}
|
637
|
+
Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options, unsigned int restartsInitiated) {
|
638
|
+
spawnThreadRealMain(spawner, options, restartsInitiated);
|
516
639
|
}
|
517
640
|
|
518
641
|
void
|
519
|
-
Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
642
|
+
Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, unsigned int restartsInitiated) {
|
520
643
|
TRACE_POINT();
|
521
644
|
this_thread::disable_interruption di;
|
522
645
|
this_thread::disable_syscall_interruption dsi;
|
523
646
|
|
524
647
|
PoolPtr pool = getPool();
|
525
|
-
if (pool == NULL) {
|
526
|
-
return;
|
527
|
-
}
|
528
648
|
Pool::DebugSupportPtr debug = pool->debugSupport;
|
529
649
|
|
530
650
|
bool done = false;
|
531
651
|
while (!done) {
|
532
652
|
bool shouldFail = false;
|
533
|
-
if (debug != NULL) {
|
653
|
+
if (debug != NULL && debug->spawning) {
|
534
654
|
UPDATE_TRACE_POINT();
|
535
655
|
this_thread::restore_interruption ri(di);
|
536
656
|
this_thread::restore_syscall_interruption rsi(dsi);
|
@@ -562,6 +682,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
|
562
682
|
throw SpawnException("Simulated failure");
|
563
683
|
} else {
|
564
684
|
process = spawner->spawn(options);
|
685
|
+
process->setGroup(shared_from_this());
|
565
686
|
}
|
566
687
|
} catch (const thread_interrupted &) {
|
567
688
|
break;
|
@@ -572,23 +693,43 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
|
572
693
|
}
|
573
694
|
|
574
695
|
UPDATE_TRACE_POINT();
|
575
|
-
|
576
|
-
if (pool == NULL) {
|
577
|
-
break;
|
578
|
-
}
|
696
|
+
ScopeGuard guard(boost::bind(Process::maybeShutdown, process));
|
579
697
|
unique_lock<boost::mutex> lock(pool->syncher);
|
580
|
-
|
581
|
-
if (
|
698
|
+
|
699
|
+
if (!isAlive()) {
|
700
|
+
if (process != NULL) {
|
701
|
+
P_DEBUG("Group is being shut down so dropping process " <<
|
702
|
+
process->inspect() << " which we just spawned and exiting spawn loop");
|
703
|
+
} else {
|
704
|
+
P_DEBUG("The group is being shut down. A process failed "
|
705
|
+
"to be spawned anyway, so ignoring this error and exiting "
|
706
|
+
"spawn loop");
|
707
|
+
}
|
708
|
+
// We stop immediately because any previously assumed invariants
|
709
|
+
// may have been violated.
|
710
|
+
break;
|
711
|
+
} else if (restartsInitiated != this->restartsInitiated) {
|
712
|
+
if (process != NULL) {
|
713
|
+
P_DEBUG("A restart was issued for the group, so dropping process " <<
|
714
|
+
process->inspect() << " which we just spawned and exiting spawn loop");
|
715
|
+
} else {
|
716
|
+
P_DEBUG("A restart was issued for the group. A process failed "
|
717
|
+
"to be spawned anyway, so ignoring this error and exiting "
|
718
|
+
"spawn loop");
|
719
|
+
}
|
720
|
+
// We stop immediately because any previously assumed invariants
|
721
|
+
// may have been violated.
|
582
722
|
break;
|
583
723
|
}
|
584
724
|
|
585
725
|
verifyInvariants();
|
586
|
-
assert(m_spawning
|
587
|
-
|
726
|
+
assert(m_spawning);
|
727
|
+
|
588
728
|
UPDATE_TRACE_POINT();
|
589
729
|
vector<Callback> actions;
|
590
730
|
if (process != NULL) {
|
591
731
|
attach(process, actions);
|
732
|
+
guard.clear();
|
592
733
|
if (getWaitlist.empty()) {
|
593
734
|
pool->assignSessionsToGetWaiters(actions);
|
594
735
|
} else {
|
@@ -604,7 +745,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
|
604
745
|
if (enabledCount == 0) {
|
605
746
|
enableAllDisablingProcesses(actions);
|
606
747
|
}
|
607
|
-
assignExceptionToGetWaiters(exception, actions);
|
748
|
+
Pool::assignExceptionToGetWaiters(getWaitlist, exception, actions);
|
608
749
|
pool->assignSessionsToGetWaiters(actions);
|
609
750
|
done = true;
|
610
751
|
}
|
@@ -616,15 +757,10 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
|
616
757
|
|
617
758
|
done = done
|
618
759
|
|| ((unsigned long) enabledCount >= options.minProcesses && getWaitlist.empty())
|
619
|
-
|| pool->atFullCapacity(false)
|
620
|
-
|| m_restarting;
|
760
|
+
|| pool->atFullCapacity(false);
|
621
761
|
m_spawning = !done;
|
622
762
|
if (done) {
|
623
|
-
|
624
|
-
P_DEBUG("Spawn loop aborted because the group is being restarted");
|
625
|
-
} else {
|
626
|
-
P_DEBUG("Spawn loop done");
|
627
|
-
}
|
763
|
+
P_DEBUG("Spawn loop done");
|
628
764
|
} else {
|
629
765
|
P_DEBUG("Continue spawning");
|
630
766
|
}
|
@@ -637,7 +773,7 @@ Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options) {
|
|
637
773
|
UPDATE_TRACE_POINT();
|
638
774
|
}
|
639
775
|
|
640
|
-
if (debug != NULL) {
|
776
|
+
if (debug != NULL && debug->spawning) {
|
641
777
|
debug->debugger->send("Spawn loop done");
|
642
778
|
}
|
643
779
|
}
|
@@ -649,6 +785,7 @@ Group::shouldSpawn() const {
|
|
649
785
|
(unsigned long) enabledCount < options.minProcesses
|
650
786
|
|| (enabledCount > 0 && pqueue.top()->atFullCapacity())
|
651
787
|
)
|
788
|
+
&& isAlive()
|
652
789
|
&& !poolAtFullCapacity();
|
653
790
|
}
|
654
791
|
|
@@ -661,8 +798,11 @@ void
|
|
661
798
|
Group::restart(const Options &options) {
|
662
799
|
vector<Callback> actions;
|
663
800
|
|
801
|
+
assert(isAlive());
|
664
802
|
assert(!m_restarting);
|
665
803
|
P_DEBUG("Restarting group " << name);
|
804
|
+
// Tell the restarter thread to exit as soon as possible.
|
805
|
+
restartsInitiated++;
|
666
806
|
m_spawning = false;
|
667
807
|
m_restarting = true;
|
668
808
|
detachAll(actions);
|
@@ -692,15 +832,11 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
|
|
692
832
|
SpawnerPtr newSpawner = spawnerFactory->create(options);
|
693
833
|
SpawnerPtr oldSpawner;
|
694
834
|
|
695
|
-
// Standard resource management boilerplate stuff...
|
696
835
|
UPDATE_TRACE_POINT();
|
697
836
|
PoolPtr pool = getPool();
|
698
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
699
|
-
return;
|
700
|
-
}
|
701
837
|
|
702
838
|
Pool::DebugSupportPtr debug = pool->debugSupport;
|
703
|
-
if (debug != NULL) {
|
839
|
+
if (debug != NULL && debug->restarting) {
|
704
840
|
this_thread::restore_interruption ri(di);
|
705
841
|
this_thread::restore_syscall_interruption rsi(dsi);
|
706
842
|
this_thread::interruption_point();
|
@@ -708,9 +844,9 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
|
|
708
844
|
debug->messages->recv("Finish restarting");
|
709
845
|
}
|
710
846
|
|
711
|
-
|
712
|
-
|
713
|
-
|
847
|
+
ScopedLock l(pool->syncher);
|
848
|
+
if (!isAlive()) {
|
849
|
+
P_DEBUG("Group " << name << " is shutting down, so aborting restart");
|
714
850
|
return;
|
715
851
|
}
|
716
852
|
|
@@ -728,9 +864,93 @@ Group::finalizeRestart(GroupPtr self, Options options, SpawnerFactoryPtr spawner
|
|
728
864
|
if (!getWaitlist.empty()) {
|
729
865
|
spawn();
|
730
866
|
}
|
731
|
-
P_DEBUG("Restart of group " << name << " done");
|
732
867
|
verifyInvariants();
|
733
|
-
|
868
|
+
|
869
|
+
l.unlock();
|
870
|
+
oldSpawner.reset();
|
871
|
+
P_DEBUG("Restart of group " << name << " done");
|
872
|
+
if (debug != NULL && debug->restarting) {
|
873
|
+
debug->debugger->send("Restarting done");
|
874
|
+
}
|
875
|
+
}
|
876
|
+
|
877
|
+
void
|
878
|
+
Group::startCheckingDetachedProcesses(bool immediately) {
|
879
|
+
if (!detachedProcessesCheckerActive && !detachedProcesses.empty()) {
|
880
|
+
P_DEBUG("Starting detached processes checker");
|
881
|
+
getPool()->nonInterruptableThreads.create_thread(
|
882
|
+
boost::bind(&Group::detachedProcessesCheckerMain, this, shared_from_this()),
|
883
|
+
"Detached processes checker: " + name,
|
884
|
+
POOL_HELPER_THREAD_STACK_SIZE
|
885
|
+
);
|
886
|
+
detachedProcessesCheckerActive = true;
|
887
|
+
} else if (detachedProcessesCheckerActive && immediately) {
|
888
|
+
detachedProcessesCheckerCond.notify_all();
|
889
|
+
}
|
890
|
+
}
|
891
|
+
|
892
|
+
void
|
893
|
+
Group::detachedProcessesCheckerMain(GroupPtr self) {
|
894
|
+
TRACE_POINT();
|
895
|
+
PoolPtr pool = getPool();
|
896
|
+
|
897
|
+
while (!this_thread::interruption_requested()) {
|
898
|
+
unique_lock<boost::mutex> lock(pool->syncher);
|
899
|
+
assert(detachedProcessesCheckerActive);
|
900
|
+
|
901
|
+
if (getLifeStatus() == SHUT_DOWN) {
|
902
|
+
P_DEBUG("Stopping detached processes checker");
|
903
|
+
detachedProcessesCheckerActive = false;
|
904
|
+
break;
|
905
|
+
}
|
906
|
+
|
907
|
+
UPDATE_TRACE_POINT();
|
908
|
+
if (!detachedProcesses.empty()) {
|
909
|
+
P_TRACE(2, "Checking whether any detached processes have exited...");
|
910
|
+
ProcessList::iterator it = detachedProcesses.begin();
|
911
|
+
ProcessList::iterator end = detachedProcesses.end();
|
912
|
+
while (it != end) {
|
913
|
+
const ProcessPtr process = *it;
|
914
|
+
if (process->canBeShutDown()) {
|
915
|
+
it++;
|
916
|
+
P_DEBUG("Detached process " << process->inspect() << " has exited.");
|
917
|
+
shutdownAndRemoveProcess(process);
|
918
|
+
} else {
|
919
|
+
P_DEBUG("Detached process " << process->inspect() << " not yet exited. "
|
920
|
+
"sessions = " << process->sessions);
|
921
|
+
it++;
|
922
|
+
}
|
923
|
+
}
|
924
|
+
}
|
925
|
+
|
926
|
+
UPDATE_TRACE_POINT();
|
927
|
+
if (detachedProcesses.empty()) {
|
928
|
+
UPDATE_TRACE_POINT();
|
929
|
+
P_DEBUG("Stopping detached processes checker");
|
930
|
+
detachedProcessesCheckerActive = false;
|
931
|
+
|
932
|
+
vector<Callback> actions;
|
933
|
+
if (shutdownCanFinish()) {
|
934
|
+
UPDATE_TRACE_POINT();
|
935
|
+
finishShutdown(actions);
|
936
|
+
}
|
937
|
+
|
938
|
+
verifyInvariants();
|
939
|
+
verifyExpensiveInvariants();
|
940
|
+
lock.unlock();
|
941
|
+
UPDATE_TRACE_POINT();
|
942
|
+
runAllActions(actions);
|
943
|
+
break;
|
944
|
+
} else {
|
945
|
+
UPDATE_TRACE_POINT();
|
946
|
+
verifyInvariants();
|
947
|
+
verifyExpensiveInvariants();
|
948
|
+
}
|
949
|
+
|
950
|
+
UPDATE_TRACE_POINT();
|
951
|
+
detachedProcessesCheckerCond.timed_wait(lock,
|
952
|
+
posix_time::milliseconds(10));
|
953
|
+
}
|
734
954
|
}
|
735
955
|
|
736
956
|
bool
|
@@ -763,26 +983,21 @@ Group::generateSecret(const SuperGroupPtr &superGroup) {
|
|
763
983
|
}
|
764
984
|
|
765
985
|
|
766
|
-
// Thread-safe
|
767
986
|
SuperGroupPtr
|
768
987
|
Process::getSuperGroup() const {
|
769
|
-
|
770
|
-
|
771
|
-
return group->getSuperGroup();
|
772
|
-
} else {
|
773
|
-
return SuperGroupPtr();
|
774
|
-
}
|
988
|
+
assert(getLifeStatus() != SHUT_DOWN);
|
989
|
+
return getGroup()->getSuperGroup();
|
775
990
|
}
|
776
991
|
|
777
992
|
string
|
778
993
|
Process::inspect() const {
|
994
|
+
assert(getLifeStatus() != SHUT_DOWN);
|
779
995
|
stringstream result;
|
780
|
-
result << "(pid=";
|
781
|
-
result << pid;
|
996
|
+
result << "(pid=" << pid;
|
782
997
|
GroupPtr group = getGroup();
|
783
998
|
if (group != NULL) {
|
784
|
-
|
785
|
-
result << group->name;
|
999
|
+
// This Process hasn't been attached to a Group yet.
|
1000
|
+
result << ", group=" << group->name;
|
786
1001
|
}
|
787
1002
|
result << ")";
|
788
1003
|
return result.str();
|
@@ -791,68 +1006,113 @@ Process::inspect() const {
|
|
791
1006
|
|
792
1007
|
const string &
|
793
1008
|
Session::getConnectPassword() const {
|
794
|
-
return
|
1009
|
+
return getProcess()->connectPassword;
|
795
1010
|
}
|
796
1011
|
|
797
1012
|
pid_t
|
798
1013
|
Session::getPid() const {
|
799
|
-
return
|
1014
|
+
return getProcess()->pid;
|
800
1015
|
}
|
801
1016
|
|
802
1017
|
const string &
|
803
1018
|
Session::getGupid() const {
|
804
|
-
return
|
1019
|
+
return getProcess()->gupid;
|
805
1020
|
}
|
806
1021
|
|
807
1022
|
const GroupPtr
|
808
1023
|
Session::getGroup() const {
|
809
|
-
return
|
1024
|
+
return getProcess()->getGroup();
|
810
1025
|
}
|
811
1026
|
|
812
1027
|
void
|
813
1028
|
Session::requestOOBW() {
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
}
|
1029
|
+
ProcessPtr process = getProcess();
|
1030
|
+
assert(!process->isShutDown());
|
1031
|
+
process->getGroup()->requestOOBW(process);
|
818
1032
|
}
|
819
1033
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
:
|
825
|
-
|
826
|
-
|
1034
|
+
|
1035
|
+
PipeWatcher::DataCallback PipeWatcher::onData;
|
1036
|
+
|
1037
|
+
PipeWatcher::PipeWatcher(const FileDescriptor &_fd, const char *_name, pid_t _pid, bool _print)
|
1038
|
+
: fd(_fd),
|
1039
|
+
name(_name),
|
1040
|
+
pid(_pid),
|
1041
|
+
print(_print)
|
827
1042
|
{
|
828
|
-
|
829
|
-
watcher.set<PipeWatcher, &PipeWatcher::onReadable>(this);
|
1043
|
+
started = false;
|
830
1044
|
}
|
831
1045
|
|
832
|
-
|
833
|
-
|
1046
|
+
void
|
1047
|
+
PipeWatcher::initialize() {
|
1048
|
+
oxt::thread(boost::bind(threadMain, shared_from_this()),
|
1049
|
+
"PipeWatcher: PID " + toString(pid) + " " + name + ", fd " + toString(fd),
|
1050
|
+
POOL_HELPER_THREAD_STACK_SIZE);
|
834
1051
|
}
|
835
1052
|
|
836
1053
|
void
|
837
1054
|
PipeWatcher::start() {
|
838
|
-
|
839
|
-
|
1055
|
+
lock_guard<boost::mutex> lock(startSyncher);
|
1056
|
+
started = true;
|
1057
|
+
startCond.notify_all();
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
void
|
1061
|
+
PipeWatcher::threadMain(shared_ptr<PipeWatcher> self) {
|
1062
|
+
TRACE_POINT();
|
1063
|
+
self->threadMain();
|
840
1064
|
}
|
841
1065
|
|
842
1066
|
void
|
843
|
-
PipeWatcher::
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
1067
|
+
PipeWatcher::threadMain() {
|
1068
|
+
TRACE_POINT();
|
1069
|
+
{
|
1070
|
+
unique_lock<boost::mutex> lock(startSyncher);
|
1071
|
+
while (!started) {
|
1072
|
+
startCond.wait(lock);
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
UPDATE_TRACE_POINT();
|
1077
|
+
while (!this_thread::interruption_requested()) {
|
1078
|
+
char buf[1024 * 8];
|
1079
|
+
ssize_t ret;
|
1080
|
+
|
1081
|
+
UPDATE_TRACE_POINT();
|
1082
|
+
ret = syscalls::read(fd, buf, sizeof(buf));
|
1083
|
+
if (ret == 0) {
|
1084
|
+
break;
|
1085
|
+
} else if (ret == -1) {
|
1086
|
+
UPDATE_TRACE_POINT();
|
1087
|
+
if (errno == ECONNRESET) {
|
1088
|
+
break;
|
1089
|
+
} else if (errno != EAGAIN) {
|
1090
|
+
int e = errno;
|
1091
|
+
P_WARN("Cannot read from process " << pid << " " << name <<
|
1092
|
+
": " << strerror(e) << " (errno=" << e << ")");
|
1093
|
+
break;
|
1094
|
+
}
|
1095
|
+
} else if (ret == 1 && buf[0] == '\n') {
|
1096
|
+
UPDATE_TRACE_POINT();
|
1097
|
+
P_LOG(print ? LVL_INFO : LVL_DEBUG,
|
1098
|
+
"[App " << pid << " " << name << "] ");
|
1099
|
+
} else {
|
1100
|
+
UPDATE_TRACE_POINT();
|
1101
|
+
vector<StaticString> lines;
|
1102
|
+
ssize_t ret2 = ret;
|
1103
|
+
if (ret2 > 0 && buf[ret2 - 1] == '\n') {
|
1104
|
+
ret2--;
|
1105
|
+
}
|
1106
|
+
split(StaticString(buf, ret2), '\n', lines);
|
1107
|
+
foreach (const StaticString line, lines) {
|
1108
|
+
P_LOG(print ? LVL_INFO : LVL_DEBUG,
|
1109
|
+
"[App " << pid << " " << name << "] " << line);
|
1110
|
+
}
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
if (onData != NULL) {
|
1114
|
+
onData(buf, ret);
|
852
1115
|
}
|
853
|
-
} else if (fdToForwardTo != -1) {
|
854
|
-
// Don't care about errors.
|
855
|
-
write(fdToForwardTo, buf, ret);
|
856
1116
|
}
|
857
1117
|
}
|
858
1118
|
|