passenger 2.0.3 → 2.0.4

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 (275) hide show
  1. data/Rakefile +8 -5
  2. data/bin/passenger-install-apache2-module +6 -1
  3. data/doc/ApplicationPool algorithm.txt +23 -5
  4. data/doc/Architectural overview.html +778 -0
  5. data/doc/Security of user switching support.html +1 -1
  6. data/doc/Users guide.html +113 -31
  7. data/doc/Users guide.txt +104 -10
  8. data/doc/cxxapi/ApplicationPoolServer_8h-source.html +683 -0
  9. data/doc/cxxapi/ApplicationPool_8h-source.html +224 -0
  10. data/doc/cxxapi/Application_8h-source.html +448 -0
  11. data/doc/cxxapi/Bucket_8h-source.html +61 -0
  12. data/doc/cxxapi/Configuration_8h-source.html +195 -0
  13. data/doc/cxxapi/DummySpawnManager_8h-source.html +126 -0
  14. data/doc/cxxapi/Exceptions_8h-source.html +244 -0
  15. data/doc/cxxapi/Hooks_8h-source.html +63 -0
  16. data/doc/cxxapi/Logging_8h-source.html +136 -0
  17. data/doc/cxxapi/MessageChannel_8h-source.html +524 -0
  18. data/doc/cxxapi/SpawnManager_8h-source.html +593 -0
  19. data/doc/cxxapi/StandardApplicationPool_8h-source.html +732 -0
  20. data/doc/cxxapi/System_8h-source.html +251 -0
  21. data/doc/cxxapi/Utils_8h-source.html +283 -0
  22. data/doc/cxxapi/annotated.html +59 -0
  23. data/doc/cxxapi/classClient-members.html +35 -0
  24. data/doc/cxxapi/classClient.html +117 -0
  25. data/doc/cxxapi/classDirectoryMapper-members.html +38 -0
  26. data/doc/cxxapi/classDirectoryMapper.html +203 -0
  27. data/doc/cxxapi/classHooks-members.html +33 -0
  28. data/doc/cxxapi/classHooks.html +43 -0
  29. data/doc/cxxapi/classPassenger_1_1Application-members.html +38 -0
  30. data/doc/cxxapi/classPassenger_1_1Application.html +229 -0
  31. data/doc/cxxapi/classPassenger_1_1ApplicationPool-members.html +41 -0
  32. data/doc/cxxapi/classPassenger_1_1ApplicationPool.html +359 -0
  33. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer-members.html +36 -0
  34. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer.html +225 -0
  35. data/doc/cxxapi/classPassenger_1_1ApplicationPool__inherit__graph.map +1 -0
  36. data/doc/cxxapi/classPassenger_1_1ApplicationPool__inherit__graph.md5 +1 -0
  37. data/doc/cxxapi/classPassenger_1_1ApplicationPool__inherit__graph.png +0 -0
  38. data/doc/cxxapi/classPassenger_1_1Application_1_1Session-members.html +43 -0
  39. data/doc/cxxapi/classPassenger_1_1Application_1_1Session.html +329 -0
  40. data/doc/cxxapi/classPassenger_1_1BusyException-members.html +33 -0
  41. data/doc/cxxapi/classPassenger_1_1BusyException.html +47 -0
  42. data/doc/cxxapi/classPassenger_1_1ConfigurationException-members.html +33 -0
  43. data/doc/cxxapi/classPassenger_1_1ConfigurationException.html +44 -0
  44. data/doc/cxxapi/classPassenger_1_1DummySpawnManager-members.html +33 -0
  45. data/doc/cxxapi/classPassenger_1_1DummySpawnManager.html +51 -0
  46. data/doc/cxxapi/classPassenger_1_1FileNotFoundException-members.html +33 -0
  47. data/doc/cxxapi/classPassenger_1_1FileNotFoundException.html +51 -0
  48. data/doc/cxxapi/classPassenger_1_1FileNotFoundException__inherit__graph.map +1 -0
  49. data/doc/cxxapi/classPassenger_1_1FileNotFoundException__inherit__graph.md5 +1 -0
  50. data/doc/cxxapi/classPassenger_1_1FileNotFoundException__inherit__graph.png +0 -0
  51. data/doc/cxxapi/classPassenger_1_1FileSystemException-members.html +38 -0
  52. data/doc/cxxapi/classPassenger_1_1FileSystemException.html +61 -0
  53. data/doc/cxxapi/classPassenger_1_1FileSystemException__inherit__graph.map +1 -0
  54. data/doc/cxxapi/classPassenger_1_1FileSystemException__inherit__graph.md5 +1 -0
  55. data/doc/cxxapi/classPassenger_1_1FileSystemException__inherit__graph.png +0 -0
  56. data/doc/cxxapi/classPassenger_1_1IOException-members.html +33 -0
  57. data/doc/cxxapi/classPassenger_1_1IOException.html +54 -0
  58. data/doc/cxxapi/classPassenger_1_1IOException__inherit__graph.map +1 -0
  59. data/doc/cxxapi/classPassenger_1_1IOException__inherit__graph.md5 +1 -0
  60. data/doc/cxxapi/classPassenger_1_1IOException__inherit__graph.png +0 -0
  61. data/doc/cxxapi/classPassenger_1_1MessageChannel-members.html +47 -0
  62. data/doc/cxxapi/classPassenger_1_1MessageChannel.html +591 -0
  63. data/doc/cxxapi/classPassenger_1_1SpawnException-members.html +35 -0
  64. data/doc/cxxapi/classPassenger_1_1SpawnException.html +77 -0
  65. data/doc/cxxapi/classPassenger_1_1SpawnManager-members.html +37 -0
  66. data/doc/cxxapi/classPassenger_1_1SpawnManager.html +255 -0
  67. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool-members.html +43 -0
  68. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool.html +400 -0
  69. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool__inherit__graph.map +1 -0
  70. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool__inherit__graph.md5 +1 -0
  71. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool__inherit__graph.png +0 -0
  72. data/doc/cxxapi/classPassenger_1_1SystemException-members.html +37 -0
  73. data/doc/cxxapi/classPassenger_1_1SystemException.html +154 -0
  74. data/doc/cxxapi/classPassenger_1_1SystemException__inherit__graph.map +1 -0
  75. data/doc/cxxapi/classPassenger_1_1SystemException__inherit__graph.md5 +1 -0
  76. data/doc/cxxapi/classPassenger_1_1SystemException__inherit__graph.png +0 -0
  77. data/doc/cxxapi/classPassenger_1_1TempFile-members.html +36 -0
  78. data/doc/cxxapi/classPassenger_1_1TempFile.html +128 -0
  79. data/doc/cxxapi/classPassenger_1_1Thread-members.html +35 -0
  80. data/doc/cxxapi/classPassenger_1_1Thread.html +100 -0
  81. data/doc/cxxapi/classboost_1_1this__thread_1_1disable__syscall__interruption-members.html +33 -0
  82. data/doc/cxxapi/classboost_1_1this__thread_1_1disable__syscall__interruption.html +46 -0
  83. data/doc/cxxapi/classboost_1_1this__thread_1_1enable__syscall__interruption-members.html +33 -0
  84. data/doc/cxxapi/classboost_1_1this__thread_1_1enable__syscall__interruption.html +44 -0
  85. data/doc/cxxapi/classboost_1_1this__thread_1_1restore__syscall__interruption-members.html +33 -0
  86. data/doc/cxxapi/classboost_1_1this__thread_1_1restore__syscall__interruption.html +44 -0
  87. data/doc/cxxapi/definitions_8h-source.html +28 -0
  88. data/doc/cxxapi/doxygen.css +433 -0
  89. data/doc/cxxapi/doxygen.png +0 -0
  90. data/doc/cxxapi/files.html +42 -0
  91. data/doc/cxxapi/ftv2blank.png +0 -0
  92. data/doc/cxxapi/ftv2doc.png +0 -0
  93. data/doc/cxxapi/ftv2folderclosed.png +0 -0
  94. data/doc/cxxapi/ftv2folderopen.png +0 -0
  95. data/doc/cxxapi/ftv2lastnode.png +0 -0
  96. data/doc/cxxapi/ftv2link.png +0 -0
  97. data/doc/cxxapi/ftv2mlastnode.png +0 -0
  98. data/doc/cxxapi/ftv2mnode.png +0 -0
  99. data/doc/cxxapi/ftv2node.png +0 -0
  100. data/doc/cxxapi/ftv2plastnode.png +0 -0
  101. data/doc/cxxapi/ftv2pnode.png +0 -0
  102. data/doc/cxxapi/ftv2vertline.png +0 -0
  103. data/doc/cxxapi/functions.html +215 -0
  104. data/doc/cxxapi/functions_func.html +210 -0
  105. data/doc/cxxapi/functions_type.html +46 -0
  106. data/doc/cxxapi/functions_vars.html +48 -0
  107. data/doc/cxxapi/graph_legend.dot +22 -0
  108. data/doc/cxxapi/graph_legend.html +87 -0
  109. data/doc/cxxapi/graph_legend.png +0 -0
  110. data/doc/cxxapi/group__Configuration.html +236 -0
  111. data/doc/cxxapi/group__Configuration.png +0 -0
  112. data/doc/cxxapi/group__Core.html +51 -0
  113. data/doc/cxxapi/group__Core.png +0 -0
  114. data/doc/cxxapi/group__Exceptions.html +43 -0
  115. data/doc/cxxapi/group__Hooks.html +46 -0
  116. data/doc/cxxapi/group__Hooks.png +0 -0
  117. data/doc/cxxapi/group__Support.html +406 -0
  118. data/doc/cxxapi/hierarchy.html +67 -0
  119. data/doc/cxxapi/index.html +11 -0
  120. data/doc/cxxapi/inherit__graph__0.map +1 -0
  121. data/doc/cxxapi/inherit__graph__0.md5 +1 -0
  122. data/doc/cxxapi/inherit__graph__0.png +0 -0
  123. data/doc/cxxapi/inherit__graph__1.map +1 -0
  124. data/doc/cxxapi/inherit__graph__1.md5 +1 -0
  125. data/doc/cxxapi/inherit__graph__1.png +0 -0
  126. data/doc/cxxapi/inherit__graph__10.map +1 -0
  127. data/doc/cxxapi/inherit__graph__10.md5 +1 -0
  128. data/doc/cxxapi/inherit__graph__10.png +0 -0
  129. data/doc/cxxapi/inherit__graph__11.map +1 -0
  130. data/doc/cxxapi/inherit__graph__11.md5 +1 -0
  131. data/doc/cxxapi/inherit__graph__11.png +0 -0
  132. data/doc/cxxapi/inherit__graph__12.map +1 -0
  133. data/doc/cxxapi/inherit__graph__12.md5 +1 -0
  134. data/doc/cxxapi/inherit__graph__12.png +0 -0
  135. data/doc/cxxapi/inherit__graph__13.map +1 -0
  136. data/doc/cxxapi/inherit__graph__13.md5 +1 -0
  137. data/doc/cxxapi/inherit__graph__13.png +0 -0
  138. data/doc/cxxapi/inherit__graph__14.map +2 -0
  139. data/doc/cxxapi/inherit__graph__14.md5 +1 -0
  140. data/doc/cxxapi/inherit__graph__14.png +0 -0
  141. data/doc/cxxapi/inherit__graph__15.map +1 -0
  142. data/doc/cxxapi/inherit__graph__15.md5 +1 -0
  143. data/doc/cxxapi/inherit__graph__15.png +0 -0
  144. data/doc/cxxapi/inherit__graph__16.map +1 -0
  145. data/doc/cxxapi/inherit__graph__16.md5 +1 -0
  146. data/doc/cxxapi/inherit__graph__16.png +0 -0
  147. data/doc/cxxapi/inherit__graph__17.map +1 -0
  148. data/doc/cxxapi/inherit__graph__17.md5 +1 -0
  149. data/doc/cxxapi/inherit__graph__17.png +0 -0
  150. data/doc/cxxapi/inherit__graph__18.map +1 -0
  151. data/doc/cxxapi/inherit__graph__18.md5 +1 -0
  152. data/doc/cxxapi/inherit__graph__18.png +0 -0
  153. data/doc/cxxapi/inherit__graph__19.map +2 -0
  154. data/doc/cxxapi/inherit__graph__19.md5 +1 -0
  155. data/doc/cxxapi/inherit__graph__19.png +0 -0
  156. data/doc/cxxapi/inherit__graph__2.map +1 -0
  157. data/doc/cxxapi/inherit__graph__2.md5 +1 -0
  158. data/doc/cxxapi/inherit__graph__2.png +0 -0
  159. data/doc/cxxapi/inherit__graph__20.map +1 -0
  160. data/doc/cxxapi/inherit__graph__20.md5 +1 -0
  161. data/doc/cxxapi/inherit__graph__20.png +0 -0
  162. data/doc/cxxapi/inherit__graph__21.map +1 -0
  163. data/doc/cxxapi/inherit__graph__21.md5 +1 -0
  164. data/doc/cxxapi/inherit__graph__21.png +0 -0
  165. data/doc/cxxapi/inherit__graph__3.map +1 -0
  166. data/doc/cxxapi/inherit__graph__3.md5 +1 -0
  167. data/doc/cxxapi/inherit__graph__3.png +0 -0
  168. data/doc/cxxapi/inherit__graph__4.map +1 -0
  169. data/doc/cxxapi/inherit__graph__4.md5 +1 -0
  170. data/doc/cxxapi/inherit__graph__4.png +0 -0
  171. data/doc/cxxapi/inherit__graph__5.map +1 -0
  172. data/doc/cxxapi/inherit__graph__5.md5 +1 -0
  173. data/doc/cxxapi/inherit__graph__5.png +0 -0
  174. data/doc/cxxapi/inherit__graph__6.map +1 -0
  175. data/doc/cxxapi/inherit__graph__6.md5 +1 -0
  176. data/doc/cxxapi/inherit__graph__6.png +0 -0
  177. data/doc/cxxapi/inherit__graph__7.map +1 -0
  178. data/doc/cxxapi/inherit__graph__7.md5 +1 -0
  179. data/doc/cxxapi/inherit__graph__7.png +0 -0
  180. data/doc/cxxapi/inherit__graph__8.map +1 -0
  181. data/doc/cxxapi/inherit__graph__8.md5 +1 -0
  182. data/doc/cxxapi/inherit__graph__8.png +0 -0
  183. data/doc/cxxapi/inherit__graph__9.map +2 -0
  184. data/doc/cxxapi/inherit__graph__9.md5 +1 -0
  185. data/doc/cxxapi/inherit__graph__9.png +0 -0
  186. data/doc/cxxapi/inherits.html +102 -0
  187. data/doc/cxxapi/main.html +27 -0
  188. data/doc/cxxapi/modules.html +34 -0
  189. data/doc/cxxapi/namespacePassenger.html +208 -0
  190. data/doc/cxxapi/namespacePassenger_1_1InterruptableCalls.html +43 -0
  191. data/doc/cxxapi/namespacemembers.html +70 -0
  192. data/doc/cxxapi/namespacemembers_func.html +66 -0
  193. data/doc/cxxapi/namespacemembers_type.html +46 -0
  194. data/doc/cxxapi/namespaces.html +35 -0
  195. data/doc/cxxapi/structPassenger_1_1AnythingToString-members.html +33 -0
  196. data/doc/cxxapi/structPassenger_1_1AnythingToString.html +49 -0
  197. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4-members.html +33 -0
  198. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4.html +49 -0
  199. data/doc/cxxapi/tab_b.gif +0 -0
  200. data/doc/cxxapi/tab_l.gif +0 -0
  201. data/doc/cxxapi/tab_r.gif +0 -0
  202. data/doc/cxxapi/tabs.css +102 -0
  203. data/doc/cxxapi/tree.html +167 -0
  204. data/doc/rdoc/classes/ConditionVariable.html +160 -0
  205. data/doc/rdoc/classes/Exception.html +120 -0
  206. data/doc/rdoc/classes/GC.html +113 -0
  207. data/doc/rdoc/classes/IO.html +150 -0
  208. data/doc/rdoc/classes/Passenger.html +136 -0
  209. data/doc/rdoc/classes/Passenger/AbstractRequestHandler.html +402 -0
  210. data/doc/rdoc/classes/Passenger/AbstractServer.html +649 -0
  211. data/doc/rdoc/classes/Passenger/AbstractServer/ServerAlreadyStarted.html +97 -0
  212. data/doc/rdoc/classes/Passenger/AbstractServer/ServerError.html +96 -0
  213. data/doc/rdoc/classes/Passenger/AbstractServer/ServerNotStarted.html +97 -0
  214. data/doc/rdoc/classes/Passenger/AbstractServer/UnknownMessage.html +96 -0
  215. data/doc/rdoc/classes/Passenger/AppInitError.html +137 -0
  216. data/doc/rdoc/classes/Passenger/Application.html +298 -0
  217. data/doc/rdoc/classes/Passenger/ConsoleTextTemplate.html +172 -0
  218. data/doc/rdoc/classes/Passenger/FrameworkInitError.html +143 -0
  219. data/doc/rdoc/classes/Passenger/HTMLTemplate.html +175 -0
  220. data/doc/rdoc/classes/Passenger/InitializationError.html +141 -0
  221. data/doc/rdoc/classes/Passenger/MessageChannel.html +488 -0
  222. data/doc/rdoc/classes/Passenger/NativeSupport.html +320 -0
  223. data/doc/rdoc/classes/Passenger/SpawnManager.html +375 -0
  224. data/doc/rdoc/classes/Passenger/UnknownError.html +125 -0
  225. data/doc/rdoc/classes/Passenger/Utils.html +578 -0
  226. data/doc/rdoc/classes/Passenger/VersionNotFound.html +140 -0
  227. data/doc/rdoc/classes/PlatformInfo.html +302 -0
  228. data/doc/rdoc/classes/RakeExtensions.html +197 -0
  229. data/doc/rdoc/created.rid +1 -0
  230. data/doc/rdoc/files/DEVELOPERS_TXT.html +230 -0
  231. data/doc/rdoc/files/README.html +150 -0
  232. data/doc/rdoc/files/ext/passenger/native_support_c.html +92 -0
  233. data/doc/rdoc/files/lib/passenger/abstract_request_handler_rb.html +126 -0
  234. data/doc/rdoc/files/lib/passenger/abstract_server_rb.html +127 -0
  235. data/doc/rdoc/files/lib/passenger/application_rb.html +123 -0
  236. data/doc/rdoc/files/lib/passenger/console_text_template_rb.html +122 -0
  237. data/doc/rdoc/files/lib/passenger/constants_rb.html +114 -0
  238. data/doc/rdoc/files/lib/passenger/dependencies_rb.html +130 -0
  239. data/doc/rdoc/files/lib/passenger/exceptions_rb.html +118 -0
  240. data/doc/rdoc/files/lib/passenger/html_template_rb.html +122 -0
  241. data/doc/rdoc/files/lib/passenger/message_channel_rb.html +118 -0
  242. data/doc/rdoc/files/lib/passenger/platform_info_rb.html +122 -0
  243. data/doc/rdoc/files/lib/passenger/simple_benchmarking_rb.html +118 -0
  244. data/doc/rdoc/files/lib/passenger/spawn_manager_rb.html +139 -0
  245. data/doc/rdoc/files/lib/passenger/utils_rb.html +130 -0
  246. data/doc/rdoc/files/lib/rake/extensions_rb.html +122 -0
  247. data/doc/rdoc/fr_class_index.html +71 -0
  248. data/doc/rdoc/fr_file_index.html +63 -0
  249. data/doc/rdoc/fr_method_index.html +120 -0
  250. data/doc/rdoc/index.html +26 -0
  251. data/doc/rdoc/rdoc-style.css +187 -0
  252. data/ext/apache2/ApplicationPool.h +12 -0
  253. data/ext/apache2/ApplicationPoolServer.h +6 -0
  254. data/ext/apache2/ApplicationPoolServerExecutable.cpp +6 -0
  255. data/ext/apache2/Bucket.cpp +63 -69
  256. data/ext/apache2/Bucket.h +2 -0
  257. data/ext/apache2/Configuration.cpp +24 -0
  258. data/ext/apache2/Configuration.h +16 -1
  259. data/ext/apache2/Hooks.cpp +5 -3
  260. data/ext/apache2/StandardApplicationPool.h +30 -11
  261. data/lib/passenger/abstract_request_handler.rb +3 -0
  262. data/lib/passenger/abstract_server.rb +3 -3
  263. data/lib/passenger/application.rb +4 -2
  264. data/lib/passenger/dependencies.rb +10 -1
  265. data/lib/passenger/platform_info.rb +40 -1
  266. data/lib/passenger/rack/request_handler.rb +4 -2
  267. data/lib/passenger/railz/application_spawner.rb +7 -1
  268. data/lib/passenger/utils.rb +6 -8
  269. data/lib/rake/gempackagetask.rb +0 -6
  270. data/lib/rake/rdoctask.rb +84 -23
  271. data/test/ApplicationPoolTest.cpp +36 -0
  272. data/test/ruby/rails/minimal_spawner_spec.rb +8 -0
  273. data/test/ruby/utils_spec.rb +33 -0
  274. data/test/stub/rails_apps/foobar/config/environments/production.rb +0 -1
  275. metadata +262 -5
