passenger 4.0.1 → 4.0.2

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.

@@ -1,9 +1,9 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
- <meta name="generator" content="AsciiDoc 8.6.7">
6
- <title>Phusion Passenger Standalone users guide</title>
2
+ <html lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
+ <meta name="generator" content="AsciiDoc 8.6.7">
6
+ <title>Phusion Passenger Standalone users guide</title>
7
7
  <style type="text/css">
8
8
  /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
9
9
 
@@ -603,7 +603,7 @@ div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock
603
603
  div.verseblock { border-left-width: 0; margin-left: 3em; }
604
604
  div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;}
605
605
  div.admonitionblock td.content { border-left: 3px solid #E8E8E8; }
606
- </style>
606
+ </style>
607
607
  <script type="text/javascript">
608
608
  /*<![CDATA[*/
609
609
  var asciidoc = { // Namespace.
@@ -797,7 +797,8 @@ install: function(toclevels) {
797
797
  }
798
798
  asciidoc.install();
799
799
  /*]]>*/
800
- </script><style type="text/css">
800
+ </script>
801
+ <style type="text/css">
801
802
  body {
802
803
  margin: 1em auto 1em auto;
803
804
  padding: 0 1em 0 1em;
@@ -1054,11 +1055,12 @@ pre {
1054
1055
  }
1055
1056
 
1056
1057
  </style>
1057
- </head>
1058
+ </head>
1058
1059
  <body class="article">
1059
1060
  <div id="topbar" style="display: none">
1060
1061
  <div class="title">
1061
- <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAAEV95QVAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sGCRMSACDxkZwAAAAidEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOHqHdDAAAAZUlEQVQY032PSxbAIAjEIlfygL6ePF3UUvpzNjgSYWwqAMEhUQnANnsC7TQPeJpsVWzMuh2sog4vDTUbqP081zECrW4dtdaQGSIKlWluPyjK+VXxAz5XfcKufCzA130AfQHWB30HZxlPaP080xsAAAAASUVORK5CYII=" width="11" height="10" alt=""><a href="javascript:void(Mizuho.smoothlyScrollToToc())">Phusion Passenger Standalone users guide</a>
1062
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAAEV95QVAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sGCRMSACDxkZwAAAAidEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOHqHdDAAAAZUlEQVQY032PSxbAIAjEIlfygL6ePF3UUvpzNjgSYWwqAMEhUQnANnsC7TQPeJpsVWzMuh2sog4vDTUbqP081zECrW4dtdaQGSIKlWluPyjK+VXxAz5XfcKufCzA130AfQHWB30HZxlPaP080xsAAAAASUVORK5CYII=" width="11" height="10" alt="">
1063
+ <a href="javascript:void(Mizuho.smoothlyScrollToToc())">Phusion Passenger Standalone users guide</a>
1062
1064
  </div>
1063
1065
  <a href="javascript:void(0)" id="current_section"></a>
1064
1066
  </div>
@@ -1068,11 +1070,12 @@ pre {
1068
1070
  <div class="sectionbody">
1069
1071
  <div class="paragraph"><p><span class="image">
1070
1072
  <a class="image" href="http://www.phusion.nl/">
1071
- <img src="images/phusion_banner.png" alt="images/phusion_banner.png"></a>
1073
+ <img src="images/phusion_banner.png" alt="images/phusion_banner.png">
1074
+ </a>
1072
1075
  </span></p></div>
1073
1076
  <div class="paragraph"><p>Phusion Passenger Standalone is a web server that allows one to run Ruby web applications.
1074
1077
  Here are some of the highlights:</p></div>
1075
- <div class="ulist"><ul>
1078
+ <div class="ulist"><ul>
1076
1079
  <li>
1077
1080
  <p>
1078
1081
  Unlike Phusion Passenger for Apache and Phusion Passenger for Nginx, Phusion Passenger
@@ -1132,7 +1135,7 @@ if it doesn’t work on your POSIX-compliant operating system.</p></div>
1132
1135
  </div>
1133
1136
  <div class="sect2">
1134
1137
  <a href="javascript:void(0)" class="comments empty" title="Add a comment"><span class="count"></span></a><span class="anchor_helper" id="_where_to_get_support"></span><h3 data-comment-topic="where-to-get-support-xkx7rx" data-anchor="_where_to_get_support">1.2. Where to get support</h3>
1135
- <div class="ulist"><ul>
1138
+ <div class="ulist"><ul>
1136
1139
  <li>
1137
1140
  <p>
1138
1141
  <a href="http://code.google.com/p/phusion-passenger/issues/list">Issue tracker</a> - report
@@ -1877,5 +1880,5 @@ Mizuho.topicListReceived = $.proxy(function(result) {
1877
1880
  $(document).ready(Mizuho.initializeCommenting);
1878
1881
 
1879
1882
  </script>
1880
- </body>
1883
+ </body>
1881
1884
  </html>
@@ -26,7 +26,7 @@
26
26
  #define _PASSENGER_CONSTANTS_H_
27
27
 
28
28
  /* Don't forget to update lib/phusion_passenger.rb too. */
29
- #define PASSENGER_VERSION "4.0.1"
29
+ #define PASSENGER_VERSION "4.0.2"
30
30
 
31
31
  #define FEEDBACK_FD 3
32
32
 
@@ -52,11 +52,12 @@
52
52
  */
53
53
 
54
54
  #include "MD5.h"
55
- #include <string.h>
56
- #include <boost/detail/endian.hpp>
57
- #include "StrIntUtils.h"
58
-
59
- namespace Passenger {
55
+ #include "../../boost/detail/endian.hpp" /* File is C compatible. */
56
+ #ifdef __cplusplus
57
+ #include <string.h>
58
+ #include "StrIntUtils.h"
59
+ namespace Passenger {
60
+ #endif
60
61
 
61
62
  #if defined(BOOST_BIG_ENDIAN)
62
63
  # define ARCH_IS_BIG_ENDIAN 1
@@ -390,17 +391,19 @@ md5_finish(md5_state_t *pms, md5_byte_t digest[16])
390
391
  digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
391
392
  }
392
393
 
393
- std::string
394
- md5_hex(const StaticString &input)
395
- {
396
- md5_state_t pms;
397
- md5_byte_t digest[16];
394
+ #ifdef __cplusplus
395
+ std::string
396
+ md5_hex(const StaticString &input)
397
+ {
398
+ md5_state_t pms;
399
+ md5_byte_t digest[16];
398
400
 
399
- md5_init(&pms);
400
- md5_append(&pms, (const md5_byte_t *) input.data(), input.size());
401
- md5_finish(&pms, digest);
401
+ md5_init(&pms);
402
+ md5_append(&pms, (const md5_byte_t *) input.data(), input.size());
403
+ md5_finish(&pms, digest);
402
404
 
403
- return toHex(StaticString((const char *) digest, 16));
404
- }
405
+ return toHex(StaticString((const char *) digest, 16));
406
+ }
405
407
 
406
- } // namespace Passenger
408
+ } // namespace Passenger
409
+ #endif
@@ -50,9 +50,14 @@
50
50
  #ifndef _PASSENGER_MD5_H_
51
51
  #define _PASSENGER_MD5_H_
52
52
 
53
- #include <string>
54
- #include <boost/cstdint.hpp>
55
- #include "../StaticString.h"
53
+ #ifdef __cplusplus
54
+ #include "../../boost/cstdint.hpp"
55
+ #include <string>
56
+ #include <StaticString.h>
57
+ namespace Passenger {
58
+ #else
59
+ #include <stdint.h>
60
+ #endif
56
61
 
57
62
  /*
58
63
  * This package supports both compile-time and run-time determination of CPU
@@ -64,8 +69,6 @@
64
69
  * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
65
70
  */
66
71
 
67
- namespace Passenger {
68
-
69
72
  typedef uint8_t md5_byte_t; /* 8-bit byte */
70
73
  typedef uint32_t md5_word_t; /* 32-bit word */
71
74
 
@@ -90,9 +93,11 @@ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
90
93
  /* Finish the message and return the digest. */
91
94
  void md5_finish(md5_state_t *pms, md5_byte_t digest[MD5_SIZE]);
92
95
 
93
- /* Convenience method for directly converting data into a hexadecimal MD5 string. */
94
- std::string md5_hex(const StaticString &input);
96
+ #ifdef __cplusplus
97
+ /* Convenience method for directly converting data into a hexadecimal MD5 string. */
98
+ std::string md5_hex(const StaticString &input);
95
99
 
96
- } // namespace Passenger
100
+ } // namespace Passenger
101
+ #endif
97
102
 
98
103
  #endif /* _PASSENGER_MD5_H_ */
@@ -31,9 +31,9 @@ module PhusionPassenger
31
31
  PACKAGE_NAME = 'passenger'
32
32
 
33
33
  # Phusion Passenger version number. Don't forget to edit ext/common/Constants.h too.
34
- VERSION_STRING = '4.0.1'
34
+ VERSION_STRING = '4.0.2'
35
35
 
36
- PREFERRED_NGINX_VERSION = '1.4.0'
36
+ PREFERRED_NGINX_VERSION = '1.4.1'
37
37
  PREFERRED_PCRE_VERSION = '8.32'
38
38
  STANDALONE_INTERFACE_VERSION = 1
39
39
 
@@ -102,7 +102,8 @@ module Packaging
102
102
  ] + ASCII_DOCS
103
103
 
104
104
  EXCLUDE_GLOB = [
105
- 'test/stub/rails_apps/3.0/empty/help/**/*'
105
+ 'test/stub/rails_apps/3.0/empty/help/**/*',
106
+ 'test/stub/*.dSYM'
106
107
  ]
107
108
  end
108
109
 
@@ -70,14 +70,6 @@ class RequestHandler
70
70
 
71
71
  attr_reader :concurrency
72
72
 
73
- # Specifies the maximum allowed memory usage, in MB. If after having processed
74
- # a request AbstractRequestHandler detects that memory usage has risen above
75
- # this limit, then it will gracefully exit (that is, exit after having processed
76
- # all pending requests).
77
- #
78
- # A value of 0 (the default) indicates that there's no limit.
79
- attr_accessor :memory_limit
80
-
81
73
  # The number of times the main loop has iterated so far. Mostly useful
82
74
  # for unit test assertions.
83
75
  attr_reader :iterations
@@ -94,7 +86,6 @@ class RequestHandler
94
86
  # +owner_pipe+ must be the readable part of a pipe IO object.
95
87
  #
96
88
  # Additionally, the following options may be given:
97
- # - memory_limit: Used to set the +memory_limit+ attribute.
98
89
  # - detach_key
99
90
  # - connect_password
100
91
  # - pool_account_username
@@ -111,7 +102,6 @@ class RequestHandler
111
102
  )
112
103
  @thread_handler = options["thread_handler"] || ThreadHandler
113
104
  @concurrency = 1
114
- @memory_limit = options["memory_limit"] || 0
115
105
  if options["pool_account_password_base64"]
116
106
  @pool_account_password = options["pool_account_password_base64"].unpack('m').first
117
107
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-06 00:00:00.000000000 Z
12
+ date: 2013-05-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -169,7 +169,6 @@ files:
169
169
  - bin/passenger-install-nginx-module
170
170
  - bin/passenger-memory-stats
171
171
  - bin/passenger-status
172
- - doc/ApplicationPool algorithm.txt
173
172
  - doc/Architectural overview.html
174
173
  - doc/Architectural overview.idmap.txt
175
174
  - doc/Architectural overview.txt
metadata.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJRh4ZKAAoJECrHRaUKISqMAQAH/jFHy9rLAO/8bYVs8+M329/M
6
- vu8LA/dGFzpYoR1uyUP2R0uDwGIif+vuawrCDsowtFXmPYPj7I+1vjP0Z3egiytn
7
- KTBjwPG1yRXvmBdoS7T/L6CoIfbxawrbbVaDpUEx/wsq8V4wOMrpvvNuSWsCBVnF
8
- V1JnCm6/O5+ZVrXbheEUKEw4/KhXQ4v80lvjMGzOD+LiFeDvPOpONDgZIhfRBWsl
9
- OMLhHjtLi0QfQVLb7KbY18atgEY8vPd0xbAfK5rR8hWhsHsv97zWgU+G/xJgJqIZ
10
- pjWnkZL/8H6H4ZkbEb4JDLQhyFQeI2Atym+4MR+Sb/ru90BxSlNWXyCQC2LcSNo=
11
- =+ZYC
5
+ iQEcBAABAgAGBQJRiUovAAoJECrHRaUKISqM+WoIAIOplVMq6lTNkFzoNJ28RM8i
6
+ OdBHR1+p7veJJBWyTQhNRGyi416rayEUZL8pRlaeak7688fdiJZ+6spYkZmAWsvp
7
+ LSu1UGS70jeZamGFsdnrMk9d2zY0r8+kOsjDksZ2t4awTmASYuNRlfvglxF1HIan
8
+ l0lffOJNXyBddrN315x5Lo0i8fWXjSahqXRgP+7ZU3+van8r5vK4UBeXGVTk8w2h
9
+ MiOmf54mXFCq/Bg85FX7mS6Odxlu25+UkSm88nq5zTDS8C7OAlP295rE9bFBGFJ5
10
+ 63zXJEH/lcpFp3OisX4Sixz9wGiza7pmtQd9veQMwmlX/PtL3nME3NbXLlCkp5g=
11
+ =gRq7
12
12
  -----END PGP SIGNATURE-----
@@ -1,615 +0,0 @@
1
- = ApplicationPool algorithm
2
-
3
-
4
- == Introduction
5
-
6
- For efficiency reasons, Passenger keeps a pool spawned Rails/Ruby applications.
7
- Please read the C++ API documentation for the ApplicationPool class for a full
8
- introduction. This document describes an algorithm for managing the pool, in a
9
- high-level way.
10
-
11
- The algorithm should strive to keep spawning to a minimum.
12
-
13
-
14
- == Definitions
15
-
16
- === Vocabulary
17
-
18
- - "Application root":
19
- The toplevel directory in which an application is contained. For Rails
20
- application, this is the same as RAILS_ROOT, i.e. the directory that contains
21
- "app/", "public/", etc. For a Rack application, this is the directory that
22
- contains "config.ru".
23
-
24
- - "Active application process":
25
- An application process that has more than 0 sessions.
26
-
27
- === Types
28
-
29
- Most of the types that we use in this document are pretty standard. But we
30
- explicitly define some special types:
31
-
32
- - list<SomeType>
33
- A doubly linked list which contains elements of type SomeType. It supports
34
- all the usual list operations that one can expect from a linked list, like
35
- add_to_back(), etc.
36
-
37
- We assume that operations that insert an element into the list return an
38
- iterator object. An iterator object is an opaque object which represents a
39
- specific position in the list; it probably contains the links to the previous
40
- and the next iterator, as well as a reference to the actual list element,
41
- depending on the list implementation.
42
-
43
- The following operations deserve special mention:
44
- * remove(iterator)
45
- Removes the specified element from the list, as represented by the given
46
- iterator. This operation can be done in O(1) time.
47
-
48
- * move_to_front(iterator)
49
- Moves the specified element - as represented by the given iterator - to
50
- the front of the list. This operation can be done in O(1) time.
51
-
52
- - Group
53
- A compound type (class) which contains information about an application root,
54
- such as the application processes that have been spawned for this application
55
- root.
56
-
57
- A Group has the following members:
58
- * name (string):
59
- This group's key in the _groups_ map of the application pool.
60
-
61
- * app_root (string):
62
- This group's application root. Note that it is *not* guaranteed that all
63
- ProcessInfo objects in _processes_ have the same application root.
64
-
65
- * processes (list<ProcessInfo>):
66
- A list of ProcessInfo objects.
67
-
68
- Invariant:
69
- processes is non-empty.
70
- for all 0 <= i < processes.size() - 1:
71
- processes[i].group_name == name
72
- if processes[i].process is active:
73
- processes[i + 1].process is active
74
-
75
- * size (unsigned integer):
76
- The number of items in _processes_.
77
- Invariant:
78
- if !detached:
79
- size == processes.size()
80
-
81
- * max_requests (unsigned integer):
82
- The maximum number of requests that each application process in
83
- this group may process. After having processed this
84
- many requests, the application process will be shut down.
85
- A value of 0 indicates that there is no maximum.
86
-
87
- * min_processes (unsigned integer):
88
- The minimum number of processes that the cleaner thread should keep in
89
- this group. Defaults to 0.
90
-
91
- * spawning (boolean): Whether a background thread is currently spawning
92
- a new process for this group.
93
-
94
- * spawner_thread: A handle to the background thread that is currently
95
- spawning a new process. Only valid if _spawning_ is true.
96
-
97
- * detached (boolean): If true, then it indicates that this Group is
98
- no longer accessible via _groups_.
99
- Set to false by the constructor.
100
- Invariant:
101
- (detached) == (This Group is accessible via _groups_.)
102
- if detached:
103
- for all process_info objects p that have once been in this.processes:
104
- p.detached
105
-
106
- * environment (string): Does nothing. Data is stored in memory for analytics
107
- purposes.
108
-
109
- - ProcessInfo
110
- A compound type (class) which contains a reference to an application process
111
- object, as well as various metadata, such as iterators for various linked
112
- lists. These iterators make it possible to perform actions on the linked
113
- lists in O(1) time.
114
-
115
- A ProcessInfo has the following members:
116
- * process - A process object, representing an application process.
117
- * group_name (string) - The name of the group that this ProcessInfo belongs
118
- to.
119
- * identifier (string) - A key that uniquely identifies this ProcessInfo in
120
- this application pool. This key allows external processes to refer to a
121
- specific ProcessInfo object without knowing its memory pointer. It's set
122
- to a random string by ProcessInfo's constructor.
123
- * start_time (timestamp with milisecond resolution) - The time at which this
124
- application process was started. It's set to the current time by the
125
- constructor.
126
- * processed_requests (integer) - The number of requests processed by this
127
- application instance so far. Set to 0 by the constructor.
128
- * last_used (time) - The last time a session for this application process
129
- was opened or closed.
130
- * sessions (integer) - The number of open sessions for this application
131
- process. It's set to 0 by the constructor.
132
- Invariant:
133
- (sessions == 0 && !detached) == (This ProcessInfo is in inactive_apps.)
134
- * iterator - The iterator for this ProcessInfo in the linked list
135
- groups[process_info.group_name].processes
136
- * ia_iterator - The iterator for this ProcessInfo in the linked list
137
- inactive_apps. This iterator is only valid if this ProcessInfo really is
138
- in that list.
139
- * detached (boolean) - If true, then it indicates that this ProcessInfo is
140
- no longer accessible via _groups_; this implies that it's no longer
141
- contained in its associated Group's _processes_ member, a), and that
142
- _iterator_ and _ia_iterator_ are no longer valid.
143
- Set to false by the constructor.
144
- Invariant:
145
- (detached) == (This ProcessInfo is accessible via _groups_.)
146
-
147
- - PoolOptions
148
- A structure containing additional information used by the spawn manager's
149
- spawning process, as well as by the get() function.
150
-
151
- A PoolOptions has at least the following members:
152
- * app_group_name (string) - A name which is used to group application
153
- processes together.
154
- * max_requests (unsigned integer) - The maximum number of requests that the
155
- application process may process. After having processed this many requests,
156
- the application process will be shut down. A value of 0 indicates that there
157
- is no maximum.
158
- * min_processes (unsigned integer) - The minimum number of processes for the
159
- current group that the cleaner thread should keep around.
160
- * use_global_queue (boolean) - Whether to use a global queue for all
161
- application processes, or a queue that's private to the application process.
162
- The users guide explains this feature in more detail.
163
- * restart_dir (string) - The directory in which the algorithm should look for
164
- restart.txt and always_restart.txt. The existance and modification times of
165
- these files tell the algorithm whether an application should be restarted.
166
- * environment (string) - The environment (RAILS_ENV/RACK_ENV) in which the app
167
- should run.
168
-
169
- === Special functions
170
-
171
- - spawn(app_root, options)
172
- Spawns a new application process at the given application root with the given
173
- spawn options. Throws an exception if something went wrong. This function is
174
- thread-safe. Note that application process initialization can take an arbitrary
175
- amount of time.
176
-
177
- === Instance variables
178
-
179
- The algorithm requires the following instance variables for storing state
180
- information:
181
-
182
- - lock: mutex
183
- This lock is used for implementing thread-safetiness. We assume that it
184
- is non-recursive, i.e. if a thread locks a mutex that it has already locked,
185
- then it will result in a deadlock.
186
-
187
- - groups: map[string => Group]
188
- Maps an application root to its Group object. This map contains all
189
- application processes in the pool.
190
-
191
- Invariant:
192
- for all values g in groups:
193
- !g.detached
194
- g.size <= count
195
- for all i in g.processes:
196
- !i.detached
197
- (sum of all g.size in groups) == count
198
-
199
- - max: integer
200
- The maximum number of ProcessInfo objects that may exist in the pool.
201
-
202
- - max_per_app: integer
203
- The maximum number of ProcessInfo objects that may be simultaneously alive
204
- for a single Group.
205
-
206
- - count: integer
207
- The current number of ProcessInfo objects in the pool.
208
- Since 'max' can be set dynamically during the life time of an application
209
- pool, 'count > max' is possible.
210
-
211
- - active: integer
212
- The number of application processes in the pool that are active.
213
- Invariant:
214
- active <= count
215
-
216
- - inactive_apps: list<ProcessInfo>
217
- A linked list of ProcessInfo objects. All application processes in this list
218
- are inactive.
219
-
220
- Invariant:
221
- inactive_apps.size() == count - active
222
- for all x in inactive_apps:
223
- x can be accessed from _groups_.
224
- x.sessions == 0
225
-
226
- - waiting_on_global_queue: integer
227
- If global queuing mode is enabled, then when get() is waiting for a backend
228
- process to become idle, this variable will be incremented. When get() is done
229
- waiting, this variable will be decremented.
230
-
231
-
232
- == Class relations
233
-
234
- Here's an UML diagram in ASCII art:
235
-
236
- [ProcessInfo] 1..* --------+
237
- |
238
- |
239
-
240
- 1
241
- [ApplicationPool] [Group]
242
- 1 0..*
243
-
244
- | |
245
- +-------------------+
246
-
247
-
248
- == Algorithm in pseudo code
249
-
250
- # Thread-safetiness notes:
251
- # - All wait commands are to unlock the lock during waiting.
252
-
253
-
254
- # Connect to an existing application process, or spawn a new application process
255
- # and connect to that if necessary.
256
- # 'app_root' refers to an application root.
257
- # 'options' is an object of type 'PoolOptions', which contains additional
258
- # information which may be relevant for spawning.
259
- #
260
- # Returns a Session object, representing a single HTTP request/response pair.
261
- function get(app_root, options):
262
- MAX_ATTEMPTS = 10
263
- attempt = 0
264
- while (true):
265
- attempt++
266
- lock.synchronize:
267
- process_info, group = checkout_without_lock(app_root, options)
268
- try:
269
- return process_info.process.connect()
270
- on exception:
271
- # The app process seems to have crashed.
272
- # So we remove this process from our data
273
- # structures.
274
- lock.synchronize:
275
- detach_without_lock(process_info.identifier)
276
- process_info.sessions--
277
- if (attempt == MAX_ATTEMPTS):
278
- propagate exception
279
-
280
-
281
- # Detach the process with the given identifier from the pool's data structures.
282
- function detach(identifier):
283
- lock.synchronize:
284
- return detach_without_lock(identifier)
285
-
286
-
287
- # Checkout a process from the application pool and mark it as being used.
288
- # If there's no appropriate process in the pool, or if there are not
289
- # enough processes, then one will be spawned.
290
- #
291
- # Returns a pair of [ProcessInfo, Group].
292
- # All exceptions that occur are propagated.
293
- private function checkout_without_lock(app_root, options):
294
- group = groups[options.app_group_name]
295
-
296
- if needs_restart(app_root, options):
297
- Tell spawn server to reload code for options.app_group_name.
298
- if (group != null):
299
- detach_group_without_lock(group)
300
- group = null
301
-
302
- if (group != null):
303
- # There are existing processes for this app group.
304
- processes = group.processes
305
-
306
- if (processes.front.sessions == 0):
307
- # There is an inactive process, so we use it.
308
- process_info = processes.front
309
- processes.move_to_back(process_info.iterator)
310
- inactive_apps.remove(process_info.ia_iterator)
311
- mutate_max(active + 1)
312
- else:
313
- # All existing processes are active. We either use
314
- # one of them now or we wait until one of them becomes
315
- # available. And, if we're allowed to, we spawn an
316
- # extra process in the background.
317
- if spawning_allowed(group, options) and !group.spawning:
318
- spawn_in_background(group, options)
319
- process_info = select_process(processes, options)
320
- if (process_info == null):
321
- goto beginning of function
322
- else:
323
- # There are no processes for this app group.
324
- if (active >= max):
325
- # Looks like the pool is full and all processes are busy.
326
- # Wait until the pool appears to have changed in such a
327
- # way that we can spawn a new app group, and restart
328
- # this function.
329
- new_app_group_creatable.wait
330
- goto beginning of function
331
- elsif count >= max:
332
- # The pool is full, and not all processes are busy, but
333
- # we're in a though situation nevertheless: there are
334
- # several processes which are inactive, and none of them
335
- # belong to our current app group, so we must kill one
336
- # of them in order to free a spot in the pool. But which
337
- # one do we kill? We want to minimize spawning.
338
- #
339
- # It's probably a good idea to keep some kind of
340
- # statistics in order to decide this. We want the
341
- # application root that gets the least traffic to be
342
- # killed. But for now, we kill a random application
343
- # process.
344
- process_info = inactive_apps.pop_front
345
- process_info.detached = true
346
- group = groups[process_info.group_name]
347
- processes = group.processes
348
- processes.remove(process_info.iterator)
349
- if processes.empty():
350
- detach_group_without_lock(group)
351
- else:
352
- group.size--
353
- mutate_count(count - 1)
354
- process_info = new ProcessInfo
355
- process_info.process = spawn(app_root, options)
356
- process_info.group_name = options.app_group_name
357
- group = new Group
358
- group.name = options.app_group_name
359
- group.app_root = app_root
360
- group.size = 1
361
- groups[options.app_group_name] = group
362
- iterator = group.processes.add_to_back(process_info)
363
- process_info.iterator = iterator
364
- mutate_count(count + 1)
365
- mutate_active(active + 1)
366
- if (options.min_processes > 1) and spawning_allowed(group, options):
367
- spawn_in_background(group, options)
368
-
369
- group.max_requests = options.max_requests
370
- group.min_processes = options.min_processes
371
- group.environment = options.environment
372
-
373
- process_info.last_used = current_time()
374
- process_info.sessions++
375
-
376
- return [process_info, group]
377
-
378
-
379
- private function mutate_active(value):
380
- if (value < active):
381
- new_app_group_creatable.notify_all
382
- global_queue_position_became_available.notify_all
383
- active = value
384
-
385
- private function mutate_count(value):
386
- # No point in notifying new_app_group_creatable here;
387
- # if _count_ is being increased then that means the pool
388
- # isn't full, and nobody is waiting on
389
- # new_app_group_creatable.
390
- global_queue_position_became_available.notify_all
391
- count = value
392
-
393
- private function mutate_max(value):
394
- if (value > max):
395
- new_app_group_creatable.notify_all
396
- # We will want any code waiting on the global queue
397
- # to go ahead and spawn another process.
398
- global_queue_position_became_available.notify_all
399
- max = value
400
-
401
-
402
- private function needs_restart(app_root, options):
403
- if (options.restart_dir is not set):
404
- restart_dir = app_root + "/tmp"
405
- else if (options.restart_dir is an absolute path):
406
- restart_dir = options.restart_dir
407
- else:
408
- restart_dir = app_root + "/" + options.restart_dir
409
-
410
- return (file_exists("$restart_dir/always_restart.txt")) or
411
- (we haven't seen "$restart_dir/restart.txt" before) or
412
- ("$restart_dir/restart.txt" changed since the last time we checked)
413
-
414
-
415
- private function spawning_allowed(group, options):
416
- return ( count < max ) and
417
- ( (max_per_app == 0) or (group.size < max_per_app) )
418
-
419
-
420
- # Precondition: !group.detached
421
- private function detach_group_without_lock(group):
422
- for all process_info in group.processes:
423
- if (process_info.sessions == 0):
424
- inactive_apps.remove(process_info.ia_iterator)
425
- else:
426
- mutate_active(active - 1)
427
- group.processes.remove(process_info.iterator)
428
- process_info.detached = true
429
- mutate_count(count - 1)
430
- if (group.spawning):
431
- group.spawner_thread.interrupt_and_join
432
- group.spawner_thread = null
433
- group.spawning = false
434
- group.detached = true
435
- groups.remove(options.app_group_name)
436
-
437
-
438
- private function select_process(processes, options):
439
- if options.use_global_queue:
440
- # So we wait until _active_ has changed, then
441
- # we restart this function and try again.
442
- waiting_on_global_queue++
443
- global_queue_position_became_available.wait
444
- waiting_on_global_queue--
445
- return null
446
- else:
447
- # So we connect to an already active process.
448
- # This connection will be put into that
449
- # process's private queue.
450
- process_info = an element in _processes_ with the smallest _session_ value
451
- processes.move_to_back(process_info.iterator)
452
- return process_info
453
-
454
-
455
- # Preconditions:
456
- # !group.detached
457
- # !group.spawning
458
- private function spawn_in_background(group, options):
459
- group.spawning = true
460
- group.spawner_thread = new thread(spawner_thread_callback,
461
- with these arguments to the thread function:
462
- group, options)
463
-
464
-
465
- private function spawner_thread_callback(group, options):
466
- Ignore thread interruptions in this function
467
- while true:
468
- try:
469
- Allow thread interruptions in this block
470
- process = spawn(app_root, options)
471
- on thread interruption:
472
- lock.synchronize:
473
- group.spawning = false
474
- group.spawner_thread = null
475
- return
476
- on exception:
477
- lock.synchronize:
478
- if (!group.detached):
479
- group.spawning = false
480
- group.spawner_thread = null
481
- # We want to report the error to the browser
482
- # but there's no way to do this in this thread, so
483
- # we just remove the entire group and have the next
484
- # get() call spawn the process and display the
485
- # error.
486
- remove_group_without_lock(group)
487
- return
488
-
489
- lock.synchronize:
490
- if (group.detached):
491
- return
492
- else:
493
- process_info = new ProcessInfo
494
- process_info.process = process
495
- process_info.group_name = options.app_group_name
496
- process_info.iterator = group.processes.add_to_front(process_info)
497
- process_info.ia_iterator = inactive_apps.add_to_back(process_info)
498
- group.size++
499
- mutate_count(count + 1)
500
- if (group.size >= options.min_processes) or
501
- (!spawning_allowed(group, options)):
502
- group.spawning = false
503
- group.spawner_thread = null
504
- return
505
-
506
-
507
- private function detach_without_lock(identifier):
508
- for group in groups:
509
- processes = group.processes
510
- for process_info in processes:
511
- if process_info.identifier == identifier:
512
- # Found a matching process.
513
- process_info.detached = true
514
- processes.remove(process_info.iterator)
515
- group.size--
516
- if processes.empty():
517
- detach_group_without_lock(group)
518
- if process_info.sessions == 0:
519
- inactive_apps.remove(process_info.ia_iterator)
520
- else:
521
- mutate_active(active - 1)
522
- mutate_count(count - 1)
523
- return true
524
- return false
525
-
526
-
527
- # The following function is to be called when a session has been closed.
528
- # _process_info_ is a weak reference to the ProcessInfo that belongs to
529
- # the process whose session has been closed; it evaluates to NULL if the
530
- # ProcessInfo object that it belongs to has been destroyed.
531
- callback session_has_been_closed(session, process_info):
532
- Convert process_info into a normal reference.
533
-
534
- # We check process_info.detached without locking. This should be safe:
535
- # even if a boolean update isn't atomic on the current CPU, a non-zero
536
- # value evaluates to true. Once true, _detached_ will never become false,
537
- # so instruction reorderings by the compiler or CPU won't cause any
538
- # damage.
539
- if (process_info == null) or (process_info.detached):
540
- return
541
-
542
- lock.synchronize:
543
- if process_info.detached:
544
- return
545
-
546
- group = groups[process_info.group_name]
547
- processes = group.processes
548
- process_info.processed++
549
-
550
- if (group.max_requests > 0) and (process_info.processed >= group.max_requests):
551
- # The application process has processed its maximum allowed
552
- # number of requests, so we shut it down.
553
- process_info.detached = true
554
- processes.remove(process_info.iterator)
555
- group.size--
556
- if processes.empty():
557
- detach_group_without_lock(group)
558
- mutate_count(count - 1)
559
- if (process_info.sessions == 0):
560
- inactive_apps.remove(process_info.ia_iterator)
561
- else:
562
- mutate_active(active - 1)
563
- else:
564
- process_info.last_used = current_time()
565
- process_info.sessions--
566
- if (process_info.sessions == 0):
567
- processes.move_to_front(process_info.iterator)
568
- process_info.ia_iterator = inactive_apps.add_to_back(process_info)
569
- mutate_active(active - 1)
570
-
571
-
572
- # The following thread will be responsible for cleaning up idle application
573
- # process, i.e. processes that haven't been used for a while.
574
- # This can be disabled per app when setting it's maxIdleTime to 0.
575
- thread cleaner:
576
- lock.synchronize:
577
- while true:
578
- # If MAX_IDLE_TIME is 0 we don't clean up any processes,
579
- # giving us the option to persist the processes
580
- # forever unless it's killed in order to free up space
581
- # for another process.
582
- if (MAX_IDLE_TIME == 0):
583
- Wait until the thread has been signalled to quit
584
- or until MAX_IDLE_TIME changed.
585
- if thread has been signalled to quit:
586
- return
587
- else:
588
- restart loop
589
- else:
590
- Wait until MAX_IDLE_TIME seconds have passed,
591
- or until the thread has been signalled to quit,
592
- or until MAX_IDLE_TIME changed.
593
- if thread has been signalled to quit:
594
- return
595
- else if MAX_IDLE_TIME changed:
596
- restart loop
597
-
598
- # Invariant:
599
- # From this point on, MAX_IDLE_TIME > 0
600
-
601
- now = current_time()
602
- for all process_info in inactive_apps:
603
- if (now - process_info.last_used > MAX_IDLE_TIME):
604
- process = process_info.process
605
- group = groups[process_info.group_name]
606
- if (group.size > group.min_processes):
607
- processes = group.processes
608
- processes.remove(process_info.iterator)
609
- process_info.detached = true
610
- inactive_apps.remove(process_info.ia_iterator)
611
- group.size--
612
- mutate_count(count - 1)
613
- if processes.empty():
614
- detach_group_without_lock(group)
615
-