passenger 4.0.27 → 4.0.28

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 (156) hide show
  1. data.tar.gz.asc +7 -7
  2. data/.gitignore +1 -0
  3. data/NEWS +22 -0
  4. data/build/preprocessor.rb +10 -0
  5. data/build/rpm.rb +74 -65
  6. data/debian.template/rules.template +8 -0
  7. data/dev/copy_boost_headers.rb +11 -2
  8. data/doc/Users guide Apache.idmap.txt +161 -145
  9. data/doc/Users guide Apache.txt +12 -1
  10. data/doc/Users guide Nginx.idmap.txt +142 -126
  11. data/doc/Users guide Nginx.txt +14 -1
  12. data/doc/Users guide Standalone.txt +1 -0
  13. data/doc/users_guide_snippets/environment_variables.txt +1 -1
  14. data/doc/users_guide_snippets/installation.txt +2 -0
  15. data/doc/users_guide_snippets/tips.txt +118 -0
  16. data/ext/apache2/Configuration.cpp +0 -6
  17. data/ext/apache2/Configuration.hpp +0 -5
  18. data/ext/apache2/ConfigurationCommands.cpp +7 -0
  19. data/ext/apache2/ConfigurationFields.hpp +2 -0
  20. data/ext/apache2/ConfigurationSetters.cpp +24 -0
  21. data/ext/apache2/CreateDirConfig.cpp +1 -0
  22. data/ext/apache2/Hooks.cpp +0 -1
  23. data/ext/apache2/MergeDirConfig.cpp +7 -0
  24. data/ext/apache2/SetHeaders.cpp +5 -1
  25. data/ext/boost/cregex.hpp +39 -0
  26. data/ext/boost/libs/regex/src/c_regex_traits.cpp +193 -0
  27. data/ext/boost/libs/regex/src/cpp_regex_traits.cpp +117 -0
  28. data/ext/boost/libs/regex/src/cregex.cpp +660 -0
  29. data/ext/boost/libs/regex/src/instances.cpp +32 -0
  30. data/ext/boost/libs/regex/src/internals.hpp +35 -0
  31. data/ext/boost/libs/regex/src/posix_api.cpp +296 -0
  32. data/ext/boost/libs/regex/src/regex.cpp +227 -0
  33. data/ext/boost/libs/regex/src/regex_debug.cpp +59 -0
  34. data/ext/boost/libs/regex/src/regex_raw_buffer.cpp +72 -0
  35. data/ext/boost/libs/regex/src/regex_traits_defaults.cpp +692 -0
  36. data/ext/boost/libs/regex/src/static_mutex.cpp +179 -0
  37. data/ext/boost/libs/regex/src/wc_regex_traits.cpp +301 -0
  38. data/ext/boost/libs/regex/src/wide_posix_api.cpp +315 -0
  39. data/ext/boost/libs/regex/src/winstances.cpp +35 -0
  40. data/ext/boost/regex.h +100 -0
  41. data/ext/boost/regex.hpp +37 -0
  42. data/ext/boost/regex/concepts.hpp +1128 -0
  43. data/ext/boost/regex/config.hpp +435 -0
  44. data/ext/boost/regex/config/borland.hpp +72 -0
  45. data/ext/boost/regex/config/cwchar.hpp +207 -0
  46. data/ext/boost/regex/mfc.hpp +190 -0
  47. data/ext/boost/regex/pattern_except.hpp +100 -0
  48. data/ext/boost/regex/pending/object_cache.hpp +165 -0
  49. data/ext/boost/regex/pending/static_mutex.hpp +179 -0
  50. data/ext/boost/regex/pending/unicode_iterator.hpp +776 -0
  51. data/ext/boost/regex/regex_traits.hpp +35 -0
  52. data/ext/boost/regex/user.hpp +93 -0
  53. data/ext/boost/regex/v4/basic_regex.hpp +782 -0
  54. data/ext/boost/regex/v4/basic_regex_creator.hpp +1571 -0
  55. data/ext/boost/regex/v4/basic_regex_parser.hpp +2874 -0
  56. data/ext/boost/regex/v4/c_regex_traits.hpp +211 -0
  57. data/ext/boost/regex/v4/char_regex_traits.hpp +81 -0
  58. data/ext/boost/regex/v4/cpp_regex_traits.hpp +1099 -0
  59. data/ext/boost/regex/v4/cregex.hpp +330 -0
  60. data/ext/boost/regex/v4/error_type.hpp +59 -0
  61. data/ext/boost/regex/v4/fileiter.hpp +455 -0
  62. data/ext/boost/regex/v4/instances.hpp +222 -0
  63. data/ext/boost/regex/v4/iterator_category.hpp +91 -0
  64. data/ext/boost/regex/v4/iterator_traits.hpp +135 -0
  65. data/ext/boost/regex/v4/match_flags.hpp +138 -0
  66. data/ext/boost/regex/v4/match_results.hpp +702 -0
  67. data/ext/boost/regex/v4/mem_block_cache.hpp +99 -0
  68. data/ext/boost/regex/v4/perl_matcher.hpp +587 -0
  69. data/ext/boost/regex/v4/perl_matcher_common.hpp +996 -0
  70. data/ext/boost/regex/v4/perl_matcher_non_recursive.hpp +1642 -0
  71. data/ext/boost/regex/v4/perl_matcher_recursive.hpp +991 -0
  72. data/ext/boost/regex/v4/primary_transform.hpp +146 -0
  73. data/ext/boost/regex/v4/protected_call.hpp +81 -0
  74. data/ext/boost/regex/v4/regbase.hpp +180 -0
  75. data/ext/boost/regex/v4/regex.hpp +202 -0
  76. data/ext/boost/regex/v4/regex_format.hpp +1156 -0
  77. data/ext/boost/regex/v4/regex_fwd.hpp +73 -0
  78. data/ext/boost/regex/v4/regex_grep.hpp +155 -0
  79. data/ext/boost/regex/v4/regex_iterator.hpp +201 -0
  80. data/ext/boost/regex/v4/regex_match.hpp +382 -0
  81. data/ext/boost/regex/v4/regex_merge.hpp +93 -0
  82. data/ext/boost/regex/v4/regex_raw_buffer.hpp +210 -0
  83. data/ext/boost/regex/v4/regex_replace.hpp +99 -0
  84. data/ext/boost/regex/v4/regex_search.hpp +217 -0
  85. data/ext/boost/regex/v4/regex_split.hpp +172 -0
  86. data/ext/boost/regex/v4/regex_token_iterator.hpp +342 -0
  87. data/ext/boost/regex/v4/regex_traits.hpp +189 -0
  88. data/ext/boost/regex/v4/regex_traits_defaults.hpp +371 -0
  89. data/ext/boost/regex/v4/regex_workaround.hpp +232 -0
  90. data/ext/boost/regex/v4/states.hpp +301 -0
  91. data/ext/boost/regex/v4/sub_match.hpp +512 -0
  92. data/ext/boost/regex/v4/syntax_type.hpp +105 -0
  93. data/ext/boost/regex/v4/u32regex_iterator.hpp +193 -0
  94. data/ext/boost/regex/v4/u32regex_token_iterator.hpp +377 -0
  95. data/ext/boost/regex/v4/w32_regex_traits.hpp +741 -0
  96. data/ext/boost/regex_fwd.hpp +33 -0
  97. data/ext/common/AgentsStarter.h +0 -11
  98. data/ext/common/ApplicationPool2/Common.h +1 -7
  99. data/ext/common/ApplicationPool2/DirectSpawner.h +3 -3
  100. data/ext/common/ApplicationPool2/Group.h +166 -69
  101. data/ext/common/ApplicationPool2/Implementation.cpp +55 -10
  102. data/ext/common/ApplicationPool2/Options.h +45 -10
  103. data/ext/common/ApplicationPool2/PipeWatcher.h +1 -2
  104. data/ext/common/ApplicationPool2/Pool.h +29 -7
  105. data/ext/common/ApplicationPool2/Process.h +22 -3
  106. data/ext/common/ApplicationPool2/Session.h +1 -0
  107. data/ext/common/ApplicationPool2/SmartSpawner.h +5 -10
  108. data/ext/common/ApplicationPool2/Spawner.h +10 -15
  109. data/ext/common/ApplicationPool2/SuperGroup.h +10 -9
  110. data/ext/common/Constants.h +1 -3
  111. data/ext/common/Hooks.h +193 -0
  112. data/ext/common/Logging.cpp +67 -2
  113. data/ext/common/Logging.h +23 -1
  114. data/ext/common/Utils.cpp +0 -21
  115. data/ext/common/Utils.h +0 -42
  116. data/ext/common/Utils/CachedFileStat.hpp +1 -1
  117. data/ext/common/Utils/StrIntUtils.h +61 -14
  118. data/ext/common/Utils/StringMap.h +4 -0
  119. data/ext/common/agents/HelperAgent/AgentOptions.h +4 -4
  120. data/ext/common/agents/HelperAgent/Main.cpp +2 -3
  121. data/ext/common/agents/HelperAgent/RequestHandler.h +65 -2
  122. data/ext/common/agents/LoggingAgent/FilterSupport.h +3 -1
  123. data/ext/common/agents/Watchdog/Main.cpp +8 -72
  124. data/ext/nginx/CacheLocationConfig.c +29 -1
  125. data/ext/nginx/Configuration.c +0 -12
  126. data/ext/nginx/Configuration.h +0 -1
  127. data/ext/nginx/ConfigurationCommands.c +10 -0
  128. data/ext/nginx/ConfigurationFields.h +2 -0
  129. data/ext/nginx/CreateLocationConfig.c +4 -0
  130. data/ext/nginx/MergeLocationConfig.c +6 -0
  131. data/ext/oxt/system_calls.cpp +7 -1
  132. data/ext/oxt/system_calls.hpp +7 -7
  133. data/helper-scripts/node-loader.js +6 -2
  134. data/helper-scripts/rack-loader.rb +5 -2
  135. data/helper-scripts/rack-preloader.rb +5 -2
  136. data/lib/phusion_passenger.rb +1 -1
  137. data/lib/phusion_passenger/apache2/config_options.rb +8 -0
  138. data/lib/phusion_passenger/constants.rb +0 -1
  139. data/lib/phusion_passenger/nginx/config_options.rb +9 -2
  140. data/lib/phusion_passenger/platform_info/apache.rb +2 -1
  141. data/lib/phusion_passenger/platform_info/compiler.rb +15 -1
  142. data/lib/phusion_passenger/platform_info/cxx_portability.rb +2 -0
  143. data/node_lib/phusion_passenger/httplib_emulation.js +85 -17
  144. data/node_lib/phusion_passenger/request_handler.js +10 -2
  145. data/rpm/Vagrantfile +32 -0
  146. data/rpm/get_distro_id.py +4 -0
  147. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +2 -2
  148. data/test/cxx/ApplicationPool2/PoolTest.cpp +60 -9
  149. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +2 -6
  150. data/test/cxx/CachedFileStatTest.cpp +5 -5
  151. data/test/cxx/RequestHandlerTest.cpp +3 -6
  152. data/test/cxx/UtilsTest.cpp +30 -0
  153. data/test/node/httplib_emulation_spec.js +491 -0
  154. data/test/node/spec_helper.js +25 -0
  155. metadata +78 -2
  156. metadata.gz.asc +7 -7