data/Rakefile CHANGED
@@ -24,10 +24,12 @@ require 'rake/extensions'
24
24
  require 'rake/cplusplus'
25
25
  require 'passenger/platform_info'
26
26
 
27
+ verbose true
28
+
27
29
  ##### Configuration
28
30
 
29
31
  # Don't forget to edit Configuration.h too
30
- PACKAGE_VERSION = "2.0.3"
32
+ PACKAGE_VERSION = "2.0.4"
31
33
  OPTIMIZE = ["yes", "on", "true"].include?(ENV['OPTIMIZE'])
32
34
 
33
35
  include PlatformInfo
@@ -35,6 +37,7 @@ APXS2.nil? and raise "Could not find 'apxs' or 'apxs2'."
35
37
  APACHE2CTL.nil? and raise "Could not find 'apachectl' or 'apache2ctl'."
36
38
  HTTPD.nil? and raise "Could not find the Apache web server binary."
37
39
  APR_FLAGS.nil? and raise "Could not find Apache Portable Runtime (APR)."
40
+ APU_FLAGS.nil? and raise "Could not find Apache Portable Runtime Utility (APU)."
38
41
 
39
42
  CXX = "g++"
40
43
  THREADING_FLAGS = "-D_REENTRANT"
@@ -107,7 +110,7 @@ end
107
110
  ##### Apache module
