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.

Files changed (159) hide show
  1. data/.travis.yml +3 -0
  2. data/NEWS +77 -7
  3. data/README.md +3 -11
  4. data/bin/passenger-install-apache2-module +24 -20
  5. data/bin/passenger-install-nginx-module +25 -23
  6. data/build/agents.rb +11 -0
  7. data/build/apache2.rb +9 -5
  8. data/build/basics.rb +37 -30
  9. data/build/common_library.rb +4 -1
  10. data/build/cplusplus_support.rb +5 -5
  11. data/build/cxx_tests.rb +28 -8
  12. data/build/integration_tests.rb +6 -3
  13. data/build/nginx.rb +3 -3
  14. data/build/packaging.rb +95 -57
  15. data/build/ruby_extension.rb +34 -21
  16. data/build/ruby_tests.rb +4 -2
  17. data/build/test_basics.rb +1 -1
  18. data/dev/run_travis.sh +36 -1
  19. data/doc/Users guide Apache.html +425 -308
  20. data/doc/Users guide Apache.idmap.txt +78 -70
  21. data/doc/Users guide Apache.index.sqlite3 +0 -0
  22. data/doc/Users guide Apache.txt +33 -92
  23. data/doc/Users guide Nginx.html +519 -220
  24. data/doc/Users guide Nginx.idmap.txt +78 -60
  25. data/doc/Users guide Nginx.txt +115 -26
  26. data/doc/Users guide Standalone.html +8 -2
  27. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +1 -7
  28. data/doc/users_guide_snippets/installation.txt +167 -22
  29. data/doc/users_guide_snippets/rackup_specifications.txt +4 -0
  30. data/doc/users_guide_snippets/since_version.txt +1 -0
  31. data/doc/users_guide_snippets/support_information.txt +3 -7
  32. data/doc/users_guide_snippets/tips.txt +0 -24
  33. data/ext/apache2/Configuration.cpp +11 -33
  34. data/ext/apache2/Configuration.hpp +3 -18
  35. data/ext/apache2/DirectoryMapper.h +20 -70
  36. data/ext/apache2/Hooks.cpp +2 -2
  37. data/ext/common/AgentsStarter.cpp +0 -2
  38. data/ext/common/AgentsStarter.h +0 -1
  39. data/ext/common/AgentsStarter.hpp +1 -3
  40. data/ext/common/ApplicationPool2/AppTypes.cpp +74 -0
  41. data/ext/common/ApplicationPool2/AppTypes.h +202 -0
  42. data/ext/common/ApplicationPool2/Common.h +12 -10
  43. data/ext/common/ApplicationPool2/DirectSpawner.h +256 -0
  44. data/ext/common/ApplicationPool2/DummySpawner.h +90 -0
  45. data/ext/common/ApplicationPool2/Group.h +311 -94
  46. data/ext/common/ApplicationPool2/Implementation.cpp +405 -145
  47. data/ext/common/ApplicationPool2/Options.h +24 -26
  48. data/ext/common/ApplicationPool2/PipeWatcher.h +20 -13
  49. data/ext/common/ApplicationPool2/Pool.h +326 -183
  50. data/ext/common/ApplicationPool2/Process.h +205 -55
  51. data/ext/common/ApplicationPool2/README.md +1 -1
  52. data/ext/common/ApplicationPool2/Session.h +21 -10
  53. data/ext/common/ApplicationPool2/SmartSpawner.h +801 -0
  54. data/ext/common/ApplicationPool2/Spawner.h +141 -1149
  55. data/ext/common/ApplicationPool2/SpawnerFactory.h +132 -0
  56. data/ext/common/ApplicationPool2/SuperGroup.h +146 -223
  57. data/ext/common/Constants.h +4 -2
  58. data/ext/common/Exceptions.h +23 -1
  59. data/ext/common/Logging.cpp +17 -6
  60. data/ext/common/Logging.h +37 -7
  61. data/ext/common/ResourceLocator.h +1 -1
  62. data/ext/common/Utils.cpp +49 -1
  63. data/ext/common/Utils.h +13 -4
  64. data/ext/common/{AnsiColorConstants.h → Utils/AnsiColorConstants.h} +0 -0
  65. data/ext/common/{BCrypt.cpp → Utils/BCrypt.cpp} +0 -0
  66. data/ext/common/{BCrypt.h → Utils/BCrypt.h} +0 -0
  67. data/ext/common/{Blowfish.c → Utils/Blowfish.c} +0 -0
  68. data/ext/common/{Blowfish.h → Utils/Blowfish.h} +0 -0
  69. data/ext/common/Utils/CachedFileStat.hpp +27 -25
  70. data/ext/common/Utils/Curl.h +184 -0
  71. data/ext/common/{HttpConstants.h → Utils/HttpConstants.h} +3 -0
  72. data/ext/common/Utils/IOUtils.cpp +6 -2
  73. data/ext/common/{IniFile.h → Utils/IniFile.h} +0 -0
  74. data/ext/common/Utils/LargeFiles.cpp +30 -0
  75. data/ext/common/Utils/LargeFiles.h +40 -0
  76. data/ext/common/Utils/StrIntUtils.cpp +72 -8
  77. data/ext/common/Utils/StrIntUtils.h +24 -2
  78. data/ext/common/Utils/StringMap.h +12 -2
  79. data/ext/common/Utils/VariantMap.h +51 -2
  80. data/ext/common/Utils/jsoncpp.cpp +1 -1
  81. data/ext/common/agents/Base.cpp +147 -11
  82. data/ext/common/agents/HelperAgent/AgentOptions.h +14 -6
  83. data/ext/common/agents/HelperAgent/Main.cpp +79 -19
  84. data/ext/common/agents/HelperAgent/RequestHandler.h +36 -16
  85. data/ext/common/agents/LoggingAgent/LoggingServer.h +3 -5
  86. data/ext/common/agents/LoggingAgent/Main.cpp +2 -4
  87. data/ext/common/agents/LoggingAgent/RemoteSender.h +18 -24
  88. data/ext/common/agents/SpawnPreparer.cpp +7 -0
  89. data/ext/common/agents/Watchdog/Main.cpp +96 -38
  90. data/ext/nginx/Configuration.c +26 -22
  91. data/ext/nginx/Configuration.h +4 -2
  92. data/ext/nginx/ContentHandler.c +23 -52
  93. data/ext/nginx/ContentHandler.h +5 -11
  94. data/ext/nginx/config +10 -3
  95. data/ext/nginx/ngx_http_passenger_module.c +21 -6
  96. data/ext/nginx/ngx_http_passenger_module.h +4 -1
  97. data/ext/oxt/dynamic_thread_group.hpp +9 -1
  98. data/ext/oxt/system_calls.cpp +2 -2
  99. data/ext/ruby/extconf.rb +2 -1
  100. data/helper-scripts/backtrace-sanitizer.rb +2 -0
  101. data/helper-scripts/wsgi-loader.py +54 -21
  102. data/lib/phusion_passenger.rb +5 -3
  103. data/lib/phusion_passenger/abstract_installer.rb +18 -41
  104. data/lib/phusion_passenger/admin_tools/memory_stats.rb +2 -2
  105. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  106. data/lib/phusion_passenger/common_library.rb +23 -3
  107. data/lib/phusion_passenger/debug_logging.rb +10 -3
  108. data/lib/phusion_passenger/packaging.rb +1 -0
  109. data/lib/phusion_passenger/platform_info.rb +113 -115
  110. data/lib/phusion_passenger/platform_info/compiler.rb +224 -134
  111. data/lib/phusion_passenger/platform_info/cxx_portability.rb +143 -0
  112. data/lib/phusion_passenger/platform_info/depcheck.rb +371 -0
  113. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +124 -0
  114. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +97 -0
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +39 -0
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +118 -0
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +137 -0
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +15 -0
  119. data/lib/phusion_passenger/platform_info/operating_system.rb +6 -5
  120. data/lib/phusion_passenger/platform_info/ruby.rb +45 -34
  121. data/lib/phusion_passenger/request_handler.rb +35 -22
  122. data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -6
  123. data/lib/phusion_passenger/ruby_core_enhancements.rb +7 -1
  124. data/lib/phusion_passenger/standalone/runtime_installer.rb +43 -34
  125. data/lib/phusion_passenger/utils/robust_interruption.rb +34 -18
  126. data/passenger.gemspec +25 -0
  127. data/resources/templates/standalone/config.erb +3 -1
  128. data/test/config.json.travis +2 -2
  129. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +37 -5
  130. data/test/cxx/ApplicationPool2/PoolTest.cpp +143 -50
  131. data/test/cxx/ApplicationPool2/ProcessTest.cpp +8 -0
  132. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +28 -17
  133. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +31 -26
  134. data/test/cxx/RequestHandlerTest.cpp +17 -1
  135. data/test/cxx/UtilsTest.cpp +84 -10
  136. data/test/integration_tests/apache2_tests.rb +49 -163
  137. data/test/integration_tests/hello_world_wsgi_spec.rb +2 -2
  138. data/test/integration_tests/mycook_spec.rb +1 -1
  139. data/test/integration_tests/nginx_tests.rb +37 -19
  140. data/test/ruby/request_handler_spec.rb +1 -0
  141. data/test/ruby/spec_helper.rb +52 -1
  142. data/test/stub/nginx/nginx.conf.erb +2 -0
  143. data/test/stub/rack/start.rb +5 -0
  144. data/test/stub/rails3.0/Gemfile.lock +30 -30
  145. data/test/stub/rails3.1/Gemfile +1 -1
  146. data/test/stub/rails3.1/Gemfile.lock +3 -3
  147. data/test/stub/rails3.2/Gemfile +1 -1
  148. data/test/stub/rails3.2/Gemfile.lock +4 -4
  149. data/test/stub/rails_apps/2.3/mycook/app/controllers/welcome_controller.rb +1 -1
  150. data/test/stub/rails_apps/2.3/mycook/app/helpers/recipes_helper.rb +2 -0
  151. data/test/stub/rails_apps/2.3/mycook/app/helpers/test_helper.rb +2 -0
  152. data/test/stub/rails_apps/2.3/mycook/app/helpers/uploads_helper.rb +2 -0
  153. data/test/stub/rails_apps/2.3/mycook/app/helpers/welcome_helper.rb +2 -0
  154. data/test/support/nginx_controller.rb +2 -1
  155. metadata +160 -156
  156. data/build/gempackagetask.rb +0 -99
  157. data/build/packagetask.rb +0 -186
  158. data/ext/common/StringListCreator.h +0 -83
  159. 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, 2012 Phusion
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 atomatically.
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 detachGroup(const GroupPtr &group, vector<Callback> &postLockActions) {
240
- group->detachAll(postLockActions);
241
- group->setSuperGroup(SuperGroupPtr());
242
- while (!group->getWaitlist.empty()) {
243
- getWaitlist.push(group->getWaitlist.front());
244
- group->getWaitlist.pop();
245
- }
246
- for (unsigned int i = 0; i < groups.size(); i++) {
247
- if (groups[i] == group) {
248
- groups.erase(groups.begin() + i);
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
- void detachGroups(const vector<GroupPtr> &groups, vector<Callback> &postLockActions) {
255
- vector<GroupPtr>::const_iterator it, end = groups.end();
256
-
257
- for (it = groups.begin(); it != end; it++) {
258
- const GroupPtr &group = *it;
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 != NULL) {
261
- detachGroup(group, postLockActions);
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
- try {
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 realDoInitialize(const Options &options, unsigned int generation) {
294
- vector<ComponentInfo> componentInfos;
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 realDoRestart(const Options &options, unsigned int generation) {
374
- TRACE_POINT();
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
- lock_guard<boost::mutex> lock(getPoolSyncher(pool));
453
- if (OXT_UNLIKELY(getPool() == NULL || this->generation != generation)) {
454
- return;
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 &pool, const Options &options) {
508
- this->pool = pool;
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
- createNonInterruptableThread(
438
+ createInterruptableThread(
519
439
  boost::bind(
520
- &SuperGroup::doInitialize,
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
- // Thread-safe.
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
- // Thread-safe.
537
- void setPool(const PoolPtr &pool) {
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
- abort();
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, bool allowReinitialization = true) {
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
- detachGroups(groups, postLockActions);
501
+ detachAllGroups(groups, postLockActions);
579
502
  defaultGroup = NULL;
580
503
  if (getWaitlist.empty() || !allowReinitialization) {
581
504
  setState(DESTROYING);
582
- createNonInterruptableThread(
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
- createNonInterruptableThread(
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
- createNonInterruptableThread(
529
+ createInterruptableThread(
605
530
  boost::bind(
606
- &SuperGroup::doInitialize,
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 * 64);
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
- abort();
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
- createNonInterruptableThread(
600
+ createInterruptableThread(
675
601
  boost::bind(
676
- &SuperGroup::doInitialize,
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
- abort();
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
- &SuperGroup::doRestart,
719
- this,
642
+ doRestart,
720
643
  // Keep reference to self to prevent destruction.
721
644
  shared_from_this(),
722
645
  options.copyAndPersist().clearLogger(),