How do you use flock and clean up lock files?

J

John Carter

So I'm using flock, but I have this noxious race condition when I try to
clean up the lock files.

I need a way of knowing if anything has a file open at the same time as
me, or I must always leave lock files lying around. Any suggestions?


Here is the sequence..
Process A: fd = open( "lockfile", 'a')
Process A: flock(fd, LOCK_EX)

Process A: do stuff in critical section...

Process B : fd = open( "lockfile", 'a')
Process B: flock(fd, LOCK_EX) // Blocks waiting for lock

Process A: Finishes, doesn't know about B, wants to clean up...
Process A: unlink fd
Process A: close(fd)

Process B: Unblocks, B grabs lock.

Process C : fd = open( "lockfile", 'a') // CREATES A NEW ONE!
Process C: flock(fd, LOCK_EX) // DOESN'T BLOCK!


Here is a chunk of ruby that demonstrates this...


fork do
fd = open( "lockfile", 'a')
puts fd.stat.ino
fd.flock(File::LOCK_EX)
puts "Sleeping"
sleep 10
File.unlink "lockfile"
fd.close
end

sleep 1
fork do
fd = open( "lockfile", 'a')
puts fd.stat.ino
puts "Waiting for lock"
fd.flock(File::LOCK_EX) # Blocks waiting for lock
puts "Got lock"
sleep 100
end

sleep 15

puts "Open file #{Time.now}"
fd = open( "lockfile", 'a') # CREATES A NEW ONE!
puts fd.stat.ino
fd.flock(File::LOCK_EX) # DOESN'T BLOCK!

puts "Did it block #{Time.now}"

====================================

Here is the result...
ruby -w try.rb
6157824
Sleeping
6157824
Waiting for lock
Got lock
Open file Mon Aug 14 14:10:55 +1200 2006
6158309 <-------- Note new INODE NUMBER,
Did it block Mon Aug 14 14:10:55 +1200 2006 <---- No it didn't!
__________________



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
J

John Carter

Don't worry about leaving lock files around. If you really don't like it,
put it in /tmp or /dev/shm.

Sigh! Just seems so...messy.

Sigh! I even thought of using whatever "fuser -v" uses, but strace tells
me it scans /proc! And that's gives me worse aesthetic collywobbles.


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
C

Christopher Brown

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Sorry to jump in on the thread like this...
John are you using flock because it's the easiest & most available
way to sync across processes? If you had easier access to one of the
other sync primitives, would you be using it (i.e. a mutex)?
I'm just wondering what the dominant pattern is, and if we are using
it because something better hasn't come along.

Peace,
Chris


Sigh! Just seems so...messy.

Sigh! I even thought of using whatever "fuser -v" uses, but strace
tells
me it scans /proc! And that's gives me worse aesthetic collywobbles.


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly
wrong later."

From this principle, all of life and physics may be deduced.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (Darwin)

iD8DBQFE3/fZrOGxDZoCCzURAiyiAJ95KDkPwOh54dSChGvFHEN1Eu8GqwCdGBCM
n5wt4dgBkie9fZIeJhnL0Kg=
=Xxgx
-----END PGP SIGNATURE-----
 
J

John Carter

John are you using flock because it's the easiest & most available way to
sync across processes? If you had easier access to one of the other sync
primitives, would you be using it (i.e. a mutex)?
I'm just wondering what the dominant pattern is, and if we are using it
because something better hasn't come along.


I'm have a generic Daemon class exactly like Process.fork but
Daemon.spawn does things like...

* Check, via a lock file, that another copy of itself isn't running
somewhere.

* Has a method to kill all copies of itself (via fuser)

* Detaches itself from the controlling terminal (making it self a true
Daemon) so it doesn't die if parent process dies.

* Can wait until the dameon has started, can wait for it to die.

I suspect I could use fcntl and mandatory locking, but I'm not sure that
would help with this problem.

Ps: (What do Windowsy types do instead of "Process.fork"? )


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
C

Christopher Brown

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ah, gotcha. You really don't need strong cross-process synch, but
just proof another of your processes already exists.

