10

In Ruby 1.9.x, what might be a simple way to either not allow my Ruby script to run again, or wait for the previous instance to finish?**

I'm hoping to avoid messy file-locking or process table checking.

Is there something like a global mutex or semaphore already in the core? I studied the native Mutex but that only seems to apply to threads within one Ruby process, not across different processes.

** Later on I might add timeout functionality, or limit to N instances, or look to be using more than one global lock (one per system-wide resource that should only have max one instance).

Community
  • 1
  • 1
Marcos
  • 4,796
  • 5
  • 40
  • 64

2 Answers2

5

This very short code will freeze in place until a lockfile in /tmp named after your script is locked exclusively:

File.open("/tmp/#{File.basename $0}.lock", File::RDWR|File::CREAT, 0644).flock(File::LOCK_EX)

Any other program locking it, Ruby or not, needs only to terminate or be killed, for the newer process instance to unblock and continue on. So for now this workaround does what I need. I can call my Ruby program with

timeout 1m ./myrubyscript.rb

from the wrapping bash script if I get impatient, for instance. (In that example, myrubyscript.rb will terminate after 1 minute whether or not and got its file lock to continue doing what it was written to do.)

Marcos
  • 4,796
  • 5
  • 40
  • 64
2

This is usually solved at the OS level, by checking for a flag-file and not trying to launch again, not in the script itself.

But, if you want to check in the script, look for a semaphore file in a known location. If it exists and it's not within a given time window that fits in your launch window, then delete the file and create a new one. If it's in your time window then exit.

You can use file-locking, or storing timestamps in the file, or many other checks if you don't launch using a cron-type mechanism. Each approach has its pluses and minuses.

You'll want to trap interrupts so that when you exit from a Control-C you erase that file too.

Read through the related questions on the right. Though they are covering various other languages, the suggested answers will help understand the problems and possible ways to work around them across all languages.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • I already wrap most of my Ruby with Bash scripts that themselves use code like `[ -z "$(pidof -x -o %PPID -o $$ ${0##*/})" ] || exec echo "$(date) $0:$$ already smells PID:$(pidof -x $0),...bye!!"` however that serves other purposes. An even better solution already contained within Ruby itself is to lock any fixed file for writing eg. `/tmp/scriptname.rb.lock`. At exit or even if my code crashes (which it often does raising exceptions) the system will clear that lock. I'll just go with that hack until there's a clean, native interface to global semaphores--which I'll have a need for anyway. – Marcos Mar 12 '12 at 18:56