Created
April 27, 2009 13:52
-
-
Save jittat/102496 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'ftools' | |
# DirInit::Manager handles directory initialization and clean-up when | |
# there are many concurrent processes that wants to modify the | |
# directory in the same way. | |
# | |
# An example usage is when each process wants to copy some temporary | |
# files to the directory and delete these files after finishing its | |
# job. Problems may occur when the first process delete the files | |
# while the second process is still using the files. | |
# | |
# This library maintain a reference counter on the processes using the | |
# directory. It locks the dir to manage critical section when | |
# updating the reference counter. | |
# | |
# Example usage: | |
# | |
# dman = DirInit::Manager.new("mydir") | |
# | |
# dman.setup do | |
# # do some initialization | |
# end | |
# | |
# #... do anything you want | |
# | |
# dman.teardown do | |
# # clean up | |
# end | |
# | |
# DirInit::Manager ensures that the block passed to <tt>setup</tt> | |
# only runs once by the first process in the concurrent dir usage and | |
# block passed to <tt>teardown</tt> runs once by the last process in | |
# that concurrent activities leaving that dir. | |
module DirInit | |
class Manager | |
def initialize(dir_name, usage_filename='.usage_counter') | |
@dir_name = dir_name | |
@usage_filename = usage_filename | |
end | |
# Check if someone has initialized the dir. If not, call block. | |
def setup # :yields: block | |
dir = File.new(@dir_name) | |
dir.flock(File::LOCK_EX) | |
begin | |
counter_filename = get_counter_filename | |
if File.exist? counter_filename | |
# someone is here | |
f = File.new(counter_filename,"r+") | |
counter = f.read.to_i | |
f.seek(0) | |
f.write("#{counter+1}\n") | |
f.close | |
else | |
# i'm the first, create the counter file | |
counter = 0 | |
f = File.new(counter_filename,"w") | |
f.write("1\n") | |
f.close | |
end | |
# if no one is here | |
if counter == 0 | |
if block_given? | |
yield | |
end | |
end | |
rescue | |
raise | |
ensure | |
# make sure it unlock the directory | |
dir.flock(File::LOCK_UN) | |
end | |
end | |
# Check if I am the last one using the dir. If true, call block. | |
def teardown | |
dir = File.new(@dir_name) | |
dir.flock(File::LOCK_EX) | |
begin | |
counter_filename = get_counter_filename | |
if File.exist? counter_filename | |
# someone is here | |
f = File.new(counter_filename,"r+") | |
counter = f.read.to_i | |
f.seek(0) | |
f.write("#{counter-1}\n") | |
f.close | |
if counter == 1 | |
# i'm the last one | |
File.delete(counter_filename) | |
if block_given? | |
yield | |
end | |
end | |
else | |
# This is BAD | |
raise "Error: reference count missing" | |
end | |
rescue | |
raise | |
ensure | |
# make sure it unlock the directory | |
dir.flock(File::LOCK_UN) | |
end | |
end | |
protected | |
def get_counter_filename | |
return File.join(@dir_name,@usage_filename) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment