detect changes to "links.rktd" by content instead of timestamp

The 1-second granularity of filesystem timestamps is not
good enough to deetct changes to the file --- especially
when `raco pkg' installs links and runs `raco setup' (in
the same Rcket process), in which case a newly installed link
might not be detected.

The longer-term repair is probably to add file-changed
events to Racket (based on `inotify' and similar interfaces
provided by OSes).
This commit is contained in:
Matthew Flatt 2013-06-21 13:13:34 -06:00
parent f6f18b39fa
commit 208c7d23a6
4 changed files with 730 additions and 647 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
finally, set EXPECTED_PRIM_COUNT to the right value and finally, set EXPECTED_PRIM_COUNT to the right value and
USE_COMPILED_STARTUP to 1 and `make' again. */ USE_COMPILED_STARTUP to 1 and `make' again. */
#define USE_COMPILED_STARTUP 0 #define USE_COMPILED_STARTUP 1
#define EXPECTED_PRIM_COUNT 1105 #define EXPECTED_PRIM_COUNT 1105
#define EXPECTED_UNSAFE_COUNT 100 #define EXPECTED_UNSAFE_COUNT 100

View File

@ -343,7 +343,7 @@
" file-name)))" " file-name)))"
"(define-values(user-links-path)(find-system-path 'links-file))" "(define-values(user-links-path)(find-system-path 'links-file))"
"(define-values(user-links-cache)(make-hasheq))" "(define-values(user-links-cache)(make-hasheq))"
"(define-values(user-links-timestamp) -inf.0)" "(define-values(user-links-stamp) #f)"
"(define-values(links-path)(find-links-path!" "(define-values(links-path)(find-links-path!"
"(lambda()" "(lambda()"
"(let((d(let((c(find-system-path 'config-dir)))" "(let((d(let((c(find-system-path 'config-dir)))"
@ -354,7 +354,37 @@
"(and d" "(and d"
" (build-path d \"links.rktd\"))))))" " (build-path d \"links.rktd\"))))))"
"(define-values(links-cache)(make-hasheq))" "(define-values(links-cache)(make-hasheq))"
"(define-values(links-timestamp) -inf.0)" "(define-values(links-stamp) #f)"
"(define-values(file->stamp)"
"(lambda(path)"
"(call-with-continuation-prompt"
"(lambda()"
"(with-continuation-mark"
" exception-handler-key"
"(lambda(exn)"
"(if(exn:fail:filesystem? exn)"
"(abort-current-continuation "
"(default-continuation-prompt-tag)"
"(lambda() #f))"
"(lambda()(raise exn))))"
"(let((p(open-input-file path)))"
"(dynamic-wind"
" void"
"(lambda()"
"(let((bstr(read-bytes 8192 p)))"
"(if(and(bytes? bstr)"
"((bytes-length bstr) . >= . 8192))"
"(apply"
" bytes-append"
"(cons"
" bstr"
"(let loop()"
"(let((bstr(read-bytes 8192 p)))"
"(if(eof-object? bstr)"
" null"
"(cons bstr(loop)))))))"
" bstr)))"
"(lambda()(close-input-port p)))))))))"
"(define-values(get-linked-collections)" "(define-values(get-linked-collections)"
"(lambda(user?)" "(lambda(user?)"
"(call/ec(lambda(esc)" "(call/ec(lambda(esc)"
@ -375,22 +405,20 @@
"(if user?" "(if user?"
"(begin" "(begin"
"(set! user-links-cache(make-hasheq))" "(set! user-links-cache(make-hasheq))"
"(set! user-links-timestamp ts))" "(set! user-links-stamp ts))"
"(begin" "(begin"
"(set! links-cache(make-hasheq))" "(set! links-cache(make-hasheq))"
"(set! links-timestamp ts))))" "(set! links-stamp ts))))"
"(if(exn:fail? exn)" "(if(exn:fail? exn)"
"(esc(make-hasheq))" "(esc(make-hasheq))"
" exn))))" " exn))))"
"(with-continuation-mark" "(with-continuation-mark"
" exception-handler-key" " exception-handler-key"
"(make-handler #f)" "(make-handler #f)"
"(let((ts(file-or-directory-modify-seconds(if user?" "(let((ts(file->stamp(if user?"
" user-links-path" " user-links-path"
" links-path)" " links-path))))"
" #f " "(if(not(equal? ts(if user? user-links-stamp links-stamp)))"
"(lambda() -inf.0))))"
"(if(ts . > .(if user? user-links-timestamp links-timestamp))"
"(with-continuation-mark" "(with-continuation-mark"
" exception-handler-key" " exception-handler-key"
"(make-handler ts)" "(make-handler ts)"
@ -459,10 +487,10 @@
"(if user?" "(if user?"
"(begin" "(begin"
"(set! user-links-cache ht)" "(set! user-links-cache ht)"
"(set! user-links-timestamp ts))" "(set! user-links-stamp ts))"
"(begin" "(begin"
"(set! links-cache ht)" "(set! links-cache ht)"
"(set! links-timestamp ts)))" "(set! links-stamp ts)))"
" ht))))" " ht))))"
"(if user?" "(if user?"
" user-links-cache" " user-links-cache"

View File

@ -407,7 +407,7 @@
(define-values (user-links-path) (find-system-path 'links-file)) (define-values (user-links-path) (find-system-path 'links-file))
(define-values (user-links-cache) (make-hasheq)) (define-values (user-links-cache) (make-hasheq))
(define-values (user-links-timestamp) -inf.0) (define-values (user-links-stamp) #f)
(define-values (links-path) (find-links-path! (define-values (links-path) (find-links-path!
;; This thunk is called once per place, and the result ;; This thunk is called once per place, and the result
@ -423,7 +423,43 @@
(and d (and d
(build-path d "links.rktd")))))) (build-path d "links.rktd"))))))
(define-values (links-cache) (make-hasheq)) (define-values (links-cache) (make-hasheq))
(define-values (links-timestamp) -inf.0) (define-values (links-stamp) #f)
(define-values (file->stamp)
(lambda (path)
;; We'd prefer to do something lighter than read the file every time!
;; Using just the file's modification date almost works, but 1-second
;; granularity isn't fine enough. To do this right, probably Racket needs
;; to provide more support from the OS's filesystem (along the lines of
;; inotify, but the interface varies among platforms).
(call-with-continuation-prompt
(lambda ()
(with-continuation-mark
exception-handler-key
(lambda (exn)
(if (exn:fail:filesystem? exn)
(abort-current-continuation
(default-continuation-prompt-tag)
(lambda () #f))
(lambda () (raise exn))))
(let ([p (open-input-file path)])
(dynamic-wind
void
(lambda ()
(let ([bstr (read-bytes 8192 p)])
(if (and (bytes? bstr)
((bytes-length bstr) . >= . 8192))
(apply
bytes-append
(cons
bstr
(let loop ()
(let ([bstr (read-bytes 8192 p)])
(if (eof-object? bstr)
null
(cons bstr (loop)))))))
bstr)))
(lambda () (close-input-port p)))))))))
(define-values (get-linked-collections) (define-values (get-linked-collections)
(lambda (user?) (lambda (user?)
@ -445,10 +481,10 @@
(if user? (if user?
(begin (begin
(set! user-links-cache (make-hasheq)) (set! user-links-cache (make-hasheq))
(set! user-links-timestamp ts)) (set! user-links-stamp ts))
(begin (begin
(set! links-cache (make-hasheq)) (set! links-cache (make-hasheq))
(set! links-timestamp ts)))) (set! links-stamp ts))))
(if (exn:fail? exn) (if (exn:fail? exn)
(esc (make-hasheq)) (esc (make-hasheq))
;; re-raise the exception (which is probably a break) ;; re-raise the exception (which is probably a break)
@ -456,12 +492,10 @@
(with-continuation-mark (with-continuation-mark
exception-handler-key exception-handler-key
(make-handler #f) (make-handler #f)
(let ([ts (file-or-directory-modify-seconds (if user? (let ([ts (file->stamp (if user?
user-links-path user-links-path
links-path) links-path))])
#f (if (not (equal? ts (if user? user-links-stamp links-stamp)))
(lambda () -inf.0))])
(if (ts . > . (if user? user-links-timestamp links-timestamp))
(with-continuation-mark (with-continuation-mark
exception-handler-key exception-handler-key
(make-handler ts) (make-handler ts)
@ -532,14 +566,14 @@
(hash-for-each (hash-for-each
ht ht
(lambda (k v) (hash-set! ht k (reverse v)))) (lambda (k v) (hash-set! ht k (reverse v))))
;; save table & timestamp: ;; save table & file content:
(if user? (if user?
(begin (begin
(set! user-links-cache ht) (set! user-links-cache ht)
(set! user-links-timestamp ts)) (set! user-links-stamp ts))
(begin (begin
(set! links-cache ht) (set! links-cache ht)
(set! links-timestamp ts))) (set! links-stamp ts)))
ht)))) ht))))
(if user? (if user?
user-links-cache user-links-cache