Skip to content

Instantly share code, notes, and snippets.

@jittat
Created April 27, 2009 13:52
Show Gist options
  • Save jittat/102496 to your computer and use it in GitHub Desktop.
Save jittat/102496 to your computer and use it in GitHub Desktop.
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