108
111
 
109
112
  class APACHE2
110
- CXXFLAGS = "-I.. -fPIC #{OPTIMIZATION_FLAGS} #{APR_FLAGS} #{APXS2_FLAGS} #{CXXFLAGS}"
113
+ CXXFLAGS = "-I.. -fPIC #{OPTIMIZATION_FLAGS} #{APR_FLAGS} #{APU_FLAGS} #{APXS2_FLAGS} #{::CXXFLAGS}"
111
114
  OBJECTS = {
112
115
  'Configuration.o' => %w(Configuration.cpp Configuration.h),
113
116
  'Bucket.o' => %w(Bucket.cpp Bucket.h),
@@ -184,8 +187,8 @@ end
184
187
  ##### Unit tests
185
188
 
186
189
  class TEST
187
- CXXFLAGS = ::CXXFLAGS + " -Isupport -DTESTING_SPAWN_MANAGER -DTESTING_APPLICATION_POOL "
188
- AP2_FLAGS = "-I../ext/apache2 -I../ext #{APR_FLAGS}"
190
+ CXXFLAGS = "#{::CXXFLAGS} -Isupport -DTESTING_SPAWN_MANAGER -DTESTING_APPLICATION_POOL "
191
+ AP2_FLAGS = "-I../ext/apache2 -I../ext #{APR_FLAGS} #{APU_FLAGS}"
189
192
 
190
193
  AP2_OBJECTS = {
191
194
  'CxxTestMain.o' => %w(CxxTestMain.cpp),
@@ -368,7 +371,7 @@ subdir 'doc' do
368
371
  end
369
372
  end
370
373
 
371
- Rake::RDocTask.new do |rd|
374
+ Rake::RDocTask.new(:clobber_rdoc => "rdoc:clobber", :rerdoc => "rdoc:force") do |rd|
372
375
  rd.main = "README"
373
376
  rd.rdoc_dir = "doc/rdoc"
374
377
  rd.rdoc_files.include("README", "DEVELOPERS.TXT",
@@ -47,6 +47,7 @@ class Installer
47
47
  Dependencies::Apache2,
48
48
  Dependencies::Apache2_DevHeaders,
49
49
  Dependencies::APR_DevHeaders,
50
+ Dependencies::APU_DevHeaders,
50
51
  Dependencies::FastThread,
51
52
  Dependencies::Rack
52
53
  ]
@@ -187,7 +188,11 @@ private
187
188
  color_puts '<banner>Compiling and installing Apache 2 module...</banner>'
188
189
  puts "cd #{PASSENGER_ROOT}"
189
190
  puts "rake clean apache2"
190
- return system("rake", "clean", "apache2")
191
+ if ENV['TRACE']
192
+ return system("rake", "--trace", "clean", "apache2")
193
+ else
194
+ return system("rake", "clean", "apache2")
195
+ end
191
196
  end
192
197
 
193
198
  def show_apache2_config_snippets
@@ -119,6 +119,11 @@ information:
119
119
  for all keys app_root in restart_times:
120
120
  apps.has_key(app_root)
121
121
 
122
+ - waiting_on_global_queue: integer
123
+ If global queuing mode is enabled, then when get() is waiting for a backend
124
+ process to become idle, this variable will be incremented. When get() is done
125
+ waiting, this variable will be decremented.
126
+
122
127
  - app_instance_count: map[string => unsigned int]
123
128
  Maps an application root to the number of spawned applications.
124
129
 
@@ -197,13 +202,22 @@ function spawn_or_use_existing(app_root):
197
202
  # All apps are active, and the pool is full.
198
203
  # -OR-
199
204
  # All apps are active and the number of max instances
200
- # spawned for this application has been reached.
205
+ # spawned for this application domain has been reached.
201
206
  #
202
207
  # We're not allowed to spawn a new application instance.
203
- # So we connect to an already active application. This connection
204
- # will be put into that application's queue.
205
- container = a container in _list_ with the smallest _session_ value
206
- list.move_to_back(container.iterator)
208
+ if use_global_queue:
209
+ # So we wait until _active_ has changed, then
210
+ # we restart this function and try again.
211
+ waiting_on_global_queue++
212
+ wait until _active_ has changed
213
+ waiting_on_global_queue--
214
+ goto beginning of function
215
+ else:
216
+ # So we connect to an already active application.
217
+ # This connection will be put into that
218
+ # application's private queue.
219
+ container = a container in _list_ with the smallest _session_ value
220
+ list.move_to_back(container.iterator)
207
221
  else:
208
222
  # All apps are active, but the pool hasn't reached its
209
223
  # maximum yet. So we spawn a new app.
@@ -221,6 +235,10 @@ function spawn_or_use_existing(app_root):
221
235
  wait until
222
236
  (active < max) and
223
237
  (max_per_app == 0 or app_instance_count[app_root] < max_per_app)
238
+ # FIXME: there's a possible concurrency issue here. After
239
+ # waiting and having reacquired the lock, the state might have
240
+ # been completely changed, and there may now exist an instance
241
+ # of this application domain.
224
242
  if count == max:
225
243
  # Here we are in a though situation. There are several
226
244
  # apps which are inactive, and none of them have
@@ -0,0 +1,778 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <meta name="generator" content="AsciiDoc 8.2.7" />
7
+ <style type="text/css">
8
+ /* Debug borders */
9
+ p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
10
+ /*
11
+ border: 1px solid red;
12
+ */
13
+ }
14
+
15
+ body {
16
+ margin: 1em 5% 1em 5%;
17
+ }
18
+
19
+ a {
20
+ color: blue;
21
+ text-decoration: underline;
22
+ }
23
+ a:visited {
24
+ color: fuchsia;
25
+ }
26
+
27
+ em {
28
+ font-style: italic;
29
+ color: navy;
30
+ }
31
+
32
+ strong {
33
+ font-weight: bold;
34
+ color: #083194;
35
+ }
36
+
37
+ tt {
38
+ color: navy;
39
+ }
40
+
41
+ h1, h2, h3, h4, h5, h6 {
42
+ color: #527bbd;
43
+ font-family: sans-serif;
44
+ margin-top: 1.2em;
45
+ margin-bottom: 0.5em;
46
+ line-height: 1.3;
47
+ }
48
+
49
+ h1, h2, h3 {
50
+ border-bottom: 2px solid silver;
51
+ }
52
+ h2 {
53
+ padding-top: 0.5em;
54
+ }
55
+ h3 {
56
+ float: left;
57
+ }
58
+ h3 + * {
59
+ clear: left;
60
+ }
61
+
62
+ div.sectionbody {
63
+ font-family: serif;
64
+ margin-left: 0;
65
+ }
66
+
67
+ hr {
68
+ border: 1px solid silver;
69
+ }
70
+
71
+ p {
72
+ margin-top: 0.5em;
73
+ margin-bottom: 0.5em;
74
+ }
75
+
76
+ ul, ol, li > p {
77
+ margin-top: 0;
78
+ }
79
+
80
+ pre {
81
+ padding: 0;
82
+ margin: 0;
83
+ }
84
+
85
+ span#author {
86
+ color: #527bbd;
87
+ font-family: sans-serif;
88
+ font-weight: bold;
89
+ font-size: 1.1em;
90
+ }
91
+ span#email {
92
+ }
93
+ span#revision {
94
+ font-family: sans-serif;
95
+ }
96
+
97
+ div#footer {
98
+ font-family: sans-serif;
99
+ font-size: small;
100
+ border-top: 2px solid silver;
101
+ padding-top: 0.5em;
102
+ margin-top: 4.0em;
103
+ }
104
+ div#footer-text {
105
+ float: left;
106
+ padding-bottom: 0.5em;
107
+ }
108
+ div#footer-badges {
109
+ float: right;
110
+ padding-bottom: 0.5em;
111
+ }
112
+
113
+ div#preamble,
114
+ div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
115
+ div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
116
+ div.admonitionblock {
117
+ margin-right: 10%;
118
+ margin-top: 1.5em;
119
+ margin-bottom: 1.5em;
120
+ }
121
+ div.admonitionblock {
122
+ margin-top: 2.5em;
123
+ margin-bottom: 2.5em;
124
+ }
125
+
126
+ div.content { /* Block element content. */
127
+ padding: 0;
128
+ }
129
+
130
+ /* Block element titles. */
131
+ div.title, caption.title {
132
+ color: #527bbd;
133
+ font-family: sans-serif;
134
+ font-weight: bold;
135
+ text-align: left;
136
+ margin-top: 1.0em;
137
+ margin-bottom: 0.5em;
138
+ }
139
+ div.title + * {
140
+ margin-top: 0;
141
+ }
142
+
143
+ td div.title:first-child {
144
+ margin-top: 0.0em;
145
+ }
146
+ div.content div.title:first-child {
147
+ margin-top: 0.0em;
148
+ }
149
+ div.content + div.title {
150
+ margin-top: 0.0em;
151
+ }
152
+
153
+ div.sidebarblock > div.content {
154
+ background: #ffffee;
155
+ border: 1px solid silver;
156
+ padding: 0.5em;
157
+ }
158
+
159
+ div.listingblock {
160
+ margin-right: 0%;
161
+ }
162
+ div.listingblock > div.content {
163
+ border: 1px solid silver;
164
+ background: #f4f4f4;
165
+ padding: 0.5em;
166
+ }
167
+
168
+ div.quoteblock {
169
+ padding-left: 2.0em;
170
+ }
171
+ div.quoteblock > div.attribution {
172
+ padding-top: 0.5em;
173
+ text-align: right;
174
+ }
175
+
176
+ div.verseblock {
177
+ padding-left: 2.0em;
178
+ }
179
+ div.verseblock > div.content {
180
+ white-space: pre;
181
+ }
182
+ div.verseblock > div.attribution {
183
+ padding-top: 0.75em;
184
+ text-align: left;
185
+ }
186
+ /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
187
+ div.verseblock + div.attribution {
188
+ text-align: left;
189
+ }
190
+
191
+ div.admonitionblock .icon {
192
+ vertical-align: top;
193
+ font-size: 1.1em;
194
+ font-weight: bold;
195
+ text-decoration: underline;
196
+ color: #527bbd;
197
+ padding-right: 0.5em;
198
+ }
199
+ div.admonitionblock td.content {
200
+ padding-left: 0.5em;
201
+ border-left: 2px solid silver;
202
+ }
203
+
204
+ div.exampleblock > div.content {
205
+ border-left: 2px solid silver;
206
+ padding: 0.5em;
207
+ }
208
+
209
+ div.imageblock div.content { padding-left: 0; }
210
+ div.imageblock img { border: 1px solid silver; }
211
+ span.image img { border-style: none; }
212
+
213
+ dl {
214
+ margin-top: 0.8em;
215
+ margin-bottom: 0.8em;
216
+ }
217
+ dt {
218
+ margin-top: 0.5em;
219
+ margin-bottom: 0;
220
+ font-style: normal;
221
+ }
222
+ dd > *:first-child {
223
+ margin-top: 0.1em;
224
+ }
225
+
226
+ ul, ol {
227
+ list-style-position: outside;
228
+ }
229
+ div.olist > ol {
230
+ list-style-type: decimal;
231
+ }
232
+ div.olist2 > ol {
233
+ list-style-type: lower-alpha;
234
+ }
235
+
236
+ div.tableblock > table {
237
+ border: 3px solid #527bbd;
238
+ }
239
+ thead {
240
+ font-family: sans-serif;
241
+ font-weight: bold;
242
+ }
243
+ tfoot {
244
+ font-weight: bold;
245
+ }
246
+
247
+ div.hlist {
248
+ margin-top: 0.8em;
249
+ margin-bottom: 0.8em;
250
+ }
251
+ div.hlist td {
252
+ padding-bottom: 15px;
253
+ }
254
+ td.hlist1 {
255
+ vertical-align: top;
256
+ font-style: normal;
257
+ padding-right: 0.8em;
258
+ }
259
+ td.hlist2 {
260
+ vertical-align: top;
261
+ }
262
+
263
+ @media print {
264
+ div#footer-badges { display: none; }
265
+ }
266
+
267
+ div#toctitle {
268
+ color: #527bbd;
269
+ font-family: sans-serif;
270
+ font-size: 1.1em;
271
+ font-weight: bold;
272
+ margin-top: 1.0em;
273
+ margin-bottom: 0.1em;
274
+ }
275
+
276
+ div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
277
+ margin-top: 0;
278
+ margin-bottom: 0;
279
+ }
280
+ div.toclevel2 {
281
+ margin-left: 2em;
282
+ font-size: 0.9em;
283
+ }
284
+ div.toclevel3 {
285
+ margin-left: 4em;
286
+ font-size: 0.9em;
287
+ }
288
+ div.toclevel4 {
289
+ margin-left: 6em;
290
+ font-size: 0.9em;
291
+ }
292
+ /* Workarounds for IE6's broken and incomplete CSS2. */
293
+
294
+ div.sidebar-content {
295
+ background: #ffffee;
296
+ border: 1px solid silver;
297
+ padding: 0.5em;
298
+ }
299
+ div.sidebar-title, div.image-title {
300
+ color: #527bbd;
301
+ font-family: sans-serif;
302
+ font-weight: bold;
303
+ margin-top: 0.0em;
304
+ margin-bottom: 0.5em;
305
+ }
306
+
307
+ div.listingblock div.content {
308
+ border: 1px solid silver;
309
+ background: #f4f4f4;
310
+ padding: 0.5em;
311
+ }
312
+
313
+ div.quoteblock-attribution {
314
+ padding-top: 0.5em;
315
+ text-align: right;
316
+ }
317
+
318
+ div.verseblock-content {
319
+ white-space: pre;
320
+ }
321
+ div.verseblock-attribution {
322
+ padding-top: 0.75em;
323
+ text-align: left;
324
+ }
325
+
326
+ div.exampleblock-content {
327
+ border-left: 2px solid silver;
328
+ padding-left: 0.5em;
329
+ }
330
+
331
+ /* IE6 sets dynamically generated links as visited. */
332
+ div#toc a:visited { color: blue; }
333
+
334
+ /* Because IE6 child selector is broken. */
335
+ div.olist2 ol {
336
+ list-style-type: lower-alpha;
337
+ }
338
+ div.olist2 div.olist ol {
339
+ list-style-type: decimal;
340
+ }
341
+ </style>
342
+ <script type="text/javascript">
343
+ /*<![CDATA[*/
344
+ window.onload = function(){generateToc(3)}
345
+ /* Author: Mihai Bazon, September 2002
346
+ * http://students.infoiasi.ro/~mishoo
347
+ *
348
+ * Table Of Content generator
349
+ * Version: 0.4
350
+ *
351
+ * Feel free to use this script under the terms of the GNU General Public
352
+ * License, as long as you do not remove or alter this notice.
353
+ */
354
+
355
+ /* modified by Troy D. Hanson, September 2006. License: GPL */
356
+ /* modified by Stuart Rackham, October 2006. License: GPL */
357
+
358
+ function getText(el) {
359
+ var text = "";
360
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {
361
+ if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
362
+ text += i.data;
363
+ else if (i.firstChild != null)
364
+ text += getText(i);
365
+ }
366
+ return text;
367
+ }
368
+
369
+ function TocEntry(el, text, toclevel) {
370
+ this.element = el;
371
+ this.text = text;
372
+ this.toclevel = toclevel;
373
+ }
374
+
375
+ function tocEntries(el, toclevels) {
376
+ var result = new Array;
377
+ var re = new RegExp('[hH]([2-'+(toclevels+1)+'])');
378
+ // Function that scans the DOM tree for header elements (the DOM2
379
+ // nodeIterator API would be a better technique but not supported by all
380
+ // browsers).
381
+ var iterate = function (el) {
382
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {
383
+ if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
384
+ var mo = re.exec(i.tagName)
385
+ if (mo)
386
+ result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
387
+ iterate(i);
388
+ }
389
+ }
390
+ }
391
+ iterate(el);
392
+ return result;
393
+ }
394
+
395
+ // This function does the work. toclevels = 1..4.
396
+ function generateToc(toclevels) {
397
+ var toc = document.getElementById("toc");
398
+ var entries = tocEntries(document.getElementsByTagName("body")[0], toclevels);
399
+ for (var i = 0; i < entries.length; ++i) {
400
+ var entry = entries[i];
401
+ if (entry.element.id == "")
402
+ entry.element.id = "toc" + i;
403
+ var a = document.createElement("a");
404
+ a.href = "#" + entry.element.id;
405
+ a.appendChild(document.createTextNode(entry.text));
406
+ var div = document.createElement("div");
407
+ div.appendChild(a);
408
+ div.className = "toclevel" + entry.toclevel;
409
+ toc.appendChild(div);
410
+ }
411
+ if (entries.length == 0)
412
+ document.getElementById("header").removeChild(toc);
413
+ }
414
+ /*]]>*/
415
+ </script>
416
+ <title>Passenger architectural overview</title>
417
+ </head>
418
+ <body>
419
+ <div id="header">
420
+ <h1>Passenger architectural overview</h1>
421
+ <div id="toc">
422
+ <div id="toctitle">Table of Contents</div>
423
+ <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
424
+ </div>
425
+ </div>
426
+ <div id="preamble">
427
+ <div class="sectionbody">
428
+ <div class="para"><p>This document describes Passenger's architure in a global way. The purpose of
429
+ this document is to lower the barrier to entry for new contributors, as well
430
+ as to explain (some of the) design choices that we have made.</p></div>
431
+ <div class="para"><p>Or it can be a fun read for people who just want to know how Passenger works.</p></div>
432
+ </div>
433
+ </div>
434
+ <h2 id="_about_the_involved_technologies">1. About the involved technologies</h2>
435
+ <div class="sectionbody">
436
+ <h3 id="typical_web_applications">1.1. Typical web applications</h3><div style="clear:left"></div>
437
+ <div class="para"><p>Before we describe Passenger, it is important to understand how typical web
438
+ applications work, from the point of view of someone who wants to connect the
439
+ application to a web server.</p></div>
440
+ <div class="para"><p>A typical, isolated, web application accepts an HTTP request from some I/O
441
+ channel, processes it internally, and outputs an HTTP response, which is sent
442
+ back to the client. This is done in a loop, until the application is commanded
443
+ to exit. This does not necessarily mean that the web application speaks HTTP
444
+ directly: it just means that the web application accepts some kind of
445
+ representation of an HTTP request.</p></div>
446
+ <div class="para"><p><span class="image">
447
+ <img src="images/typical_isolated_web_application.png" alt="Architecture of a typical web application in isolation" title="Architecture of a typical web application in isolation" />
448
+ </span></p></div>
449
+ <div class="para"><p>Few web applications are accessible directly by HTTP clients. Common setups
450
+ are:</p></div>
451
+ <div class="olist"><ol>
452
+ <li>
453
+ <p>
454
+ The web application is contained in an application server. This application
455
+ server may or may not be able to contain multiple web applications. The
456
+ application server is then connected to the web server. The web server
457
+ dispatches requests to the application server, which in turn dispatches
458
+ requests to the correct web application, in a format that the web application
459
+ understands. Conversely, HTTP responses outputted by the web application are
460
+ sent to the application server, which in turn sends them to the web server,
461
+ and eventually to the HTTP client.
462
+ </p>
463
+ <div class="para"><p>A typical example of such a setup is a J2EE application, contained in the
464
+ Tomcat web server, behind the Apache web server.</p></div>
465
+ </li>
466
+ <li>
467
+ <p>
468
+ The web application is contained in a web server. In this case, the web
469
+ server acts like an application server. This is the case for PHP applications,
470
+ on Apache servers with <em>mod_php</em>. Note that this does not necessarily mean
471
+ that the web application is run inside the same process as the web server:
472
+ it just means that the web server manages applications.
473
+ </p>
474
+ </li>
475
+ <li>
476
+ <p>
477
+ The web application <strong>is</strong> a web server, and can accept HTTP requests
478
+ directly. This is the case for the Trac bug tracking system, running in its
479
+ standalone server. In many setups, such web applications sit behind a different
480
+ web server, instead of accepting HTTP requests directly. The frontend web
481
+ server acts like a reverse HTTP proxy.
482
+ </p>
483
+ </li>
484
+ <li>
485
+ <p>
486
+ The web application does not speak HTTP directly, but is connected directly
487
+ to the web server through some communication adapter. CGI, FastCGI and SCGI
488
+ are good examples of this.
489
+ </p>
490
+ </li>
491
+ </ol></div>
492
+ <div class="para"><p>These descriptions are true for virtually all web applications, whether they're
493
+ based on PHP, Django, J2EE, ASP.NET, Ruby on Rails, or whatever. Note that all
494
+ of these setups provide the same functionality, i.e. no setup can do something
495
+ that a different setup can't. The critical reader will notice that all of these
496
+ setups are identical to the one described in the first diagram, if the
497
+ combination of web servers, application servers, web applications etc. are
498
+ considered to be a single entity; a black box if you will.</p></div>
499
+ <div class="para"><p>It should also be noted that these setups do not enforce any particular
500
+ I/O processing implementation. The web servers, application servers, web
501
+ applications, etc. could process I/O serially (i.e. one request at a time),
502
+ could multiplex I/O with a single thread (e.g. by using <tt>select(2)</tt> or
503
+ <tt>poll(2)</tt>) or it could process I/O with multiple threads and/or multiple
504
+ processes.</p></div>
505
+ <div class="para"><p>Of course, there are many variations possible. For example, load balancers
506
+ could be used. But that is outside the scope of this document.</p></div>
507
+ <h3 id="_ruby_on_rails">1.2. Ruby on Rails</h3><div style="clear:left"></div>
508
+ <div class="para"><p>Every Ruby on Rails application has a <em>dispatcher</em>. This dispatcher is
509
+ responsible for processing HTTP requests. It does not speak HTTP directly.
510
+ Instead, it accepts data structures that contain the information of an
511
+ HTTP request. Thus, the dispatcher is particularly interesting to
512
+ developers who wish to develop software which connects Ruby on Rails to an
513
+ HTTP processing layer (e.g. a web server).</p></div>
514
+ <div class="para"><p>The Ruby on Rails dispatcher can only process requests serially, i.e. one
515
+ at a time. It is not possible to process two requests at the same time
516
+ with threads, because parts of Ruby on Rails are not thread-safe. (In
517
+ practice, this isn't as big of a problem as some people imagine. This will
518
+ be elaborated further in <a href="#concurrent_requests">Handling of concurrent requests</a>.)</p></div>
519
+ <div class="para"><p>A particularly interesting thing to note, is that a lot of the memory
520
+ occupied by Ruby on Rails applications is spent on storing the program code
521
+ (i.e. the <a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract
522
+ syntax tree (AST)</a>) in memory. This is observed through the use of the
523
+ memory statistics function in <a href="http://www.rubyenterpriseedition.com/">Ruby
524
+ Enterprise Edition</a>. Also, a lot of the startup time of a Ruby on Rails
525
+ application is spent on bootstrapping the Rails framework.</p></div>
526
+ <h3 id="_apache">1.3. Apache</h3><div style="clear:left"></div>
527
+ <div class="para"><p>The Apache web server has a pluggable I/O multiprocessing (the ability to
528
+ handle more than 1 concurrent HTTP client at the same time) architecture. An
529
+ Apache module which implements a particular multiprocessing strategy, is called
530
+ a Multi-Processing Module (MPM). The
531
+ <a href="http://httpd.apache.org/docs/2.0/mod/prefork.html">prefork MPM</a> &#8212; which
532
+ also happens to be the default &#8212; appears to be the most popular one. This MPM
533
+ spawns multiple worker child processes. HTTP requests are first accepted by a
534
+ so-called control process, and then forwarded to one of the worker processes.
535
+ The next section contains a diagram which shows the prefork MPM's architecture.</p></div>
536
+ </div>
537
+ <h2 id="_passenger_architecture">2. Passenger architecture</h2>
538
+ <div class="sectionbody">
539
+ <h3 id="_overview">2.1. Overview</h3><div style="clear:left"></div>
540
+ <div class="para"><p>Passenger's architecture is a lot like setup #2 described in
541
+ <a href="#typical_web_applications">Typical web applications</a>. In other words,
542
+ Passenger extends Apache and allows it to act like an application server.
543
+ Passenger's architecture &#8212; assuming Apache 2 with the prefork MPM is used &#8212;
544
+ is shown in the following diagram:</p></div>
545
+ <div class="para"><p><span class="image">
546
+ <img src="images/passenger_architecture.png" alt="Passenger's architecture" title="Passenger's architecture" />
547
+ </span></p></div>
548
+ <div class="para"><p>Passenger consists of an Apache module, <em>mod_passenger</em>. This is written in
549
+ C++, and can be found in the directory <em>ext/apache2</em>. The module is active in
550
+ the Apache control process and in all the Apache worker processes. When an
551
+ HTTP request comes in, <em>mod_passenger</em> will check whether the request should
552
+ be handled by a Ruby on Rails application. If so, then <em>mod_passenger</em> will
553
+ spawn the corresponding Rails application (if necessary) and forward the
554
+ request to that application.</p></div>
555
+ <div class="para"><p>It should be noted that the Ruby on Rails application does <strong>not</strong> run in the
556
+ same address space as Apache. This differentiates Passenger from other
557
+ application-server-inside-web-server software such as mod_php, mod_perl and
558
+ mod_ruby. If the Rails application crashes or leak memory, it will have no
559
+ effect on Apache. In fact, stability is one of our highest goals. Passenger
560
+ is carefully designed and implemented so that Apache shouldn't crash because
561
+ of Passenger.</p></div>
562
+ <h3 id="_spawning_and_caching_of_code_and_applications">2.2. Spawning and caching of code and applications</h3><div style="clear:left"></div>
563
+ <div class="para"><p>A very naive implementation of Passenger would spawn a Ruby on Rails
564
+ application every time an HTTP request is received, just like CGI would.
565
+ However, spawning Ruby on Rails applications is expensive. It can take 1 or 2
566
+ seconds on a modern PC, and possibly much longer on a heavily loaded server.
567
+ This overhead is particularily unacceptable on shared hosts. A less naive
568
+ implementation would keep spawned Ruby on Rails application instances alive,
569
+ similar to how Lighttpd's FastCGI implementation works.
570
+ However, this still has several problems:</p></div>
571
+ <div class="olist"><ol>
572
+ <li>
573
+ <p>
574
+ The first request to a Rails website will be slow, and subsequent requests
575
+ will be fast. But the first request to a different Rails website - on the
576
+ same web server - will still be slow.
577
+ </p>
578
+ </li>
579
+ <li>
580
+ <p>
581
+ As we've explained earlier in this article, a lot of memory in a Rails
582
+ application is spent on storing the AST of the Ruby on Rails framework and
583
+ the application. Especially on shared hosts and on memory-constrained
584
+ Virtual Private Servers (VPS), this can be a problem.
585
+ </p>
586
+ </li>
587
+ </ol></div>
588
+ <div class="para"><p>Both of these problems are very much solvable, and we've chosen to do just
589
+ that.</p></div>
590
+ <div class="para"><p>The first problem can be solved by preloading Rails applications, i.e. by
591
+ running the Rails application before a request is ever made to that website.
592
+ This is the approach taken by most Rails hosts, for example in the form of a
593
+ Mongrel cluster which is running all the time. However, this is unacceptable
594
+ for a shared host: such an application would just sit there and waste memory
595
+ even if it's not doing anything. Instead, we've chosen to take a different
596
+ approach, which solves both of the aforementioned problems.</p></div>
597
+ <div class="para"><p>We spawn Rails applications via a <em>spawn server</em>. The spawn server caches Ruby
598
+ on Rails framework code and application code in memory. Spawning a Rails
599
+ application for the first time will still be slow, but subsequent spawn
600
+ attempts will be very fast. Furthermore, because the framework code is cached
601
+ independently from the application code, spawning a different Rails application
602
+ will also be very fast, as long as that application is using a Rails framework
603
+ version that has already been cached.</p></div>
604
+ <div class="para"><p>Another implication of the spawn server is that different Ruby on Rails will
605
+ share memory with each other, thus solving problem #2. This is described in
606
+ detail in <a href="#spawn_server">the next section</a>.</p></div>
607
+ <div class="para"><p>But despite the caching of framework code and application code, spawning is
608
+ still expensive compared to an HTTP request. We want to avoid spawning whenever
609
+ possible. This is why we've introduced the <strong>application pool</strong>. Spawned
610
+ application instances are kept alive, and their handles are stored into this
611
+ pool, allowing each application instance to be reused later. Thus, Passenger
612
+ has very good average case performance.</p></div>
613
+ <div class="para"><p>The application pool is shared between different worker processes. Because the
614
+ worker processes cannot share memory with each other, either shared memory must
615
+ be used to implement the application pool, or a client/server architecture must
616
+ be implemented. We've chosen the latter because it is easier
617
+ to implement. The Apache control process acts like a server for the application
618
+ pool. However, this does not mean that all HTTP request/response data go
619
+ through the control process. A worker process queries the pool for a connection
620
+ session with a Rails application. Once this session has been obtained, the
621
+ worker process will communicate directly with the Rails application.</p></div>
622
+ <div class="para"><p>The application pool is implemented inside <em>mod_passenger</em>. One can find
623
+ detailed documentation about it in
624
+ <a href="cxxapi/index.html">the C++ API documentation</a>,
625
+ in particular the documentation about the <tt>ApplicationPool</tt>,
626
+ <tt>StandardApplicationPool</tt> and <tt>ApplicationPoolServer</tt> classes.</p></div>
627
+ <div class="admonitionblock">
628
+ <table><tr>
629
+ <td class="icon">
630
+ <img src="./images/icons/note.png" alt="Note" />
631
+ </td>
632
+ <td class="content">At the moment, Passenger does not support Apache with the worker MPM
633
+ (which uses threads instead of processes). But because the application pool is
634
+ implemented in a modular way, supporting the worker MPM shouldn't take more
635
+ than 10 lines of code.</td>
636
+ </tr></table>
637
+ </div>
638
+ <div class="para"><p>The application pool is responsible for spawning applications, caching
639
+ spawned applications' handles, and cleaning up applications which have been
640
+ idle for an extended period of time.</p></div>
641
+ <h3 id="spawn_server">2.3. The spawn server</h3><div style="clear:left"></div>
642
+ <div class="para"><p>The spawn server is written in Ruby, and its code can be found in the directory
643
+ <em>lib/passenger</em>. Its main executable is <em>bin/passenger-spawn-server</em>.
644
+ <a href="rdoc/index.html">The spawn server's RDoc documentation</a> documents the
645
+ implementation in detail.</p></div>
646
+ <div class="para"><p>The spawn server consists of 3 logical layers:</p></div>
647
+ <div class="olist"><ol>
648
+ <li>
649
+ <p>
650
+ <strong>The spawn manager.</strong> This is the topmost layer, and acts like a fascade for
651
+ all the underlying layers. Clients who use the spawn server only communicate
652
+ with this layer.
653
+ </p>
654
+ </li>
655
+ <li>
656
+ <p>
657
+ <strong>The framework spawner server.</strong> The spawn manager spawns a framework spawner
658
+ server for each unique Ruby on Rails framework version. Each framework
659
+ spawner server caches the code for exactly one Ruby on Rails framework
660
+ version. A spawn request for an application is forwarded to the framework
661
+ spawner server that contains the correct Ruby on Rails version for the
662
+ application.
663
+ </p>
664
+ </li>
665
+ <li>
666
+ <p>
667
+ <strong>The application spawner server.</strong> This is to the framework spawner server
668
+ what the framework spawner server is to the spawn manager. The framework
669
+ spawner server spawns an application spawner server for each unique Ruby on
670
+ Rails application (here &#8220;application&#8221; does not mean a running process, but
671
+ a set of (source code) files). An application spawner server caches the
672
+ code for exactly one application.
673
+ </p>
674
+ </li>
675
+ </ol></div>
676
+ <div class="para"><p><span class="image">
677
+ <img src="images/spawn_server_architecture.png" alt="The spawn server's architecture" title="The spawn server's architecture" />
678
+ </span></p></div>
679
+ <div class="para"><p>As you can see, we have two layers of code caching: when the spawn server
680
+ receives a request to spawn a new application instance, it will forward the
681
+ request to the correct framework spawner server (and will spawn that framework
682
+ spawner server if it doesn't already exist), which &#8212; in turn &#8212; will forward
683
+ it to the correct application spawner server (which will, again, be created if
684
+ it doesn't already exist).</p></div>
685
+ <div class="para"><p>Each layer is only responsible for the layer directly below. The spawn manager
686
+ only knows about framework spawner servers, and a framework spawner server only
687
+ knows about its application spawner servers. The application spawner server is,
688
+ however, not responsible for managing spawned application instances. If an
689
+ application instance is spawned by mod_passenger, its information will be sent
690
+ back to mod_passenger, which will be fully responsible for managing the
691
+ application instance's life time (through the application pool).</p></div>
692
+ <div class="para"><p>Also note that each layer is a seperate process. This is required because a
693
+ single Ruby process can only load a single Ruby on Rails framework and a
694
+ single application.</p></div>
695
+ <h4 id="_memory_sharing">2.3.1. Memory sharing</h4>
696
+ <div class="para"><p>On most modern Unix operating systems, when a child process is created, it will
697
+ share most of its memory with the parent process. Processes are not supposed to
698
+ be able to access each others' memory, so the operating system makes a copy of
699
+ a piece of memory when it is written to by the parent process or the child
700
+ process. This is called copy-on-write (COW). Detailed background information
701
+ can be found on <a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise
702
+ Edition's website</a>.</p></div>
703
+ <div class="para"><p>The spawn server makes use of this useful fact. Each layer shares its Ruby AST
704
+ memory with all of its lower layers, as long as the AST nodes in question
705
+ haven't been written to. This means that all spawned Rails applications will
706
+ &#8212; if possible &#8212; share the Ruby on Rails framework's code, as well as its own
707
+ application code, with each other. This results in a dramatic reduction in
708
+ memory usage.</p></div>
709
+ <div class="admonitionblock">
710
+ <table><tr>
711
+ <td class="icon">
712
+ <img src="./images/icons/note.png" alt="Note" />
713
+ </td>
714
+ <td class="content">
715
+ <div class="para"><p>Sharing memory only works if <a href="http://www.rubyenterpriseedition.com/">Ruby
716
+ Enterprise Edition</a> is used. This is because the standard Ruby interpreter's
717
+ garbage collector isn't copy-on-write friendly. Please visit the Ruby
718
+ Enterprise Edition website for technical details.</p></div>
719
+ <div class="para"><p>Passenger works fine with standard Ruby. You still get to enjoy reduced Rails
720
+ startup times. You just won't be able to benefit from memory sharing.</p></div>
721
+ </td>
722
+ </tr></table>
723
+ </div>
724
+ <div class="para"><p>Note that <a href="http://rubini.us/">Rubinius</a>'s garbage collector is already
725
+ copy-on-write friendly.</p></div>
726
+ <h3 id="concurrent_requests">2.4. Handling of concurrent requests</h3><div style="clear:left"></div>
727
+ <div class="para"><p>As explained earlier, a single Rails application instance can only handle a
728
+ single request at the same time. This is obviously undesirable. But before we
729
+ dive into the solution, let us take a look how the &#8220;competition&#8221; solves this
730
+ problem. PHP has similar problems: a single PHP script can also process only
731
+ one HTTP request at a time.</p></div>
732
+ <div class="ilist"><ul>
733
+ <li>
734
+ <p>
735
+ mod_php &#8220;solves&#8221; this problem by using Apache's MPM. In other words,
736
+ mod_php doesn't do anything by itself at all. A single Apache worker
737
+ process/thread can only handle 1 PHP request at a time, but Apache spawns
738
+ multiple worker processes/threads.
739
+ </p>
740
+ </li>
741
+ <li>
742
+ <p>
743
+ PHP-FastCGI solves the problem by spawning multiple persistent PHP servers.
744
+ The number of PHP servers is independent from the number of Apache worker
745
+ processes/threads. This approach is a lot like existing Rails setups, in
746
+ which a frontend web server proxies requests to a persistent Mongrel cluster.
747
+ </p>
748
+ </li>
749
+ </ul></div>
750
+ <div class="para"><p>Passenger cannot use the mod_php way because it would force us to spawn a new
751
+ Rails application for each request, which is &#8212; as explained earlier &#8212;
752
+ unacceptably slow. Instead, Passenger uses the PHP-FastCGI approach. We
753
+ maintain a pool of application instances, and whenever a request is received,
754
+ we forward the request to one of the application instances in the pool. The
755
+ size of the pool is configurable, which is useful for administrators of servers
756
+ that are either heavily loaded or have little memory.</p></div>
757
+ <div class="para"><p>The reader might also be interested in studying the application pool's
758
+ algorithm, which is non-trivial. The algorithm is documented in detail in
759
+ <a href="ApplicationPool%20algorithm.txt">ApplicationPool algorithm.txt</a>.</p></div>
760
+ </div>
761
+ <h2 id="_appendix_a_about_this_document">3. Appendix A: About this document</h2>
762
+ <div class="sectionbody">
763
+ <div class="para"><p>The text of this document is licensed under the
764
+ <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons
765
+ Attribution-Share Alike 3.0 Unported License</a>.</p></div>
766
+ <div class="para"><p><span class="image">
767
+ <a class="image" href="link:http://creativecommons.org/licenses/by-sa/3.0/">
768
+ <img src="images/by_sa.png" alt="images/by_sa.png" />
769
+ </a>
770
+ </span></p></div>
771
+ </div>
772
+ <div id="footer">
773
+ <div id="footer-text">
774
+ Last updated 2008-12-01 14:21:21 CEST
775
+ </div>
776
+ </div>
777
+ </body>
778
+ </html>