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) 2010
|
3
|
+
* Copyright (c) 2010-2013 Phusion
|
4
4
|
*
|
5
5
|
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
6
|
*
|
@@ -29,6 +29,7 @@
|
|
29
29
|
#include <vector>
|
30
30
|
#include <utility>
|
31
31
|
#include <boost/shared_array.hpp>
|
32
|
+
#include <ApplicationPool2/AppTypes.h>
|
32
33
|
#include <Account.h>
|
33
34
|
#include <UnionStation.h>
|
34
35
|
#include <Constants.h>
|
@@ -98,6 +99,7 @@ private:
|
|
98
99
|
result.push_back(&postexecChroot);
|
99
100
|
|
100
101
|
result.push_back(&ruby);
|
102
|
+
result.push_back(&python);
|
101
103
|
result.push_back(&loggingAgentAddress);
|
102
104
|
result.push_back(&loggingAgentUsername);
|
103
105
|
result.push_back(&loggingAgentPassword);
|
@@ -163,10 +165,10 @@ public:
|
|
163
165
|
|
164
166
|
/** The application's type, used for determining the command to invoke to
|
165
167
|
* spawn an application process as well as determining the startup file's
|
166
|
-
* filename.
|
167
|
-
* In case of the latter, 'startCommand' and
|
168
|
-
* be set) will dictate the startup command
|
169
|
-
* filename. */
|
168
|
+
* filename. It can be one of the app type names in AppType.cpp, or the
|
169
|
+
* empty string (default). In case of the latter, 'startCommand' and
|
170
|
+
* 'startupFile' (which MUST be set) will dictate the startup command
|
171
|
+
* and the startup file's filename. */
|
170
172
|
StaticString appType;
|
171
173
|
|
172
174
|
/** The command for spawning the application process. This is a list of
|
@@ -236,6 +238,12 @@ public:
|
|
236
238
|
* is a Ruby app.
|
237
239
|
*/
|
238
240
|
StaticString ruby;
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Path to the Python interpreter to use, in case the application to spawn
|
244
|
+
* is a Python app.
|
245
|
+
*/
|
246
|
+
StaticString python;
|
239
247
|
|
240
248
|
/**
|
241
249
|
* Any rights that the spawned application process may have. The SpawnManager
|
@@ -362,6 +370,7 @@ public:
|
|
362
370
|
spawnMethod = "smart";
|
363
371
|
defaultUser = "nobody";
|
364
372
|
ruby = "ruby";
|
373
|
+
python = "python";
|
365
374
|
rights = DEFAULT_BACKEND_ACCOUNT_RIGHTS;
|
366
375
|
debugger = false;
|
367
376
|
loadShellEnvvars = true;
|
@@ -498,6 +507,7 @@ public:
|
|
498
507
|
appendKeyValue (vec, "preexec_chroot", preexecChroot);
|
499
508
|
appendKeyValue (vec, "postexec_chroot", postexecChroot);
|
500
509
|
appendKeyValue (vec, "ruby", ruby);
|
510
|
+
appendKeyValue (vec, "python", python);
|
501
511
|
appendKeyValue (vec, "logging_agent_address", loggingAgentAddress);
|
502
512
|
appendKeyValue (vec, "logging_agent_username", loggingAgentUsername);
|
503
513
|
appendKeyValue (vec, "logging_agent_password", loggingAgentPassword);
|
@@ -528,39 +538,27 @@ public:
|
|
528
538
|
} else if (appType == "rack") {
|
529
539
|
return ruby + "\1" + resourceLocator.getHelperScriptsDir() + "/rack-loader.rb";
|
530
540
|
} else if (appType == "wsgi") {
|
531
|
-
return "
|
532
|
-
} else if (appType == "node") {
|
533
|
-
return "node\1" + resourceLocator.getHelperScriptsDir() + "/node-loader.js";
|
541
|
+
return python + "\1" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
|
534
542
|
} else {
|
535
543
|
return startCommand;
|
536
544
|
}
|
537
545
|
}
|
538
546
|
|
539
547
|
StaticString getStartupFile() const {
|
540
|
-
|
541
|
-
|
542
|
-
} else if (appType == "rack") {
|
543
|
-
return "config.ru";
|
544
|
-
} else if (appType == "wsgi") {
|
545
|
-
return "passenger_wsgi.py";
|
546
|
-
} else if (appType == "node") {
|
547
|
-
return "passenger_node.js";
|
548
|
-
} else {
|
548
|
+
const char *result = getAppTypeStartupFile(getAppType(appType));
|
549
|
+
if (result == NULL) {
|
549
550
|
return startupFile;
|
551
|
+
} else {
|
552
|
+
return result;
|
550
553
|
}
|
551
554
|
}
|
552
555
|
|
553
556
|
StaticString getProcessTitle() const {
|
554
|
-
|
555
|
-
|
556
|
-
} else if (appType == "rack") {
|
557
|
-
return "Passenger RackApp";
|
558
|
-
} else if (appType == "wsgi") {
|
559
|
-
return "Passenger WsgiApp";
|
560
|
-
} else if (appType == "node") {
|
561
|
-
return "Passenger NodeJsApp";
|
562
|
-
} else {
|
557
|
+
const char *result = getAppTypeProcessTitle(getAppType(appType));
|
558
|
+
if (result == NULL) {
|
563
559
|
return processTitle;
|
560
|
+
} else {
|
561
|
+
return result;
|
564
562
|
}
|
565
563
|
}
|
566
564
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2012 Phusion
|
3
|
+
* Copyright (c) 2012-2013 Phusion
|
4
4
|
*
|
5
5
|
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
6
|
*
|
@@ -27,9 +27,10 @@
|
|
27
27
|
|
28
28
|
#include <boost/shared_ptr.hpp>
|
29
29
|
#include <boost/enable_shared_from_this.hpp>
|
30
|
-
#include <
|
30
|
+
#include <boost/thread.hpp>
|
31
|
+
#include <boost/function.hpp>
|
32
|
+
#include <sys/types.h>
|
31
33
|
#include <FileDescriptor.h>
|
32
|
-
#include <ev++.h>
|
33
34
|
|
34
35
|
namespace Passenger {
|
35
36
|
namespace ApplicationPool2 {
|
@@ -37,19 +38,25 @@ namespace ApplicationPool2 {
|
|
37
38
|
using namespace boost;
|
38
39
|
|
39
40
|
|
41
|
+
/** A PipeWatcher lives until the file descriptor is closed. */
|
40
42
|
struct PipeWatcher: public enable_shared_from_this<PipeWatcher> {
|
41
|
-
|
43
|
+
// For unit tests.
|
44
|
+
typedef function<void (const char *data, unsigned int size)> DataCallback;
|
45
|
+
static DataCallback onData;
|
46
|
+
|
42
47
|
FileDescriptor fd;
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
const char *name;
|
49
|
+
pid_t pid;
|
50
|
+
bool print;
|
51
|
+
bool started;
|
52
|
+
boost::mutex startSyncher;
|
53
|
+
condition_variable startCond;
|
54
|
+
|
55
|
+
PipeWatcher(const FileDescriptor &_fd, const char *name, pid_t pid, bool _print);
|
56
|
+
void initialize();
|
51
57
|
void start();
|
52
|
-
void
|
58
|
+
static void threadMain(shared_ptr<PipeWatcher> self);
|
59
|
+
void threadMain();
|
53
60
|
};
|
54
61
|
|
55
62
|
typedef shared_ptr<PipeWatcher> PipeWatcherPtr;
|
@@ -36,6 +36,7 @@
|
|
36
36
|
#include <boost/make_shared.hpp>
|
37
37
|
#include <boost/function.hpp>
|
38
38
|
#include <boost/foreach.hpp>
|
39
|
+
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
39
40
|
#include <oxt/dynamic_thread_group.hpp>
|
40
41
|
#include <oxt/backtrace.hpp>
|
41
42
|
#include <ApplicationPool2/Common.h>
|
@@ -43,15 +44,15 @@
|
|
43
44
|
#include <ApplicationPool2/Group.h>
|
44
45
|
#include <ApplicationPool2/SuperGroup.h>
|
45
46
|
#include <ApplicationPool2/Session.h>
|
46
|
-
#include <ApplicationPool2/
|
47
|
+
#include <ApplicationPool2/SpawnerFactory.h>
|
47
48
|
#include <ApplicationPool2/Options.h>
|
48
49
|
#include <UnionStation.h>
|
49
50
|
#include <Logging.h>
|
50
51
|
#include <SafeLibev.h>
|
51
|
-
#include <AnsiColorConstants.h>
|
52
52
|
#include <Exceptions.h>
|
53
53
|
#include <RandomGenerator.h>
|
54
54
|
#include <Utils/Lock.h>
|
55
|
+
#include <Utils/AnsiColorConstants.h>
|
55
56
|
#include <Utils/SystemTime.h>
|
56
57
|
#include <Utils/MessagePassing.h>
|
57
58
|
#include <Utils/VariantMap.h>
|
@@ -94,6 +95,11 @@ public:
|
|
94
95
|
MessageBoxPtr debugger;
|
95
96
|
MessageBoxPtr messages;
|
96
97
|
|
98
|
+
// Choose aspects to debug.
|
99
|
+
bool restarting;
|
100
|
+
bool spawning;
|
101
|
+
bool superGroup;
|
102
|
+
|
97
103
|
// The following fields may only be accessed by Pool.
|
98
104
|
boost::mutex syncher;
|
99
105
|
unsigned int spawnLoopIteration;
|
@@ -101,6 +107,9 @@ public:
|
|
101
107
|
DebugSupport() {
|
102
108
|
debugger = make_shared<MessageBox>();
|
103
109
|
messages = make_shared<MessageBox>();
|
110
|
+
restarting = true;
|
111
|
+
spawning = true;
|
112
|
+
superGroup = false;
|
104
113
|
spawnLoopIteration = 0;
|
105
114
|
}
|
106
115
|
};
|
@@ -116,8 +125,7 @@ public:
|
|
116
125
|
unsigned int max;
|
117
126
|
unsigned long long maxIdleTime;
|
118
127
|
|
119
|
-
|
120
|
-
ev::timer analyticsCollectionTimer;
|
128
|
+
condition_variable garbageCollectionCond;
|
121
129
|
|
122
130
|
/**
|
123
131
|
* Code can register background threads in one of these dynamic thread groups
|
@@ -130,6 +138,12 @@ public:
|
|
130
138
|
*/
|
131
139
|
dynamic_thread_group interruptableThreads;
|
132
140
|
dynamic_thread_group nonInterruptableThreads;
|
141
|
+
|
142
|
+
enum LifeStatus {
|
143
|
+
ALIVE,
|
144
|
+
SHUTTING_DOWN,
|
145
|
+
SHUT_DOWN
|
146
|
+
} lifeStatus;
|
133
147
|
|
134
148
|
SuperGroupMap superGroups;
|
135
149
|
|
@@ -209,8 +223,11 @@ public:
|
|
209
223
|
}
|
210
224
|
|
211
225
|
void fullVerifyInvariants() const {
|
226
|
+
TRACE_POINT();
|
212
227
|
verifyInvariants();
|
228
|
+
UPDATE_TRACE_POINT();
|
213
229
|
verifyExpensiveInvariants();
|
230
|
+
UPDATE_TRACE_POINT();
|
214
231
|
StringMap<SuperGroupPtr>::const_iterator sg_it, sg_end = superGroups.end();
|
215
232
|
for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
|
216
233
|
pair<StaticString, SuperGroupPtr> p = *sg_it;
|
@@ -308,6 +325,19 @@ public:
|
|
308
325
|
|
309
326
|
getWaitlist = newWaitlist;
|
310
327
|
}
|
328
|
+
|
329
|
+
template<typename Queue>
|
330
|
+
static void assignExceptionToGetWaiters(Queue &getWaitlist,
|
331
|
+
const ExceptionPtr &exception,
|
332
|
+
vector<Callback> &postLockActions)
|
333
|
+
{
|
334
|
+
while (!getWaitlist.empty()) {
|
335
|
+
postLockActions.push_back(boost::bind(
|
336
|
+
getWaitlist.front().callback, SessionPtr(),
|
337
|
+
exception));
|
338
|
+
getWaitlist.pop();
|
339
|
+
}
|
340
|
+
}
|
311
341
|
|
312
342
|
void possiblySpawnMoreProcessesForExistingGroups() {
|
313
343
|
StringMap<SuperGroupPtr>::const_iterator sg_it, sg_end = superGroups.end();
|
@@ -343,14 +373,6 @@ public:
|
|
343
373
|
}
|
344
374
|
}
|
345
375
|
|
346
|
-
void migrateGroupGetWaitlistToPool(const GroupPtr &group) {
|
347
|
-
getWaitlist.reserve(getWaitlist.size() + group->getWaitlist.size());
|
348
|
-
while (!group->getWaitlist.empty()) {
|
349
|
-
getWaitlist.push_back(group->getWaitlist.front());
|
350
|
-
group->getWaitlist.pop();
|
351
|
-
}
|
352
|
-
}
|
353
|
-
|
354
376
|
void migrateSuperGroupGetWaitlistToPool(const SuperGroupPtr &superGroup) {
|
355
377
|
getWaitlist.reserve(getWaitlist.size() + superGroup->getWaitlist.size());
|
356
378
|
while (!superGroup->getWaitlist.empty()) {
|
@@ -364,24 +386,26 @@ public:
|
|
364
386
|
* the SuperGroup may have a non-empty getWaitlist so be sure to do
|
365
387
|
* something with it.
|
366
388
|
*
|
367
|
-
*
|
368
|
-
*
|
369
|
-
* call.
|
389
|
+
* Also, one of the post lock actions can potentially perform a long-running
|
390
|
+
* operation, so running them in a thread is advised.
|
370
391
|
*/
|
371
|
-
void forceDetachSuperGroup(SuperGroupPtr superGroup,
|
392
|
+
void forceDetachSuperGroup(const SuperGroupPtr &superGroup,
|
393
|
+
vector<Callback> &postLockActions,
|
394
|
+
const SuperGroup::ShutdownCallback &callback)
|
395
|
+
{
|
396
|
+
const SuperGroupPtr sp = superGroup; // Prevent premature destruction.
|
372
397
|
bool removed = superGroups.remove(superGroup->name);
|
373
398
|
assert(removed);
|
374
399
|
(void) removed; // Shut up compiler warning.
|
375
|
-
superGroup->destroy(postLockActions,
|
376
|
-
superGroup->setPool(PoolPtr());
|
400
|
+
superGroup->destroy(false, postLockActions, callback);
|
377
401
|
}
|
378
402
|
|
379
403
|
bool detachProcessUnlocked(const ProcessPtr &process, vector<Callback> &postLockActions) {
|
380
|
-
|
381
|
-
if (group != NULL && group->getPool().get() == this) {
|
404
|
+
if (OXT_LIKELY(process->isAlive())) {
|
382
405
|
verifyInvariants();
|
383
406
|
|
384
|
-
|
407
|
+
const GroupPtr group = process->getGroup();
|
408
|
+
const SuperGroupPtr superGroup = group->getSuperGroup();
|
385
409
|
assert(superGroup->state != SuperGroup::INITIALIZING);
|
386
410
|
assert(superGroup->getWaitlist.empty());
|
387
411
|
|
@@ -431,6 +455,17 @@ public:
|
|
431
455
|
}
|
432
456
|
}
|
433
457
|
|
458
|
+
struct DetachSuperGroupWaitTicket {
|
459
|
+
boost::mutex syncher;
|
460
|
+
condition_variable cond;
|
461
|
+
SuperGroup::ShutdownResult result;
|
462
|
+
bool done;
|
463
|
+
|
464
|
+
DetachSuperGroupWaitTicket() {
|
465
|
+
done = false;
|
466
|
+
}
|
467
|
+
};
|
468
|
+
|
434
469
|
struct DisableWaitTicket {
|
435
470
|
boost::mutex syncher;
|
436
471
|
condition_variable cond;
|
@@ -442,10 +477,26 @@ public:
|
|
442
477
|
}
|
443
478
|
};
|
444
479
|
|
480
|
+
static void syncDetachSuperGroupCallback(SuperGroup::ShutdownResult result,
|
481
|
+
shared_ptr<DetachSuperGroupWaitTicket> ticket)
|
482
|
+
{
|
483
|
+
LockGuard l(ticket->syncher);
|
484
|
+
ticket->done = true;
|
485
|
+
ticket->result = result;
|
486
|
+
ticket->cond.notify_one();
|
487
|
+
}
|
488
|
+
|
489
|
+
static void waitDetachSuperGroupCallback(shared_ptr<DetachSuperGroupWaitTicket> ticket) {
|
490
|
+
ScopedLock l(ticket->syncher);
|
491
|
+
while (!ticket->done) {
|
492
|
+
ticket->cond.wait(l);
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
445
496
|
static void syncDisableProcessCallback(const ProcessPtr &process, DisableResult result,
|
446
497
|
shared_ptr<DisableWaitTicket> ticket)
|
447
498
|
{
|
448
|
-
|
499
|
+
LockGuard l(ticket->syncher);
|
449
500
|
ticket->done = true;
|
450
501
|
ticket->result = result;
|
451
502
|
ticket->cond.notify_one();
|
@@ -465,17 +516,37 @@ public:
|
|
465
516
|
return superGroups.get(options.getAppGroupName()).get();
|
466
517
|
}
|
467
518
|
|
468
|
-
void garbageCollect(
|
469
|
-
|
519
|
+
static void garbageCollect(PoolPtr self) {
|
520
|
+
TRACE_POINT();
|
521
|
+
{
|
522
|
+
ScopedLock lock(self->syncher);
|
523
|
+
self->garbageCollectionCond.timed_wait(lock,
|
524
|
+
posix_time::seconds(5));
|
525
|
+
}
|
526
|
+
while (!this_thread::interruption_requested()) {
|
527
|
+
try {
|
528
|
+
UPDATE_TRACE_POINT();
|
529
|
+
unsigned long long sleepTime = self->realGarbageCollect();
|
530
|
+
ScopedLock lock(self->syncher);
|
531
|
+
self->garbageCollectionCond.timed_wait(lock,
|
532
|
+
posix_time::microseconds(sleepTime));
|
533
|
+
} catch (const thread_interrupted &) {
|
534
|
+
break;
|
535
|
+
} catch (const tracable_exception &e) {
|
536
|
+
P_WARN("ERROR: " << e.what() << "\n Backtrace:\n" << e.backtrace());
|
537
|
+
}
|
538
|
+
}
|
539
|
+
}
|
540
|
+
|
541
|
+
unsigned long long realGarbageCollect() {
|
470
542
|
TRACE_POINT();
|
471
543
|
ScopedLock lock(syncher);
|
472
544
|
SuperGroupMap::iterator it, end = superGroups.end();
|
473
|
-
vector<SuperGroupPtr> superGroupsToDetach;
|
474
545
|
vector<Callback> actions;
|
475
546
|
unsigned long long now = SystemTime::getUsec();
|
476
547
|
unsigned long long nextGcRunTime = 0;
|
477
548
|
|
478
|
-
P_DEBUG("Garbage collection time");
|
549
|
+
P_DEBUG("Garbage collection time...");
|
479
550
|
verifyInvariants();
|
480
551
|
|
481
552
|
// For all supergroups and groups...
|
@@ -521,7 +592,7 @@ public:
|
|
521
592
|
group->options.getMaxPreloaderIdleTime() * 1000000;
|
522
593
|
if (now >= spawnerGcTime) {
|
523
594
|
P_DEBUG("Garbage collect idle spawner: group=" << group->name);
|
524
|
-
group->
|
595
|
+
group->cleanupSpawner(actions);
|
525
596
|
} else if (nextGcRunTime == 0
|
526
597
|
|| spawnerGcTime < nextGcRunTime) {
|
527
598
|
nextGcRunTime = spawnerGcTime;
|
@@ -529,36 +600,27 @@ public:
|
|
529
600
|
}
|
530
601
|
}
|
531
602
|
|
532
|
-
// ...remove entire supergroup if it has become garbage
|
533
|
-
// collectable after detaching idle processes.
|
534
|
-
if (superGroup->garbageCollectable(now)) {
|
535
|
-
superGroupsToDetach.push_back(superGroup);
|
536
|
-
}
|
537
|
-
|
538
603
|
superGroup->verifyInvariants();
|
539
604
|
}
|
540
605
|
|
541
|
-
vector<SuperGroupPtr>::const_iterator it2;
|
542
|
-
for (it2 = superGroupsToDetach.begin(); it2 != superGroupsToDetach.end(); it2++) {
|
543
|
-
P_DEBUG("Garbage collect SuperGroup: " << (*it2)->inspect());
|
544
|
-
detachSuperGroup(*it2, false, &actions);
|
545
|
-
}
|
546
|
-
|
547
606
|
verifyInvariants();
|
607
|
+
lock.unlock();
|
548
608
|
|
549
609
|
// Schedule next garbage collection run.
|
550
|
-
|
610
|
+
unsigned long long sleepTime;
|
551
611
|
if (nextGcRunTime == 0 || nextGcRunTime <= now) {
|
552
|
-
|
612
|
+
sleepTime = maxIdleTime;
|
553
613
|
} else {
|
554
|
-
|
614
|
+
sleepTime = nextGcRunTime - now;
|
555
615
|
}
|
556
616
|
P_DEBUG("Garbage collection done; next garbage collect in " <<
|
557
|
-
std::fixed << std::setprecision(3) <<
|
617
|
+
std::fixed << std::setprecision(3) << (sleepTime / 1000000.0) << " sec");
|
558
618
|
|
559
|
-
|
619
|
+
UPDATE_TRACE_POINT();
|
560
620
|
runAllActions(actions);
|
561
|
-
|
621
|
+
UPDATE_TRACE_POINT();
|
622
|
+
actions.clear();
|
623
|
+
return sleepTime;
|
562
624
|
}
|
563
625
|
|
564
626
|
struct ProcessAnalyticsLogEntry {
|
@@ -569,22 +631,56 @@ public:
|
|
569
631
|
|
570
632
|
typedef shared_ptr<ProcessAnalyticsLogEntry> ProcessAnalyticsLogEntryPtr;
|
571
633
|
|
572
|
-
void collectAnalytics(
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
634
|
+
static void collectAnalytics(PoolPtr self) {
|
635
|
+
TRACE_POINT();
|
636
|
+
syscalls::usleep(3000000);
|
637
|
+
while (!this_thread::interruption_requested()) {
|
638
|
+
try {
|
639
|
+
UPDATE_TRACE_POINT();
|
640
|
+
unsigned long long sleepTime = self->realCollectAnalytics();
|
641
|
+
syscalls::usleep(sleepTime);
|
642
|
+
} catch (const thread_interrupted &) {
|
643
|
+
break;
|
644
|
+
} catch (const tracable_exception &e) {
|
645
|
+
P_WARN("ERROR: " << e.what() << "\n Backtrace:\n" << e.backtrace());
|
646
|
+
}
|
647
|
+
}
|
648
|
+
}
|
649
|
+
|
650
|
+
static void collectPids(const ProcessList &processes, vector<pid_t> &pids) {
|
651
|
+
foreach (const ProcessPtr &process, processes) {
|
652
|
+
pids.push_back(process->pid);
|
653
|
+
}
|
654
|
+
}
|
655
|
+
|
656
|
+
static void updateProcessMetrics(const ProcessList &processes,
|
657
|
+
const ProcessMetricMap &allMetrics,
|
658
|
+
vector<ProcessPtr> &processesToDetach)
|
659
|
+
{
|
660
|
+
foreach (const ProcessPtr &process, processes) {
|
661
|
+
ProcessMetricMap::const_iterator metrics_it =
|
662
|
+
allMetrics.find(process->pid);
|
663
|
+
if (metrics_it != allMetrics.end()) {
|
664
|
+
process->metrics = metrics_it->second;
|
665
|
+
// If the process is missing from 'allMetrics' then either 'ps'
|
666
|
+
// failed or the process really is gone. We double check by sending
|
667
|
+
// it a signal.
|
668
|
+
} else if (!process->dummy && !process->osProcessExists()) {
|
669
|
+
P_WARN("Process " << process->inspect() << " no longer exists! "
|
670
|
+
"Detaching it from the pool.");
|
671
|
+
processesToDetach.push_back(process);
|
672
|
+
}
|
577
673
|
}
|
578
674
|
}
|
579
675
|
|
580
|
-
|
581
|
-
PoolPtr self = shared_from_this(); // Keep pool object alive.
|
676
|
+
unsigned long long realCollectAnalytics() {
|
582
677
|
TRACE_POINT();
|
583
678
|
this_thread::disable_interruption di;
|
584
679
|
this_thread::disable_syscall_interruption dsi;
|
585
680
|
vector<pid_t> pids;
|
586
681
|
unsigned int max;
|
587
682
|
|
683
|
+
P_DEBUG("Analytics collection time...");
|
588
684
|
// Collect all the PIDs.
|
589
685
|
{
|
590
686
|
UPDATE_TRACE_POINT();
|
@@ -603,24 +699,9 @@ public:
|
|
603
699
|
|
604
700
|
for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
|
605
701
|
const GroupPtr &group = *g_it;
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
const ProcessPtr &process = *p_it;
|
610
|
-
pids.push_back(process->pid);
|
611
|
-
}
|
612
|
-
|
613
|
-
p_end = group->disablingProcesses.end();
|
614
|
-
for (p_it = group->disablingProcesses.begin(); p_it != p_end; p_it++) {
|
615
|
-
const ProcessPtr &process = *p_it;
|
616
|
-
pids.push_back(process->pid);
|
617
|
-
}
|
618
|
-
|
619
|
-
p_end = group->disabledProcesses.end();
|
620
|
-
for (p_it = group->disabledProcesses.begin(); p_it != p_end; p_it++) {
|
621
|
-
const ProcessPtr &process = *p_it;
|
622
|
-
pids.push_back(process->pid);
|
623
|
-
}
|
702
|
+
collectPids(group->enabledProcesses, pids);
|
703
|
+
collectPids(group->disablingProcesses, pids);
|
704
|
+
collectPids(group->disabledProcesses, pids);
|
624
705
|
}
|
625
706
|
}
|
626
707
|
}
|
@@ -639,6 +720,8 @@ public:
|
|
639
720
|
{
|
640
721
|
UPDATE_TRACE_POINT();
|
641
722
|
vector<ProcessAnalyticsLogEntryPtr> logEntries;
|
723
|
+
vector<ProcessPtr> processesToDetach;
|
724
|
+
vector<Callback> actions;
|
642
725
|
ScopedLock l(syncher);
|
643
726
|
SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
|
644
727
|
|
@@ -649,36 +732,10 @@ public:
|
|
649
732
|
|
650
733
|
for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
|
651
734
|
const GroupPtr &group = *g_it;
|
652
|
-
ProcessList::iterator p_it, p_end = group->enabledProcesses.end();
|
653
|
-
|
654
|
-
for (p_it = group->enabledProcesses.begin(); p_it != p_end; p_it++) {
|
655
|
-
ProcessPtr &process = *p_it;
|
656
|
-
ProcessMetricMap::const_iterator metrics_it =
|
657
|
-
allMetrics.find(process->pid);
|
658
|
-
if (metrics_it != allMetrics.end()) {
|
659
|
-
process->metrics = metrics_it->second;
|
660
|
-
}
|
661
|
-
}
|
662
735
|
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
ProcessMetricMap::const_iterator metrics_it =
|
667
|
-
allMetrics.find(process->pid);
|
668
|
-
if (metrics_it != allMetrics.end()) {
|
669
|
-
process->metrics = metrics_it->second;
|
670
|
-
}
|
671
|
-
}
|
672
|
-
|
673
|
-
p_end = group->disabledProcesses.end();
|
674
|
-
for (p_it = group->disabledProcesses.begin(); p_it != p_end; p_it++) {
|
675
|
-
ProcessPtr &process = *p_it;
|
676
|
-
ProcessMetricMap::const_iterator metrics_it =
|
677
|
-
allMetrics.find(process->pid);
|
678
|
-
if (metrics_it != allMetrics.end()) {
|
679
|
-
process->metrics = metrics_it->second;
|
680
|
-
}
|
681
|
-
}
|
736
|
+
updateProcessMetrics(group->enabledProcesses, allMetrics, processesToDetach);
|
737
|
+
updateProcessMetrics(group->disablingProcesses, allMetrics, processesToDetach);
|
738
|
+
updateProcessMetrics(group->disabledProcesses, allMetrics, processesToDetach);
|
682
739
|
|
683
740
|
// Log to Union Station.
|
684
741
|
if (group->options.analytics && loggerFactory != NULL) {
|
@@ -694,7 +751,15 @@ public:
|
|
694
751
|
}
|
695
752
|
}
|
696
753
|
|
754
|
+
UPDATE_TRACE_POINT();
|
755
|
+
foreach (const ProcessPtr process, processesToDetach) {
|
756
|
+
detachProcessUnlocked(process, actions);
|
757
|
+
}
|
758
|
+
UPDATE_TRACE_POINT();
|
759
|
+
processesToDetach.clear();
|
760
|
+
|
697
761
|
l.unlock();
|
762
|
+
UPDATE_TRACE_POINT();
|
698
763
|
while (!logEntries.empty()) {
|
699
764
|
ProcessAnalyticsLogEntryPtr entry = logEntries.back();
|
700
765
|
logEntries.pop_back();
|
@@ -702,16 +767,24 @@ public:
|
|
702
767
|
"processes", entry->key);
|
703
768
|
logger->message(entry->data.str());
|
704
769
|
}
|
770
|
+
|
771
|
+
UPDATE_TRACE_POINT();
|
772
|
+
runAllActions(actions);
|
773
|
+
UPDATE_TRACE_POINT();
|
774
|
+
// Run destructors with updated trace point.
|
775
|
+
actions.clear();
|
705
776
|
}
|
706
777
|
|
707
778
|
end:
|
708
779
|
// Sleep for about 4 seconds, aligned to seconds boundary
|
709
780
|
// for saving power on laptops.
|
710
|
-
ev_now_update(libev->getLoop());
|
711
781
|
unsigned long long currentTime = SystemTime::getUsec();
|
712
782
|
unsigned long long deadline =
|
713
783
|
roundUp<unsigned long long>(currentTime, 1000000) + 4000000;
|
714
|
-
|
784
|
+
P_DEBUG("Analytics collection done; next analytics collection in " <<
|
785
|
+
std::fixed << std::setprecision(3) << ((deadline - currentTime) / 1000000.0) <<
|
786
|
+
" sec");
|
787
|
+
return deadline - currentTime;
|
715
788
|
}
|
716
789
|
|
717
790
|
SuperGroupPtr createSuperGroup(const Options &options) {
|
@@ -752,16 +825,10 @@ public:
|
|
752
825
|
this->randomGenerator = make_shared<RandomGenerator>();
|
753
826
|
}
|
754
827
|
|
828
|
+
lifeStatus = ALIVE;
|
755
829
|
max = 6;
|
756
830
|
maxIdleTime = 60 * 1000000;
|
757
831
|
|
758
|
-
garbageCollectionTimer.set<Pool, &Pool::garbageCollect>(this);
|
759
|
-
garbageCollectionTimer.set(maxIdleTime / 1000000.0, 0.0);
|
760
|
-
libev->start(garbageCollectionTimer);
|
761
|
-
analyticsCollectionTimer.set<Pool, &Pool::collectAnalytics>(this);
|
762
|
-
analyticsCollectionTimer.set(3.0, 0.0);
|
763
|
-
libev->start(analyticsCollectionTimer);
|
764
|
-
|
765
832
|
// The following code only serve to instantiate certain inline methods
|
766
833
|
// so that they can be invoked from gdb.
|
767
834
|
(void) SuperGroupPtr().get();
|
@@ -771,8 +838,23 @@ public:
|
|
771
838
|
}
|
772
839
|
|
773
840
|
~Pool() {
|
774
|
-
|
775
|
-
|
841
|
+
if (lifeStatus != SHUT_DOWN) {
|
842
|
+
P_BUG("You must call Pool::destroy() before actually destroying the Pool object!");
|
843
|
+
}
|
844
|
+
}
|
845
|
+
|
846
|
+
void initialize() {
|
847
|
+
LockGuard l(syncher);
|
848
|
+
interruptableThreads.create_thread(
|
849
|
+
boost::bind(collectAnalytics, shared_from_this()),
|
850
|
+
"Pool analytics collector",
|
851
|
+
POOL_HELPER_THREAD_STACK_SIZE
|
852
|
+
);
|
853
|
+
interruptableThreads.create_thread(
|
854
|
+
boost::bind(garbageCollect, shared_from_this()),
|
855
|
+
"Pool garbage collector",
|
856
|
+
POOL_HELPER_THREAD_STACK_SIZE
|
857
|
+
);
|
776
858
|
}
|
777
859
|
|
778
860
|
void initDebugging() {
|
@@ -782,34 +864,29 @@ public:
|
|
782
864
|
|
783
865
|
void destroy() {
|
784
866
|
TRACE_POINT();
|
785
|
-
|
786
|
-
|
867
|
+
ScopedLock lock(syncher);
|
868
|
+
assert(lifeStatus == ALIVE);
|
869
|
+
|
870
|
+
lifeStatus = SHUTTING_DOWN;
|
871
|
+
|
872
|
+
while (!superGroups.empty()) {
|
873
|
+
string name = superGroups.begin()->second->name;
|
874
|
+
lock.unlock();
|
875
|
+
detachSuperGroupByName(name);
|
876
|
+
lock.lock();
|
877
|
+
}
|
787
878
|
|
788
879
|
UPDATE_TRACE_POINT();
|
880
|
+
lock.unlock();
|
789
881
|
interruptableThreads.interrupt_and_join_all();
|
790
882
|
nonInterruptableThreads.join_all();
|
883
|
+
lock.lock();
|
884
|
+
|
885
|
+
lifeStatus = SHUT_DOWN;
|
791
886
|
|
792
887
|
UPDATE_TRACE_POINT();
|
793
|
-
ScopedLock l(syncher);
|
794
|
-
SuperGroupMap::iterator it;
|
795
|
-
vector<SuperGroupPtr>::iterator it2;
|
796
|
-
vector<SuperGroupPtr> superGroupsToDetach;
|
797
|
-
vector<Callback> actions;
|
798
|
-
for (it = superGroups.begin(); it != superGroups.end(); it++) {
|
799
|
-
superGroupsToDetach.push_back(it->second);
|
800
|
-
}
|
801
|
-
for (it2 = superGroupsToDetach.begin(); it2 != superGroupsToDetach.end(); it2++) {
|
802
|
-
detachSuperGroup(*it2, false, &actions);
|
803
|
-
}
|
804
|
-
|
805
888
|
verifyInvariants();
|
806
889
|
verifyExpensiveInvariants();
|
807
|
-
l.unlock();
|
808
|
-
runAllActions(actions);
|
809
|
-
|
810
|
-
// detachSuperGroup() may launch additional threads, so wait for them.
|
811
|
-
interruptableThreads.interrupt_and_join_all();
|
812
|
-
nonInterruptableThreads.join_all();
|
813
890
|
}
|
814
891
|
|
815
892
|
// 'lockNow == false' may only be used during unit tests. Normally we
|
@@ -817,6 +894,7 @@ public:
|
|
817
894
|
void asyncGet(const Options &options, const GetCallback &callback, bool lockNow = true) {
|
818
895
|
DynamicScopedLock lock(syncher, lockNow);
|
819
896
|
|
897
|
+
assert(lifeStatus == ALIVE);
|
820
898
|
verifyInvariants();
|
821
899
|
P_TRACE(2, "asyncGet(appRoot=" << options.appRoot << ")");
|
822
900
|
|
@@ -965,7 +1043,7 @@ public:
|
|
965
1043
|
createSuperGroup(options);
|
966
1044
|
}
|
967
1045
|
}
|
968
|
-
return get(options2, &ticket)->
|
1046
|
+
return get(options2, &ticket)->getGroup();
|
969
1047
|
}
|
970
1048
|
|
971
1049
|
void setMax(unsigned int max) {
|
@@ -996,18 +1074,10 @@ public:
|
|
996
1074
|
}
|
997
1075
|
}
|
998
1076
|
|
999
|
-
void activateNewMaxIdleTime() {
|
1000
|
-
LockGuard l(syncher);
|
1001
|
-
garbageCollectionTimer.stop();
|
1002
|
-
garbageCollectionTimer.start(maxIdleTime / 1000000.0, 0.0);
|
1003
|
-
}
|
1004
|
-
|
1005
1077
|
void setMaxIdleTime(unsigned long long value) {
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
}
|
1010
|
-
libev->run(boost::bind(&Pool::activateNewMaxIdleTime, this));
|
1078
|
+
LockGuard l(syncher);
|
1079
|
+
maxIdleTime = value;
|
1080
|
+
garbageCollectionCond.notify_all();
|
1011
1081
|
}
|
1012
1082
|
|
1013
1083
|
unsigned int utilization(bool lock = true) const {
|
@@ -1062,6 +1132,11 @@ public:
|
|
1062
1132
|
}
|
1063
1133
|
return result;
|
1064
1134
|
}
|
1135
|
+
|
1136
|
+
unsigned int getSuperGroupCount() const {
|
1137
|
+
LockGuard l(syncher);
|
1138
|
+
return superGroups.size();
|
1139
|
+
}
|
1065
1140
|
|
1066
1141
|
SuperGroupPtr findSuperGroupBySecret(const string &secret, bool lock = true) const {
|
1067
1142
|
DynamicScopedLock l(syncher, lock);
|
@@ -1086,21 +1161,29 @@ public:
|
|
1086
1161
|
}
|
1087
1162
|
return ProcessPtr();
|
1088
1163
|
}
|
1089
|
-
|
1090
|
-
bool
|
1091
|
-
|
1092
|
-
|
1093
|
-
assert(lock || postLockActions != NULL);
|
1094
|
-
DynamicScopedLock l(syncher, lock);
|
1164
|
+
|
1165
|
+
bool detachSuperGroupByName(const string &name) {
|
1166
|
+
TRACE_POINT();
|
1167
|
+
ScopedLock l(syncher);
|
1095
1168
|
|
1096
|
-
|
1169
|
+
SuperGroupPtr superGroup = superGroups.get(name);
|
1170
|
+
if (OXT_LIKELY(superGroup != NULL)) {
|
1097
1171
|
if (OXT_LIKELY(superGroups.get(superGroup->name) != NULL)) {
|
1172
|
+
UPDATE_TRACE_POINT();
|
1098
1173
|
verifyInvariants();
|
1099
1174
|
verifyExpensiveInvariants();
|
1100
1175
|
|
1101
1176
|
vector<Callback> actions;
|
1102
|
-
|
1103
|
-
|
1177
|
+
shared_ptr<DetachSuperGroupWaitTicket> ticket =
|
1178
|
+
make_shared<DetachSuperGroupWaitTicket>();
|
1179
|
+
ExceptionPtr exception = copyException(
|
1180
|
+
GetAbortedException("The containg SuperGroup was detached."));
|
1181
|
+
|
1182
|
+
forceDetachSuperGroup(superGroup, actions,
|
1183
|
+
boost::bind(syncDetachSuperGroupCallback, _1, ticket));
|
1184
|
+
assignExceptionToGetWaiters(superGroup->getWaitlist,
|
1185
|
+
exception, actions);
|
1186
|
+
#if 0
|
1104
1187
|
/* If this SuperGroup had get waiters, either
|
1105
1188
|
* on itself or in one of its groups, then we must
|
1106
1189
|
* reprocess them immediately. Detaching such a
|
@@ -1108,26 +1191,25 @@ public:
|
|
1108
1191
|
*/
|
1109
1192
|
migrateSuperGroupGetWaitlistToPool(superGroup);
|
1110
1193
|
|
1194
|
+
UPDATE_TRACE_POINT();
|
1111
1195
|
assignSessionsToGetWaiters(actions);
|
1196
|
+
#endif
|
1112
1197
|
possiblySpawnMoreProcessesForExistingGroups();
|
1113
1198
|
|
1114
1199
|
verifyInvariants();
|
1115
1200
|
verifyExpensiveInvariants();
|
1116
1201
|
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
postLockActions->push_back(actions[i]);
|
1127
|
-
}
|
1202
|
+
l.unlock();
|
1203
|
+
UPDATE_TRACE_POINT();
|
1204
|
+
runAllActions(actions);
|
1205
|
+
actions.clear();
|
1206
|
+
|
1207
|
+
UPDATE_TRACE_POINT();
|
1208
|
+
ScopedLock l2(ticket->syncher);
|
1209
|
+
while (!ticket->done) {
|
1210
|
+
ticket->cond.wait(l2);
|
1128
1211
|
}
|
1129
|
-
|
1130
|
-
return true;
|
1212
|
+
return ticket->result == SuperGroup::SUCCESS;
|
1131
1213
|
} else {
|
1132
1214
|
return false;
|
1133
1215
|
}
|
@@ -1136,11 +1218,14 @@ public:
|
|
1136
1218
|
}
|
1137
1219
|
}
|
1138
1220
|
|
1139
|
-
bool
|
1140
|
-
|
1221
|
+
bool detachSuperGroupBySecret(const string &superGroupSecret) {
|
1222
|
+
ScopedLock l(syncher);
|
1141
1223
|
SuperGroupPtr superGroup = findSuperGroupBySecret(superGroupSecret, false);
|
1142
1224
|
if (superGroup != NULL) {
|
1143
|
-
|
1225
|
+
string name = superGroup->name;
|
1226
|
+
superGroup.reset();
|
1227
|
+
l.unlock();
|
1228
|
+
return detachSuperGroupByName(name);
|
1144
1229
|
} else {
|
1145
1230
|
return false;
|
1146
1231
|
}
|
@@ -1174,23 +1259,63 @@ public:
|
|
1174
1259
|
DisableResult disableProcess(const string &gupid) {
|
1175
1260
|
ScopedLock l(syncher);
|
1176
1261
|
ProcessPtr process = findProcessByGupid(gupid, false);
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
ticket->
|
1262
|
+
if (process != NULL) {
|
1263
|
+
GroupPtr group = process->getGroup();
|
1264
|
+
// Must be a shared_ptr to be interruption-safe.
|
1265
|
+
shared_ptr<DisableWaitTicket> ticket = make_shared<DisableWaitTicket>();
|
1266
|
+
DisableResult result = group->disable(process,
|
1267
|
+
boost::bind(syncDisableProcessCallback, _1, _2, ticket));
|
1268
|
+
group->verifyInvariants();
|
1269
|
+
group->verifyExpensiveInvariants();
|
1270
|
+
if (result == DR_DEFERRED) {
|
1271
|
+
l.unlock();
|
1272
|
+
ScopedLock l2(ticket->syncher);
|
1273
|
+
while (!ticket->done) {
|
1274
|
+
ticket->cond.wait(l2);
|
1275
|
+
}
|
1276
|
+
return ticket->result;
|
1277
|
+
} else {
|
1278
|
+
return result;
|
1189
1279
|
}
|
1190
|
-
return ticket->result;
|
1191
1280
|
} else {
|
1192
|
-
return
|
1281
|
+
return DR_NOOP;
|
1282
|
+
}
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
unsigned int restartGroupsByAppRoot(const string &appRoot) {
|
1286
|
+
ScopedLock l(syncher);
|
1287
|
+
SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
|
1288
|
+
unsigned int result = 0;
|
1289
|
+
|
1290
|
+
for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
|
1291
|
+
const SuperGroupPtr &superGroup = sg_it->second;
|
1292
|
+
foreach (const GroupPtr &group, superGroup->groups) {
|
1293
|
+
if (group->options.appRoot == appRoot) {
|
1294
|
+
result++;
|
1295
|
+
if (!group->restarting()) {
|
1296
|
+
group->restart(group->options);
|
1297
|
+
}
|
1298
|
+
}
|
1299
|
+
}
|
1193
1300
|
}
|
1301
|
+
|
1302
|
+
return result;
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
unsigned int restartSuperGroupsByAppRoot(const string &appRoot) {
|
1306
|
+
ScopedLock l(syncher);
|
1307
|
+
SuperGroupMap::iterator sg_it, sg_end = superGroups.end();
|
1308
|
+
unsigned int result = 0;
|
1309
|
+
|
1310
|
+
for (sg_it = superGroups.begin(); sg_it != sg_end; sg_it++) {
|
1311
|
+
const SuperGroupPtr &superGroup = sg_it->second;
|
1312
|
+
if (superGroup->options.appRoot == appRoot) {
|
1313
|
+
result++;
|
1314
|
+
superGroup->restart(superGroup->options);
|
1315
|
+
}
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
return result;
|
1194
1319
|
}
|
1195
1320
|
|
1196
1321
|
/**
|
@@ -1238,6 +1363,9 @@ public:
|
|
1238
1363
|
if (group != NULL) {
|
1239
1364
|
result << group->name << ":" << endl;
|
1240
1365
|
result << " App root: " << group->options.appRoot << endl;
|
1366
|
+
if (group->restarting()) {
|
1367
|
+
result << " (restarting...)" << endl;
|
1368
|
+
}
|
1241
1369
|
if (group->spawning()) {
|
1242
1370
|
result << " (spawning new process...)" << endl;
|
1243
1371
|
}
|
@@ -1251,8 +1379,8 @@ public:
|
|
1251
1379
|
return result.str();
|
1252
1380
|
}
|
1253
1381
|
|
1254
|
-
string toXml(bool includeSecrets = true) const {
|
1255
|
-
|
1382
|
+
string toXml(bool includeSecrets = true, bool lock = true) const {
|
1383
|
+
DynamicScopedLock l(syncher, lock);
|
1256
1384
|
stringstream result;
|
1257
1385
|
SuperGroupMap::const_iterator sg_it;
|
1258
1386
|
vector<GroupPtr>::const_iterator g_it;
|
@@ -1265,11 +1393,25 @@ public:
|
|
1265
1393
|
result << "<max>" << max << "</max>";
|
1266
1394
|
result << "<utilization>" << utilization(false) << "</utilization>";
|
1267
1395
|
result << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
|
1396
|
+
|
1397
|
+
if (includeSecrets) {
|
1398
|
+
vector<GetWaiter>::const_iterator w_it, w_end = getWaitlist.end();
|
1399
|
+
|
1400
|
+
result << "<get_wait_list>";
|
1401
|
+
for (w_it = getWaitlist.begin(); w_it != w_end; w_it++) {
|
1402
|
+
const GetWaiter &waiter = *w_it;
|
1403
|
+
result << "<item>";
|
1404
|
+
result << "<app_group_name>" << escapeForXml(waiter.options.getAppGroupName()) << "</app_group_name>";
|
1405
|
+
result << "</item>";
|
1406
|
+
}
|
1407
|
+
result << "</get_wait_list>";
|
1408
|
+
}
|
1268
1409
|
|
1269
1410
|
result << "<supergroups>";
|
1270
1411
|
for (sg_it = superGroups.begin(); sg_it != superGroups.end(); sg_it++) {
|
1271
1412
|
const SuperGroupPtr &superGroup = sg_it->second;
|
1272
1413
|
|
1414
|
+
result << "<supergroup>";
|
1273
1415
|
result << "<name>" << escapeForXml(superGroup->name) << "</name>";
|
1274
1416
|
result << "<state>" << superGroup->getStateName() << "</state>";
|
1275
1417
|
result << "<get_wait_list_size>" << superGroup->getWaitlist.size() << "</get_wait_list_size>";
|
@@ -1289,6 +1431,7 @@ public:
|
|
1289
1431
|
group->inspectXml(result, includeSecrets);
|
1290
1432
|
result << "</group>";
|
1291
1433
|
}
|
1434
|
+
result << "</supergroup>";
|
1292
1435
|
}
|
1293
1436
|
result << "</supergroups>";
|
1294
1437
|
|