Ensuring only one instance of a script is running

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

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

thanks for the tip.

a @ http://codeforpeople.com/

···

On May 20, 2008, at 10:25 PM, Daniel Berger wrote:

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.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Daniel Berger wrote:

DATA.flock(File::LOCK_EX)

Google seems to think it's original, FWIW.

It's kind of unusual for a new ruby idiom to show up. Yippee.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Daniel Berger wrote:

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

···

--
Posted via http://www.ruby-forum.com/\.

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 :slight_smile:

Thanks,
Sean

···

On Wed, May 21, 2008 at 5:25 AM, Daniel Berger <djberg96@gmail.com> wrote:

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

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.

···

On Wed, May 21, 2008 at 01:25:08PM +0900, Daniel Berger wrote:

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

You're welcome. :slight_smile:

I've wasted a lot of time with pid files...

Regards,

Dan

···

On May 20, 10:45 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

On May 20, 2008, at 10:25 PM, Daniel Berger wrote:

> 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 :wink:

thanks for the tip.

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

···

On Wed, May 21, 2008 at 7:08 AM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

Daniel Berger wrote:

DATA.flock(File::LOCK_EX)

Google seems to think it's original, FWIW.

> 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

Yep, that's definitely cleaner, thanks.

I think I'll use this :slight_smile:

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

Regards,

Dan

···

On May 25, 7:46 am, "Sean O'Halpin" <sean.ohal...@gmail.com> wrote:

On Wed, May 21, 2008 at 5:25 AM, DanielBerger<djber...@gmail.com> wrote:

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

Regards,

Dan

···

On May 20, 11:18 pm, Heesob Park <pha...@gmail.com> wrote:

DanielBergerwrote:
> 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)

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/

···

On May 28, 2008, at 4:35 PM, Mike Kasick wrote:

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.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Hear hear. You could also use a process manager such as daemontools or runit -
that's generally what I do.

···

On Wed, May 21, 2008 at 10:40:49PM +0900, Daniel Berger wrote:

I've wasted a lot of time with pid files...

--
Jos Backus
jos at catnook.com

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. :slight_smile:

Regards,

Dan

···

On May 25, 11:43 am, "Xavier Noria" <f...@hashref.com> wrote:

On Wed, May 21, 2008 at 7:08 AM, Joel VanderWerf > > <vj...@path.berkeley.edu> wrote:
> DanielBergerwrote:

>> DATA.flock(File::LOCK_EX)

> Google seems to think it's original, FWIW.

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

Good to know, thanks Ara.

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

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

···

On May 28, 5:48 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

On May 28, 2008, at 4:35 PM, Mike Kasick wrote:

> 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 installposixlock

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 ifposixlockis 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