@@ -0,0 +1,33 @@
1
+ /*
2
+ *
3
+ * Copyright (c) 1998-2002
4
+ * John Maddock
5
+ *
6
+ * Use, modification and distribution are subject to the
7
+ * Boost Software License, Version 1.0. (See accompanying file
8
+ * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9
+ *
10
+ */
11
+
12
+ /*
13
+ * LOCATION: see http://www.boost.org/libs/regex for documentation.
14
+ * FILE regex_fwd.cpp
15
+ * VERSION see <boost/version.hpp>
16
+ * DESCRIPTION: Forward declares boost::basic_regex<> and
17
+ * associated typedefs.
18
+ */
19
+
20
+ #ifndef BOOST_REGEX_FWD_HPP
21
+ #define BOOST_REGEX_FWD_HPP
22
+
23
+ #ifndef BOOST_REGEX_CONFIG_HPP
24
+ #include <boost/regex/config.hpp>
25
+ #endif
26
+
27
+ #include <boost/regex/v4/regex_fwd.hpp>
28
+
29
+ #endif
30
+
31
+
32
+
33
+
@@ -395,17 +395,6 @@ public:
395
395
  .set ("temp_dir", getSystemTempDir());
396
396
  extraParams.addTo(params);
397
397
 
398
- // .setUid ("web_server_worker_uid", webServerWorkerUid)
399
- // .setGid ("web_server_worker_gid", webServerWorkerGid)
400
- // .set ("debug_log_file", debugLogFile)
401
- // .set ("temp_dir", tempDir.empty() ? getSystemTempDir() : tempDir)
402
- // .setBool("user_switching", userSwitching)
403
- // .set ("default_user", defaultUser)
404
- // .set ("default_group", defaultGroup)
405
- // .set ("default_ruby", defaultRubyCommand)
406
- // .setInt ("max_pool_size", maxPoolSize)
407
- // .setInt ("max_instances_per_app", maxInstancesPerApp)
408
-
409
398
  fds = createUnixSocketPair();
