rubyists-

due to whacky behavior exhibited on various platforms regarding flock, i would
like to suggest an implementation of a File#lock method similar the following
one.  any suggestions to improve it welcome.

file File_lock.rb
----CUT----
# File#lock - a method for safely locking a file between processes and/or
# threads

class File

  require 'thread'

private
  attr_writer :sem

public
  attr_reader :sem

  def lock
    sem = Mutex.new unless sem
    sem.synchronize do
      catch (:eintr){
	begin
	  loop { flock (LOCK_EX | LOCK_NB) and break or Thread.pass}
	rescue Errno::EINTR
	  throw :eintr
	end
      }

      yield self

      catch (:eintr){
	begin
	  flock LOCK_UN
	rescue Errno::EINTR
	  throw :eintr
	end
      }
    end
  end

end


if $0 == __FILE__
# this program coordinates access to an output file for the purpose of #
# inserting a timestamp between multiple threads spawned from within multiple
# forked processes.  the coordination is considered successful if no
# timestamps are inserted non-sequentially

require 'thread'
require 'ftools'



pid = Process.pid

n_child    = (ARGV.shift or 4).to_i
n_thread   = (ARGV.shift or 4).to_i
n_mark     = (ARGV.shift or 2 << 9).to_i
outpath    = (ARGV.shift or '/tmp/out')
sortedpath = (ARGV.shift or '/tmp/sorted')


File.rm_f outpath
File.rm_f sortedpath

n_mark_per_thread = n_mark / n_child / n_thread
cids              = []

$stdout.sync = true
$stdout.puts "================"
$stdout.puts "N_CHILD   : #{n_child}"
$stdout.puts "N_THREAD  : #{n_thread}"
$stdout.puts "N_MARK    : #{n_mark}"
$stdout.puts "OUTPATH   : #{outpath}"
$stdout.puts "================"
$stdout.puts "\n#{pid} STARTED"


n_child.times {
  cids <<
    Process.fork {
      ppid = Process.ppid
      pid  = Process.pid
      sem  = Mutex.new
      output = "\t#{ppid}.#{pid} STARTED\n"
      File.open (outpath, 'a+') { |file|
	threads = []
	n_thread.times {
	  threads <<
	    Thread.new (threads.size) { |tid|
	      sem.synchronize {
		output << "\t\t#{ppid}.#{pid}.#{tid} STARTED\n"
	      }
	      n_mark_per_thread.times {
		file.lock { |f|
		  f.seek IO::SEEK_END
		  f.syswrite "%10.5f\n" % Time.now.to_f
		}
		Thread.pass
	      }
	    }
	}
	threads.map {|t| t.join}
      }
      $stdout.puts output
      exit!
    }
}

cids.map { |cid| Process.waitpid cid }
cids.map { |cid| $stdout.puts "\t#{pid}.#{cid} FINISHED" }

output = IO.readlines outpath
sorted = output.sort {|a,b| a.to_f <=> b.to_f}

if output != sorted
  File.open (sortedpath, 'w') {|f| f.puts sorted }
  $stdout.puts "\nERROR!"
  $stdout.puts "SUGGEST 'diff -c #{outpath} #{sortedpath}'"
else
  $stdout.puts "\nSUCCESS!"
end

end
----CUT----

-a

-- 

 ====================================
 | Ara Howard
 | NOAA Forecast Systems Laboratory
 | Information and Technology Services
 | Data Systems Group
 | R/FST 325 Broadway
 | Boulder, CO 80305-3328
 | Email: ahoward / fsl.noaa.gov
 | Phone:  303-497-7238
 | Fax:    303-497-7259
 ====================================