We got nominated! Help us out and vote for GitHub as Best Bootstrapped Startup of 2008. (You can vote once a day.) [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
Explicitly document locking in the ApplicationPool algorithm doucment. 
Move the lock in get() to inside the loop for better concurrency.
Hongli Lai (Phusion) (author)
Sat Mar 22 05:47:01 -0700 2008
commit  0e8110d0392ae39dbb4b2e98022f40f31d0744db
tree    d6eaf5c670adc34ea6e3dcdee334ab8f7f834773
parent  25b2de77fa2955915c4b9be8d878413316afd13a
...
54
55
56
 
 
 
 
 
 
 
57
58
59
60
61
 
 
 
 
 
62
63
64
...
107
108
109
110
111
112
113
114
115
116
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
139
140
...
207
208
209
210
 
211
212
213
...
224
225
226
227
228
229
230
231
232
233
234
 
 
 
 
 
 
 
 
 
235
236
237
...
258
259
260
261
262
263
264
265
266
 
 
 
 
 
 
 
267
268
269
270
271
272
273
274
275
276
277
278
 
 
 
 
 
 
 
 
 
 
 
279
...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
119
120
121
 
 
 
 
122
123
124
...
126
127
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
216
217
218
 
219
220
221
222
...
233
234
235
 
 
 
 
 
 
 
 
236
237
238
239
240
241
242
243
244
245
246
247
...
268
269
270
 
 
 
 
 
 
271
272
273
274
275
276
277
278
 
 
 
 
 
 
 
 
 
 
 
279
280
281
282
283
284
285
286
287
288
289
290
0
@@ -54,11 +54,23 @@ explicitly define some special types:
0
     inactive_apps. This iterator is only valid if this AppContainer really is
0
     in that list.
0
 
0
+=== Special functions
0
+
0
+- spawn(app_root)
0
+ Spawns a new instance of the application at the given application root.
0
+ Throws an exception if something went wrong. This function is thread-safe.
0
+ Note that application initialization can take an arbitrary amount of time.
0
+
0
 === Instance variables
0
 
0
 The algorithm requires the following instance variables for storing state
0
 information:
0
 
0
+- lock: mutex
0
+ This lock is used for implementing thread-safetiness. We assume that it
0
+ is non-recursive, i.e. if a thread locks a mutex that it has already locked,
0
+ then it will result in a deadlock.
0
+
0
 - apps: map[string => list<AppContainer>]
0
   Maps an application root to a list of AppContainers. Thus, this map contains
0
   all application instances that are in the pool.
0
@@ -107,10 +119,6 @@ information:
0
 == Algorithm in pseudo code
0
 
0
 # Thread-safetiness notes:
0
-# - The following functions are to be run inside the same lock:
0
-# * get()
0
-# * session_has_been_closed()
0
-# * The cleaner thread's main function.
0
 # - All wait commands are to unlock the lock during waiting.
0
 
0
 function get(app_root):
0
@@ -118,23 +126,24 @@ function get(app_root):
0
   attempt = 0
0
   while (true):
0
     attempt++
0
- container, list = spawn_or_use_existing(app_root)
0
- container.last_used = current_time()
0
- container.sessions++
0
- try:
0
- return container.app.connect()
0
- on exception:
0
- container.sessions--
0
- if (attempt == MAX_ATTEMPTS):
0
- propagate exception
0
- else:
0
- # The app instance seems to have crashed. So we remove this
0
- # instance from our data structures.
0
- list.remove(container.iterator)
0
- if list.empty():
0
- apps.remove(app_root)
0
- count--
0
- active--
0
+ lock.synchronize:
0
+ container, list = spawn_or_use_existing(app_root)
0
+ container.last_used = current_time()
0
+ container.sessions++
0
+ try:
0
+ return container.app.connect()
0
+ on exception:
0
+ container.sessions--
0
+ if (attempt == MAX_ATTEMPTS):
0
+ propagate exception
0
+ else:
0
+ # The app instance seems to have crashed. So we remove this
0
+ # instance from our data structures.
0
+ list.remove(container.iterator)
0
+ if list.empty():
0
+ apps.remove(app_root)
0
+ count--
0
+ active--
0
 
0
 
0
 # Returns a pair of [AppContainer, list<AppContainer>] that matches the
0
@@ -207,7 +216,7 @@ function spawn_or_use_existing(app_root):
0
     container = new AppContainer
0
     # TODO: we should unlock the mutex during spawning,
0
     # and add some kind of timeout check.
0
- container.app = spawn(app)
0
+ container.app = spawn(app_root)
0
     container.sessions = 0;
0
     list = apps[app_root]
0
     if list == nil:
0
@@ -224,14 +233,15 @@ function spawn_or_use_existing(app_root):
0
 # _container_ is the AppContainer that contains the application for which a
0
 # session has been closed.
0
 function session_has_been_closed(container):
0
- list = apps[container.app.app_root]
0
- if list != nil:
0
- container.last_used = current_time()
0
- container.sessions--
0
- if container.sessions == 0:
0
- list.move_to_front(container.iterator)
0
- container.ia_iterator = inactive_apps.add_to_back(container.app)
0
- active--
0
+ lock.synchronize:
0
+ list = apps[container.app.app_root]
0
+ if list != nil:
0
+ container.last_used = current_time()
0
+ container.sessions--
0
+ if container.sessions == 0:
0
+ list.move_to_front(container.iterator)
0
+ container.ia_iterator = inactive_apps.add_to_back(container.app)
0
+ active--
0
 
0
 
0
 function needs_restart(app_root):
0
@@ -258,22 +268,23 @@ function needs_restart(app_root):
0
 # The following thread will be responsible for cleaning up idle application
0
 # instances, i.e. instances that haven't been used for a while.
0
 thread cleaner:
0
- done = false
0
- while !done:
0
- Wait until CLEAN_INTERVAL seconds have expired, or until the thread has been signalled to quit.
0
- if thread has been signalled to quit:
0
- done = true
0
- break
0
+ lock.synchronize:
0
+ done = false
0
+ while !done:
0
+ Wait until CLEAN_INTERVAL seconds have expired, or until the thread has been signalled to quit.
0
+ if thread has been signalled to quit:
0
+ done = true
0
+ break
0
     
0
- now = current_time()
0
- for all container in inactive_apps:
0
- app = container.app
0
- app_list = apps[app.app_root]
0
- if now - container.last_used > MAX_IDLE_TIME:
0
- app_list.remove(container.iterator)
0
- inactive_apps.remove(iterator for container)
0
- count--
0
- if app_list.empty():
0
- apps.remove(app.app_root)
0
- restart_file_times.remove(app.app_root)
0
+ now = current_time()
0
+ for all container in inactive_apps:
0
+ app = container.app
0
+ app_list = apps[app.app_root]
0
+ if now - container.last_used > MAX_IDLE_TIME:
0
+ app_list.remove(container.iterator)
0
+ inactive_apps.remove(iterator for container)
0
+ count--
0
+ if app_list.empty():
0
+ apps.remove(app.app_root)
0
+ restart_file_times.remove(app.app_root)
0
 
...
557
558
559
560
561
562
563
...
565
566
567
 
568
569
570
...
557
558
559
 
560
561
562
...
564
565
566
567
568
569
570
0
@@ -557,7 +557,6 @@ public:
0
   
0
   virtual Application::SessionPtr
0
   get(const string &appRoot, bool lowerPrivilege = true, const string &lowestUser = "nobody") {
0
- mutex::scoped_lock l(lock);
0
     unsigned int attempt;
0
     const unsigned int MAX_ATTEMPTS = 5;
0
     
0
@@ -565,6 +564,7 @@ public:
0
     while (true) {
0
       attempt++;
0
       
0
+ mutex::scoped_lock l(lock);
0
       pair<AppContainerPtr, AppContainerList *> p(
0
         spawnOrUseExisting(l, appRoot, lowerPrivilege, lowestUser)
0
       );

Comments

    No one has commented yet.