410
399
  pid = syscalls::fork();
411
400
  if (pid == 0) {
@@ -111,10 +111,6 @@ struct Ticket {
111
111
 
112
112
  struct SpawnerConfig {
113
113
  // Used by SmartSpawner and DirectSpawner.
114
- /** Whether to print the preloader's and application's stdout. */
115
- bool forwardStdout;
116
- /** Whether to print the preloader's and application's stderr. */
117
- bool forwardStderr;
118
114
  /** A random generator to use. */
119
115
  RandomGeneratorPtr randomGenerator;
120
116
 
@@ -124,9 +120,7 @@ struct SpawnerConfig {
124
120
  unsigned int spawnTime;
125
121
 
126
122
  SpawnerConfig(const RandomGeneratorPtr &randomGenerator = RandomGeneratorPtr())
127
- : forwardStdout(true),
128
- forwardStderr(true),
129
- concurrency(1),
123
+ : concurrency(1),
130
124
  spawnerCreationSleepTime(0),
131
125
  spawnTime(0)
132
126
  {
@@ -222,15 +222,15 @@ public:
222
222
  details.stderrCapturer =
223
223
  make_shared<BackgroundIOCapturer>(
224
224
  errorPipe.first,
225
- string("[App ") + toString(pid) + " stderr] ",
226
- config->forwardStderr);
225
+ pid,
226
+ // The cast works around a compilation problem in Clang.
227
+ (const char *) "stderr");
227
228
  details.stderrCapturer->start();
228
229
  details.pid = pid;
229
230
  details.adminSocket = adminSocket.second;
230
231
  details.io = BufferedIO(adminSocket.second);
231
232
  details.errorPipe = errorPipe.first;
232
233
  details.options = &options;
233
- details.forwardStderr = config->forwardStderr;
234
234
  details.debugDir = debugDir;
235
235
 
236
236
  ProcessPtr process;
@@ -37,12 +37,14 @@
37
37
  #include <oxt/macros.hpp>
38
38
  #include <oxt/thread.hpp>
39
39
  #include <oxt/dynamic_thread_group.hpp>
40
+ #include <cstdlib>
40
41
  #include <cassert>
41
42
  #include <ApplicationPool2/Common.h>
42
43
  #include <ApplicationPool2/ComponentInfo.h>
43
44
  #include <ApplicationPool2/SpawnerFactory.h>
44
45
  #include <ApplicationPool2/Process.h>
45
46
  #include <ApplicationPool2/Options.h>
47
+ #include <Hooks.h>
46
48
  #include <Utils.h>
47
49
  #include <Utils/CachedFileStat.hpp>
48
50
  #include <Utils/FileChangeChecker.h>
@@ -79,6 +81,16 @@ private:
79
81
  callback(_callback)
80
82
  { }
81
83
  };
84
+
85
+ struct RouteResult {
86
+ Process *process;
87
+ bool finished;
88
+
89
+ RouteResult(Process *p, bool _finished = false)
90
+ : process(p),
91
+ finished(_finished)
92
+ { }
93
+ };
82
94
 
83
95
  /**
84
96
  * Protects `m_shuttingDown`.
@@ -194,6 +206,9 @@ private:
194
206
  bool anotherGroupIsWaitingForCapacity() const;
195
207
  bool testOverflowRequestQueue() const;
196
208
  const ResourceLocator &getResourceLocator() const;
209
+ void runAttachHooks(const ProcessPtr process) const;
210
+ void runDetachHooks(const ProcessPtr process) const;
211
+ void setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const;
197
212
 
198
213
  void verifyInvariants() const {
199
214
  // !a || b: logical equivalent of a IMPLIES b.
@@ -208,8 +223,7 @@ private:
208
223
  assert((lifeStatus == ALIVE) == (spawner != NULL));
209
224
 
210
225
  // Verify getWaitlist invariants.
211
- assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || pqueue.top()->atFullUtilization() ));
212
- assert(!( !enabledProcesses.empty() && !pqueue.top()->atFullUtilization() ) || ( getWaitlist.empty() ));
226
+ assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || verifyNoRequestsOnGetWaitlistAreRoutable() ));
213
227
  assert(!( enabledProcesses.empty() && !spawning() && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
214
228
  assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || spawning() || restarting() || poolAtFullCapacity() ));
215
229
 
@@ -272,6 +286,19 @@ private:
272
286
  }
273
287
  #endif
274
288
  }
289
+
290
+ #ifndef NDEBUG
291
+ bool verifyNoRequestsOnGetWaitlistAreRoutable() const {
292
+ deque<GetWaiter>::const_iterator it, end = getWaitlist.end();
293
+
294
+ for (it = getWaitlist.begin(); it != end; it++) {
295
+ if (route(it->options).process != NULL) {
296
+ return false;
297
+ }
298
+ }
299
+ return true;
300
+ }
301
+ #endif
275
302
 
276
303
  /**
277
304
  * Sets options for this Group. Called at creation time and at restart time.
@@ -303,18 +330,71 @@ private:
303
330
  static void doCleanupSpawner(SpawnerPtr spawner) {
304
331
  spawner->cleanup();
305
332
  }
306
-
307
- SessionPtr newSession(Process *process = NULL) {
308
- if (process == NULL) {
309
- assert(enabledCount > 0);
310
- process = pqueue.top();
333
+
334
+ unsigned int generateStickySessionId() {
335
+ unsigned int result;
336
+
337
+ while (true) {
338
+ result = (unsigned int) rand();
339
+ if (findProcessWithStickySessionId(result) == NULL) {
340
+ return result;
341
+ }
311
342
  }
343
+ // Never reached; shut up compiler warning.
344
+ return 0;
345
+ }
346
+
347
+ /* Determines which process to route a get() action to. The returned process
348
+ * is guaranteed to be `canBeRoutedTo()`, i.e. not at full utilization.
349
+ *
350
+ * A request is routed to an enabled processes, or if there are none,
351
+ * from a disabling process. The rationale is as follows:
352
+ * If there are no enabled process, then waiting for one to spawn is too
353
+ * expensive. The next best thing is to route to disabling processes
354
+ * until more processes have been spawned.
355
+ */
356
+ RouteResult route(const Options &options) const {
357
+ if (OXT_LIKELY(enabledCount > 0)) {
358
+ if (options.stickySessionId == 0) {
359
+ if (pqueue.top()->canBeRoutedTo()) {
360
+ return RouteResult(pqueue.top());
361
+ } else {
362
+ return RouteResult(NULL, true);
363
+ }
364
+ } else {
365
+ Process *process = findProcessWithStickySessionId(options.stickySessionId);
366
+ if (process != NULL) {
367
+ if (process->canBeRoutedTo()) {
368
+ return RouteResult(process);
369
+ } else {
370
+ return RouteResult(NULL, false);
371
+ }
372
+ } else if (pqueue.top()->canBeRoutedTo()) {
373
+ return RouteResult(pqueue.top());
374
+ } else {
375
+ return RouteResult(NULL, true);
376
+ }
377
+ }
378
+ } else {
379
+ Process *process = findProcessWithLowestUtilization(disablingProcesses);
380
+ if (process->canBeRoutedTo()) {
381
+ return RouteResult(process);
382
+ } else {
383
+ return RouteResult(NULL, true);
384
+ }
385
+ }
386
+ }
387
+
388
+ SessionPtr newSession(Process *process) {
312
389
  SessionPtr session = process->newSession();
313
390
  session->onInitiateFailure = _onSessionInitiateFailure;
314
391
  session->onClose = _onSessionClose;
315
392
  if (process->enabled == Process::ENABLED) {
316
- assert(process == pqueue.top());
317
- pqueue.pop();
393
+ if (process == pqueue.top()) {
394
+ pqueue.pop();
395
+ } else {
396
+ pqueue.erase(process->pqHandle);
397
+ }
318
398
  process->pqHandle = pqueue.push(process, process->utilization());
319
399
  }
320
400
  return session;
@@ -325,7 +405,7 @@ private:
325
405
  && (newOptions.maxRequestQueueSize == 0
326
406
  || getWaitlist.size() < newOptions.maxRequestQueueSize)))
327
407
  {
328
- getWaitlist.push(GetWaiter(newOptions.copyAndPersist().clearLogger(), callback));
408
+ getWaitlist.push_back(GetWaiter(newOptions.copyAndPersist().clearLogger(), callback));
329
409
  return true;
330
410
  } else {
331
411
  P_WARN("Request queue is full. Returning an error");
@@ -334,6 +414,17 @@ private:
334
414
  }
335
415
  }
336
416
 
417
+ Process *findProcessWithStickySessionId(unsigned int id) const {
418
+ ProcessList::const_iterator it, end = enabledProcesses.end();
419
+ for (it = enabledProcesses.begin(); it != end; it++) {
420
+ Process *process = it->get();
421
+ if (process->stickySessionId == id) {
422
+ return process;
423
+ }
424
+ }
425
+ return NULL;
426
+ }
427
+
337
428
  Process *findProcessWithLowestUtilization(const ProcessList &processes) const {
338
429
  Process *result = NULL;
339
430
  ProcessList::const_iterator it, end = processes.end();
@@ -403,40 +494,38 @@ private:
403
494
  P_BUG("Unknown destination list");
404
495
  }
405
496
  }
