utils: Add a non-blocking option for 'fcntl-flock'.
* guix/utils.scm (F_SETLK): New variable. (fcntl-flock): Add 'wait?' keyword parameter; honor it. * tests/utils.scm ("fcntl-flock non-blocking"): New test.master
parent
e7f34eb0dc
commit
c7445833eb
|
@ -244,6 +244,13 @@ buffered data is lost."
|
||||||
((string-contains %host-type "linux") 7) ; *-linux-gnu
|
((string-contains %host-type "linux") 7) ; *-linux-gnu
|
||||||
(else 9)))) ; *-gnu*
|
(else 9)))) ; *-gnu*
|
||||||
|
|
||||||
|
(define F_SETLK
|
||||||
|
;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
|
||||||
|
(compile-time-value
|
||||||
|
(cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
|
||||||
|
((string-contains %host-type "linux") 6) ; *-linux-gnu
|
||||||
|
(else 8)))) ; *-gnu*
|
||||||
|
|
||||||
(define F_xxLCK
|
(define F_xxLCK
|
||||||
;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
|
;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
|
||||||
(compile-time-value
|
(compile-time-value
|
||||||
|
@ -271,9 +278,11 @@ buffered data is lost."
|
||||||
(define fcntl-flock
|
(define fcntl-flock
|
||||||
(let* ((ptr (dynamic-func "fcntl" (dynamic-link)))
|
(let* ((ptr (dynamic-func "fcntl" (dynamic-link)))
|
||||||
(proc (pointer->procedure int ptr `(,int ,int *))))
|
(proc (pointer->procedure int ptr `(,int ,int *))))
|
||||||
(lambda (fd-or-port operation)
|
(lambda* (fd-or-port operation #:key (wait? #t))
|
||||||
"Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
|
"Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
|
||||||
must be a symbol, one of 'read-lock, 'write-lock, or 'unlock."
|
must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
|
||||||
|
true, block until the lock is acquired; otherwise, thrown an 'flock-error'
|
||||||
|
exception if it's already taken."
|
||||||
(define (operation->int op)
|
(define (operation->int op)
|
||||||
(case op
|
(case op
|
||||||
((read-lock) (vector-ref F_xxLCK 0))
|
((read-lock) (vector-ref F_xxLCK 0))
|
||||||
|
@ -289,7 +298,9 @@ must be a symbol, one of 'read-lock, 'write-lock, or 'unlock."
|
||||||
;; XXX: 'fcntl' is a vararg function, but here we happily use the
|
;; XXX: 'fcntl' is a vararg function, but here we happily use the
|
||||||
;; standard ABI; crossing fingers.
|
;; standard ABI; crossing fingers.
|
||||||
(let ((err (proc fd
|
(let ((err (proc fd
|
||||||
|
(if wait?
|
||||||
F_SETLKW ; lock & wait
|
F_SETLKW ; lock & wait
|
||||||
|
F_SETLK) ; non-blocking attempt
|
||||||
(make-c-struct %struct-flock
|
(make-c-struct %struct-flock
|
||||||
(list (operation->int operation)
|
(list (operation->int operation)
|
||||||
SEEK_SET
|
SEEK_SET
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
(equal? (get-bytevector-all decompressed) data)))))
|
(equal? (get-bytevector-all decompressed) data)))))
|
||||||
|
|
||||||
(false-if-exception (delete-file temp-file))
|
(false-if-exception (delete-file temp-file))
|
||||||
(test-equal "fcntl-flock"
|
(test-equal "fcntl-flock wait"
|
||||||
42 ; the child's exit status
|
42 ; the child's exit status
|
||||||
(let ((file (open-file temp-file "w0")))
|
(let ((file (open-file temp-file "w0")))
|
||||||
;; Acquire an exclusive lock.
|
;; Acquire an exclusive lock.
|
||||||
|
@ -182,6 +182,48 @@
|
||||||
(close-port file)
|
(close-port file)
|
||||||
result)))))))
|
result)))))))
|
||||||
|
|
||||||
|
(test-equal "fcntl-flock non-blocking"
|
||||||
|
EAGAIN ; the child's exit status
|
||||||
|
(match (pipe)
|
||||||
|
((input . output)
|
||||||
|
(match (primitive-fork)
|
||||||
|
(0
|
||||||
|
(dynamic-wind
|
||||||
|
(const #t)
|
||||||
|
(lambda ()
|
||||||
|
(close-port output)
|
||||||
|
|
||||||
|
;; Wait for the green light.
|
||||||
|
(read-char input)
|
||||||
|
|
||||||
|
;; Open FILE read-only so we can have a read lock.
|
||||||
|
(let ((file (open-file temp-file "w")))
|
||||||
|
(catch 'flock-error
|
||||||
|
(lambda ()
|
||||||
|
;; This attempt should throw EAGAIN.
|
||||||
|
(fcntl-flock file 'write-lock #:wait? #f))
|
||||||
|
(lambda (key errno)
|
||||||
|
(primitive-exit errno))))
|
||||||
|
(primitive-exit -1))
|
||||||
|
(lambda ()
|
||||||
|
(primitive-exit -2))))
|
||||||
|
(pid
|
||||||
|
(close-port input)
|
||||||
|
(let ((file (open-file temp-file "w")))
|
||||||
|
;; Acquire an exclusive lock.
|
||||||
|
(fcntl-flock file 'write-lock)
|
||||||
|
|
||||||
|
;; Tell the child to continue.
|
||||||
|
(write 'green-light output)
|
||||||
|
(force-output output)
|
||||||
|
|
||||||
|
(match (waitpid pid)
|
||||||
|
((_ . status)
|
||||||
|
(let ((result (status:exit-val status)))
|
||||||
|
(fcntl-flock file 'unlock)
|
||||||
|
(close-port file)
|
||||||
|
result)))))))))
|
||||||
|
|
||||||
;; This is actually in (guix store).
|
;; This is actually in (guix store).
|
||||||
(test-equal "store-path-package-name"
|
(test-equal "store-path-package-name"
|
||||||
"bash-4.2-p24"
|
"bash-4.2-p24"
|
||||||
|
|
Reference in New Issue