Locking a File


You want to prevent other threads or processes from modifying a file that you're working on.


Open the file, then lock it with File#flock. There are two kinds of lock; pass in the File constant for the kind you want.

  • File::LOCK_EX gives you an exclusive lock, or write lock. If your thread has an exclusive lock on a file, no other thread or process can get a lock on that file. Use this when you want to write to a file without anyone else being able to write to it.
  • File::LOCK_SH will give you a shared lock, or read lock. Other threads and processes can get their own shared locks on the file, but no one can get an exclusive lock. Use this when you want to read a file and know that it won't change while you're reading it.

Once you're done using the file, you need to unlock it. Call File#flock again, and pass in File::LOCK_UN as the lock type. You can skip this step if you're running on Windows.

The best way to handle all this is to enclose the locking and unlocking in a method that takes a block, the way open does:

	 def flock(file, mode)
	 success = file.flock(mode)
	 if success
	 yield file
	 return success

This makes it possible to lock a file without having to worry about unlocking it later. Even if your block raises an exception, the file will be unlocked and another thread can use it.

	open('output', 'w') do |f|
	 flock(f, File::LOCK_EX) do |f|
	 f << "Kiss me, I've got a write lock on a file!"



Different operating systems support different ways of locking files. Ruby's flock implementation tries to hide the differences behind a common interface that looks like Unix's file locking interface. In general, you can use flock as though you were on Unix, and your scripts will work across platforms.

On Unix, both exclusive and shared locks work only if all threads and processes play by the rules. If one thread has an exclusive lock on a file, another thread can still open the file without locking it and wreak havoc by overwriting its contents. That's why it's important to get a lock on any file that might conceivably be used by another thread or another process on the system.

Ruby's block-oriented coding style makes it easy to do the right thing with locking. The following shortcut method works with the flock method previously defined. It takes care of opening, locking, unlocking, and closing a file, letting you focus on whatever you want to do with the file's contents.

	def open_lock(filename, openmode="r", lockmode=nil)
	 if openmode == 'r' || openmode == 'rb'
	 lockmode ||= File::LOCK_SH
	 lockmode ||= File::LOCK_EX
	 value = nil
	 open(filename, openmode) do |f|
	 flock(f, lockmode) do
	 value = yield f
	 f.flock(File::LOCK_UN) # Comment this line out on Windows.
	 return value

This code creates two threads, each of which want to access the same file. Thanks to locks, we can guarantee that only one thread is accessing the file at a time (see Chapter 20 if you're not comfortable with threads).

	t1 = Thread.new do
	 puts 'Thread 1 is requesting a lock.'
	 open_lock('output', 'w') do |f|
	 puts 'Thread 1 has acquired a lock.'
	 f << "At last we're alone!"
	 puts 'Thread 1 has released its lock.'
	t2 = Thread.new do
	 puts 'Thread 2 is requesting a lock.'
	 open_lock('output', 'r') do |f|
	 puts 'Thread 2 has acquired a lock.'
	 puts "File contents: #{f.read}"
	 puts 'Thread 2 has released its lock.'
	 # Thread 1 is requesting a lock.
	 # Thread 1 has acquired a lock.
	 # Thread 2 is requesting a lock.
	 # Thread 1 has released its lock.
	 # Thread 2 has acquired a lock.
	 # File contents: At last we're alone!
	 # Thread 2 has released its lock.


Nonblocking locks

If you try to get an exclusive or shared lock on a file, your thread will block until Ruby can lock the file. But you might be left waiting a long time, perhaps forever. The code that has the file locked may be buggy and in an infinite loop; or it may itself be blocking, waiting to lock a file that you have locked.

You can avoid deadlock and similar problems by asking for a nonblocking lock. When you do, if Ruby can't lock the file for you, File#flock returns false, rather than waiting (possibly forever) for another thread or process to release its lock. If you don't get a lock, you can wait a while and try again, or you can raise an exception and let the user deal with it.

To make a lock into a nonblocking lock, use the OR operator (|) to combine File:: LOCK_NB with either File::LOCK_EX or File::LOCK_SH.

The following code will print "I've got a lock!" if it can get an exclusive lock on the file "output"; otherwise it will print "I couldn't get a lock." and continue:

	def try_lock
	 puts "I couldn't get a lock." unless
	 open_lock('contested', 'w', File::LOCK_EX | File::LOCK_NB) do
	 puts "I've got a lock!"
	# I've got a lock!
	open('contested', 'w').flock(File::LOCK_EX) # Get a lock, hold it forever.
	# I couldn't get a lock.


See Also

  • Chapter 20, especially Recipe 20.11, "Avoiding Deadlock," which covers other types of deadlock problems in a multithreaded environment



Date and Time



Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming


Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration

Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net