406
-
497
+
407
498
  template<typename Lock>
408
499
  void assignSessionsToGetWaitersQuickly(Lock &lock) {
500
+ if (getWaitlist.empty()) {
501
+ verifyInvariants();
502
+ lock.unlock();
503
+ return;
504
+ }
505
+
409
506
  SmallVector<GetAction, 50> actions;
507
+ unsigned int i = 0;
508
+ bool done = false;
509
+
410
510
  actions.reserve(getWaitlist.size());
411
-
412
- // Checkout sessions from enabled processes, or if there are none,
413
- // from disabling processes.
414
- if (enabledCount > 0) {
415
- while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->atFullUtilization()) {
511
+
512
+ while (!done && i < getWaitlist.size()) {
513
+ const GetWaiter &waiter = getWaitlist[i];
514
+ RouteResult result = route(waiter.options);
515
+ if (result.process != NULL) {
416
516
  GetAction action;
417
- action.callback = getWaitlist.front().callback;
418
- action.session = newSession();
419
- getWaitlist.pop();
517
+ action.callback = waiter.callback;
518
+ action.session = newSession(result.process);
519
+ getWaitlist.erase(getWaitlist.begin() + i);
420
520
  actions.push_back(action);
421
- }
422
- } else if (disablingCount > 0) {
423
- bool done = false;
424
- while (!getWaitlist.empty() && !done) {
425
- Process *process = findProcessWithLowestUtilization(
426
- disablingProcesses);
427
- assert(process != NULL);
428
- if (process->atFullUtilization()) {
429
- done = true;
430
- } else {
431
- GetAction action;
432
- action.callback = getWaitlist.front().callback;
433
- action.session = newSession(process);
434
- getWaitlist.pop();
435
- actions.push_back(action);
521
+ } else {
522
+ done = result.finished;
523
+ if (!result.finished) {
524
+ i++;
436
525
  }
437
526
  }
438
527
  }
439
-
528
+
440
529
  verifyInvariants();
441
530
  lock.unlock();
442
531
  SmallVector<GetAction, 50>::const_iterator it, end = actions.end();
@@ -446,26 +535,22 @@ private:
446
535
  }
447
536
 
448
537
  void assignSessionsToGetWaiters(vector<Callback> &postLockActions) {
449
- if (enabledCount > 0) {
450
- while (!getWaitlist.empty() && pqueue.top() != NULL && !pqueue.top()->atFullUtilization()) {
538
+ unsigned int i = 0;
539
+ bool done = false;
540
+
541
+ while (!done && i < getWaitlist.size()) {
542
+ const GetWaiter &waiter = getWaitlist[i];
543
+ RouteResult result = route(waiter.options);
544
+ if (result.process != NULL) {
451
545
  postLockActions.push_back(boost::bind(
452
- getWaitlist.front().callback, newSession(),
546
+ waiter.callback,
547
+ newSession(result.process),
453
548
  ExceptionPtr()));
454
- getWaitlist.pop();
455
- }
456
- } else if (disablingCount > 0) {
457
- bool done = false;
458
- while (!getWaitlist.empty() && !done) {
459
- Process *process = findProcessWithLowestUtilization(
460
- disablingProcesses);
461
- assert(process != NULL);
462
- if (process->atFullUtilization()) {
463
- done = true;
464
- } else {
465
- postLockActions.push_back(boost::bind(
466
- getWaitlist.front().callback, newSession(process),
467
- ExceptionPtr()));
468
- getWaitlist.pop();
549
+ getWaitlist.erase(getWaitlist.begin() + i);
550
+ } else {
551
+ done = result.finished;
552
+ if (!result.finished) {
553
+ i++;
469
554
  }
470
555
  }
471
556
  }
@@ -641,25 +726,33 @@ public:
641
726
  * put on this wait list, which must be processed as soon as the necessary
642
727
  * resources have become free.
643
728
  *
644
- * 'std::' is required because Solaris in its infinite wisdom has a C
645
- * struct in its system headers called 'queue'.
646
- * http://code.google.com/p/phusion-passenger/issues/detail?id=840
647
- *
648
- * Invariant 1:
729
+ * ### Invariant 1 (safety)
730
+ *
731
+ * If requests are queued in the getWaitlist, then that's because there are
732
+ * no processes that can serve them.
733
+ *
649
734
  * if getWaitlist is non-empty:
650
- * enabledProcesses.empty() || (all enabled processes are at full capacity)
651
- * Equivalently:
652
- * if !enabledProcesses.empty() && (an enabled process is not at full capacity):
653
- * getWaitlist is empty.
735
+ * enabledProcesses.empty() || (no request in getWaitlist is routeable)
736
+ *
737
+ * Here, "routeable" is defined as `route(options).process != NULL`.
738
+ *
739
+ * ### Invariant 2 (progress)
654
740
  *
655
- * Invariant 2:
741
+ * The only reason why there are no enabled processes, while at the same time we're
742
+ * not spawning or waiting for pool capacity, is because there is nothing to do.
743
+ *
656
744
  * if enabledProcesses.empty() && !spawning() && !restarting() && !poolAtFullCapacity():
657
745
  * getWaitlist is empty
746
+ *
658
747
  * Equivalently:
748
+ * If requests are queued in the getWaitlist, then either we have processes that can process
749
+ * them (some time in the future), or we're actively trying to spawn processes, unless we're
750
+ * unable to do that because of resource limits.
751
+ *
659
752
  * if getWaitlist is non-empty:
660
753
  * !enabledProcesses.empty() || spawning() || restarting() || poolAtFullCapacity()
661
754
  */
662
- std::queue<GetWaiter> getWaitlist;
755
+ deque<GetWaiter> getWaitlist;
663
756
  /**
664
757
  * Disable() commands that couldn't finish immediately will put their callbacks
665
758
  * in this queue. Note that there may be multiple DisableWaiters pointing to the
@@ -760,9 +853,8 @@ public:
760
853
  }
761
854
  return SessionPtr();
762
855
  } else {
763
- Process *process = pqueue.top();
764
- assert(process != NULL);
765
- if (process->atFullUtilization()) {
856
+ RouteResult result = route(newOptions);
857
+ if (result.process == NULL) {
766
858
  /* Looks like all processes are at full utilization.
767
859
  * Wait until a new one has been spawned or until
768
860
  * resources have become free.
@@ -772,8 +864,8 @@ public:
772
864
  }
773
865
  return SessionPtr();
774
866
  } else {
775
- P_DEBUG("Session checked out from process " << process->inspect());
776
- return newSession();
867
+ P_DEBUG("Session checked out from process " << result.process->inspect());
868
+ return newSession(result.process);
777
869
  }
778
870
  }
779
871
  }
@@ -825,6 +917,7 @@ public:
825
917
  assert(isAlive());
826
918
 
827
919
  process->setGroup(shared_from_this());
920
+ process->stickySessionId = generateStickySessionId();
828
921
  P_DEBUG("Attaching process " << process->inspect());
829
922
  addProcessToList(process, enabledProcesses);
830
923
 
@@ -858,6 +951,8 @@ public:
858
951
 
859
952
  // Update GC sleep timer.
860
953
  wakeUpGarbageCollector();
954
+
955
+ postLockActions.push_back(boost::bind(&Group::runAttachHooks, this, process));
861
956
  }
862
957
 
863
958
  /**
@@ -889,6 +984,8 @@ public:
889
984
 
890
985
  addProcessToList(process, detachedProcesses);
891
986
  startCheckingDetachedProcesses(false);
987
+
988
+ postLockActions.push_back(boost::bind(&Group::runDetachHooks, this, process));
892
989
  }
893
990
 
894
991
  /**