Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 539 lines (414 sloc) 24.719 kb
ff377f20 »
2008-08-24 Testing markdown for README.
1 Introduction
2 ============
3
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
4 *daemon_controller* is a library for starting and stopping specific daemons
5 programmatically in a robust, race-condition-free manner.
6
7 It's not a daemon monitoring system like God or Monit. It's also not a library
8 for writing daemons.
9
10 It provides the following functionality:
11
12 * Starting daemons. If the daemon fails to start then an exception will be
13 raised. *daemon_controller* can even detect failures that occur after the
14 daemon has already daemonized.
15
16 Starting daemons is done in a race-condition-free manner. If another
17 process using *daemon_controller* is trying to start the same daemon,
18 then *daemon_controller* will guarantee serialization.
19
20 *daemon_controller* also raises an exception if it detects that the daemon
21 is already started.
22 * Connecting to a daemon, starting it if it's not already started. This too
23 is done in a race-condition-free manner. If the daemon fails to start then
24 an exception will be raised.
25 * Stopping daemons.
26 * Checking whether a daemon is running.
27
a073491a »
2013-05-01 Add Ubuntu and Debian installation instructions
28 ## Installation with RubyGems
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
29
9aaa0a02 »
2009-11-06 Add resource links and installation instructions to the README.
30 gem install daemon_controller
31
22daefda »
2013-03-11 Add documentation about cryptographic verification and promote the us…
32 This gem is signed using PGP with the [Phusion Software Signing key](http://www.phusion.nl/about/gpg). That key in turn is signed by [the rubygems-openpgp Certificate Authority](http://www.rubygems-openpgp-ca.org/).
33
34 You can verify the authenticity of the gem by following [The Complete Guide to Verifying Gems with rubygems-openpgp](http://www.rubygems-openpgp-ca.org/blog/the-complete-guide-to-verifying-gems-with-rubygems-openpgp.html).
35
a073491a »
2013-05-01 Add Ubuntu and Debian installation instructions
36 ## Installation on Ubuntu
37
38 Use our [PPA](https://launchpad.net/~phusion.nl/+archive/misc):
39
40 sudo add-apt-repository ppa:phusion.nl/misc
41 sudo apt-get update
ab169312 »
2013-05-03 Fix Ubuntu/Debian installation instructions.
42 sudo apt-get install ruby-daemon-controller
a073491a »
2013-05-01 Add Ubuntu and Debian installation instructions
43
44 ## Installation on Debian
45
46 Our Ubuntu Lucid packages are compatible with Debian 6.
47
48 sudo sh -c 'echo deb http://ppa.launchpad.net/phusion.nl/misc/ubuntu lucid main > /etc/apt/sources.list.d/phusion-misc.list'
49 sudo sh -c 'echo deb-src http://ppa.launchpad.net/phusion.nl/misc/ubuntu lucid main >> /etc/apt/sources.list.d/phusion-misc.list'
50 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 2AC745A50A212A8C
51 sudo apt-get update
ab169312 »
2013-05-03 Fix Ubuntu/Debian installation instructions.
52 sudo apt-get install ruby-daemon-controller
a073491a »
2013-05-01 Add Ubuntu and Debian installation instructions
53
5895b01a »
2013-12-11 Add RPM packaging
54 ## Installation on RHEL, CentOS and Amazon Linux
55
56 Enable our YUM repository:
57
58 # RHEL 6, CentOS 6
59 curl -L https://oss-binaries.phusionpassenger.com/yumgems/phusion-misc/el.repo | \
60 sudo tee /etc/yum.repos.d/phusion-misc.repo
61
62 # Amazon Linux
63 curl -L https://oss-binaries.phusionpassenger.com/yumgems/phusion-misc/amazon.repo | \
64 sudo tee /etc/yum.repos.d/phusion-misc.repo
65
66 Install our package:
67
06314bb5 »
2013-12-11 Add RPM packaging
68 sudo yum install rubygem-daemon_controller
5895b01a »
2013-12-11 Add RPM packaging
69
9aaa0a02 »
2009-11-06 Add resource links and installation instructions to the README.
70 ## Resources
71
a88c8fdb »
2012-02-04 Add builtin support for pinging sockets.
72 * [Website](https://github.com/FooBarWidget/daemon_controller)
cac1bae5 »
2009-11-13 Update documentation.
73 * [RDoc](http://rdoc.info/projects/FooBarWidget/daemon_controller)
9aaa0a02 »
2009-11-06 Add resource links and installation instructions to the README.
74 * [Git repository](git://github.com/FooBarWidget/daemon_controller.git)
75
76
77 What is it for?
78 ===============
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
79
ff377f20 »
2008-08-24 Testing markdown for README.
80 There is a lot of software (both Rails related and unrelated) which rely on
81 servers or daemons. To name a few, in no particular order:
82
83 * [Ultrasphinx](http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/), a
84 Rails library for full-text searching. It makes use the [Sphinx search
85 software](http://www.sphinxsearch.com/) for indexing and searching. Indexing
86 is done by running a command, while searching is done by querying the Sphinx
87 search server.
88 * [acts_as_ferret](http://projects.jkraemer.net/acts_as_ferret/wiki), another
89 Rails library for full-text searching. It uses the Ferret search software.
90 On production environments, it relies on the Ferret DRB server for both
91 searching and indexing.
92 * [BackgrounDRb](http://backgroundrb.rubyforge.org/), a Ruby job server and
93 scheduler. Scheduling is done by contacting the BackgrounDRb daemon.
94 * [mongrel_cluster](http://mongrel.rubyforge.org/wiki/MongrelCluster), which
95 starts and stops multiple Mongrel daemons.
96
97 Relying on daemons is quite common, but not without problems. Let's go over
98 some of them.
99
100 ### Starting daemons is a hassle
101
102 If you've used similar software, then you might agree that managing these
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
103 daemons is a hassle. If you're using BackgrounDRb, then the daemon must be
ff377f20 »
2008-08-24 Testing markdown for README.
104 running. Starting the daemon is not hard, but it is annoying. It's also
105 possible that the system administrator forgets to start the daemon. While
106 configuring the system to automatically start a daemon at startup is not hard,
107 it is an extra thing to do, and thus a hassle. We thought, why can't such
108 daemons be automatically started? Indeed, this won't be possible if the daemon
109 is to be run on a remote machine. But in by far the majority of use cases, the
110 daemon runs on the same host as the Rails application. If a Rails application -
111 or indeed, <em>any</em> application - is configured to contact a daemon on the
112 local host, then why not start the daemon automatically on demand?
113
114 ### Daemon starting code may not be robust or efficient
115
116 We've also observed that people write daemon controlling code over and over
117 again. Consider for example UltraSphinx, which provides a
118 `rake sphinx:daemon:start` Rake task to start the daemon. The time that a
119 daemon needs to initialize is variable, and depends on things such as the
120 current system load. The Sphinx daemon usually needs less than a second before
121 we can connect to it. However, the way different software handles starting of a
122 daemon varies. We've observed that waiting a fixed amount of time is by far the
123 most common way. For example, UltraSphinx's daemon starting code looks like
124 this:
125
126 system "searchd --config '#{Ultrasphinx::CONF_PATH}'"
127 sleep(4) # give daemon a chance to write the pid file
128 if ultrasphinx_daemon_running?
129 say "started successfully"
130 else
131 say "failed to start"
132 end
133
134 This is in no way a slam against UltraSphinx. However, if the daemon starts in
135 200 miliseconds, then the user who issued the start command will be waiting for
136 3.8 seconds for no good reason. This is not good for usability or for the
137 user's patience.
138
139 ### Startup error handling
140
141 Different software handles daemon startup errors in different ways. Some might
142 not even handle errors at all. For example, consider 'mongrel_cluster'. If
143 there's a typo in one of your application source files, then 'mongrel_cluster'
144 will not report the error. Instead, you have to check its log files to see what
145 happened. This is not good for usability: many people will be wondering why
146 they can't connect to their Mongrel ports after issuing a
147 `mongrel_rails cluster::start` - until they realize that they should read the
148 log file. But the thing is, not everybody realizes this. And typing in an extra
149 command to read the log file to check whether Mongrel started correctly, is
150 just a big hassle. Why can't the daemon startup code report such errors
151 immediately?
152
153 ### Stale or corrupt Pid files
154
155 Suppose that you're running a Mongrel cluster, and your server suddenly powers
156 off because of a power outage. When the server is online again, it fails to
157 start your Mongrel cluster because the PID file that it had written still
158 exists, and wasn't cleaned up properly (it's supposed to be cleaned up when
159 Mongrel exits). mongrel_cluster provides the `--clean` option to check whether
160 the PID file is *stale*, and will automatically clean it up if it is. But not
161 all daemon controlling software supports this. Why can't all software check for
162 stale PID files automatically?
163
164
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
165 ## Implementation issues
ff377f20 »
2008-08-24 Testing markdown for README.
166
167 From the problem descriptions, it would become apparent that our wishlist is as
168 follows. Why is this wishlist often not implemented? Let's go over them.
169
170 - **A daemon should be automatically started on demand, instead of requiring the user to manually start it.**
171
172 The most obvious problems are related to concurrency. Suppose that your web
173 application has a search box, and you want to start the search daemon if it
174 isn't already started, then connect to. Two problems will arise:
175
176 * Suppose that Rails process A is still starting the daemon. At the same
177 time, another visitor tries to search something, and Rails process B
178 notices that the daemon is not running. If B tries to start the daemon
179 while it's already being started by A, then things can go wrong.
180 *A robust daemon starter must ensure that only one process at the same time may start the daemon.*
181 * It's not a good idea to wait a fixed amount of time for the daemon to
182 start, because you don't know in advance how long it will take for it to
183 start. For example, if you wait 2 seconds, then try to connect to the
184 daemon, and the daemon isn't done initializing yet, then it will seem as
185 if the daemon failed to start.
186
187 These are the most probable reasons why people don't try to write
188 auto-starting code, and instead require the user to start the daemon
189 manually.
190
191 These problems, as well as several less obvious problems, are closely
192 related to the next few points.
193
194 - **The daemon starter must wait until the daemon is done initializing, no longer and no shorter**
195
196 Because only after the daemon is fully initialized, is it safe to connect
197 to it. And because the user should not have to wait longer than he really
198 has to. During startup, the daemon will have to be continuously checked
199 whether it's done initializing or whether an error occured. Writing this
200 code can be quite a hassle, which is why most people don't do it.
201
202 - **The daemon starter must report any startup errors**
203
204 If the daemon starting command - e.g. `sphinx -c config_file.conf`,
205 `apachectl start` or `mongrel_rails cluster::start` - reports startup
206 errors, then all is fine as long as the user is starting the command from a
207 terminal. A problem occurs when the error occurs after the daemon has
208 already gone into the background. Such errors are only reported to the log
209 file.
210 *The daemon starter should also check the log file for any startup errors.*
211
212 Furthermore, it should be able to raise startup errors as exceptions. This
213 allows the the application to decide what to do with the error. For less
214 experienced system administrators, the error might be displayed in the
215 browser, allowing the administrators to become aware of the problem without
216 forcing them to manually check the log files. Or the error might be emailed
217 to a system administrator's email address.
218
219 - **The daemon starter must be able to correct stale or corrupted PID files**
220
221 If the PID file is stale, or for some reason has been corrupted, then the
222 daemon starter must be able to cope with that.
223 *It should check whether the PID file contains a valid PID, and whether the PID exists.*
224
225
226 Introducing daemon_controller
227 =============================
228
229 *daemon_controller* is a library for managing daemons in a robust manner. It is
230 not a tool for managing daemons. Rather, it is a library which lets you write
231 applications that manage daemons in a robust manner. For example,
232 'mongrel_cluster' or UltraSphinx may be adapted to utilize this library, for
233 more robust daemon management.
234
235 *daemon_controller* implements all items in the aforementioned wishlist. It
236 provides the following functionalities:
237
238 ### Starting a daemon
239
240 This ensures that no two processes can start the same daemon at the same time.
241 It will also reports any startup errors, even errors that occur after the
242 daemon has already gone into the background but before it has fully initialized
243 yet. It also allows you to set a timeout, and will try to abort the daemon if
244 it takes too long to initialize.
245
246 The start function won't return until the daemon has been fully initialized,
247 and is responding to connections. So if the start function has returned, then
248 the daemon is guaranteed to be usable.
249
250 ### Stopping a daemon
251
252 It will stop the daemon, but only if it's already running. Any errors
253 are reported. If the daemon isn't already running, then it will silently
254 succeed. Just like starting a daemon, you can set a timeout for stopping the
255 daemon.
256
257 Like the start function, the stop function won't return until the daemon is no
258 longer running. This makes it save to immediately start the same daemon again
259 after having stopped it, without worrying that the previous daemon instance
260 hasn't exited yet and might conflict with the newly started daemon instance.
261
262 ### Connecting to a daemon, starting it if it isn't running
263
264 Every daemon has to be connected to using a different way. As a developer, you
265 tell 'daemon_controller' how to connect to the daemon. It will then attempt to
266 do that, and if that fails, it will check whether the daemon is running. If it
267 isn't running, then it will automatically start the daemon, and attempt to
268 connect to the daemon again. Failures are reported.
269
270 ### Checking whether a daemon is running
271
272 This information is retrieved from the PID file. It also checks whether the PID
273 file is stale.
274
275 ### All failures are reported via exceptions
276
277 So that you can exactly determine how you want to handle errors.
278
279 ### Lots and lots of error checking
280
281 So that there are very few ways in which the system can screw up.
282
283 daemon_controller's goal is to make daemon management less of a hassle, and as
284 automatic and straightforward as possible.
285
286
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
287 What about Monit/God?
288 =====================
289
290 daemon_controller is not a replacement for [Monit](http://www.tildeslash.com/monit/)
291 or [God](http://god.rubyforge.org/). Rather, it is a solution to the following
292 problem:
293
fc25248a »
2008-08-26 Fix README markup rendering.
294 > **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
295 > features in our application!
ae4fecc9 »
2008-08-26 Fix README markup rendering.
296 > **Ninh:** cool. *pulls from repository*
297 > **Ninh:** hey Hongli, it doesn't work.
8d601a68 »
2008-08-26 Fix README markup rendering.
298 > **Hongli:** what do you mean, it doesn't work?
ae4fecc9 »
2008-08-26 Fix README markup rendering.
299 > **Ninh:** it says "connection refused", or something
8d601a68 »
2008-08-26 Fix README markup rendering.
300 > **Hongli:** oh I forgot to mention it, you have to run the Sphinx search
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
301 > daemon before it works. type "rake sphinx:daemon:start" to do
302 > that
ae4fecc9 »
2008-08-26 Fix README markup rendering.
303 > **Ninh:** great. but now I get a different error. something about
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
304 > BackgrounDRb.
c43b7d1b »
2008-08-26 Fix README markup rendering.
305 > **Hongli:** oops, I forgot to mention this too. you need to start the
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
306 > BackgrounDRb server with "rake backgroundrb:start_server"
ae4fecc9 »
2008-08-26 Fix README markup rendering.
307 > **Ninh:** okay, so every time I want to use this app, I have to type
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
308 > "rake sphinx:daemon:start", "rake backgroundrb:start_server" and
309 > "./script/server"?
c43b7d1b »
2008-08-26 Fix README markup rendering.
310 > **Hongli:** yep
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
311
312 Imagine the above conversation becoming just:
313
c43b7d1b »
2008-08-26 Fix README markup rendering.
314 > **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
55cfa8b2 »
2009-11-06 Update introduction and fix formatting on Github.
315 > features in our application!
ae4fecc9 »
2008-08-26 Fix README markup rendering.
316 > **Ninh:** cool. *pulls from repository*
317 > **Ninh:** awesome, it works!
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
318
319 This is not something that can be achieved with Monit/God. Monit/God are for
320 monitoring daemons, auto-restarting them when they use too much resources.
321 daemon_controller's goal is to allow developers to implement daemon
322 starting/stopping and daemon auto-starting code that's robust. daemon_controller
323 is intended to be used to make daemon-dependent applications Just Work(tm)
324 without having to start the daemons manually.
325
326
ff377f20 »
2008-08-24 Testing markdown for README.
327 Tutorial #1: controlling Apache
328 ===============================
329
330 Suppose that you're a [Phusion Passenger](http://www.modrails.com/) developer,
331 and you need to write tests for the Apache module. In particular, you want to
332 test whether the different Phusion Passenger configuration directives are
333 working as expected. Obviously, to test the Apache module, the Apache web
334 server must be running. For every test, you will want the unit test suite to:
335
336 1. Write an Apache configuration file, with the relevant configuration
337 directive set to a specific value.
338 2. Start Apache.
339 3. Send an HTTP request to Apache and check whether the HTTP response matches
340 your expectations.
341 4. Stop Apache.
342
343 That can be done with the following code:
344
345 require 'daemon_controller'
346
347 File.open("apache.conf", "w") do |f|
348 f.write("PidFile apache.pid\n")
349 f.write("LogFile apache.log\n")
350 f.write("Listen 1234\n")
351 f.write(... other relevant configuration options ...)
352 end
353
354 controller = DaemonController.new(
355 :identifier => 'Apache web server',
356 :start_command => 'apachectl -f apache.conf -k start',
a88c8fdb »
2012-02-04 Add builtin support for pinging sockets.
357 :ping_command => [:tcp, 'localhost', 1234],
ff377f20 »
2008-08-24 Testing markdown for README.
358 :pid_file => 'apache.pid',
359 :log_file => 'apache.log',
51c5d6e1 »
2010-08-04 Fix typo; closes bug #3.
360 :start_timeout => 25
ff377f20 »
2008-08-24 Testing markdown for README.
361 )
362 controller.start
363
364 .... apache is now started ....
365 .... some test code here ....
366
367 controller.stop
368
369 The `File.open` line is obvious: it writes the relevant Apache configuration
370 file.
371
372 The next line is for creating a new DaemonController object. We pass a
373 human-readable identifier for this daemon ("Apache web server") to the
374 constructor. This is used for generating friendlier error messages.
375 We also tell it how Apache is supposed to be started (`:start_command`), how to
376 check whether it can be connected to (`:ping_command`), and where its PID file
377 and log file is. If Apache failed with an error during startup, then it will be
378 reported. If Apache failed with an error after it has gone into the background,
379 then that will be reported too: the given log file is monitored for new error
380 messages.
381 Finally, a timeout of 25 seconds is given. If Apache doesn't start within 25
382 seconds, then an exception will be raised.
383
384 The ping command is just a `Proc` which returns true or false. If the Proc
385 raises `Errno::ECONNREFUSED`, then that's also interpreted by DaemonController
386 as meaning that the daemon isn't responding yet.
387
388 After `controller.start` has returned, we can continue with the test case. At
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
389 this point, we know that Apache is done with initializing.
ff377f20 »
2008-08-24 Testing markdown for README.
390 When we're done with Apache, we stop it with `controller.stop`. This does not
391 return until Apache has fully stopped.
392
393 The cautious reader might notice that the socket returned by the ping command
394 is never closed. That's true, because DaemonController will close it
395 automatically for us, if it notices that the ping command proc's return value
396 responds to `#close`.
397
398 From this example, it becomes apparent that for daemon_controller to work, you
399 must know how to start the daemon, how to contact the daemon, and you must know
400 where it will put its PID file and log file.
401
402
403 Tutorial #2: Sphinx indexing and search server management
404 =========================================================
405
406 We at Phusion are currently developing a web application with full-text search
407 capabilities, and we're using Sphinx for this purpose. We want to make the
408 lives of our developers and our system administrators as easy as possible, so
409 that there's little room for human screw-up, and so we've developed this
410 library. Our Sphinx search daemon is completely managed through this library
411 and is automatically started on demand.
412
29c3b2b4 »
2008-08-26 Fix typos and add a subsection which explains differences between thi…
413 Our Sphinx config file is generated from an ERB template. This ERB template
ff377f20 »
2008-08-24 Testing markdown for README.
414 writes different values in the config file, depending on whether we're in
415 development, test or production mode. We will want to regenerate this config
416 file every time, just before we start the search daemon.
417 But there's more. The search daemon will fail if there is no search index. If a
418 new developer has just checked out the application's source code, then there is
419 no search index yet. We don't want him to go through the pain of having to
420 generate the index manually. (That said, it isn't that much of a pain, but it's
421 just yet-another-thing to do, which can and should be automated.) So before
422 starting the daemon, we will also want to check whether the index exists. If
423 not, then we'll generate it, and then start the daemon. Of course, no two Rails
424 processes may generate the config file or the index at the same time.
425
426 When querying the search server, we will want to automatically start it if it
427 isn't running.
428
429 This can be achieved with the following code:
430
431 require 'daemon_controller'
432
433 class SearchServer
434 SEARCH_SERVER_PORT = 1234
435
436 def initialize
437 @controller = DaemonController.new(
438 :identifier => 'Sphinx search server',
439 :start_command => "searchd -c config/sphinx.conf",
440 :before_start => method(:before_start),
a88c8fdb »
2012-02-04 Add builtin support for pinging sockets.
441 :ping_command => [:tcp, 'localhost', SEARCH_SERVER_PORT],
ff377f20 »
2008-08-24 Testing markdown for README.
442 :pid_file => 'tmp/pids/sphinx.pid',
b224e2d1 »
2008-08-26 Fix a typo in the example code of tutorial #2.
443 :log_file => 'log/sphinx.log')
ff377f20 »
2008-08-24 Testing markdown for README.
444 end
445
446 def query(search_terms)
447 socket = @controller.connect do
448 TCPSocket.new('localhost', SEARCH_SERVER_PORT)
449 end
450 send_query(socket, search_terms)
451 return retrieve_results(socket)
452 end
453
454 private
455 def before_start
456 generate_configuration_file
457 if !index_exists?
458 generate_index
459 end
460 end
461
462 ...
463 end
464
465 Notice the `:before_start` option. We pass a block of code which is to be run,
466 just before the daemon is started. This block, along with starting the daemon,
467 is completely serialized. That is, if you're inside the block, then it's
468 guaranteed that no other process is running this block at the same time as well.
469
470 The `#query` method is the method for querying the search server with search
471 terms. It returns a list of result. It uses `DaemonController#connect`: one
472 passes a block of that method, which contains code for connecting to the
473 daemon. If the block returns nil, or if it raises `Errno::ECONNREFUSED`, then
474 `DaemonController#connect` will automatically take care of auto-starting the
475 Sphinx daemon for us.
476
477
478 A little bit of history
479 =======================
480
481 The issue of managing daemons has been a thorn in our eyes for quite some time
482 now. Until now, we've solved this problem by equipping any daemons that we
483 write with the ability to gracefully handle being concurrently started, the
484 ability to initialize as much as possible *before* forking into the background,
485 etc. However, equipping all this robustness into our code over and over is a
486 lot of work. We've considered documenting a standard behavior for daemons so
487 that they can properly support auto-starting and such.
488
489 However, we've recently realized that that's probably a futile effort.
490 Convincing everybody to write a lot of code for a bit more robustness is
491 probably not realistic. So we took the pragmatic approach and developed a
492 library which adds more robustness on top of daemons' existing behavior. And
493 thus, daemon_controller was born. It is a little bit less efficient compared to
494 when the daemon is designed from the beginning with such abilities in mind, but
495 it's compatible with virtually all daemons, and is easy to use.
496
497
50088c2b »
2011-02-13 Update documentation on lock files
498 Concurrency and compatibility notes
499 ===================================
500 DaemonController uses a lock file and the Ruby `File#flock` API to guarantee
501 synchronization. This has a few implications:
502
503 * On most Ruby implementations, including MRI, `File#flock` is implemented
504 with the POSIX `flock()` system call or the Windows file locking APIs.
505 This kind of file locking works pretty much the way we expect it would.
506 Multiple threads can safely use daemon_controller concurrently. Multiple
507 processes can safely use daemon_controller concurrently. There will be no
508 race conditions.
509
510 However `flock()` is not implemented on Solaris. daemon_controller, if
511 used in MRI does not currently work on Solaris. You need to use JRuby
512 which does not use `flock()` to implement `File#flock`.
513
514 * On JRuby `File#flock` is implemented through the Java file locking API,
515 which on Unix is implemented with the `fcntl()` system calls. This is a
516 different kind of lock with very strange semantics.
517
518 * If *any* process/thread closes the lock file, then the lock on that file
519 will be removed even if that process/thread never requested a lock.
520 * Fcntl locks are usually implemented indepedently from `flock()` locks so
521 if a file is already locked with `flock()` then `fcntl()` will not block
522 when.
523 * The JVM's file locking API only allows inter-process synchronization. It
524 cannot be used to synchronize threads. If a thread has obtained a file
525 lock, then another thread within the same JVM process will not block upon
526 trying to lock the same file.
527
528 In other words, if you're on JRuby then don't concurrently access
529 daemon_controller from multiple threads without manual locking. Also be
530 careful with mixing MRI processes that use daemon_controller with JRuby
531 processes that use daemon_controller.
82fc885e »
2009-02-25 Java cannot use lock files to synchronize threads, so document this f…
532
533
ff377f20 »
2008-08-24 Testing markdown for README.
534 API documentation
535 =================
536
537 Detailed API documentation is available in the form of inline comments in
538 `lib/daemon_controller.rb`.
Something went wrong with that request. Please try again.