passenger 4.0.48 → 4.0.49

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 (218) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +36 -2
  5. data/.travis.yml +1 -1
  6. data/CHANGELOG +16 -0
  7. data/Rakefile +0 -1
  8. data/build/apache2.rb +4 -4
  9. data/build/common_library.rb +18 -18
  10. data/build/cplusplus_support.rb +2 -2
  11. data/build/documentation.rb +1 -1
  12. data/build/integration_tests.rb +12 -4
  13. data/build/misc.rb +12 -7
  14. data/build/packaging.rb +14 -14
  15. data/build/preprocessor.rb +10 -10
  16. data/build/rake_extensions.rb +11 -11
  17. data/build/ruby_extension.rb +2 -2
  18. data/dev/ci/inituidgid +24 -0
  19. data/dev/ci/run_jenkins.sh +57 -0
  20. data/dev/ci/run_rpm_tests.sh +77 -0
  21. data/dev/{run_travis.sh → ci/run_travis.sh} +60 -4
  22. data/doc/Users guide Nginx.txt +2 -2
  23. data/doc/users_guide_snippets/environment_variables.txt +0 -2
  24. data/doc/users_guide_snippets/tips.txt +20 -1
  25. data/ext/apache2/Bucket.cpp +18 -18
  26. data/ext/apache2/Bucket.h +4 -4
  27. data/ext/apache2/Configuration.cpp +7 -7
  28. data/ext/apache2/Configuration.hpp +43 -43
  29. data/ext/apache2/DirectoryMapper.h +5 -5
  30. data/ext/apache2/Hooks.cpp +142 -142
  31. data/ext/apache2/MergeDirConfig.cpp +40 -40
  32. data/ext/common/Account.h +17 -17
  33. data/ext/common/AccountsDatabase.h +9 -9
  34. data/ext/common/AgentsStarter.cpp +2 -2
  35. data/ext/common/AgentsStarter.h +40 -40
  36. data/ext/common/ApplicationPool2/Common.h +10 -6
  37. data/ext/common/ApplicationPool2/ComponentInfo.h +2 -2
  38. data/ext/common/ApplicationPool2/DirectSpawner.h +17 -17
  39. data/ext/common/ApplicationPool2/DummySpawner.h +5 -5
  40. data/ext/common/ApplicationPool2/Group.h +54 -38
  41. data/ext/common/ApplicationPool2/Implementation.cpp +76 -49
  42. data/ext/common/ApplicationPool2/Options.h +98 -91
  43. data/ext/common/ApplicationPool2/Pool.h +70 -69
  44. data/ext/common/ApplicationPool2/Process.h +21 -21
  45. data/ext/common/ApplicationPool2/Session.h +11 -11
  46. data/ext/common/ApplicationPool2/SmartSpawner.h +60 -60
  47. data/ext/common/ApplicationPool2/Socket.h +19 -19
  48. data/ext/common/ApplicationPool2/Spawner.h +64 -72
  49. data/ext/common/ApplicationPool2/SpawnerFactory.h +4 -4
  50. data/ext/common/ApplicationPool2/SuperGroup.h +41 -41
  51. data/ext/common/BackgroundEventLoop.cpp +1 -1
  52. data/ext/common/BackgroundEventLoop.h +2 -2
  53. data/ext/common/Constants.h +1 -1
  54. data/ext/common/EventedBufferedInput.h +5 -5
  55. data/ext/common/EventedClient.h +51 -51
  56. data/ext/common/EventedMessageServer.h +39 -39
  57. data/ext/common/EventedServer.h +32 -32
  58. data/ext/common/Exceptions.h +23 -23
  59. data/ext/common/FileDescriptor.h +18 -18
  60. data/ext/common/Logging.cpp +1 -1
  61. data/ext/common/MessageClient.h +27 -27
  62. data/ext/common/MessageReadersWriters.h +79 -79
  63. data/ext/common/MessageServer.h +59 -59
  64. data/ext/common/RandomGenerator.h +12 -12
  65. data/ext/common/ResourceLocator.h +8 -8
  66. data/ext/common/SafeLibev.h +54 -25
  67. data/ext/common/ServerInstanceDir.h +31 -31
  68. data/ext/common/StaticString.h +50 -48
  69. data/ext/common/Utils.cpp +73 -78
  70. data/ext/common/Utils.h +6 -6
  71. data/ext/common/Utils/Base64.cpp +3 -3
  72. data/ext/common/Utils/Base64.h +7 -7
  73. data/ext/common/Utils/BlockingQueue.h +9 -9
  74. data/ext/common/Utils/BufferedIO.h +17 -17
  75. data/ext/common/Utils/CachedFileStat.hpp +16 -16
  76. data/ext/common/Utils/Dechunker.h +25 -25
  77. data/ext/common/Utils/FileChangeChecker.h +10 -10
  78. data/ext/common/Utils/MemZeroGuard.h +5 -5
  79. data/ext/common/Utils/MemoryBarrier.h +1 -1
  80. data/ext/common/Utils/MessageIO.h +61 -61
  81. data/ext/common/Utils/ProcessMetricsCollector.h +40 -40
  82. data/ext/common/Utils/ScopeGuard.h +7 -7
  83. data/ext/common/Utils/SpeedMeter.h +1 -1
  84. data/ext/common/Utils/StrIntUtils.cpp +13 -13
  85. data/ext/common/Utils/StrIntUtils.h +3 -3
  86. data/ext/common/Utils/StringScanning.h +5 -5
  87. data/ext/common/Utils/SystemMetricsCollector.h +2 -2
  88. data/ext/common/Utils/SystemTime.h +10 -10
  89. data/ext/common/Utils/Template.h +2 -2
  90. data/ext/common/Utils/Timer.h +6 -6
  91. data/ext/common/Utils/VariantMap.h +29 -29
  92. data/ext/common/agents/Base.cpp +19 -19
  93. data/ext/common/agents/HelperAgent/AgentOptions.h +1 -1
  94. data/ext/common/agents/HelperAgent/FileBackedPipe.h +6 -6
  95. data/ext/common/agents/HelperAgent/Main.cpp +44 -43
  96. data/ext/common/agents/HelperAgent/RequestHandler.cpp +4 -4
  97. data/ext/common/agents/HelperAgent/RequestHandler.h +29 -28
  98. data/ext/common/agents/HelperAgent/ScgiRequestParser.h +56 -50
  99. data/ext/common/agents/LoggingAgent/AdminController.h +8 -8
  100. data/ext/common/agents/LoggingAgent/DataStoreId.h +17 -17
  101. data/ext/common/agents/LoggingAgent/FilterSupport.h +167 -167
  102. data/ext/common/agents/LoggingAgent/LoggingServer.h +122 -122
  103. data/ext/common/agents/LoggingAgent/Main.cpp +7 -7
  104. data/ext/common/agents/LoggingAgent/RemoteSender.h +54 -54
  105. data/ext/common/agents/SpawnPreparer.cpp +4 -4
  106. data/ext/common/agents/TempDirToucher.c +2 -2
  107. data/ext/common/agents/Watchdog/AgentWatcher.cpp +47 -47
  108. data/ext/common/agents/Watchdog/HelperAgentWatcher.cpp +7 -7
  109. data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +7 -7
  110. data/ext/common/agents/Watchdog/Main.cpp +22 -22
  111. data/ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp +9 -9
  112. data/ext/libeio/eio.c +1 -1
  113. data/ext/nginx/Configuration.c +30 -30
  114. data/ext/nginx/Configuration.h +1 -1
  115. data/ext/nginx/ContentHandler.c +54 -54
  116. data/ext/nginx/ContentHandler.h +3 -3
  117. data/ext/nginx/StaticContentHandler.c +2 -2
  118. data/ext/nginx/ngx_http_passenger_module.c +21 -21
  119. data/ext/oxt/detail/backtrace_enabled.hpp +1 -1
  120. data/ext/oxt/detail/context.hpp +1 -1
  121. data/ext/oxt/detail/spin_lock_darwin.hpp +4 -4
  122. data/ext/oxt/detail/spin_lock_gcc_x86.hpp +3 -3
  123. data/ext/oxt/detail/spin_lock_pthreads.hpp +4 -4
  124. data/ext/oxt/detail/tracable_exception_disabled.hpp +1 -1
  125. data/ext/oxt/dynamic_thread_group.hpp +18 -18
  126. data/ext/oxt/implementation.cpp +9 -8
  127. data/ext/oxt/macros.hpp +2 -2
  128. data/ext/oxt/system_calls.cpp +11 -11
  129. data/ext/oxt/system_calls.hpp +13 -13
  130. data/ext/oxt/thread.hpp +22 -14
  131. data/ext/ruby/passenger_native_support.c +55 -55
  132. data/lib/phusion_passenger.rb +24 -24
  133. data/lib/phusion_passenger/common_library.rb +2 -0
  134. data/lib/phusion_passenger/loader_shared_helpers.rb +18 -18
  135. data/lib/phusion_passenger/packaging.rb +9 -4
  136. data/lib/phusion_passenger/platform_info/apache.rb +45 -31
  137. data/lib/phusion_passenger/platform_info/compiler.rb +11 -11
  138. data/lib/phusion_passenger/rack/thread_handler_extension.rb +1 -1
  139. data/lib/phusion_passenger/request_handler/thread_handler.rb +8 -8
  140. data/lib/phusion_passenger/standalone/app_finder.rb +16 -16
  141. data/lib/phusion_passenger/standalone/command.rb +22 -22
  142. data/packaging/rpm/LICENSE.txt +19 -0
  143. data/packaging/rpm/Makefile +13 -0
  144. data/packaging/rpm/README.md +41 -0
  145. data/packaging/rpm/Vagrantfile +38 -0
  146. data/{rpm/Vagrantfile → packaging/rpm/Vagrantfile.centos} +0 -0
  147. data/packaging/rpm/build +170 -0
  148. data/packaging/rpm/create_project +41 -0
  149. data/packaging/rpm/git_update +88 -0
  150. data/packaging/rpm/image/Dockerfile +37 -0
  151. data/packaging/rpm/image/Gemfile +3 -0
  152. data/packaging/rpm/image/Gemfile.lock +12 -0
  153. data/packaging/rpm/image/RPM-GPG-KEY-amazon-ga +19 -0
  154. data/packaging/rpm/image/amazon2014-i386.cfg +96 -0
  155. data/packaging/rpm/image/amazon2014-x86_64.cfg +96 -0
  156. data/packaging/rpm/image/site-defaults.cfg +168 -0
  157. data/packaging/rpm/internal/build_tasks.rb +238 -0
  158. data/packaging/rpm/internal/dummygpg +11 -0
  159. data/packaging/rpm/internal/exec_build +42 -0
  160. data/packaging/rpm/internal/get_distro_arch +14 -0
  161. data/packaging/rpm/internal/get_distro_id +10 -0
  162. data/packaging/rpm/internal/git_update +27 -0
  163. data/packaging/rpm/internal/inituidgid +17 -0
  164. data/packaging/rpm/internal/my_init +344 -0
  165. data/packaging/rpm/internal/python27 +3 -0
  166. data/packaging/rpm/internal/repo_update +46 -0
  167. data/packaging/rpm/internal/setuser +26 -0
  168. data/packaging/rpm/internal/tracking_helper +40 -0
  169. data/packaging/rpm/jenkins_release +99 -0
  170. data/packaging/rpm/lib/build_tasks_support.rb +402 -0
  171. data/packaging/rpm/lib/preprocessor.rb +341 -0
  172. data/packaging/rpm/nginx_spec/404.html +119 -0
  173. data/packaging/rpm/nginx_spec/50x.html +119 -0
  174. data/packaging/rpm/nginx_spec/index.html +116 -0
  175. data/packaging/rpm/nginx_spec/nginx-auto-cc-gcc.patch +13 -0
  176. data/packaging/rpm/nginx_spec/nginx-logo.png +0 -0
  177. data/packaging/rpm/nginx_spec/nginx-upgrade +13 -0
  178. data/packaging/rpm/nginx_spec/nginx-upgrade.8 +151 -0
  179. data/packaging/rpm/nginx_spec/nginx.conf +131 -0
  180. data/packaging/rpm/nginx_spec/nginx.init +144 -0
  181. data/packaging/rpm/nginx_spec/nginx.logrotate +13 -0
  182. data/packaging/rpm/nginx_spec/nginx.service +15 -0
  183. data/packaging/rpm/nginx_spec/nginx.spec.template +559 -0
  184. data/packaging/rpm/nginx_spec/nginx.sysconfig +4 -0
  185. data/packaging/rpm/nginx_spec/passenger.conf +9 -0
  186. data/packaging/rpm/nginx_spec/poweredby.png +0 -0
  187. data/{rpm → packaging/rpm/passenger_spec}/apache-passenger.conf.in +0 -0
  188. data/{rpm → packaging/rpm/passenger_spec}/config.json +0 -0
  189. data/{rpm → packaging/rpm/passenger_spec}/passenger.logrotate +0 -0
  190. data/{rpm → packaging/rpm/passenger_spec}/passenger.spec.template +58 -31
  191. data/{rpm → packaging/rpm/passenger_spec}/passenger_dynamic_thread_group.patch +0 -0
  192. data/{rpm → packaging/rpm/passenger_spec}/passenger_tests_default_config_example.patch +0 -0
  193. data/{rpm → packaging/rpm/passenger_spec}/rubygem-passenger-4.0.18-GLIBC_HAVE_LONG_LONG.patch +0 -0
  194. data/{rpm → packaging/rpm/passenger_spec}/rubygem-passenger-4.0.18-gcc47-include-sys_types.patch +0 -0
  195. data/packaging/rpm/repo_update +114 -0
  196. data/packaging/rpm/setup-system +60 -0
  197. data/packaging/rpm/shell +10 -0
  198. data/resources/templates/standalone/config.erb +3 -1
  199. data/test/config.json.rpm-automation +1 -1
  200. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +11 -11
  201. data/test/cxx/ApplicationPool2/OptionsTest.cpp +5 -5
  202. data/test/cxx/ApplicationPool2/PoolTest.cpp +129 -89
  203. data/test/cxx/ApplicationPool2/ProcessTest.cpp +15 -15
  204. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +22 -22
  205. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +11 -11
  206. data/test/cxx/ScgiRequestParserTest.cpp +75 -61
  207. data/test/cxx/UtilsTest.cpp +86 -85
  208. data/test/gdbinit.example +3 -0
  209. data/test/integration_tests/nginx_tests.rb +3 -3
  210. data/test/integration_tests/source_packaging_test.rb +3 -1
  211. data/test/stub/nginx/nginx.conf.erb +8 -1
  212. data/test/support/nginx_controller.rb +7 -7
  213. metadata +62 -17
  214. metadata.gz.asc +7 -7
  215. data/build/rpm.rb +0 -128
  216. data/dev/rpmtool +0 -21
  217. data/dev/test_rpm_packaging.sh +0 -28
  218. data/rpm/get_distro_id.py +0 -4
