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
@@ -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
  *
@@ -29,6 +29,7 @@
29
29
  #include <list>
30
30
  #include <boost/shared_ptr.hpp>
31
31
  #include <boost/make_shared.hpp>
32
+ #include <oxt/system_calls.hpp>
32
33
  #include <oxt/macros.hpp>
33
34
  #include <sys/types.h>
34
35
  #include <cstdio>
@@ -92,29 +93,48 @@ public:
92
93
  * The admin socket, an anonymous Unix domain socket, is mapped to the process's
93
94
  * STDIN and STDOUT and has two functions.
94
95
  *
95
- * 1. It acts as the main communication channel with the process. Commands are
96
- * sent to and responses are received from it.
97
- * 2. It's used for garbage collection: closing it causes the Process to gracefully
98
- * terminate itself.
96
+ * 1. It acts as the main communication channel with the process. Commands are
97
+ * sent to and responses are received from it.
98
+ * 2. It's used for garbage collection: closing the STDIN part causes the process
99
+ * to gracefully terminate itself.
99
100
  *
100
101
  * Except for the otherwise documented parts, this class is not thread-safe,
101
102
  * so only use within the Pool lock.
102
103
  *
103
- * == Normal usage
104
+ * ## Normal usage
104
105
  *
105
- * 1. Create a session with newSession().
106
- * 2. Initiate the session by calling initiate() on it.
107
- * 3. Perform I/O through session->fd().
108
- * 4. When done, close the session by calling close() on it.
109
- * 5. Call process.sessionClosed().
106
+ * 1. Create a session with newSession().
107
+ * 2. Initiate the session by calling initiate() on it.
108
+ * 3. Perform I/O through session->fd().
109
+ * 4. When done, close the session by calling close() on it.
110
+ * 5. Call process.sessionClosed().
111
+ *
112
+ * ## Life time
113
+ *
114
+ * A Process object lives until the containing Group calls `detach(process)`,
115
+ * which indicates that it wants this Process to should down. This causes
116
+ * the Process to enter the `detached() == true` state. Processes in this
117
+ * state are stored in the `detachedProcesses` collection in the Group and
118
+ * are no longer eligible for receiving requests. They will be removed from
119
+ * the Group and destroyed when all of the following applies:
120
+ *
121
+ * 1. the OS process is gone.
122
+ * 2. `sessions == 0`
123
+ *
124
+ * This means that a Group outlives all its Processes, a Process outlives all
125
+ * its Sessions, and a Process also outlives the OS process.
110
126
  */
