github.com/hy3/cuto@v0.9.8-0.20160830082821-aa6652f877b7/util/lock_unix.go (about)

     1  // Copyright 2015 unirita Inc.
     2  // Created 2015/06/03 shanxia
     3  
     4  // +build darwin linux
     5  
     6  package util
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"syscall"
    14  	"time"
    15  )
    16  
    17  type LockHandle struct {
    18  	fd     int  //lockファイルディスクリプタ
    19  	isLock bool // ロックフラグ
    20  }
    21  
    22  var (
    23  	ErrBusy = errors.New("Locked by other process.")
    24  
    25  	lockFilePath = os.TempDir()
    26  )
    27  
    28  // ファイルを利用した同期処理機能の初期化関数。
    29  // ファイル作成が可能なファイル名を指定します。
    30  func InitLock(name string) (*LockHandle, error) {
    31  	if len(name) > 0 {
    32  		fullname := filepath.Join(lockFilePath, name)
    33  		// open処理移動
    34  		var fd int
    35  		var err error
    36  		if _, err = os.Stat(fullname); err != nil {
    37  			fd, err = syscall.Open(fullname, syscall.O_CREAT|syscall.O_RDONLY|syscall.O_CLOEXEC, 0644)
    38  		} else {
    39  			fd, err = syscall.Open(fullname, syscall.O_RDONLY|syscall.O_CLOEXEC, 0644)
    40  		}
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		return &LockHandle{fd, false}, nil
    45  	} else {
    46  		return &LockHandle{0, false}, errors.New("Invalid lockfile name.")
    47  	}
    48  }
    49  
    50  // ファイルを利用して、ロックを行います。
    51  // 引数で指定したミリ秒まで待機します。0以下を指定した場合は、リトライしません。
    52  // 他プロセスのロックが指定時間内に解けなかった場合は、ErrBusy を返します。
    53  func (l *LockHandle) Lock(timeout_millisec int) error {
    54  	if l.fd == 0 {
    55  		return errors.New("Not initialize.")
    56  	}
    57  	err := l.tryLock()
    58  
    59  	if err == nil {
    60  		return nil
    61  
    62  	} else { // Locked by other process.
    63  		if timeout_millisec > 0 {
    64  			st := time.Now()
    65  			for {
    66  				time.Sleep(1 * time.Millisecond)
    67  				err = l.tryLock()
    68  				if err == nil {
    69  					return nil // ロック成功
    70  				}
    71  				if time.Since(st).Nanoseconds() > (int64(timeout_millisec) * 1000000) {
    72  					fmt.Fprintf(os.Stderr, "Lock Timeout %v\n", err)
    73  					break
    74  				}
    75  			}
    76  		}
    77  	}
    78  	return ErrBusy
    79  }
    80  
    81  // ロック解除。
    82  func (l *LockHandle) Unlock() error {
    83  	if !l.isLock {
    84  		return errors.New("It has not been locked yet.")
    85  	}
    86  	if err := syscall.Flock(l.fd, syscall.LOCK_UN); err != nil {
    87  		return err
    88  	}
    89  	l.isLock = false
    90  	return nil
    91  }
    92  
    93  // ロックファイルの終了処理。InitLock()成功後は、必ず呼び出して下さい。
    94  func (l *LockHandle) TermLock() error {
    95  	if l.fd != 0 {
    96  		if l.isLock {
    97  			l.Unlock()
    98  		}
    99  		syscall.Close(l.fd)
   100  		l.fd = 0
   101  	}
   102  	return nil
   103  }
   104  
   105  // 実際にロック処理を行う。
   106  func (l *LockHandle) tryLock() error {
   107  	if err := syscall.Flock(l.fd, syscall.LOCK_EX); err != nil {
   108  		return err
   109  	}
   110  	l.isLock = true
   111  	return nil
   112  }