[#1816] Ruby 1.5.3 under Tru64 (Alpha)? — Clemens Hintze <clemens.hintze@...>

Hi all,

17 messages 2000/03/14

[#1989] English Ruby/Gtk Tutorial? — schneik@...

18 messages 2000/03/17

[#2241] setter() for local variables — ts <decoux@...>

18 messages 2000/03/29

[ruby-talk:02060] Re: Lockfiles

From: "David Douthitt" <DDouthitt@...>
Date: 2000-03-21 20:38:39 UTC
List: ruby-talk #2060
 David Douthitt writes:
| > Here's my code:
| > 
| > #!/opt/ruby/bin/ruby
| > 
| > class Lock

[...]

| >    def setlock (lockf = @lockfile)
| >       raise "Lockfile not set!" if lockf == nil;
| >       raise "Lock failed!" if
| >          ! system("set -o noclobber ; cat /dev/null 2> /dev/null > #{lockf}")
| 
| Why do you use such complicate system call here? I would prefer a Ruby 
| solution:
|         if not File.exist?(lockf)
|            begin File.open(lockf, "w") { |fp| fp.write(Process.pid) }
|            rescue
|               raise "Lock failed (system error)"
|            end
|         else
|            raise "Lock failed (already locked)"
|         end

This is not guaranteed to work.  This shows the danger of locks: in the
time between the test (File.exist?) and the creation (File.open) someone
else could interfere, and a lock could already be there when File.open
is reached.

The system call goes like this:

set -o noclobber          # Don't clobber files when redirected
cat /dev/null 2> /dev/null > lockfile   # create lock file, or fail if exists

No chance (I think) of someone getting the lock after we "test" it - since
the test is presumably at a system level.

| >       at_exit { self.unlock }
| >       @locked_up = true
| >    end

[...]

| >    def unlock
| >       test(?e, @lockfile) if
| >          raise "Unlock failed!" if
| >             ! system("rm -f #{@lockfile}");
| 
| What you are try to doing here? If 'rm -f' failed the exception is
| raised and 'test(?e,...)' will not performed. If 'rm' went well,
| neither 'raise' nor 'test' will be executed.
| 
|         raise "Unlock failed (no lock)" unless File.exist?(@lockfile)
|         begin
|           File.delete @lockfile
|         rescue
|           raise "Unlock failed (couldn't remove lockfile)"
|         end

I see I mangled my code :-)  What I want was probably more like this
(using your improvements nonetheless):

      return if ! File.exist?(@lockfile)
      begin
         File.delete @lockfile
      rescue
         raise "Unlock failed! (couldn't remove lockfile)"
      end

| Your code is ok! Only there are some things I would like to
| remark. 
| 
|    1. You often use constants like "/var/spool/locks" throughout your
|       whole code.

Definitely!  "/var/spool/locks" is meaningless.  @lockdir has much
more meaning.  It also allows you to change the directory easily.

   lockdir = "/var/spool/locks"

could be changed to

   lockdir = "/var/run"

or to

   lockdir = $*[0]

and so on....

|    2. You use too much system dependend things like system(...)
|       here. This it not really necessary.

When you gotta go, you gotta go :-)

|    3. You should not use things like 'test(?e, ...)'. The Ruby classes 
|       are more descriptive.

I'm still learning....

|    4. IMHO, your code is not carefully designed for the class/instance 
|       clash. Means, you do some things both in class methods and in
|       instance methods. 

I'm still finding out what I can do and can't.  Tell me more about
this class/instance distinction.  I'm forever finding myself wanting
"a bunch of X" where X inevitably turns out to be an instance
and then the first code becomes

   Xes = Hash.new

Is this typical?

|    5. You should not on any case define accessors for your class. I
|       would consider it not-so-good style if I would have to:
| 
|       lock = Lock.new("/var/spool/mail/cle")
|       lock.lockdir = "/var/spool/mail"
|       lock.setlock
| 
|       following would be better, IMO:
| 
|       lock = Lock.new("/var/spool/mail/cle", "/var/spool/mail")
|       lock.setlock

Ahhhhh..... I was wondering some about that.  I have accessors
almost everywhere :-(

| All in all I would say, that your code may be appropiate for what you
| have in mind for your apps. But it is not generic enough, IMHO! No
| steal-the-lock feature. No wait for the lock. No auto-unlock as soon
| as I do not need this file instance anymore...

Steal-the-lock?  HORRORS!

Wait for the lock?  Interesting....

Auto-unlock?  Already there.  Check for the "at_exit" call, as well as the
locked method.  locked is nice:

mylock.locked {
   ...do something while locked...
   }

....now unlocked....

One think I like about my Lock is that the names are similar to those
of the Thread module - so one less thing to have to learn.

In This Thread

Prev Next