111
127
  class Process: public enable_shared_from_this<Process> {
112
128
  private:
113
129
  friend class Group;
114
130
 
115
- /** A mutex to protect access to 'group'. */
116
- mutable boost::mutex backrefSyncher;
117
- /** Group inside the Pool that this Process belongs to. */
131
+ /** A mutex to protect access to `m_shutDown`. */
132
+ mutable boost::mutex lifetimeSyncher;
133
+
134
+ /** Group inside the Pool that this Process belongs to.
135
+ * Should never be NULL because a Group should outlive all of its Processes.
136
+ * Read-only; only set once during initialization.
137
+ */
118
138
  weak_ptr<Group> group;
119
139
 
120
140
  /** A subset of 'sockets': all sockets that speak the
@@ -125,7 +145,7 @@ private:
125
145
  ProcessList::iterator it;
126
146
  /** The handle inside the associated Group's process priority queue. */
127
147
  PriorityQueue<Process>::Handle pqHandle;
128
-
148
+
129
149
  void indexSessionSockets() {
130
150
  SocketList::iterator it;
131
151
  concurrency = 0;
@@ -165,8 +185,6 @@ public:
165
185
  string connectPassword;
166
186
  /** Admin socket, see class description. */
167
187
  FileDescriptor adminSocket;
168
- PipeWatcherPtr adminSocketWatcher;
169
- PipeWatcherPtr errorPipeWatcher;
170
188
  /** The sockets that this Process listens on for connections. */
171
189
  SocketListPtr sockets;
172
190
  /** Time at which the Spawner that created this process was created.
@@ -177,6 +195,16 @@ public:
177
195
  /** The maximum amount of concurrent sessions this process can handle.
178
196
  * 0 means unlimited. */
179
197
  int concurrency;
198
+ /** If true, then indicates that this Process does not refer to a real OS
199
+ * process. The sockets in the socket list are fake and need not be deleted,
200
+ * the admin socket need not be closed, etc.
201
+ */
202
+ bool dummy;
203
+ /** Whether it is required that shutdown() must be called before destroying
204
+ * this Process. Normally true, except for dummy Process objects created by
205
+ * Pool::asyncGet() with options.noop == true.
206
+ */
207
+ bool requiresShutdown;
180
208
 
181
209
  /*************************************************************
182
210
  * Information used by Pool. Do not write to these from
@@ -196,17 +224,42 @@ public:
196
224
  int sessions;
197
225
  /** Number of sessions opened so far. */
198
226
  unsigned int processed;
227
+ /** Do not access directly, always use `isAlive()`/`isShutDown()`/`getLifeStatus()` or
228
+ * through `lifetimeSyncher`. */
229
+ enum LifeStatus {
230
+ /** Up and operational. */
231
+ ALIVE,
232
+ /** Being shut down. The containing Group has just detached this
233
+ * Process and is now waiting for it to be shutdownable.
234
+ */
235
+ SHUTTING_DOWN,
236
+ /**
237
+ * Shut down. Object no longer usable. No more sessions are active.
238
+ */
239
+ SHUT_DOWN
240
+ } lifeStatus;
199
241
  enum EnabledStatus {
242
+ /** Up and operational. */
200
243
  ENABLED,
244
+ /** Process is being disabled. The containing Group is waiting for
245
+ * all sessions on this Process to finish. It may in some corner
246
+ * cases still be selected for processing requests.
247
+ */
201
248
  DISABLING,
249
+ /** Process is fully disabled and should not be handling any
250
+ * requests. It *may* still handle some requests, e.g. by
251
+ * the Out-of-Band-Work trigger.
252
+ */
202
253
  DISABLED
203
254
  } enabled;
204
- ProcessMetrics metrics;
205
-
206
255
  /** Marks whether the process requested out-of-band work. If so, we need to
207
256
  * wait until all sessions have ended and the process has been disabled.
208
257
  */
209
258
  bool oobwRequested;
259
+ /** Caches whether or not the OS process still exists. */
260
+ mutable bool m_osProcessExists;
261
+ /** Collected by Pool::collectAnalytics(). */
262
+ ProcessMetrics metrics;
210
263
 
211
264
  Process(const SafeLibevPtr _libev,
212
265
  pid_t _pid,
@@ -231,10 +284,15 @@ public:
231
284
  sockets(_sockets),
232
285
  spawnerCreationTime(_spawnerCreationTime),
233
286
  spawnStartTime(_spawnStartTime),
287
+ concurrency(0),
288
+ dummy(false),
289
+ requiresShutdown(true),
234
290
  sessions(0),
235
291
  processed(0),
292
+ lifeStatus(ALIVE),
236
293
  enabled(ENABLED),
237
- oobwRequested(false)
294
+ oobwRequested(false),
295
+ m_osProcessExists(true)
238
296
  {
239
297
  SpawnerConfigPtr config;
240
298
  if (_config == NULL) {
@@ -243,17 +301,17 @@ public:
243
301
  config = _config;
244
302
  }
245
303
 
246
- if (_libev != NULL) {
247
- setNonBlocking(_adminSocket);
248
- adminSocketWatcher = make_shared<PipeWatcher>(_libev, _adminSocket,
249
- config->forwardStdout ? config->forwardStdoutTo : -1);
250
- adminSocketWatcher->start();
304
+ if (_adminSocket != -1) {
305
+ PipeWatcherPtr watcher = make_shared<PipeWatcher>(_adminSocket,
306
+ "stdout", pid, config->forwardStdout);
307
+ watcher->initialize();
308
+ watcher->start();
251
309
  }
252
- if (_libev != NULL && _errorPipe != -1) {
253
- setNonBlocking(_errorPipe);
254
- errorPipeWatcher = make_shared<PipeWatcher>(_libev, _errorPipe,
255
- config->forwardStderr ? config->forwardStderrTo : -1);
256
- errorPipeWatcher->start();
310
+ if (_errorPipe != -1) {
311
+ PipeWatcherPtr watcher = make_shared<PipeWatcher>(_errorPipe,
312
+ "stderr", pid, config->forwardStderr);
313
+ watcher->initialize();
314
+ watcher->start();
257
315
  }
258
316
 
259
317
  if (OXT_LIKELY(sockets != NULL)) {
@@ -265,39 +323,118 @@ public:
265
323
  }
266
324
 
267
325
  ~Process() {
268
- if (OXT_LIKELY(sockets != NULL)) {
269
- SocketList::const_iterator it, end = sockets->end();
270
- for (it = sockets->begin(); it != end; it++) {
271
- if (getSocketAddressType(it->address) == SAT_UNIX) {
272
- string filename = parseUnixSocketAddress(it->address);
273
- syscalls::unlink(filename.c_str());
274
- }
275
- }
326
+ if (OXT_UNLIKELY(!isShutDown() && requiresShutdown)) {
327
+ P_BUG("You must call Process::shutdown() before actually "
328
+ "destroying the Process object.");
276
329
  }
277
- // The admin socket stays alive for a while thanks to adminSocketWatcher,
278
- // so we shutdown the writable part to close the child's stdin.
279
- syscalls::shutdown(adminSocket, SHUT_WR);
280
330
  }
281
-
282
- // Thread-safe.
283
- GroupPtr getGroup() const {
284
- lock_guard<boost::mutex> lock(backrefSyncher);
331
+
332
+ static void maybeShutdown(ProcessPtr process) {
333
+ if (process != NULL) {
334
+ process->shutdown();
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Thread-safe.
340
+ * @pre getLifeState() != SHUT_DOWN
341
+ * @post result != NULL
342
+ */
343
+ const GroupPtr getGroup() const {
344
+ assert(!isShutDown());
285
345
  return group.lock();
286
346
  }
287
347
 
288
- // Thread-safe.
289
348
  void setGroup(const GroupPtr &group) {
290
- lock_guard<boost::mutex> lock(backrefSyncher);
349
+ assert(this->group.lock() == NULL || this->group.lock() == group);
291
350
  this->group = group;
292
351
  }
293
-
352
+
353
+ /**
354
+ * Thread-safe.
355
+ * @pre getLifeState() != SHUT_DOWN
356
+ * @post result != NULL
357
+ */
358
+ SuperGroupPtr getSuperGroup() const;
359
+
294
360
  // Thread-safe.
295
- bool detached() const {
296
- return getGroup() == NULL;
361
+ bool isAlive() const {
362
+ lock_guard<boost::mutex> lock(lifetimeSyncher);
363
+ return lifeStatus == ALIVE;
297
364
  }
298
365
 
299
- // Thread-safe
300
- SuperGroupPtr getSuperGroup() const;
366
+ // Thread-safe.
367
+ bool isShutDown() const {
368
+ lock_guard<boost::mutex> lock(lifetimeSyncher);
369
+ return lifeStatus == SHUT_DOWN;
370
+ }
371
+
372
+ // Thread-safe.
373
+ LifeStatus getLifeStatus() const {
374
+ lock_guard<boost::mutex> lock(lifetimeSyncher);
375
+ return lifeStatus;
376
+ }
377
+
378
+ void setShuttingDown() {
379
+ {
380
+ lock_guard<boost::mutex> lock(lifetimeSyncher);
381
+ assert(lifeStatus == ALIVE);
382
+ lifeStatus = SHUTTING_DOWN;
383
+ }
384
+ if (!dummy) {
385
+ syscalls::shutdown(adminSocket, SHUT_WR);
386
+ }
387
+ }
388
+
389
+ void shutdown() {
390
+ LifeStatus ls = getLifeStatus();
391
+ if (ls == SHUT_DOWN || !requiresShutdown) {
392
+ // Some code have guards that call process->shutdown().
393
+ // Returning instead of enforcing !isShutdown() makes things easier.
394
+ return;
395
+ }
396
+
397
+ assert(sessions == 0);
398
+
399
+ if (ls == ALIVE) {
400
+ setShuttingDown();
401
+ }
402
+
403
+ P_TRACE(2, "Shutting down Process object " << inspect());
404
+ if (!dummy) {
405
+ if (OXT_LIKELY(sockets != NULL)) {
406
+ SocketList::const_iterator it, end = sockets->end();
407
+ for (it = sockets->begin(); it != end; it++) {
408
+ if (getSocketAddressType(it->address) == SAT_UNIX) {
409
+ string filename = parseUnixSocketAddress(it->address);
410
+ syscalls::unlink(filename.c_str());
411
+ }
412
+ }
413
+ }
414
+ }
415
+
416
+ lock_guard<boost::mutex> lock(lifetimeSyncher);
417
+ lifeStatus = SHUT_DOWN;
418
+ }
419
+
420
+ bool canBeShutDown() const {
421
+ return sessions == 0 && !osProcessExists();
422
+ }
423
+
424
+ /** Checks whether the OS process exists.
425
+ * Once it has been detected that it doesn't, that event is remembered
426
+ * so that we don't accidentally ping any new processes that have the
427
+ * same PID.
428
+ */
429
+ bool osProcessExists() const {
430
+ if (!dummy && m_osProcessExists) {
431
+ // Once we detect that a process is gone.
432
+ m_osProcessExists = syscalls::kill(pid, 0) == 0 || errno != ESRCH;
433
+ return m_osProcessExists;
434
+ } else {
435
+ return false;
436
+ }
437
+ }
301
438
 
302
439
  int utilization() const {
303
440
  /* Different processes within a Group may have different
@@ -320,6 +457,7 @@ public:
320
457
  }
321
458
  }
322
459
 
460
+ // TODO: remove this
323
461
  bool atFullCapacity() const {
324
462
  return atFullUtilization();
325
463
  }
@@ -360,7 +498,7 @@ public:
360
498
  socket->sessions--;
361
499
  this->sessions--;
362
500
  sessionSockets.decrease(socket->pqHandle, socket->utilization());
363
- assert(!atFullCapacity());
501
+ assert(!atFullUtilization());
364
502
  }
365
503
 
366
504
  /**
@@ -386,6 +524,19 @@ public:
386
524
  stream << "<spawn_end_time>" << spawnEndTime << "</spawn_end_time>";
387
525
  stream << "<last_used>" << lastUsed << "</last_used>";
388
526
  stream << "<uptime>" << uptime() << "</uptime>";
527
+ switch (lifeStatus) {
528
+ case ALIVE:
529
+ stream << "<life_status>alive</life_status>";
530
+ break;
531
+ case SHUTTING_DOWN:
532
+ stream << "<life_status>shutting_down</life_status>";
533
+ break;
534
+ case SHUT_DOWN:
535
+ stream << "<life_status>shut_down</life_status>";
536
+ break;
537
+ default:
538
+ P_BUG("Unknown 'lifeStatus' state " << (int) lifeStatus);
539
+ }
389
540
  switch (enabled) {
390
541
  case ENABLED:
391
542
  stream << "<enabled>enabled</enabled>";
@@ -397,8 +548,7 @@ public:
397
548
  stream << "<enabled>disabled</enabled>";
398
549
  break;
399
550
  default:
400
- stream << "<enabled>unknown</enabled>";
401
- break;
551
+ P_BUG("Unknown 'enabled' state " << (int) enabled);
402
552
  }
403
553
  if (includeSockets) {
404
554
  SocketList::const_iterator it;
@@ -76,7 +76,7 @@ carefully.
76
76
  exceptions are Spawner methods which are explicitly documented as not
77
77
  depending on the event loop.
78
78
 
79
- Pool must only calls Spawner methods from background threads. There's still a
79
+ Pool must only call Spawner methods from background threads. There's still a
80
80
  caveat though: Pool's destructor waits for all background threads to finish.
81
81
  Therefore one must not destroy Pool from the event loop. Instead, I recommend
82
82
  running the event loop in a separate thread, destroy Pool from the main
@@ -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
  *
@@ -54,7 +54,6 @@ public:
54
54
  typedef void (*Callback)(Session *session);
55
55
 
56
56
  private:
57
- /** For keeping the OS process alive until all of a Process's sessions are closed. */
58
57
  ProcessPtr process;
59
58
  /** Socket to use for this session. Guaranteed to be alive thanks to the 'process' reference. */
60
59
  Socket *socket;
@@ -87,13 +86,13 @@ public:
87
86
  Callback onInitiateFailure;
88
87
  Callback onClose;
89
88
 
90
- Session(const ProcessPtr &process, Socket *socket) {
91
- this->process = process;
92
- this->socket = socket;
93
- closed = false;
94
- onInitiateFailure = NULL;
95
- onClose = NULL;
96
- }
89
+ Session(const ProcessPtr &_process, Socket *_socket)
90
+ : process(_process),
91
+ socket(_socket),
92
+ closed(false),
93
+ onInitiateFailure(NULL),
94
+ onClose(NULL)
95
+ { }
97
96
 
98
97
  ~Session() {
99
98
  TRACE_POINT();
@@ -105,14 +104,23 @@ public:
105
104
  callOnClose();
106
105
  }
107
106
  }
108
-
107
+
109
108
  const string &getConnectPassword() const;
110
109
  pid_t getPid() const;
111
110
  const string &getGupid() const;
112
111
  const GroupPtr getGroup() const;
113
112
  void requestOOBW();
114
113
 
114
+ bool isClosed() const {
115
+ return closed;
116
+ }
117
+
118
+ /**
119
+ * @pre !isClosed()
120
+ * @post result != NULL
121
+ */
115
122
  const ProcessPtr &getProcess() const {
123
+ assert(!closed);
116
124
  return process;
117
125
  }
118
126
 
@@ -141,6 +149,9 @@ public:
141
149
  return theFd;
142
150
  }
143
151
 
152
+ /**
153
+ * This Session object becomes fully unsable after closing.
154
+ */
144
155
  void close(bool success) {
145
156
  if (OXT_LIKELY(initiated())) {
146
157
  deinitiate(success);