è¨èªã®å£ãã¶ã£å£ããã©ãããããããã§ãã
ããã»ã¹éã®å¾ ã¡åããã®ææ³ã¨ãã¦ãã¡ã¤ã«ããã¯ãããã¾ãããã®ãã¡ã¤ã«ããã¯ãã¿ã¤ã ã¢ã¦ãã§ãã£ã³ã»ã«ãããã¨ãå¯è½ã«ããããã«ä»¥ä¸ã®ã©ã¤ãã©ãªãä½ã£ãã®ã§ãã®è§£èª¬ããããã¨æãã¾ãã
対象
ä»åã®å¯¾è±¡ã¯ä»¥ä¸ã®ç°å¢ã¨ãã¾ã
ãã¡ã¤ã«ããã¯ãå®ç¾ããã·ã¹ãã ã³ã¼ã«
Linux ã§ã¯ãfcntl
㨠flock
ã¨ããã·ã¹ãã ã³ã¼ã«ã§ãã¡ã¤ã«ããã¯ãã§ãã¾ãã
ãã® 2 ã¤ã§ç²å¾ãããããã¯ã¯ã«ã¼ãã«å
ã§ã¯å¥ã®ãã®ã¨ãã¦æ±ããã¾ããã FLOCK 㮠注æ ã«ãããéããä¸é¨ã®ã·ã¹ãã ã§ã¯ flock
㨠fcntl
ãå½±é¿ãä¸ããå¯è½æ§ããããããåä¸ã®ããã°ã©ã ã®ä¸ã§ã¯ã©ã¡ããã®ããã¯ã®ã¿ã使ãããã«ããæ¹ãè¯ãããã§ããï¼ããã°ã©ã ã®è¤éæ§ã軽æ¸ããã¾ãï¼
ä»åã®è©±ã¯ãfcntl
㨠flock
ã®ã©ã¡ãã§ãå
±éã®è©±ãªã®ã§ãfcntl
ã使ã£ã¦ãã¢ãè¡ãã¾ãã
FCNTL
fcntl
ã¯ããã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ãæä½ããã·ã¹ãã ã³ã¼ã«ã§ãã
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
以ä¸ã®ã³ãã³ããæå®ãã¦ã弿°ã« flock
æ§é ä½ã渡ãã¨ãã¡ã¤ã«ããã¯ãã§ãã¾ãã
ããã¯ã¯ãã¡ã¤ã«ã®ãã¤ãåä½ã§ç¯å²ãæå®ãã¦ããã¯ãã¾ãã
åºæ¬ã¯ã¢ããã¤ã¶ãªã¼ããã¯ã§ãé å¼µãã¨å¼·å¶ããã¯ãå¯è½ã§ãã
ããã㯠POSIX ã§æ¨æºåããã¦ãã¾ãã
F_SETLK
: ããã¯ã®ç²å¾ããã³ããããã³ã°ã§è¡ãã¾ããããã¯ã®ç²å¾ã«å¤±æããå ´åã¯EACCES
ã¾ãã¯EAGAIN
ã¨ã©ã¼ãè¿ãã¾ããF_SETLKW
: ããã¯ã®ç²å¾ãè¡ãã¾ããããã¯ã®ç²å¾ã«å¤±æããå ´åã¯ããã¯ã®ç²å¾ãã§ããã¾ã§å¦çããããã¯ãã¾ããF_GETLK
: ããã¯ã®æ å ±ãåå¾ãã¾ãã
ããã¯ã®ç¨®é¡ã¯ F_RDLCK
㨠F_WRLCK
ããã flock
æ§é ä½ã§æå®ãã¾ããgo ã§ãã sync.RWMutex
ã¿ãããªæãã§ãããF_UNLCK
ãæå®ããã¨ããã¯ãè§£é¤ãã¾ãã
FLOCK
次㫠flock
ã¯ãååã®éããã¡ã¤ã«ãããã¯ããã·ã¹ãã ã³ã¼ã«ã§ãã
flock
㯠BSD ç³»ç±æ¥ã®ã·ã¹ãã ã³ã¼ã«ã§ POSIX ã«ã¯å«ã¾ãã¾ããããã·ã³ãã«ãªã¤ã³ã¿ã¼ãã§ã¤ã¹ã§ãã
ããã¯ã®ç¯å²ã¯ãã¡ã¤ã«å
¨ä½ã®ã¿ã§ãfcntl
ã®ããã«ç¹å®ã®ç¯å²ã®ã¿ãããã¯ãããã¨ã¯ã§ãã¾ããã
ããã¯ã¯ã¢ããã¤ã¶ãªã¼ããã¯ã«ãªãã¾ãã
#include <sys/file.h> int flock(int fd, int operation);
operation
ã«ä»¥ä¸ã®ãªãã¬ã¼ã·ã§ã³ãæå®ãã¦ãã¡ã¤ã«ãããã¯ãã¾ãã
LOCK_SH
: å ±æããã¯ãfcntl
ã§ããF_RDLCK
ã¿ãããªLOCK_EX
: æä»ããã¯ãfcntl
ã§ããF_WRLCK
ã¿ãããªLOCK_UN
: ããã¯ã®è§£é¤
ã¾ããoperation
ã«è«çåã§ LOCK_NB
ãæå®ããã¨ãã³ããããã³ã°ã§ããã¯ãç²å¾ããæå®ããªãå ´åã¯ããã¯ãç²å¾ã§ããã¾ã§å¦çããããã¯ãã¾ãã
Go è¨èªã«ããããã¡ã¤ã«ããã¯
Go ã§ã¯ syscall
ããã±ã¼ã¸ã« syscall.FcntlFlock()
㨠syscall.Flock()
ãç¨æããã¦ãã¾ãã
// fcntl ã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error // flock ã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ func Flock(fd int, how int) (err error)
ãããã¯ããã¾ã§ãã·ã¹ãã ã³ã¼ã«ãã©ããããã ãã®ãã®ãªã®ã§ã¿ã¤ã ã¢ã¦ãã®æ©è½ã追å ããå¿ è¦ãããã¾ãã
github ã§ file lock
ã§ã¿ã¤ã ã¢ã¦ãä»ãã®ãã¡ã¤ã«ããã¯ã® Go ã®å®è£
ã調ã¹ã¦ããã¨ä»¥ä¸ã®2ã¤ã®ã©ã¤ãã©ãªãæ¢ããã¨ãã§ãã¾ããã
github.com/gofrs/flock
gofrs/flock
ã«ã¯ãTryLockContext
ã¨ããã¡ã½ããããã context.Context
ãå©ç¨ãã¦ã¿ã¤ã ã¢ã¦ããããã¯ã®ä¸æãå®ç¾ã§ãã¾ãã
func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error)
ãããããã®å
é¨å®è£
㯠syscall.Flock
ããã³ããããã³ã°ã¢ã¼ãã§å¼ã³åºããæåããã¾ã§ for
æã§ retryDelay
ã§è¨å®ãããééã§å¼ã³åºãç¶ãããã®ã«ãªã£ã¦ãã¾ãã
ããã§ã¯ãã«ã¼ãã«å é¨ã®ããã¯ãã¥ã¼ ã«å ¥ããªãããã è¤æ°ããã»ã¹éã®ããã¯ã®é åºãå´©ãã¦ãã¾ãã¾ã ã(åè : linux - flock locking order? - Stack Overflow)
ã¾ããããã¯ã®ç²å¾ã¾ã§ æå¤§ã§ retryDelay
ã®åã®é
å»¶ãçºçãã ãã¨ã«ãªãã¾ãã
github.com/jviney/go-filelock
jviney/go-filelock
ã§ã¯ãObtain
颿°ã«ã¿ã¤ã ã¢ã¦ãã®æéãæ¸¡ãã¦ã¿ã¤ã ã¢ã¦ãä»ãã®ãã¡ã¤ã«ããã¯ãã§ãã¾ãã
func Obtain(path string, timeout time.Duration) (lock *Lock)
ãããããã®å
é¨å®è£
㯠syscall.Flock
ãããããã³ã°ã¢ã¼ãã§å®è¡ãã goroutine ãç«ã¡ä¸ãã¦ããã®çµäºã¨ã¿ã¤ã ã¢ã¦ãã Obtain
颿°å
ã§ select
æã§å¾
ã¤ãã®ã§ããã
ã¿ã¤ã ã¢ã¦ãããã¨ããã«æ°ãã goroutine ãç«ã¡ä¸ãããã® goroutine å ã§ããã¯ãç²å¾ã§ããã¾ã§å¾ ã£ã¦å³åº§ã«ããã¯ãéæ¾ããããã«ãã¦ãã¾ãã
// We hit the timeout without successfully getting the lock. // The goroutine blocked on syscall.Flock() is still running // and will eventually return at some point in the future. // If the lock is eventually obtained, it needs to be released. go func() { if err := <- flockChan; err == nil { releaseFlock(file) } }()
ããã¯ãç²å¾ã§ããªãã£ãå ´åã ããã¯éæ¾ç¨ã¨ããã¯ãåå¾ãã 2 ã¤ã® goroutine ãæ®ãç¶ãã¾ã ã
ã¾ããããã¯ãç²å¾ä¸ã®ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ãéããã¨ããã¯ãç²å¾ããã¾ã§ãããã¯ãã¾ãã ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ãè§£æ¾ãããã¨ãã§ããªã ãããã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ãæ®ãç¶ãã¾ãã
ããã§ã¯ãªã½ã¼ã¹ç®¡çã®é¢ããã¬ãã¬ãã§ãã
ã©ããã£ã¦ãã¡ã¤ã«ããã¯ã䏿ããã
ä¸ã® 2 ã¤ã®ã©ã¤ãã©ãªã¯ãã¡ã¤ã«ããã¯ã®ç´æ¥çãªä¸æãã§ããªãã£ãããã«ãã®ãããªèª²é¡ãæ®ã£ã¦ãã¾ããã
ãã®ç¹ã解決ããããã«ãããã§ã©ããã£ã¦ãããã¯ãã¦ãããã¡ã¤ã«ããã¯ã䏿ããããèããå¿ è¦ãããã¾ãã
çã㯠ã·ã°ãã« ã§ãã
ããããã³ã°ããã·ã¹ãã ã³ã¼ã«ã¯ã·ã°ãã«ãåä¿¡ãããã¨ã«ãã£ã¦ãããã¯ãè§£é¤ãã EINTR
ã¨ã©ã¼ãè¿ãããã«ãªã£ã¦ãã¾ããï¼ã¿ã¤ã ã¢ã¦ãã¨ããæèã§ã¯ alarm
ã使ããã¨ãå¤ããã§ãï¼
Timeouts for system calls are done with signals. Most blocking system calls return with EINTR when a signal happens, so you can use alarm to implement timeouts.
https://stackoverflow.com/questions/5255220/fcntl-flock-how-to-implement-a-timeout#answer-5255473
ãã®ããã·ã°ãã«ãéä¿¡ãã¦ããã¯ç²å¾å¦çã« EINTR
ãçºçããããã¨ã§ãã¡ã¤ã«ããã¯ã®ã¿ã¤ã ã¢ã¦ãã䏿ãå®ç¾ã§ãã¾ãã
ã·ã°ãã«ã«ããå²ãè¾¼ã¿ã Go ã§ãå®ç¾ããã
ã·ã°ãã«ãèªåã®ããã»ã¹ã«éãã°è§£æ±ºããã¯ããªã®ã§ãããGo ã§ã¯ä»¥ä¸ã® 2 ã¤ã®éå£ãããã¾ãã
SA_RESTART
ã«ãã£ã¦EINTR
ãããããçºçããªã- ã·ã°ãã«ã
pthread_kill
使ã£ã¦éä¿¡ããªãã¨ãããªã
ããããé ããã£ã¦è§£èª¬ãã¾ãã
SA_RESTART
ã«ãã£ã¦ EINTR
ãããããçºçããªã
SA_RESTART
㯠sigaction
ã«è¨å®ãããã©ã°ã§ããã®ãã©ã°ãè¨å®ãããã¨ãããããã³ã°ãã¦ããã·ã¹ãã ã³ã¼ã«ãã·ã°ãã«ã«ãã£ã¦ä¸æãããå ´åã§ã EINTR
ãè¿ããã«ããããã³ã°ãç¶è¡ããããã«ãªãã¾ãã
åè : ã·ã°ãã«ãã³ãã©ã¼ã«ããã·ã¹ãã ã³ã¼ã«ãã©ã¤ãã©ãªé¢æ°ã¸ã®å²ãè¾¼ã¿
Go ã§ã¯ã©ã³ã¿ã¤ã ã«ãã£ã¦å
¨ã¦ã®ã·ã°ãã«ã«å¯¾ã㦠SA_RESTART
ãè¨å®ããã¦ãã¾ããããã«ããæ¨æºã©ã¤ãã©ãªã¯ EINTR
ã®ã¨ã©ã¼ãã³ããªã³ã°ãããªãã¦ãããªãã¾ããï¼ç¢ºãã«æ¨æºã©ã¤ãã©ãªãèªãã§ãã㨠EINTR
ã®ãã³ããªã³ã°ããã¦ããªããã¨ã«æ°ã¥ãã¾ãï¼
Also, the Go standard library expects that any signal handlers will use the SA_RESTART flag. Failing to do so may cause some library calls to return "interrupted system call" errors.
https://golang.org/pkg/os/signal/#hdr-Go_programs_that_use_cgo_or_SWIG
ãã㯠Go ã®ã©ã³ã¿ã¤ã ã®åé¡ãªã®ã§ã©ãããããããã¾ãããGo è¨èªã®ã¬ã¤ã¤ã¼ã§ã¯è§£æ±ºã§ãã¾ããã
ããã§ãã è¨èªã®å£ãã¶ã£å£ã ãCGO ã®åºçªã§ããGo è¨èªãå£ãã¦ããã¾ãã
CGO ã«ãã£ã¦ç´æ¥ C è¨èªã§ sigaction
ãå®è¡ãã·ã°ãã«ãã³ãã©ãè¨å®ãããã¨ã§ Go ã®ã©ã³ã¿ã¤ã ãè¨å®ãã SA_RESTART
ã䏿¸ããã¾ãã
ãã®æç¹ã§ Windows ã¨ãã®å¯æ¬æ§ã¯æ¨ã¦ã¦ã¾ãããã¿ã¾ããã
void sighandler(int sig){ // empty handler } static struct sigaction oact; static int setup_signal(int sig) { struct sigaction act; // setup sigaction act.sa_handler = sighandler; act.sa_flags = 0; sigemptyset(&act.sa_mask); // set sigaction and cache old sigaction to oact if(sigaction(sig, &act, &oact) != 0){ return errno; } return 0; }
ã·ã°ãã«ã pthread_kill
使ã£ã¦éä¿¡ããªãã¨ãããªã
ãã¦ãSA_RESTART
ã䏿¸ãããä¸ã§èªèº«ã®ããã»ã¹ã«ã·ã°ãã«ãéãã¨ããããã³ã°ãã¦ãããã¡ã¤ã«ããã¯ã䏿ã§ããå ´åã¨ã§ããªãå ´åãåºã¦ãã¾ãã
ããã¯ãäºåã« signal
ããã±ã¼ã¸ã® signal.Ignore()
ã signal.Notify()
ãªã©ãå¼ã³åºããæã§ãã
ãããã®ã¡ã½ãããå¼ã³åºããæã« Go ã®ã©ã³ã¿ã¤ã 㯠signal mask thread
ãç«ã¡ä¸ããã®ã¹ã¬ãããå
¨ã¦ã® sigaction
ã®ç»é²ãè¡ãã¾ãã(å
·ä½çã«ã¯ ensureSigM
ã¨ãã颿°)
go/signal_unix.go at 2c5363d9c1cf51457d6d2466a63e6576e80327f8 · golang/go · GitHub
ããã«ãã£ã¦ ããã»ã¹ã¹ã³ã¼ãã«éãããã·ã°ãã« ã¯ã©ã³ã¿ã¤ã ã® signal mask thread
ã«éããã¦ãã¾ãã¾ãããã®å ´åã¯å¥ã®ã¹ã¬ããã§ãã¡ã¤ã«ããã¯ããããã¯ãã¦ããããã·ã°ãã«ãå±ãã EINTR
ãçºçãã¾ããã
ãã®è§£æ±ºç㯠ã¹ã¬ããã¹ã³ã¼ãã§ã·ã°ãã«ãéã ãã¨ã§ãã
æ®å¿µãªãã Go è¨èªã¯ OS ã®ã¹ã¬ããæèãããªããããªä½ãã«ãªã£ã¦ãããã¹ã¬ããã«å¯¾ãã¦ç´æ¥ã·ã°ãã«ãéããã¨ã¯ã§ãã¾ããã
ããã§ãã è¨èªã®å£ãã¶ã£å£ã ãCGO ã®åºçªã§ããã©ãã©ã Go è¨èªãå£ãã¦ããã¾ãã
C ã§ã¯ãpthread_kill
ã¨ãã颿°ãããç¹å®ã®ã¹ã¬ããã«ã·ã°ãã«ãéããã¨ãã§ãã¾ãã
static pthread_t tid; static int setup_signal(int sig) { ... // set self thread id tid = pthread_self(); ... } static int kill_thread(int sig) { // send signal to thread if (pthread_kill(tid, sig) == -1) { return errno; } return 0; }
ããã§ãããã¯ãã¦ãããã¡ã¤ã«ããã¯ã䏿ããããã¨ãã§ããããã«ãªãã¾ããã
github.com/kawasin73/gointr
ãä½ã£ã
ããã¾ã§ã®ç¥è¦ãå ã«ããããã³ã°ãã¦ããå¦çã䏿ãããå¦çãã©ã¤ãã©ãªåãã¾ããã
ä½¿ãæ¹ã¨ãã¦ã¯
- 䏿ã«ä½¿ãã·ã°ãã«ãæå®ãã¦
gointr.Intruptter
ã使ãã (gointr.New(syscall.SIGUSR1)
) - ããããã³ã°ããå¦çãè¡ã goroutine ãç«ã¡ä¸ãã¦
intr.Setup()
ãå®è¡ãã¦ãããããããã³ã°å¦çãè¡ã - ããããã³ã°å¦çãçµãã£ãã
intr.Close()
ãã - ããã䏿ããå ´åã¯
intr.Signal()
ãå¼ã³åºã
ã¨ãªãã¾ãã䏿ãããå ´å㯠EINTR
ãããããã³ã°ããã·ã¹ãã ã³ã¼ã«ããè¿ã£ã¦ãã¾ãã
注æç¹
注æç¹ã¨ãã¦ã¯ã以ä¸ã®ãã¨ãæãããã¾ããæ°ãã¤ãã¦ãã ããã
- ã°ãã¼ãã«å¤æ°ãå é¨ã§ä½¿ã£ã¦ãããã è¤æ°ã®ããããã³ã°å¦çã®ä¸æã«å¯¾å¿ãã¦ããªã
signal
ããã±ã¼ã¸ã®é¢æ°ãå¼ã³åºãã¨ä¸æ¸ãããsigaction
ã䏿¸ãããç´ãããã®ã§ ããããã³ã°å¦çä¸ã¯signal
ããã±ã¼ã¸ã®é¢æ°ãå¼ãã§ã¯ãããªã- CGO ã使ã£ã¦ãã
Example
package main import ( "fmt" "io" "log" "os" "os/signal" "syscall" "time" "github.com/kawasin73/gointr" ) // lock locks file using FCNTL. func lock(file *os.File) error { // write lock whole file flock := syscall.Flock_t{ Start: 0, Len: 0, Type: syscall.F_WRLCK, Whence: io.SeekStart, } if err := syscall.FcntlFlock(file.Fd(), syscall.F_SETLKW, &flock); err != nil { // FCNTL returns EINTR if interrupted by signal on blocking mode if err == syscall.EINTR { return fmt.Errorf("file lock timeout for %q", file.Name()) } return &os.PathError{Op: "fcntl", Path: file.Name(), Err: err} } return nil } func main() { signal.Ignore() file, err := os.Create("./.lock") if err != nil { log.Panic(err) } // init pthread intr := gointr.New(syscall.SIGUSR1) // init error channel chErr := make(chan error, 1) // setup timer timer := time.NewTimer(3 * time.Second) go func() { // setup the thread signal settings if terr := intr.Setup(); terr != nil { chErr <- terr return } defer func() { // reset signal settings if terr := intr.Close(); terr != nil { // if failed to reset sigaction, go runtime will be broken. // terr occurs on C memory error which does not happen. panic(terr) } }() // lock file blocking chErr <- lock(file) }() for { select { case err = <-chErr: timer.Stop() if err == nil { log.Println("lock success") } else { log.Println("lock fail err", err) } // break loop return case <-timer.C: log.Println("timeout") // send signal to the thread locking file and unblock the lock with EINTR err := intr.Signal() log.Println("signal") if err != nil { log.Panic("failed to kill thread", err) } // wait for lock result from chErr } } }
æå¾ã«
以ä¸ã§ãããããã¨ããããã¾ããã
ã¶ã£ã¡ãããããã¾ã§å³å¯ã«ãããªã Go ã使ããªãã¦ããã¨æãããå¤åèªåã使ãã¨ããã github.com/gofrs/flock ã使ãã