It sounds like you are following most of the guidance from APUE. In
which case, on *nixes, why not write a pidfile in /var/run? It's a
fairly standard way to do things.
Write the process id into /var/run/<application>.pid. Any launch of
the daemon should check for the existence of this file first and for
added hygiene, check the actual process id written into it against
what's in the process table (or against /proc/<id> if on Linux). At
the end of the day, it's much like the lockfile version, but gives a
bit more information and some tools will work directly with the
pidfile. On some Linux distros, there are tools that will create it
for you and take the daemon managment out of your hands if you choose.

The Windows process model is quite a bit different, and really favors
creating new threads (I guess not what you want here...) Fork / exec
model isn't directly supported. I guess some toolkits attempt to
fake it though. There's a nifty description of the cygwin
implementation here: http://www.cygwin.com/cygwin-ug-net/highlights.html

I guess this is drifting off-topic from Ruby, so I'll shut up now.

Peace,
Chris


I'm have a generic Daemon class exactly like Process.fork but
Daemon.spawn does things like...

* Check, via a lock file, that another copy of itself isn't running
somewhere.

* Has a method to kill all copies of itself (via fuser)

* Detaches itself from the controlling terminal (making it self a true
Daemon) so it doesn't die if parent process dies.

* Can wait until the dameon has started, can wait for it to die.

I suspect I could use fcntl and mandatory locking, but I'm not sure
that
would help with this problem.

Ps: (What do Windowsy types do instead of "Process.fork"? )


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly
wrong later."

From this principle, all of life and physics may be deduced.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (Darwin)

iD8DBQFE4ASnrOGxDZoCCzURAv7mAKDHcRZksMTCJDij3D+CayD41sn1ggCfWgVS
RnT7l0wjPDKKUoBHSAjl+nM=
=u++g
-----END PGP SIGNATURE-----
 
M

Mauricio Fernandez

Sigh! Just seems so...messy.

You can check if the file you got a lock on is the one presently on the FS.
If the lockfile was unlinked, File.stat will fail; if another process created
a file with the same name, the ino will differ. You can detect both situations
and try again:

$ cat lock.rb

def lock(filename)
dest = nil
loop do
dest.close if dest
dest = File.open(filename, "ab")
dest.flock(File::LOCK_EX)
old_stat = dest.stat
new_stat = File.stat(filename) rescue nil
break if new_stat and
old_stat.dev == new_stat.dev and
old_stat.ino == new_stat.ino
end
yield
ensure
File.unlink(filename) rescue nil
dest.close rescue nil
end

LOCKFILE = "lockfile"

$stdout.sync = true

t = Time.new
fork do
lock LOCKFILE do
puts "A"
p File.exist?(LOCKFILE)
puts "Sleeping in A"
sleep 10
end
end

sleep 1
fork do
lock LOCKFILE do
puts "B"
p File.exist?(LOCKFILE)
puts "Sleeping in B"
sleep 10
end
end

sleep 15

puts "Attempting to get lock in C"
lock LOCKFILE do
puts "C"
p File.exist?(LOCKFILE)
end


puts "Total time: #{Time.new - t}"

$ ruby lock.rb
A
true
Sleeping in A
B
true
Sleeping in B
Attempting to get lock in C
C
true
Total time: 20.007932
 
F

Francis Cianfrocca

I'm have a generic Daemon class exactly like Process.fork but
Daemon.spawn does things like...

* Check, via a lock file, that another copy of itself isn't running
somewhere.

* Has a method to kill all copies of itself (via fuser)

* Detaches itself from the controlling terminal (making it self a true
Daemon) so it doesn't die if parent process dies.

* Can wait until the dameon has started, can wait for it to die.

I suspect I could use fcntl and mandatory locking, but I'm not sure that
would help with this problem.

Ps: (What do Windowsy types do instead of "Process.fork"? )


I have to say that in my opinion, none of the problems you're trying
to solve are particularly deep nor particularly new. Stick with the
tried and true rather than inventing new practice. On Unix, just use
flock in the standard way. Don't worry about lockfile clutter- that's
what /tmp and /var/run are for. Advisory locks vanish with file
descriptors which makes them graceful when things go wrong. Like any
"mutex" object, hold them for the shortest possible period of time and
make sure you're not susceptible to hanging while you hold one-
otherwise you will go mad once you are in production.

Don't use anything that involves scanning /proc: that locks you not
only to specific Unixes but even to specific versions. Don't use
mandatory locks: this isn't what they were designed for and they are
extremely painful when things go wrong.

