[Ruby]プログラムの２重起動を防ぐ

ruby プログラムの２重起動を防ぐ方法として 次の記事
  http://altarf.net/computer/ruby/1439
を見つけたのですが、ちょっと複雑すぎる気がする。
そこで、次のコードを書いてみました。（ruby-1.9.3, MacOS 上）

--- cat single_task.rb ------

    # -*- coding: utf-8 -*-
    
    ######################
    # 処理が同時に複数 走らないようにする。
    #
    # すでに処理が走っていれば、その旨を表示して exit 1 する。
    # 他に処理が走っていなければ、 処理を行う。
    # 処理が正常処理したなら、exit 0 する。
    # 処理が正常終了しなければ exit 1 する。
    #
    # 実行例
    # -------
    # 次のように起動すれば、exit 値も確認できる。
    #    $ ruby single_task.rb; echo $?
    #
    # See http://doc.ruby-lang.org/ja/1.9.3/class/File.html
    #     http://without-truth.hatenablog.com/entry/2012/11/08/111334
    #     http://d.hatena.ne.jp/unageanu/20080127/1201423758
    ######################
    
    # p Signal.list でシグナル一覧が得られるけれど...
    # ここでは :INT, :KILL, :TERM にだけ対応する。
    
    # Ctrl-C が押された場合の処理
    Signal.trap(:INT) {
      puts "### Signal :INT"
      exit 1
    }
    # kill -9 された場合の処理
    Signal.trap(:KILL) {
      puts "### Signal :KILL"
      exit 1
    }
    # kill された場合の処理
    Signal.trap(:TERM) {
      puts "### Signal :TERM"
      exit 1
    }
    
    def single_run(&task)
      begin
        ret = 1
        File.open('lockfile', 'w') do |f|
          # ロック開始
          # File::LOCK_NB を指定しているので既にロックされている場合は
          # ブロッキングされずに false になる
          if f.flock(File::LOCK_EX | File::LOCK_NB)
            puts '### lock'
            ret = task.call
            # ロック解除
            f.flock(File::LOCK_UN)
            puts '### unlock'
          else
            # 既にロックされている
            puts "### alreadly runnng..."
          end
        end
      rescue => ex
        puts "### error #{ex.message}"
      end
      ret
    end
    
    if $0 == __FILE__
      def my_task(arg = 1)
        puts "sleep #{arg} sec(s) ..."
        sleep arg
        0 # Success
      end
    
      ret = single_run { my_task ARGV[0].to_f }
      exit ret
    end
    #--- Ed of File ---

此処  Qiita には shell-script の多重起動を制御する方法の記事が 複数 投稿されています。  
そちらのほうが確実性が高いなら、
　　ruby プログラムを shell-script で 包んで、shell-script のレベルで多重起動制御をする
のも有り と思っていますが...

簡単な rspec コードも書いてみました。

    $ rspec single_task_spec.rb
    ### sleep 1 ...
    .### lock
    ### sleep 1 ...
    ### unlock
    .### lock### alreadly runnng...
    
    ### sleep 3 ...
    ### unlock
    ### lock
    ### sleep 1 ...
    ### unlock
    .
    
    Finished in 6.01 seconds
    3 examples, 0 failures

--- cat single_task_spec.rb ------

    # -*- coding: utf-8 -*-
    
    require 'rspec'
    require './single_task.rb'
    
    describe 'apas_server' do
    
      def sample_task(s = 1)
        puts "### sleep #{s} ..."
        sleep s
        0
      end
    
      specify "run normal" do
        ret = sample_task 1
        expect(ret).to eq(0)
      end
    
      specify "run as single_task" do
        ret = single_run { sample_task 1 }
        expect(ret).to eq(0)
      end
    
      specify "run multi as single_task" do
        t = Thread.new do
          ret = single_run { sample_task 3 }
          expect(ret).to eq(0)
          # 上の処理が終わったあとなら、正常に実行できる。
          ret = single_run { sample_task 1 }
          expect(ret).to eq(0)
        end
    
        # 上の処理が実行中なので、 ret = 1 になる。
        ret = single_run { sample_task 1 }
        expect(ret).to eq(1)
    
        t.join
      end
    
      # TODO: Ctrl-C, kill などシグナルが送られた時の挙動をテストする事。
    
    end