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
@@ -0,0 +1,132 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2013 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#ifndef _PASSENGER_APPLICATION_POOL2_SPAWNER_FACTORY_H_
|
26
|
+
#define _PASSENGER_APPLICATION_POOL2_SPAWNER_FACTORY_H_
|
27
|
+
|
28
|
+
#include <ApplicationPool2/Spawner.h>
|
29
|
+
#include <ApplicationPool2/SmartSpawner.h>
|
30
|
+
#include <ApplicationPool2/DirectSpawner.h>
|
31
|
+
#include <ApplicationPool2/DummySpawner.h>
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ApplicationPool2 {
|
35
|
+
|
36
|
+
using namespace std;
|
37
|
+
using namespace boost;
|
38
|
+
using namespace oxt;
|
39
|
+
|
40
|
+
|
41
|
+
class SpawnerFactory {
|
42
|
+
private:
|
43
|
+
SafeLibevPtr libev;
|
44
|
+
ResourceLocator resourceLocator;
|
45
|
+
ServerInstanceDir::GenerationPtr generation;
|
46
|
+
RandomGeneratorPtr randomGenerator;
|
47
|
+
boost::mutex syncher;
|
48
|
+
SpawnerConfigPtr config;
|
49
|
+
DummySpawnerPtr dummySpawner;
|
50
|
+
|
51
|
+
SpawnerPtr tryCreateSmartSpawner(const Options &options) {
|
52
|
+
string dir = resourceLocator.getHelperScriptsDir();
|
53
|
+
vector<string> preloaderCommand;
|
54
|
+
if (options.appType == "classic-rails") {
|
55
|
+
preloaderCommand.push_back(options.ruby);
|
56
|
+
preloaderCommand.push_back(dir + "/classic-rails-preloader.rb");
|
57
|
+
} else if (options.appType == "rack") {
|
58
|
+
preloaderCommand.push_back(options.ruby);
|
59
|
+
preloaderCommand.push_back(dir + "/rack-preloader.rb");
|
60
|
+
} else {
|
61
|
+
return SpawnerPtr();
|
62
|
+
}
|
63
|
+
return make_shared<SmartSpawner>(libev, resourceLocator,
|
64
|
+
generation, preloaderCommand, options, config);
|
65
|
+
}
|
66
|
+
|
67
|
+
public:
|
68
|
+
SpawnerFactory(const SafeLibevPtr &_libev,
|
69
|
+
const ResourceLocator &_resourceLocator,
|
70
|
+
const ServerInstanceDir::GenerationPtr &_generation,
|
71
|
+
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
72
|
+
: libev(_libev),
|
73
|
+
resourceLocator(_resourceLocator),
|
74
|
+
generation(_generation)
|
75
|
+
{
|
76
|
+
if (_config == NULL) {
|
77
|
+
config = make_shared<SpawnerConfig>();
|
78
|
+
} else {
|
79
|
+
config = _config;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
virtual ~SpawnerFactory() { }
|
84
|
+
|
85
|
+
virtual SpawnerPtr create(const Options &options) {
|
86
|
+
if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
|
87
|
+
SpawnerPtr spawner = tryCreateSmartSpawner(options);
|
88
|
+
if (spawner == NULL) {
|
89
|
+
spawner = make_shared<DirectSpawner>(libev,
|
90
|
+
resourceLocator, generation, config);
|
91
|
+
}
|
92
|
+
return spawner;
|
93
|
+
} else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
|
94
|
+
shared_ptr<DirectSpawner> spawner = make_shared<DirectSpawner>(libev,
|
95
|
+
resourceLocator, generation, config);
|
96
|
+
return spawner;
|
97
|
+
} else if (options.spawnMethod == "dummy") {
|
98
|
+
syscalls::usleep(config->spawnerCreationSleepTime);
|
99
|
+
return getDummySpawner();
|
100
|
+
} else {
|
101
|
+
throw ArgumentException("Unknown spawn method '" + options.spawnMethod + "'");
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* SpawnerFactory always returns the same DummyFactory object upon
|
107
|
+
* creating a dummy spawner. This allows unit tests to easily
|
108
|
+
* set debugging options on the spawner.
|
109
|
+
*/
|
110
|
+
DummySpawnerPtr getDummySpawner() {
|
111
|
+
lock_guard<boost::mutex> l(syncher);
|
112
|
+
if (dummySpawner == NULL) {
|
113
|
+
dummySpawner = make_shared<DummySpawner>(resourceLocator, config);
|
114
|
+
}
|
115
|
+
return dummySpawner;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* All created Spawner objects share the same SpawnerConfig object.
|
120
|
+
*/
|
121
|
+
SpawnerConfigPtr getConfig() const {
|
122
|
+
return config;
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
typedef shared_ptr<SpawnerFactory> SpawnerFactoryPtr;
|
127
|
+
|
128
|
+
|
129
|
+
} // namespace ApplicationPool2
|
130
|
+
} // namespace Passenger
|
131
|
+
|
132
|
+
#endif /* _PASSENGER_APPLICATION_POOL2_SPAWNER_FACTORY_H_ */
|
@@ -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
|
*
|
@@ -103,7 +103,7 @@ using namespace oxt;
|
|
103
103
|
*
|
104
104
|
* It is therefore important that `doInitialize()` and `doDestroy()`
|
105
105
|
* do not interfere with other instances of the same code, and can
|
106
|
-
* commit their work
|
106
|
+
* commit their work atomically.
|
107
107
|
*
|
108
108
|
*
|
109
109
|
* ## Thread-safety
|
@@ -134,7 +134,8 @@ public:
|
|
134
134
|
* `get()` actions can still be statisfied, and the data
|
135
135
|
* structures still contain the old information. Once reloading
|
136
136
|
* is done the data structures will be atomically swapped
|
137
|
-
* with the newly reloaded ones.
|
137
|
+
* with the newly reloaded ones. The old structures will be
|
138
|
+
* destroyed in the background.
|
138
139
|
* Once the restart is completed, the state will transition
|
139
140
|
* to `READY`.
|
140
141
|
* Re-restarting won't have any effect in this state.
|
@@ -157,6 +158,17 @@ public:
|
|
157
158
|
*/
|
158
159
|
DESTROYED
|
159
160
|
};
|
161
|
+
|
162
|
+
enum ShutdownResult {
|
163
|
+
/** The SuperGroup has been successfully destroyed. */
|
164
|
+
SUCCESS,
|
165
|
+
/** The SuperGroup was not destroyed because a get or restart
|
166
|
+
* request came in while destroying.
|
167
|
+
*/
|
168
|
+
CANCELED
|
169
|
+
};
|
170
|
+
|
171
|
+
typedef function<void (ShutdownResult result)> ShutdownCallback;
|
160
172
|
|
161
173
|
private:
|
162
174
|
friend class Pool;
|
@@ -183,8 +195,6 @@ private:
|
|
183
195
|
|
184
196
|
void createInterruptableThread(const function<void ()> &func, const string &name,
|
185
197
|
unsigned int stackSize);
|
186
|
-
void createNonInterruptableThread(const function<void ()> &func, const string &name,
|
187
|
-
unsigned int stackSize);
|
188
198
|
|
189
199
|
void verifyInvariants() const {
|
190
200
|
// !a || b: logical equivalent of a IMPLIES b.
|
@@ -195,13 +205,14 @@ private:
|
|
195
205
|
(state == INITIALIZING || state == DESTROYING || state == DESTROYED));
|
196
206
|
assert(!( state == READY || state == RESTARTING || state == DESTROYING || state == DESTROYED ) ||
|
197
207
|
( getWaitlist.empty() ));
|
208
|
+
assert(!( state == DESTROYED ) || ( detachedGroups.empty() ));
|
198
209
|
}
|
199
210
|
|
200
211
|
void setState(State newState) {
|
201
212
|
state = newState;
|
202
213
|
generation++;
|
203
214
|
}
|
204
|
-
|
215
|
+
|
205
216
|
vector<ComponentInfo> loadComponentInfos(const Options &options) const {
|
206
217
|
vector<ComponentInfo> infos;
|
207
218
|
ComponentInfo info;
|
@@ -236,31 +247,45 @@ private:
|
|
236
247
|
return make_pair(GroupPtr(), 0);
|
237
248
|
}
|
238
249
|
|
239
|
-
void
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
for (
|
247
|
-
if (
|
248
|
-
|
250
|
+
static void oneGroupHasBeenShutDown(SuperGroupPtr self, GroupPtr group) {
|
251
|
+
// This function is either called from the pool event loop or directly from
|
252
|
+
// the detachAllGroups post lock actions. In both cases getPool() is never NULL.
|
253
|
+
PoolPtr pool = self->getPool();
|
254
|
+
lock_guard<boost::mutex> lock(self->getPoolSyncher(pool));
|
255
|
+
|
256
|
+
vector<GroupPtr>::iterator it, end = self->detachedGroups.end();
|
257
|
+
for (it = self->detachedGroups.begin(); it != end; it++) {
|
258
|
+
if (*it == group) {
|
259
|
+
self->detachedGroups.erase(it);
|
249
260
|
break;
|
250
261
|
}
|
251
262
|
}
|
252
263
|
}
|
253
264
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
265
|
+
/** One of the post lock actions can potentially perform a long-running
|
266
|
+
* operation, so running them in a thread is advised.
|
267
|
+
*/
|
268
|
+
void detachAllGroups(vector<GroupPtr> &groups, vector<Callback> &postLockActions) {
|
269
|
+
foreach (const GroupPtr &group, groups) {
|
259
270
|
// doRestart() may temporarily nullify elements in 'groups'.
|
260
|
-
if (group
|
261
|
-
|
271
|
+
if (group == NULL) {
|
272
|
+
continue;
|
262
273
|
}
|
274
|
+
|
275
|
+
while (!group->getWaitlist.empty()) {
|
276
|
+
getWaitlist.push(group->getWaitlist.front());
|
277
|
+
group->getWaitlist.pop();
|
278
|
+
}
|
279
|
+
detachedGroups.push_back(group);
|
280
|
+
group->shutdown(
|
281
|
+
boost::bind(oneGroupHasBeenShutDown,
|
282
|
+
shared_from_this(),
|
283
|
+
group),
|
284
|
+
postLockActions
|
285
|
+
);
|
263
286
|
}
|
287
|
+
|
288
|
+
groups.clear();
|
264
289
|
}
|
265
290
|
|
266
291
|
void assignGetWaitlistToGroups(vector<Callback> &postLockActions) {
|
@@ -282,183 +307,57 @@ private:
|
|
282
307
|
// No-op.
|
283
308
|
}
|
284
309
|
|
285
|
-
void doInitialize(SuperGroupPtr self, Options options, unsigned int generation) {
|
286
|
-
|
287
|
-
realDoInitialize(options, generation);
|
288
|
-
} catch (const thread_interrupted &) {
|
289
|
-
// Return;
|
290
|
-
}
|
310
|
+
static void doInitialize(SuperGroupPtr self, Options options, unsigned int generation) {
|
311
|
+
self->realDoInitialize(options, generation);
|
291
312
|
}
|
292
313
|
|
293
|
-
void
|
294
|
-
|
295
|
-
vector<ComponentInfo>::const_iterator it;
|
296
|
-
ExceptionPtr exception;
|
297
|
-
|
298
|
-
P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background...");
|
299
|
-
try {
|
300
|
-
componentInfos = loadComponentInfos(options);
|
301
|
-
} catch (const tracable_exception &e) {
|
302
|
-
exception = copyException(e);
|
303
|
-
}
|
304
|
-
if (componentInfos.empty() && exception == NULL) {
|
305
|
-
string message = "The directory " +
|
306
|
-
options.appRoot +
|
307
|
-
" does not seem to contain a web application.";
|
308
|
-
exception = make_shared<SpawnException>(
|
309
|
-
message, message, false);
|
310
|
-
}
|
311
|
-
|
312
|
-
PoolPtr pool = getPool();
|
313
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
314
|
-
return;
|
315
|
-
}
|
316
|
-
|
317
|
-
vector<Callback> actions;
|
318
|
-
{
|
319
|
-
unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
320
|
-
this_thread::disable_interruption di;
|
321
|
-
this_thread::disable_syscall_interruption dsi;
|
322
|
-
NOT_EXPECTING_EXCEPTIONS();
|
323
|
-
if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) {
|
324
|
-
return;
|
325
|
-
}
|
326
|
-
P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock");
|
327
|
-
assert(state == INITIALIZING);
|
328
|
-
verifyInvariants();
|
329
|
-
|
330
|
-
if (componentInfos.empty()) {
|
331
|
-
/* Somehow initialization failed. Maybe something has deleted
|
332
|
-
* the supergroup files while we're working.
|
333
|
-
*/
|
334
|
-
assert(exception != NULL);
|
335
|
-
setState(DESTROYED);
|
336
|
-
|
337
|
-
actions.reserve(getWaitlist.size());
|
338
|
-
while (!getWaitlist.empty()) {
|
339
|
-
const GetWaiter &waiter = getWaitlist.front();
|
340
|
-
actions.push_back(boost::bind(waiter.callback,
|
341
|
-
SessionPtr(), exception));
|
342
|
-
getWaitlist.pop();
|
343
|
-
}
|
344
|
-
} else {
|
345
|
-
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
346
|
-
const ComponentInfo &info = *it;
|
347
|
-
GroupPtr group = make_shared<Group>(shared_from_this(),
|
348
|
-
options, info);
|
349
|
-
groups.push_back(group);
|
350
|
-
if (info.isDefault) {
|
351
|
-
defaultGroup = group.get();
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
setState(READY);
|
356
|
-
assignGetWaitlistToGroups(actions);
|
357
|
-
}
|
358
|
-
|
359
|
-
verifyInvariants();
|
360
|
-
P_TRACE(2, "Done initializing SuperGroup " << inspect());
|
361
|
-
}
|
362
|
-
runAllActions(actions);
|
363
|
-
}
|
364
|
-
|
365
|
-
void doRestart(SuperGroupPtr self, Options options, unsigned int generation) {
|
366
|
-
try {
|
367
|
-
realDoRestart(options, generation);
|
368
|
-
} catch (const thread_interrupted &) {
|
369
|
-
// Return.
|
370
|
-
}
|
314
|
+
static void doRestart(SuperGroupPtr self, Options options, unsigned int generation) {
|
315
|
+
self->realDoRestart(options, generation);
|
371
316
|
}
|
372
317
|
|
373
|
-
void
|
374
|
-
|
375
|
-
vector<ComponentInfo> componentInfos = loadComponentInfos(options);
|
376
|
-
vector<ComponentInfo>::const_iterator it;
|
377
|
-
|
378
|
-
PoolPtr pool = getPool();
|
379
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
380
|
-
return;
|
381
|
-
}
|
382
|
-
|
383
|
-
unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
384
|
-
if (OXT_UNLIKELY(getPool() == NULL || this->generation != generation)) {
|
385
|
-
return;
|
386
|
-
}
|
387
|
-
assert(state == RESTARTING);
|
388
|
-
verifyInvariants();
|
389
|
-
|
390
|
-
vector<GroupPtr> allGroups;
|
391
|
-
vector<GroupPtr> updatedGroups;
|
392
|
-
vector<GroupPtr> newGroups;
|
393
|
-
vector<GroupPtr>::const_iterator g_it;
|
394
|
-
vector<Callback> actions;
|
395
|
-
this->options = options;
|
396
|
-
|
397
|
-
// Update the component information for existing groups.
|
398
|
-
UPDATE_TRACE_POINT();
|
399
|
-
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
400
|
-
const ComponentInfo &info = *it;
|
401
|
-
pair<GroupPtr, unsigned int> result =
|
402
|
-
findGroupCorrespondingToComponent(groups, info);
|
403
|
-
GroupPtr &group = result.first;
|
404
|
-
if (group != NULL) {
|
405
|
-
unsigned int index = result.second;
|
406
|
-
group->componentInfo = info;
|
407
|
-
updatedGroups.push_back(group);
|
408
|
-
groups[index].reset();
|
409
|
-
} else {
|
410
|
-
// This is not an existing group but a new one,
|
411
|
-
// so create it.
|
412
|
-
group = make_shared<Group>(shared_from_this(),
|
413
|
-
options, info);
|
414
|
-
newGroups.push_back(group);
|
415
|
-
}
|
416
|
-
// allGroups must be in the same order as componentInfos.
|
417
|
-
allGroups.push_back(group);
|
418
|
-
}
|
419
|
-
|
420
|
-
// Some components might have been deleted, so delete the
|
421
|
-
// corresponding groups.
|
422
|
-
detachGroups(groups, actions);
|
423
|
-
|
424
|
-
// Tell all previous existing groups to restart.
|
425
|
-
for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) {
|
426
|
-
GroupPtr group = *g_it;
|
427
|
-
group->restart(options);
|
428
|
-
}
|
429
|
-
|
430
|
-
groups = allGroups;
|
431
|
-
defaultGroup = findDefaultGroup(allGroups);
|
432
|
-
setState(READY);
|
433
|
-
assignGetWaitlistToGroups(actions);
|
434
|
-
|
435
|
-
UPDATE_TRACE_POINT();
|
436
|
-
verifyInvariants();
|
437
|
-
lock.unlock();
|
438
|
-
runAllActions(actions);
|
439
|
-
}
|
318
|
+
void realDoInitialize(const Options &options, unsigned int generation);
|
319
|
+
void realDoRestart(const Options &options, unsigned int generation);
|
440
320
|
|
441
|
-
void doDestroy(SuperGroupPtr self, unsigned int generation) {
|
321
|
+
void doDestroy(SuperGroupPtr self, unsigned int generation, ShutdownCallback callback) {
|
442
322
|
TRACE_POINT();
|
443
|
-
PoolPtr pool = getPool();
|
444
|
-
if (OXT_UNLIKELY(pool == NULL)) {
|
445
|
-
return;
|
446
|
-
}
|
447
323
|
|
448
324
|
// In the future we can run more destruction code here,
|
449
325
|
// without holding the lock. Note that any destruction
|
450
326
|
// code may not interfere with doInitialize().
|
451
327
|
|
452
|
-
|
453
|
-
|
454
|
-
|
328
|
+
// Wait until 'detachedGroups' is empty.
|
329
|
+
UPDATE_TRACE_POINT();
|
330
|
+
PoolPtr pool = getPool();
|
331
|
+
unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
332
|
+
verifyInvariants();
|
333
|
+
while (true) {
|
334
|
+
if (OXT_UNLIKELY(this->generation != generation)) {
|
335
|
+
UPDATE_TRACE_POINT();
|
336
|
+
lock.unlock();
|
337
|
+
if (callback) {
|
338
|
+
callback(CANCELED);
|
339
|
+
}
|
340
|
+
return;
|
341
|
+
} else if (detachedGroups.empty()) {
|
342
|
+
break;
|
343
|
+
} else {
|
344
|
+
UPDATE_TRACE_POINT();
|
345
|
+
lock.unlock();
|
346
|
+
syscalls::usleep(10000);
|
347
|
+
lock.lock();
|
348
|
+
verifyInvariants();
|
349
|
+
}
|
455
350
|
}
|
456
351
|
|
457
352
|
UPDATE_TRACE_POINT();
|
458
353
|
assert(state == DESTROYING);
|
459
|
-
verifyInvariants();
|
460
354
|
state = DESTROYED;
|
461
355
|
verifyInvariants();
|
356
|
+
|
357
|
+
lock.unlock();
|
358
|
+
if (callback) {
|
359
|
+
callback(SUCCESS);
|
360
|
+
}
|
462
361
|
}
|
463
362
|
|
464
363
|
/*********************/
|
@@ -467,7 +366,7 @@ private:
|
|
467
366
|
|
468
367
|
public:
|
469
368
|
mutable boost::mutex backrefSyncher;
|
470
|
-
weak_ptr<Pool> pool;
|
369
|
+
const weak_ptr<Pool> pool;
|
471
370
|
|
472
371
|
State state;
|
473
372
|
string name;
|
@@ -499,13 +398,26 @@ public:
|
|
499
398
|
* if !getWaitlist.empty():
|
500
399
|
* state == INITIALIZING
|
501
400
|
*/
|
502
|
-
queue<GetWaiter> getWaitlist;
|
401
|
+
std::queue<GetWaiter> getWaitlist;
|
402
|
+
|
403
|
+
/**
|
404
|
+
* Groups which are being shut down right now. These Groups contain a
|
405
|
+
* reference to the containg SuperGroup so that the SuperGroup is not
|
406
|
+
* actually destroyed until all Groups in this collection are done
|
407
|
+
* shutting down.
|
408
|
+
*
|
409
|
+
* Invariant:
|
410
|
+
* if state == DESTROYED:
|
411
|
+
* detachedGroups.empty()
|
412
|
+
*/
|
413
|
+
vector<GroupPtr> detachedGroups;
|
503
414
|
|
504
415
|
/** One MUST call initialize() after construction because shared_from_this()
|
505
416
|
* is not available in the constructor.
|
506
417
|
*/
|
507
|
-
SuperGroup(const PoolPtr &
|
508
|
-
|
418
|
+
SuperGroup(const PoolPtr &_pool, const Options &options)
|
419
|
+
: pool(_pool)
|
420
|
+
{
|
509
421
|
this->options = options.copyAndPersist().clearLogger();
|
510
422
|
this->name = options.getAppGroupName();
|
511
423
|
secret = generateSecret();
|
@@ -514,12 +426,18 @@ public:
|
|
514
426
|
generation = 0;
|
515
427
|
}
|
516
428
|
|
429
|
+
~SuperGroup() {
|
430
|
+
if (OXT_UNLIKELY(state != DESTROYED)) {
|
431
|
+
P_BUG("You must call Group::destroy(..., false) before "
|
432
|
+
"actually destroying the SuperGroup.");
|
433
|
+
}
|
434
|
+
verifyInvariants();
|
435
|
+
}
|
436
|
+
|
517
437
|
void initialize() {
|
518
|
-
|
438
|
+
createInterruptableThread(
|
519
439
|
boost::bind(
|
520
|
-
|
521
|
-
this,
|
522
|
-
// Keep reference to self to prevent destruction.
|
440
|
+
doInitialize,
|
523
441
|
shared_from_this(),
|
524
442
|
options.copyAndPersist(),
|
525
443
|
generation),
|
@@ -527,21 +445,20 @@ public:
|
|
527
445
|
POOL_HELPER_THREAD_STACK_SIZE);
|
528
446
|
}
|
529
447
|
|
530
|
-
|
448
|
+
/**
|
449
|
+
* Thread-safe.
|
450
|
+
*
|
451
|
+
* As long as 'state' != DESTROYED, result != NULL.
|
452
|
+
* But in thread callbacks in this file, getPool() is never NULL
|
453
|
+
* because Pool::destroy() joins all threads, so Pool can never
|
454
|
+
* be destroyed before all thread callbacks have finished.
|
455
|
+
*/
|
531
456
|
PoolPtr getPool() const {
|
532
|
-
lock_guard<boost::mutex> lock(backrefSyncher);
|
533
457
|
return pool.lock();
|
534
458
|
}
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
lock_guard<boost::mutex> lock(backrefSyncher);
|
539
|
-
this->pool = pool;
|
540
|
-
}
|
541
|
-
|
542
|
-
// Thread-safe.
|
543
|
-
bool detached() const {
|
544
|
-
return getPool() == NULL;
|
459
|
+
|
460
|
+
bool isAlive() const {
|
461
|
+
return state != DESTROYING && state != DESTROYED;
|
545
462
|
}
|
546
463
|
|
547
464
|
const char *getStateName() const {
|
@@ -557,7 +474,8 @@ public:
|
|
557
474
|
case DESTROYED:
|
558
475
|
return "DESTROYED";
|
559
476
|
default:
|
560
|
-
|
477
|
+
P_BUG("Unknown SuperGroup state " << (int) state);
|
478
|
+
return NULL; // Shut up compiler warning.
|
561
479
|
}
|
562
480
|
}
|
563
481
|
|
@@ -568,56 +486,64 @@ public:
|
|
568
486
|
* left untouched; in this case it is up to the caller to empty
|
569
487
|
* the `getWaitlist` and do something with it, otherwise the invariant
|
570
488
|
* will be broken.
|
489
|
+
*
|
490
|
+
* One of the post lock actions can potentially perform a long-running
|
491
|
+
* operation, so running them in a thread is advised.
|
571
492
|
*/
|
572
|
-
void destroy(vector<Callback> &postLockActions,
|
493
|
+
void destroy(bool allowReinitialization, vector<Callback> &postLockActions,
|
494
|
+
const ShutdownCallback &callback)
|
495
|
+
{
|
573
496
|
verifyInvariants();
|
574
497
|
switch (state) {
|
575
498
|
case INITIALIZING:
|
576
499
|
case READY:
|
577
500
|
case RESTARTING:
|
578
|
-
|
501
|
+
detachAllGroups(groups, postLockActions);
|
579
502
|
defaultGroup = NULL;
|
580
503
|
if (getWaitlist.empty() || !allowReinitialization) {
|
581
504
|
setState(DESTROYING);
|
582
|
-
|
505
|
+
createInterruptableThread(
|
583
506
|
boost::bind(
|
584
507
|
&SuperGroup::doDestroy,
|
585
508
|
this,
|
586
509
|
// Keep reference to self to prevent destruction.
|
587
510
|
shared_from_this(),
|
588
|
-
generation
|
511
|
+
generation,
|
512
|
+
callback),
|
589
513
|
"SuperGroup destroyer: " + name,
|
590
514
|
POOL_HELPER_THREAD_STACK_SIZE + 1024 * 256);
|
591
515
|
} else {
|
592
516
|
// Spawning this thread before setState() so that
|
593
517
|
// it doesn't change the state when done.
|
594
|
-
|
518
|
+
createInterruptableThread(
|
595
519
|
boost::bind(
|
596
520
|
&SuperGroup::doDestroy,
|
597
521
|
this,
|
598
522
|
// Keep reference to self to prevent destruction.
|
599
523
|
shared_from_this(),
|
600
|
-
generation
|
524
|
+
generation,
|
525
|
+
ShutdownCallback()),
|
601
526
|
"SuperGroup destroyer: " + name,
|
602
527
|
POOL_HELPER_THREAD_STACK_SIZE + 1024 * 256);
|
603
528
|
setState(INITIALIZING);
|
604
|
-
|
529
|
+
createInterruptableThread(
|
605
530
|
boost::bind(
|
606
|
-
|
607
|
-
this,
|
608
|
-
// Keep reference to self to prevent destruction.
|
531
|
+
doInitialize,
|
609
532
|
shared_from_this(),
|
610
533
|
options.copyAndPersist(),
|
611
534
|
generation),
|
612
535
|
"SuperGroup initializer: " + name,
|
613
|
-
1024 *
|
536
|
+
POOL_HELPER_THREAD_STACK_SIZE + 1024 * 256);
|
537
|
+
if (callback) {
|
538
|
+
postLockActions.push_back(boost::bind(callback, CANCELED));
|
539
|
+
}
|
614
540
|
}
|
615
541
|
break;
|
616
542
|
case DESTROYING:
|
617
543
|
case DESTROYED:
|
618
544
|
break;
|
619
545
|
default:
|
620
|
-
|
546
|
+
P_BUG("Unknown SuperGroup state " << (int) state);
|
621
547
|
}
|
622
548
|
if (allowReinitialization) {
|
623
549
|
verifyInvariants();
|
@@ -671,11 +597,9 @@ public:
|
|
671
597
|
case DESTROYED:
|
672
598
|
getWaitlist.push(GetWaiter(newOptions, callback));
|
673
599
|
setState(INITIALIZING);
|
674
|
-
|
600
|
+
createInterruptableThread(
|
675
601
|
boost::bind(
|
676
|
-
|
677
|
-
this,
|
678
|
-
// Keep reference to self to prevent destruction.
|
602
|
+
doInitialize,
|
679
603
|
shared_from_this(),
|
680
604
|
newOptions.copyAndPersist().clearLogger(),
|
681
605
|
generation),
|
@@ -684,7 +608,7 @@ public:
|
|
684
608
|
verifyInvariants();
|
685
609
|
return SessionPtr();
|
686
610
|
default:
|
687
|
-
|
611
|
+
P_BUG("Unknown SuperGroup state " << (int) state);
|
688
612
|
return SessionPtr(); // Shut up compiler warning.
|
689
613
|
};
|
690
614
|
}
|
@@ -715,8 +639,7 @@ public:
|
|
715
639
|
if (state == READY) {
|
716
640
|
createInterruptableThread(
|
717
641
|
boost::bind(
|
718
|
-
|
719
|
-
this,
|
642
|
+
doRestart,
|
720
643
|
// Keep reference to self to prevent destruction.
|
721
644
|
shared_from_this(),
|
722
645
|
options.copyAndPersist().clearLogger(),
|