#lang scheme/base
(require "base.ss"
"lifebox.ss")
(define-struct cache (load store expire lifetime last-check hash) #:transparent)
(define (create-cache load store expire lifetime)
(create-cache/internal load store expire lifetime (make-hash)))
(define (create-cacheeq load store expire lifetime)
(create-cache/internal load store expire lifetime (make-hasheq)))
(define (create-cache/internal load store expire lifetime hash)
(define cache (make-cache load store expire lifetime (current-seconds) hash))
(start-timer (/ (* lifetime 1000) 2)
(cut cache-clean! cache))
cache)
(define (cache-eq? cache)
(hash-eq? (cache-hash cache)))
(define (cache-ref cache key)
(define hash (cache-hash cache))
(define load (cache-load cache))
(define box (hash-ref hash key #f))
(define expired? (and box (lifebox-expired? box)))
(cond [(not box) (let* ([value (load key)]
[box (create-box cache value)])
(hash-set! hash key box)
value)]
[(not expired?) (lifebox-value box)]
[else (cache-remove! cache key box)
(cache-ref cache key)]))
(define (cache-set! cache key value)
(define hash (cache-hash cache))
(define store (cache-store cache))
(hash-set! hash key (create-box cache value))
(store key value))
(define (cache-clear! cache)
(define hash (cache-hash cache))
(hash-for-each hash (cut cache-remove! cache <> <>)))
(define (cache-remove! cache key box)
(define hash (cache-hash cache))
(begin (hash-remove! hash key)
((cache-expire cache) cache key (lifebox-value box))))
(define (cache-clean! cache)
(define hash (cache-hash cache))
(hash-for-each
hash
(lambda (k v)
(let ([now (current-seconds)])
(when (lifebox-expired? v now)
(cache-remove! cache k v))))))
(define (create-box cache value)
(make-lifebox (+ (current-seconds) (cache-lifetime cache)) value))
(define-struct timer (stop))
(define (start-timer period action)
(define stop (make-channel))
(define (future-evt)
(alarm-evt (+ (current-inexact-milliseconds) period)))
(thread (lambda ()
(let loop ([evt (sync (future-evt) stop)])
(if (eq? evt 'stop)
(begin (void))
(begin (action)
(loop (sync (future-evt) stop)))))))
(make-timer stop))
(define (stop-timer timer)
(channel-put (timer-stop timer) 'stop))
(provide (rename-out [create-cache make-cache]
[create-cacheeq make-cacheeq])
cache?
cache-eq?
cache-ref
cache-set!
cache-clear!
cache-clean!
start-timer
stop-timer)