@@ -14,7 +14,7 @@ namespace tut {
14
14
  PipeWatcher::DataCallback gatherOutput;
15
15
  string gatheredOutput;
16
16
  boost::mutex gatheredOutputSyncher;
17
-
17
+
18
18
  ApplicationPool2_DirectSpawnerTest() {
19
19
  createServerInstanceDirAndGeneration(serverInstanceDir, generation);
20
20
  PipeWatcher::onData = PipeWatcher::DataCallback();
@@ -29,12 +29,12 @@ namespace tut {
29
29
  unlink("stub/wsgi/passenger_wsgi.pyc");
30
30
  PipeWatcher::onData = PipeWatcher::DataCallback();
31
31
  }
32
-
32
+
33
33
  boost::shared_ptr<DirectSpawner> createSpawner(const Options &options) {
34
34
  return boost::make_shared<DirectSpawner>(
35
35
  generation, make_shared<SpawnerConfig>(*resourceLocator));
36
36
  }
37
-
37
+
38
38
  Options createOptions() {
39
39
  Options options;
40
40
  options.spawnMethod = "direct";
@@ -49,9 +49,9 @@ namespace tut {
49
49
  };
50
50
 
51
51
  DEFINE_TEST_GROUP_WITH_LIMIT(ApplicationPool2_DirectSpawnerTest, 90);
52
-
52
+
53
53
  #include "SpawnerTestCases.cpp"
54
-
54
+
55
55
  TEST_METHOD(80) {
56
56
  // If the application didn't start within the timeout
57
57
  // then whatever was written to stderr is used as the
@@ -61,10 +61,10 @@ namespace tut {
61
61
  options.startCommand = "perl\t" "-e\t" "print STDERR \"hello world\\n\"; sleep(60)";
62
62
  options.startupFile = ".";
63
63
  options.startTimeout = 300;
64
-
64
+
65
65
  DirectSpawner spawner(generation, make_shared<SpawnerConfig>(*resourceLocator));
66
66
  setLogLevel(LVL_CRIT);
67
-
67
+
68
68
  try {
69
69
  process = spawner.spawn(options);
70
70
  process->requiresShutdown = false;
@@ -75,7 +75,7 @@ namespace tut {
75
75
  ensure(e.getErrorPage().find("hello world\n") != string::npos);
76
76
  }
77
77
  }
78
-
78
+
79
79
  TEST_METHOD(81) {
80
80
  // If the application crashed during startup without returning
81
81
  // a proper error response, then its stderr output is used
@@ -84,10 +84,10 @@ namespace tut {
84
84
  options.appRoot = "stub";
85
85
  options.startCommand = "perl\t" "-e\t" "print STDERR \"hello world\\n\"";
86
86
  options.startupFile = ".";
87
-
87
+
88
88
  DirectSpawner spawner(generation, make_shared<SpawnerConfig>(*resourceLocator));
89
89
  setLogLevel(LVL_CRIT);
90
-
90
+
91
91
  try {
92
92
  process = spawner.spawn(options);
93
93
  process->requiresShutdown = false;
@@ -111,7 +111,7 @@ namespace tut {
111
111
  process = spawner->spawn(options);
112
112
  process->requiresShutdown = false;
113
113
  ensure_equals(process->sockets->size(), 1u);
114
-
114
+
115
115
  Connection conn = process->sockets->front().checkoutConnection();
116
116
  ScopeGuard guard(boost::bind(checkin, process, &conn));
117
117
  writeExact(conn.fd, "ping\n");
@@ -10,9 +10,9 @@ namespace tut {
10
10
  ApplicationPool2_OptionsTest() {
11
11
  }
12
12
  };
13
-
13
+
14
14
  DEFINE_TEST_GROUP(ApplicationPool2_OptionsTest);
15
-
15
+
16
16
  TEST_METHOD(1) {
17
17
  // Test persist().
18
18
  char appRoot[] = "appRoot";
@@ -21,18 +21,18 @@ namespace tut {
21
21
  char fooValue[] = "foo";
22
22
  char barKey[] = "PASSENGER_BAR";
23
23
  char barValue[] = "bar";
24
-
24
+
25
25
  Options options;
26
26
  options.appRoot = appRoot;
27
27
  options.processTitle = processTitle;
28
28
  options.environmentVariables.push_back(make_pair(fooKey, fooValue));
29
29
  options.environmentVariables.push_back(make_pair(barKey, barValue));
30
-
30
+
31
31
  Options options2 = options.copyAndPersist();
32
32
  appRoot[0] = processTitle[0] = 'x';
33
33
  fooKey[0] = fooValue[0] = 'x';
34
34
  barKey[0] = barValue[0] = 'x';
35
-
35
+
36
36
  ensure_equals(options2.appRoot, "appRoot");
37
37
  ensure_equals(options2.processTitle, "processTitle");
38
38
  ensure_equals(options2.environmentVariables.size(), 2u);
@@ -29,7 +29,7 @@ namespace tut {
29
29
  boost::mutex syncher;
30
30
  list<SessionPtr> sessions;
31
31
  bool retainSessions;
32
-
32
+
33
33
  ApplicationPool2_PoolTest() {
34
34
  createServerInstanceDirAndGeneration(serverInstanceDir, generation);
35
35
  retainSessions = false;
@@ -42,7 +42,7 @@ namespace tut {
42
42
  setLogLevel(LVL_ERROR); // TODO: change to LVL_WARN
43
43
  setPrintAppOutputAsDebuggingMessages(true);
44
44
  }
45
-
45
+
46
46
  ~ApplicationPool2_PoolTest() {
47
47
  // Explicitly destroy these here because they can run
48
48
  // additional code that depend on other fields in this
@@ -62,7 +62,7 @@ namespace tut {
62
62
  pool->initDebugging();
63
63
  debug = pool->debugSupport;
64
64
  }
65
-
65
+
66
66
  void clearAllSessions() {
67
67
  SessionPtr myCurrentSession;
68
68
  list<SessionPtr> mySessions;
@@ -89,7 +89,7 @@ namespace tut {
89
89
  options.defaultGroup = testConfig["default_group"].asCString();
90
90
  return options;
91
91
  }
92
-
92
+
93
93
  void _callback(const SessionPtr &session, const ExceptionPtr &e) {
94
94
  SessionPtr oldSession;
95
95
  {
@@ -125,7 +125,7 @@ namespace tut {
125
125
  char sizeHeader[sizeof(uint32_t)];
126
126
  Uint32Message::generate(sizeHeader, totalSize);
127
127
  args_array[0] = StaticString(sizeHeader, sizeof(uint32_t));
128
-
128
+
129
129
  gatheredWrite(connection, args_array.get(), args.size() + 1, NULL);
130
130
  }
131
131
 
@@ -184,31 +184,31 @@ namespace tut {
184
184
  *result = (int) pool->disableProcess(process->gupid);
185
185
  }
186
186
  };
187
-
187
+
188
188
  DEFINE_TEST_GROUP_WITH_LIMIT(ApplicationPool2_PoolTest, 100);
189
-
189
+
190
190
  TEST_METHOD(1) {
191
191
  // Test initial state.
192
192
  ensure(!pool->atFullCapacity());
193
193
  }
194
-
195
-
194
+
195
+
196
196
  /*********** Test asyncGet() behavior on a single SuperGroup and Group ***********/
197
-
197
+
198
198
  TEST_METHOD(2) {
199
199
  // asyncGet() actions on empty pools cannot be immediately satisfied.
200
200
  // Instead a new process will be spawned. In the mean time get()
201
201
  // actions are put on a wait list which will be processed as soon
202
202
  // as the new process is done spawning.
203
203
  Options options = createOptions();
204
-
204
+
205
205
  ScopedLock l(pool->syncher);
206
206
  pool->asyncGet(options, callback, false);
207
207
  ensure_equals(number, 0);
208
208
  ensure(pool->getWaitlist.empty());
209
209
  ensure(!pool->superGroups.empty());
210
210
  l.unlock();
211
-
211
+
212
212
  EVENTUALLY(5,
213
213
  result = pool->getProcessCount() == 1;
214
214
  );
@@ -216,37 +216,37 @@ namespace tut {
216
216
  ensure(currentSession != NULL);
217
217
  ensure(currentException == NULL);
218
218
  }
219
-
219
+
220
220
  TEST_METHOD(3) {
221
221
  // If one matching process already exists and it's not at full
222
222
  // capacity then asyncGet() will immediately use it.
223
223
  Options options = createOptions();
224
-
224
+
225
225
  // Spawn a process and opens a session with it.
226
226
  pool->asyncGet(options, callback);
227
227
  EVENTUALLY(5,
228
228
  result = number == 1;
229
229
  );
230
-
230
+
231
231
  // Close the session so that the process is now idle.
232
232
  ProcessPtr process = currentSession->getProcess();
233
233
  currentSession.reset();
234
234
  ensure_equals(process->busyness(), 0);
235
235
  ensure(!process->isTotallyBusy());
236
-
236
+
237
237
  // Verify test assertion.
238
238
  ScopedLock l(pool->syncher);
239
239
  pool->asyncGet(options, callback, false);
240
240
  ensure_equals("callback is immediately called", number, 2);
241
241
  }
242
-
242
+
243
243
  TEST_METHOD(4) {
244
244
  // If one matching process already exists but it's at full capacity,
245
245
  // and the limits prevent spawning of a new process,
246
246
  // then asyncGet() will put the get action on the group's wait
247
247
  // queue. When the process is no longer at full capacity it will
248
248
  // process the request.
249
-
249
+
250
250
  // Spawn a process and verify that it's at full capacity.
251
251
  // Keep its session open.
252
252
  Options options = createOptions();
@@ -261,13 +261,13 @@ namespace tut {
261
261
  currentSession.reset();
262
262
  ensure_equals(process->sessions, 1);
263
263
  ensure(process->isTotallyBusy());
264
-
264
+
265
265
  // Now call asyncGet() again.
266
266
  pool->asyncGet(options, callback);
267
267
  ensure_equals("callback is not yet called", number, 1);
268
268
  ensure_equals("the get action has been put on the wait list",
269
269
  pool->superGroups.get("test")->defaultGroup->getWaitlist.size(), 1u);
270
-
270
+
271
271
  session1.reset();
272
272
  ensure_equals("callback is called after the process becomes idle",
273
273
  number, 2);
@@ -275,7 +275,7 @@ namespace tut {
275
275
  pool->superGroups.get("test")->defaultGroup->getWaitlist.size(), 0u);
276
276
  ensure_equals(process->sessions, 1);
277
277
  }
278
-
278
+
279
279
  TEST_METHOD(5) {
280
280
  // If one matching process already exists but it's at full utilization,
281
281
  // and the limits and pool capacity allow spawning of a new process,
@@ -283,23 +283,23 @@ namespace tut {
283
283
  // queue while spawning a process in the background.
284
284
  // Either the existing process or the newly spawned process
285
285
  // will process the action, whichever becomes first available.
286
-
286
+
287
287
  // Here we test the case in which the existing process becomes
288
288
  // available first.
289
289
  initPoolDebugging();
290
-
290
+
291
291
  // Spawn a regular process and keep its session open.
292
292
  Options options = createOptions();
293
293
  debug->messages->send("Proceed with spawn loop iteration 1");
294
294
  SessionPtr session1 = pool->get(options, &ticket);
295
295
  ProcessPtr process1 = session1->getProcess();
296
-
296
+
297
297
  // Now spawn a process that never finishes.
298
298
  pool->asyncGet(options, callback);
299
-
299
+
300
300
  // Release the session on the first process.
301
301
  session1.reset();
302
-
302
+
303
303
  EVENTUALLY(1,
304
304
  result = number == 1;
305
305
  );
@@ -311,11 +311,11 @@ namespace tut {
311
311
  result = number == 1;
312
312
  );
313
313
  }
314
-
314
+
315
315
  TEST_METHOD(6) {
316
316
  // Here we test the case in which the new process becomes
317
317
  // available first.
318
-
318
+
319
319
  // Spawn a regular process.
320
320
  Options options = createOptions();
321
321
  pool->asyncGet(options, callback);
@@ -325,7 +325,7 @@ namespace tut {
325
325
  SessionPtr session1 = currentSession;
326
326
  ProcessPtr process1 = currentSession->getProcess();
327
327
  currentSession.reset();
328
-
328
+
329
329
  // As long as we don't release process1 the following get
330
330
  // action will be processed by the newly spawned process.
331
331
  pool->asyncGet(options, callback);
@@ -335,11 +335,11 @@ namespace tut {
335
335
  ensure_equals(number, 2);
336
336
  ensure(currentSession->getProcess() != process1);
337
337
  }
338
-
338
+
339
339
  TEST_METHOD(7) {
340
340
  // If multiple matching processes exist, and one of them is idle,
341
341
  // then asyncGet() will use that.
342
-
342
+
343
343
  // Spawn 3 processes and keep a session open with 1 of them.
344
344
  Options options = createOptions();
345
345
  options.minProcesses = 3;
@@ -353,7 +353,7 @@ namespace tut {
353
353
  SessionPtr session1 = currentSession;
354
354
  ProcessPtr process1 = currentSession->getProcess();
355
355
  currentSession.reset();
356
-
356
+
357
357
  // Now open another session. It should complete immediately
358
358
  // and should not use the first process.
359
359
  ScopedLock l(pool->syncher);
@@ -364,7 +364,7 @@ namespace tut {
364
364
  l.unlock();
365
365
  currentSession.reset();
366
366
  ensure(process2 != process1);
367
-
367
+
368
368
  // Now open yet another session. It should also complete immediately
369
369
  // and should not use the first or the second process.
370
370
  l.lock();
@@ -377,11 +377,11 @@ namespace tut {
377
377
  ensure(process3 != process1);
378
378
  ensure(process3 != process2);
379
379
  }
380
-
380
+
381
381
  TEST_METHOD(8) {
382
382
  // If multiple matching processes exist, then asyncGet() will use
383
383
  // the one with the smallest utilization number.
384
-
384
+
385
385
  // Spawn 2 processes, each with a concurrency of 2.
386
386
  Options options = createOptions();
387
387
  options.minProcesses = 2;
@@ -395,14 +395,14 @@ namespace tut {
395
395
  EVENTUALLY(5,
396
396
  result = pool->getProcessCount() == 2;
397
397
  );
398
-
398
+
399
399
  // asyncGet() selects some process.
400
400
  pool->asyncGet(options, callback);
401
401
  ensure_equals(number, 1);
402
402
  SessionPtr session1 = currentSession;
403
403
  ProcessPtr process1 = currentSession->getProcess();
404
404
  currentSession.reset();
405
-
405
+
406
406
  // The first process now has 1 session, so next asyncGet() should
407
407
  // select the other process.
408
408
  pool->asyncGet(options, callback);
@@ -411,7 +411,7 @@ namespace tut {
411
411
  ProcessPtr process2 = currentSession->getProcess();
412
412
  currentSession.reset();
413
413
  ensure("(1)", process1 != process2);
414
-
414
+
415
415
  // Both processes now have an equal number of sessions. Next asyncGet()
416
416
  // can select either.
417
417
  pool->asyncGet(options, callback);
@@ -419,7 +419,7 @@ namespace tut {
419
419
  SessionPtr session3 = currentSession;
420
420
  ProcessPtr process3 = currentSession->getProcess();
421
421
  currentSession.reset();
422
-
422
+
423
423
  // One process now has the lowest number of sessions. Next
424
424
  // asyncGet() should select that one.
425
425
  pool->asyncGet(options, callback);
@@ -429,20 +429,20 @@ namespace tut {
429
429
  currentSession.reset();
430
430
  ensure(process3 != process4);
431
431
  }
432
-
432
+
433
433
  TEST_METHOD(9) {
434
434
  // If multiple matching processes exist, and all of them are at full capacity,
435
435
  // and no more processes may be spawned,
436
436
  // then asyncGet() will put the action on the group's wait queue.
437
437
  // The process that first becomes not at full capacity will process the action.
438
-
438
+
439
439
  // Spawn 2 processes and open 4 sessions.
440
440
  Options options = createOptions();
441
441
  options.appGroupName = "test";
442
442
  options.minProcesses = 2;
443
443
  pool->setMax(2);
444
444
  spawnerConfig->concurrency = 2;
445
-
445
+
446
446
  vector<SessionPtr> sessions;
447
447
  int expectedNumber = 1;
448
448
  for (int i = 0; i < 4; i++) {
@@ -457,16 +457,16 @@ namespace tut {
457
457
  EVENTUALLY(5,
458
458
  result = pool->getProcessCount() == 2;
459
459
  );
460
-
460
+
461
461
  SuperGroupPtr superGroup = pool->superGroups.get("test");
462
462
  ensure_equals(superGroup->groups[0]->getWaitlist.size(), 0u);
463
463
  ensure(pool->atFullCapacity());
464
-
464
+
465
465
  // Now try to open another session.
466
466
  pool->asyncGet(options, callback);
467
467
  ensure_equals("The get request has been put on the wait list",
468
468
  pool->superGroups.get("test")->groups[0]->getWaitlist.size(), 1u);
469
-
469
+
470
470
  // Close an existing session so that one process is no
471
471
  // longer at full utilization.
472
472
  sessions[0].reset();
@@ -474,7 +474,7 @@ namespace tut {
474
474
  pool->superGroups.get("test")->groups[0]->getWaitlist.size(), 0u);
475
475
  ensure(pool->atFullCapacity());
476
476
  }
477
-
477
+
478
478
  TEST_METHOD(10) {
479
479
  // If multiple matching processes exist, and all of them are at full utilization,
480
480
  // and a new process may be spawned,
@@ -484,14 +484,14 @@ namespace tut {
484
484
  // or the newly spawned process
485
485
  // will process the action, whichever is earlier.
486
486
  // Here we test the case where an existing process is earlier.
487
-
487
+
488
488
  // Spawn 2 processes and open 4 sessions.
489
489
  Options options = createOptions();
490
490
  options.minProcesses = 2;
491
491
  pool->setMax(3);
492
492
  GroupPtr group = pool->findOrCreateGroup(options);
493
493
  spawnerConfig->concurrency = 2;
494
-
494
+
495
495
  vector<SessionPtr> sessions;
496
496
  int expectedNumber = 1;
497
497
  for (int i = 0; i < 4; i++) {
@@ -506,7 +506,7 @@ namespace tut {
506
506
  EVENTUALLY(5,
507
507
  result = pool->getProcessCount() == 2;
508
508
  );
509
-
509
+
510
510
  // The next asyncGet() should spawn a new process and the action should be queued.
511
511
  ScopedLock l(pool->syncher);
512
512
  spawnerConfig->spawnTime = 5000000;
@@ -514,7 +514,7 @@ namespace tut {
514
514
  ensure(group->spawning());
515
515
  ensure_equals(group->getWaitlist.size(), 1u);
516
516
  l.unlock();
517
-
517
+
518
518
  // Close one of the sessions. Now it will process the action.
519
519
  ProcessPtr process = sessions[0]->getProcess();
520
520
  sessions[0].reset();
@@ -523,17 +523,17 @@ namespace tut {
523
523
  ensure_equals(group->getWaitlist.size(), 0u);
524
524
  ensure_equals(pool->getProcessCount(), 2u);
525
525
  }
526
-
526
+
527
527
  TEST_METHOD(11) {
528
528
  // Here we test the case where the newly spawned process is earlier.
529
-
529
+
530
530
  // Spawn 2 processes and open 4 sessions.
531
531
  Options options = createOptions();
532
532
  options.minProcesses = 2;
533
533
  pool->setMax(3);
534
534
  GroupPtr group = pool->findOrCreateGroup(options);
535
535
  spawnerConfig->concurrency = 2;
536
-
536
+
537
537
  vector<SessionPtr> sessions;
538
538
  int expectedNumber = 1;
539
539
  for (int i = 0; i < 4; i++) {
@@ -548,7 +548,7 @@ namespace tut {
548
548
  EVENTUALLY(5,
549
549
  result = pool->getProcessCount() == 2;
550
550
  );
551
-
551
+
552
552
  // The next asyncGet() should spawn a new process. After it's done
553
553
  // spawning it will process the action.
554
554
  pool->asyncGet(options, callback);
@@ -585,7 +585,7 @@ namespace tut {
585
585
  // Test shutting down while Group is spawning.
586
586
  initPoolDebugging();
587
587
  Options options = createOptions();
588
-
588
+
589
589
  pool->asyncGet(options, callback);
590
590
  debug->debugger->recv("Begin spawn loop iteration 1");
591
591
  ensure(pool->detachSuperGroupByName("stub/rack"));
@@ -651,17 +651,17 @@ namespace tut {
651
651
  );
652
652
  ensure_equals(pool->getProcessCount(), 1u);
653
653
  }
654
-
655
-
654
+
655
+
656
656
  /*********** Test asyncGet() behavior on multiple SuperGroups,
657
657
  each with a single Group ***********/
658
-
658
+
659
659
  TEST_METHOD(20) {
660
660
  // If the pool is full, and one tries to asyncGet() from a nonexistant group,
661
661
  // then it will kill the oldest idle process and spawn a new process.
662
662
  Options options = createOptions();
663
663
  pool->setMax(2);
664
-
664
+
665
665
  // Get from /foo and close its session immediately.
666
666
  options.appRoot = "/foo";
667
667
  pool->asyncGet(options, callback);
@@ -672,7 +672,7 @@ namespace tut {
672
672
  GroupPtr group1 = process1->getGroup();
673
673
  SuperGroupPtr superGroup1 = group1->getSuperGroup();
674
674
  currentSession.reset();
675
-
675
+
676
676
  // Get from /bar and keep its session open.
677
677
  options.appRoot = "/bar";
678
678
  pool->asyncGet(options, callback);
@@ -681,25 +681,25 @@ namespace tut {
681
681
  );
682
682
  SessionPtr session2 = currentSession;
683
683
  currentSession.reset();
684
-
684
+
685
685
  // Get from /baz. The process for /foo should be killed now.
686
686
  options.appRoot = "/baz";
687
687
  pool->asyncGet(options, callback);
688
688
  EVENTUALLY(5,
689
689
  result = number == 3;
690
690
  );
691
-
691
+
692
692
  ensure_equals(pool->getProcessCount(), 2u);
693
693
  ensure_equals(superGroup1->getProcessCount(), 0u);
694
694
  }
695
-
695
+
696
696
  TEST_METHOD(21) {
697
697
  // If the pool is full, and one tries to asyncGet() from a nonexistant group,
698
698
  // and all existing processes are non-idle, then it will
699
699
  // kill the oldest process and spawn a new process.
700
700
  Options options = createOptions();
701
701
  pool->setMax(2);
702
-
702
+
703
703
  // Get from /foo and close its session immediately.
704
704
  options.appRoot = "/foo";
705
705
  pool->asyncGet(options, callback);
@@ -709,7 +709,7 @@ namespace tut {
709
709
  ProcessPtr process1 = currentSession->getProcess();
710
710
  GroupPtr group1 = process1->getGroup();
711
711
  SuperGroupPtr superGroup1 = group1->getSuperGroup();
712
-
712
+
713
713
  // Get from /bar and keep its session open.
714
714
  options.appRoot = "/bar";
715
715
  pool->asyncGet(options, callback);
@@ -718,14 +718,14 @@ namespace tut {
718
718
  );
719
719
  SessionPtr session2 = currentSession;
720
720
  currentSession.reset();
721
-
721
+
722
722
  // Get from /baz. The process for /foo should be killed now.
723
723
  options.appRoot = "/baz";
724
724
  pool->asyncGet(options, callback);
725
725
  EVENTUALLY(5,
726
726
  result = number == 3;
727
727
  );
728
-
728
+
729
729
  ensure_equals(pool->getProcessCount(), 2u);
730
730
  ensure_equals(superGroup1->getProcessCount(), 0u);
731
731
  }
@@ -935,10 +935,10 @@ namespace tut {
935
935
  result = number == 2;
936
936
  );
937
937
  }
938
-
939
-
938
+
939
+
940
940
  /*********** Test detachProcess() ***********/
941
-
941
+
942
942
  TEST_METHOD(30) {
943
943
  // detachProcess() detaches the process from the group. The pool
944
944
  // will restore the minimum number of processes afterwards.
@@ -967,7 +967,7 @@ namespace tut {
967
967
  result = process->isDead();
968
968
  );
969
969
  }
970
-
970
+
971
971
  TEST_METHOD(31) {
972
972
  // If the containing group had waiters on it, and detachProcess()
973
973
  // detaches the only process in the group, then a new process
@@ -985,7 +985,7 @@ namespace tut {
985
985
  currentSession.reset();
986
986
 
987
987
  pool->asyncGet(options, callback);
988
-
988
+
989
989
  {
990
990
  LockGuard l(pool->syncher);
991
991
  ensure_equals(pool->superGroups.get("test")->defaultGroup->getWaitlist.size(), 1u);
@@ -1003,7 +1003,7 @@ namespace tut {
1003
1003
  result = number == 2;
1004
1004
  );
1005
1005
  }
1006
-
1006
+
1007
1007
  TEST_METHOD(32) {
1008
1008
  // If the pool had waiters on it then detachProcess() will
1009
1009
  // automatically create the SuperGroups that were requested
@@ -1046,7 +1046,7 @@ namespace tut {
1046
1046
  result = number == 2;
1047
1047
  );
1048
1048
  }
1049
-
1049
+
1050
1050
  TEST_METHOD(33) {
1051
1051
  // A SuperGroup does not become garbage collectable
1052
1052
  // after detaching all its processes.
@@ -1131,7 +1131,47 @@ namespace tut {
1131
1131
  );
1132
1132
  }
1133
1133
 
1134
-
1134
+ TEST_METHOD(36) {
1135
+ // Detaching a process that is already being detached, works.
1136
+ Options options = createOptions();
1137
+ options.appGroupName = "test";
1138
+ options.minProcesses = 0;
1139
+
1140
+ initPoolDebugging();
1141
+ debug->restarting = false;
1142
+ debug->spawning = false;
1143
+ debug->detachedProcessesChecker = true;
1144
+
1145
+ pool->asyncGet(options, callback);
1146
+ EVENTUALLY(5,
1147
+ result = pool->getProcessCount() == 1;
1148
+ );
1149
+ EVENTUALLY(5,
1150
+ result = number == 1;
1151
+ );
1152
+
1153
+ ProcessPtr process = currentSession->getProcess();
1154
+ pool->detachProcess(currentSession->getProcess());
1155
+ debug->debugger->recv("About to start detached processes checker");
1156
+ {
1157
+ LockGuard l(pool->syncher);
1158
+ ensure(process->enabled == Process::DETACHED);
1159
+ }
1160
+
1161
+ pool->detachProcess(currentSession->getProcess());
1162
+ debug->messages->send("Proceed with starting detached processes checker");
1163
+ debug->messages->send("Proceed with starting detached processes checker");
1164
+
1165
+ EVENTUALLY(5,
1166
+ result = pool->getProcessCount() == 0;
1167
+ );
1168
+ currentSession.reset();
1169
+ EVENTUALLY(5,
1170
+ result = process->isDead();
1171
+ );
1172
+ }
1173
+
1174
+
1135
1175
  /*********** Test disabling and enabling processes ***********/
1136
1176
 
1137
1177
  TEST_METHOD(40) {
@@ -1140,7 +1180,7 @@ namespace tut {
1140
1180
  vector<ProcessPtr> processes = pool->getProcesses();
1141
1181
  ensure_equals("Disabling succeeds",
1142
1182
  pool->disableProcess(processes[0]->gupid), DR_SUCCESS);
1143
-
1183
+
1144
1184
  LockGuard l(pool->syncher);
1145
1185
  ensure(processes[0]->isAlive());
1146
1186
  ensure_equals("Process is disabled",
@@ -1322,7 +1362,7 @@ namespace tut {
1322
1362
  // TODO: Enabling a process that's disabled succeeds immediately.
1323
1363
  // TODO: Enabling a process that's disabling succeeds immediately. The disable
1324
1364
  // callbacks will be called with DR_CANCELED.
1325
-
1365
+
1326
1366
  TEST_METHOD(51) {
1327
1367
  // If the number of processes is already at maximum, then disabling
1328
1368
  // a process will cause that process to be disabled, without spawning
@@ -1344,9 +1384,9 @@ namespace tut {
1344
1384
  }
1345
1385
  }
1346
1386
 
1347
-
1387
+
1348
1388
  /*********** Other tests ***********/
1349
-
1389
+
1350
1390
  TEST_METHOD(60) {
1351
1391
  // The pool is considered to be at full capacity if and only
1352
1392
  // if all SuperGroups are at full capacity.
@@ -1371,7 +1411,7 @@ namespace tut {
1371
1411
  pool->detachSuperGroupByName("test");
1372
1412
  ensure(!pool->atFullCapacity());
1373
1413
  }
1374
-
1414
+
1375
1415
  TEST_METHOD(61) {
1376
1416
  // If the pool is at full capacity, then increasing 'max' will cause
1377
1417
  // new processes to be spawned. Any queued get requests are processed
@@ -1394,7 +1434,7 @@ namespace tut {
1394
1434
  );
1395
1435
  ensure_equals(pool->getProcessCount(), 3u);
1396
1436
  }
1397
-
1437
+
1398
1438
  TEST_METHOD(62) {
1399
1439
  // Each spawned process has a GUPID, which can be looked up
1400
1440
  // through findProcessByGupid().
@@ -1407,7 +1447,7 @@ namespace tut {
1407
1447
  ensure(!gupid.empty());
1408
1448
  ensure_equals(currentSession->getProcess(), pool->findProcessByGupid(gupid));
1409
1449
  }
1410
-
1450
+
1411
1451
  TEST_METHOD(63) {
1412
1452
  // findProcessByGupid() returns a NULL pointer if there is
1413
1453
  // no matching process.
@@ -1423,7 +1463,7 @@ namespace tut {
1423
1463
  ensure_equals(pool->getProcessCount(), 2u);
1424
1464
 
1425
1465
  session2.reset();
1426
-
1466
+
1427
1467
  // One of the processes still has a session open and should
1428
1468
  // not be idle cleaned.
1429
1469
  EVENTUALLY(2,
@@ -1455,7 +1495,7 @@ namespace tut {
1455
1495
  result = number == 2;
1456
1496
  );
1457
1497
  ensure_equals(pool->getProcessCount(), 2u);
1458
-
1498
+
1459
1499
  EVENTUALLY(2,
1460
1500
  SpawnerPtr spawner = pool->getSuperGroup("test1")->defaultGroup->spawner;
1461
1501
  result = static_pointer_cast<DummySpawner>(spawner)->cleanCount >= 1;
@@ -1584,7 +1624,7 @@ namespace tut {
1584
1624
  result = number == 1;
1585
1625
  );
1586
1626
  pid_t pid = currentSession->getPid();
1587
-
1627
+
1588
1628
  kill(pid, SIGTERM);
1589
1629
  // Wait until process is gone.
1590
1630
  EVENTUALLY(5,
@@ -1713,7 +1753,7 @@ namespace tut {
1713
1753
  EVENTUALLY(2,
1714
1754
  result = pool->getProcessCount() == 2;
1715
1755
  );
1716
-
1756
+
1717
1757
  // Trigger a restart. The creation of the new spawner should take a while.
1718
1758
  spawnerConfig->spawnerCreationSleepTime = 20000;
1719
1759
  touchFile("tmp.wsgi/tmp/restart.txt");
@@ -1918,7 +1958,7 @@ namespace tut {
1918
1958
 
1919
1959
  TEST_METHOD(79) {
1920
1960
  // Test sticky sessions.
1921
-
1961
+
1922
1962
  // Spawn 2 processes and get their sticky session IDs and PIDs.
1923
1963
  ensureMinProcesses(2);
1924
1964
  Options options = createOptions();
@@ -1974,9 +2014,9 @@ namespace tut {
1974
2014
  // has already been reached, then the pool should ping the process so that it can detect
1975
2015
  // when the session's connection has been released by the app.
1976
2016
 
1977
-
2017
+
1978
2018
  /*********** Test previously discovered bugs ***********/
1979
-
2019
+
1980
2020
  TEST_METHOD(85) {
1981
2021
  // Test detaching, then restarting. This should not violate any invariants.
1982
2022
  TempDirCopy dir("stub/wsgi", "tmp.wsgi");