Ensuring only one instance of a script is running

D

Daniel Berger

Hi all,

I'm probably late to the game on this, but I stumbled across an
interesting use for DATA. You can use it to ensure only one instance
of a given script is running by using flock:

class Foo
def self.mainloop
while true
puts "Looping..."
sleep 3
end
end
end

DATA.flock(File::LOCK_EX)

if $0 == __FILE__
Foo.mainloop
end

__END_

The first run will work, but trying to start the program up again will
fail instantly because of the lock on DATA. I should probably do some
cleanup there, too, but I thought I'd toss this out there and see if
this is of interest to anyone.

Or was I was recovering from a hangover in college when they mentioned
this trick in class? Anyway, there you go.

Regards,

Dan
 
A

ara.t.howard

The first run will work, but trying to start the program up again will
fail instantly because of the lock on DATA. I should probably do some
cleanup there, too, but I thought I'd toss this out there and see if
this is of interest to anyone.

Or was I was recovering from a hangover in college when they mentioned
this trick in class? Anyway, there you go.




quite interesting, i use self-locking scripts quite often, but DATA is
one step shorter ;-)

thanks for the tip.

a @ http://codeforpeople.com/
 
H

Heesob Park

Daniel said:
Hi all,

I'm probably late to the game on this, but I stumbled across an
interesting use for DATA. You can use it to ensure only one instance
of a given script is running by using flock:

class Foo
def self.mainloop
while true
puts "Looping..."
sleep 3
end
end
end

DATA.flock(File::LOCK_EX)
I think the above is actually equal to

File.new($0).flock(File::LOCK_EX)


Regards,

Park Heesob
 
S

Sean O'Halpin

Hi all,

I'm probably late to the game on this, but I stumbled across an
interesting use for DATA. You can use it to ensure only one instance
of a given script is running by using flock:

class Foo
def self.mainloop
while true
puts "Looping..."
sleep 3
end
end
end

DATA.flock(File::LOCK_EX)

if $0 == __FILE__
Foo.mainloop
end

__END_

The first run will work, but trying to start the program up again will
fail instantly because of the lock on DATA. I should probably do some
cleanup there, too, but I thought I'd toss this out there and see if
this is of interest to anyone.

Or was I was recovering from a hangover in college when they mentioned
this trick in class? Anyway, there you go.

Regards,

Dan
Nice one! However, I don't get a failure (on Linux) - instead the
second instance blocks waiting for the first instance to terminate at
which point it executes. Also, needing to specify __END__ is a little
awkward IMHO.

I wonder, does the following work on Windows?

if $0 == __FILE__
if File.open($0).flock(File::LOCK_EX|File::LOCK_NB)
Foo.mainloop
end
end

Wrapped up in a method:

$ cat single_instance.rb
def single_instance(&block)
if File.open($0).flock(File::LOCK_EX|File::LOCK_NB)
block.call
else
warn "Script #{ $0 } is already running"
end
end

$ cat self_locking.rb
require 'single_instance'
if __FILE__ == $0
single_instance do
Foo.mainloop
end
end

I think I'll use this :)

Thanks,
Sean
 
D

Daniel Berger

Locking DATA has been used in Perl for a long time.

Apparently so, because it was a use.perl blog entry that gave me the
idea. For whatever reason I never came across that idiom even while I
was mainly a Perl guy. :)

Regards,

Dan
 
D

Daniel Berger

Nice one! However, I don't get a failure (on Linux) - instead the
second instance blocks waiting for the first instance to terminate at
which point it executes. Also, needing to specify __END__ is a little
awkward IMHO.

I wonder, does the following work on Windows?

if $0 =3D=3D __FILE__
=A0 if File.open($0).flock(File::LOCK_EX|File::LOCK_NB)
=A0 =A0 Foo.mainloop
=A0 end
end

Wrapped up in a method:

$ cat single_instance.rb
def single_instance(&block)
=A0 if File.open($0).flock(File::LOCK_EX|File::LOCK_NB)
=A0 =A0 block.call
=A0 else
=A0 =A0 warn "Script #{ $0 } is already running"
=A0 end
end

$ cat self_locking.rb
require 'single_instance'
if __FILE__ =3D=3D $0
=A0 single_instance do
=A0 =A0 Foo.mainloop
=A0 end
end

Yep, that's definitely cleaner, thanks.
I think I'll use this :)

Excellent. Good to know someone found it useful. :)

Regards,

Dan
 
D

Daniel Berger

DanielBergerwrote:




I think the above is actually equal to

File.new($0).flock(File::LOCK_EX)

It would seem so. Sean O'Halpin has expanded on it a bit, too.

Regards,

Dan
 
M

Mike Kasick

The first run will work, but trying to start the program up again will
fail instantly because of the lock on DATA.

Cute trick.

However, I wouldn't recommend it's use in any distributed code. flocks
are intended to serve a purpose other than what's achieved as a side
effect here, and although it's generally safe, the behavior is unknown
at best if the script file resides in a remote file system like NFS.

For example, in Linux versions prior to 2.6.12 flocks are local only, so
this would have the intended effect. However, in Linux versions 2.6.12
and newer, flocks are emulated by POSIX locks, which should result in
only a single instance running across a set of machines attempting to
run the same script located in an NFS share.
 
A

ara.t.howard

Cute trick.

However, I wouldn't recommend it's use in any distributed code.
flocks
are intended to serve a purpose other than what's achieved as a side
effect here, and although it's generally safe, the behavior is unknown
at best if the script file resides in a remote file system like NFS.

For example, in Linux versions prior to 2.6.12 flocks are local
only, so
this would have the intended effect. However, in Linux versions
2.6.12
and newer, flocks are emulated by POSIX locks, which should result in
only a single instance running across a set of machines attempting to
run the same script located in an NFS share.

you can use the same trick even on NFS

require 'posixlock' # gem install posixlock

DATA.posixlock( File::LOCK_EX | File::LOCK_NB )

and this will work for both NFS and local fs. of course you'd need to
degrade to flock if posixlock is not installed....

my lockfile gem is also NFS safe and can be used for this purpose.
actually it can make any program run one instance without modification

rlock lockifle run_this_once.rb


regards.

a @ http://codeforpeople.com/
 
D

Daniel Berger

you can use the same trick even on NFS

=A0 =A0require 'posixlock' # gem installposixlock

=A0 =A0DATA.posixlock( File::LOCK_EX | File::LOCK_NB )

and this will work for both NFS and local fs. =A0of course you'd need to = =A0
degrade to flock ifposixlockis not installed....

my lockfile gem is also NFS safe and can be used for this purpose. =A0
actually it can make any program run one instance without modification

rlock lockifle run_this_once.rb

Good to know, thanks Ara.

BTW, I don't see posixlock on the main codeforpeople file listing at
http://rubyforge.org/frs/?group_id=3D1024

I was able to find 0.0.1 via the RAA, though. Is that the latest
version? I ask because I want to build from source.

Thanks,

Dan
 

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

Forum statistics

Threads
473,787
Messages
2,569,629
Members
45,329
Latest member
InezZ76898

Latest Threads

Top