On Windows, don't cry, just use CreateProcess. Windows has a strong
cross-process mutex, unlike Linux, so use it. It's heavyweight, but so
what? Nothing is heavier than a process, which is what you're trying
to sync in the first place.
 
K

khaines

Cry, mostly.

It depends on how close to fork() your needs are.

If your need is really closer to a fork & exec than just a fork, it's not
too hard. I posted this code in another thread the other day, but here's
a method I use in a test suite to allow the code to create new processes
on Windows and *nix. It requires Daniel Berger's win32/process lib, but
with that in hand -- no problem.

module IWATestSupport
def self.create_process(args)
@fork_ok = true unless @fork_ok == false
pid = nil
begin
raise NotImplementedError unless @fork_ok
unless pid = fork
Dir.chdir args[:dir]
exec(*args[:cmd])
end
rescue NotImplementedError
@fork_ok = false
begin
require 'rubygems'
rescue Exception
end

begin
require 'win32/process'
rescue LoadError
raise "Please install win32-process to run all tests on a Win32 platform. 'gem install win32-process' or http://rubyforge.org/projects/win32utils"
end
cwd = Dir.pwd
Dir.chdir args[:dir]
pid = Process.create:)app_name => args[:cmd].join(' '))
Dir.chdir cwd
end
pid
end
end


Kirk Haines
 
G

gwtmp01

It's heavyweight, but so what? Nothing is heavier than a process,
which is what you're trying to sync in the first place.

Hmm. One of the hallmarks of the Unix philosophy was/is the idea that
processes are 'cheap'.

I tend to think that people gravitate towards a threading solution a
little
too reflexively these days and then get bitten by all sorts of
concurrency
issues that can often be avoided via cooperating processes.

The Plan 9 papers offer some interesting insight into the process/thread
dichotomy.


Gary Wright
 
F

Francis Cianfrocca

Hmm. One of the hallmarks of the Unix philosophy was/is the idea that
processes are 'cheap'.

I tend to think that people gravitate towards a threading solution a
little
too reflexively these days and then get bitten by all sorts of
concurrency
issues that can often be avoided via cooperating processes.

The Plan 9 papers offer some interesting insight into the process/thread
dichotomy.


Gary Wright

Hmm, you were responding to me so I thought I should clarify. One of
the questions upthread was about Windows, not Plan 9. On Unix just use
flock for process sync. On Windows there is a useable cross-process
mutex.

As far as threads are concerned, I bow to no one in my disdain for
them. (But I've been programming threaded apps since Win32 was in
beta, so I think I'm qualified to disdain them.)
 
G

gwtmp01

Hmm, you were responding to me so I thought I should clarify. One of
the questions upthread was about Windows, not Plan 9. On Unix just use
flock for process sync. On Windows there is a useable cross-process
mutex.

I guess it wasn't clear to me that you were responding to
the situation with respect to windows. It seemed like a more general
comment.

Lots of people seem to think Unix threads are heavyweight also.



Gary Wright
 
J

John Carter

I have to say that in my opinion, none of the problems you're trying
to solve are particularly deep nor particularly new.

I know, that's why I assumed there was a standard solution I was missing.
Answer: Have a world writable standard clutter directory...and a
standard security hole where malware can do nasty things to temp
files and lock files.
On Windows, don't cry, just use CreateProcess. Windows has a strong
cross-process mutex, unlike Linux, so use it. It's heavyweight, but so
what? Nothing is heavier than a process, which is what you're trying
to sync in the first place.

ri CreateProcess
Nothing known about CreateProcess.

Where do I start looking for that?



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
F

Francis Cianfrocca

Nothing known about CreateProcess.

Where do I start looking for that?
CreateProcess is a Windows API. So is CreateMutex, which you'll want
to look at. Daniel Berger's Win32 library may have Ruby wrappers for
these, so I'd have a look there too.
 
M

M. Edward (Ed) Borasky

Francis said:
Depends on the Unix. Solaris threads (not LWPs) are light as a feather.
Linux 2.6 threads are almost as heavy as processes. Linux 2.4 threads are
almost unusable.
Almost unusable? How are they worse than "almost as heavy as processes?"

:)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top