(module fscache mzscheme
(require (lib "plt-match.ss")
(lib "list.ss")
(lib "etc.ss")
(lib "file.ss"))
(provide fscache?
(rename ext:make-fscache make-fscache)
make-with-input-from-file
make-with-output-to-file)
(define-struct fscache (channel manager))
(define-struct request (reply-channel))
(define-struct (request:lookup request) (path))
(define-struct (request:replace request) (path new-bytes))
(define-struct (request:prune request) (proc))
(define-struct return ())
(define-struct (failure return) (exn))
(define-struct (success return) (value))
(define return-value
(match-lambda
[(struct success (v))
v]
[(struct failure (e))
(raise e)]))
(define (ext:make-fscache)
(define cache-table
(make-hash-table 'equal))
(define (lookup p)
(define last-mod
(file-or-directory-modify-seconds p))
(define cur
(hash-table-get cache-table p
(lambda ()
(define new
(list (file-or-directory-modify-seconds p)
(with-input-from-file p
(lambda ()
(read-bytes (file-size p))))))
(hash-table-put! cache-table p new)
new)))
(if (> last-mod (first cur))
(begin (hash-table-remove! cache-table p)
(lookup p))
cur))
(define (replace p nb)
(hash-table-remove! cache-table p)
(with-output-to-file p (lambda () (display nb)) 'truncate)
#t)
(define request-channel (make-channel))
(define manager
(thread/suspend-to-kill
(lambda ()
(let loop ()
(sync
(handle-evt
request-channel
(match-lambda
[(struct request:lookup (rc p))
(with-handlers ([exn? (lambda (e)
(channel-put rc (make-failure e)))])
(channel-put rc (make-success (second (lookup p)))))
(loop)]
[(struct request:replace (rc p nb))
(with-handlers ([exn? (lambda (e)
(channel-put rc (make-failure e)))])
(channel-put rc (make-success (replace p nb))))
(loop)])))))))
(make-fscache request-channel manager))
(define (fscache-lookup an-fscache path)
(define reply-channel (make-channel))
(thread-resume (fscache-manager an-fscache))
(channel-put (fscache-channel an-fscache)
(make-request:lookup reply-channel path))
(return-value (channel-get reply-channel)))
(define (fscache-replace an-fscache path bytes)
(define reply-channel (make-channel))
(thread-resume (fscache-manager an-fscache))
(channel-put (fscache-channel an-fscache)
(make-request:replace reply-channel path bytes))
(return-value (channel-get reply-channel))
(void))
(define ((make-with-input-from-file an-fscache) path thunk . wiff-args)
(define input
(open-input-bytes (fscache-lookup an-fscache (normalize-path path))))
(parameterize ([current-input-port input])
(thunk)))
(define ((make-with-output-to-file an-fscache) path thunk . wotf-args)
(define output-bytes (open-output-bytes))
(begin0
(parameterize ([current-output-port output-bytes])
(thunk))
(fscache-replace an-fscache
(normalize-path path)
(get-output-bytes output-bytes))))
(define (test)
(define an-fscache (ext:make-fscache))
(define with-input-from-file (make-with-input-from-file an-fscache))
(define with-output-to-file (make-with-output-to-file an-fscache))
(define path "/tmp/blahblahblah")
(with-output-to-file path
(lambda () (write #t)))
(build-list 100
(lambda (i)
(with-input-from-file path read)))
(read)
(build-list 100
(lambda (i)
(with-input-from-file path read)))
(void)))