203 lines
5.2 KiB
Racket
203 lines
5.2 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/contract
|
|
)
|
|
|
|
(provide make-lru
|
|
lru?
|
|
lru-max-count
|
|
set-lru-max-count!
|
|
lru-expires?
|
|
lru-expire
|
|
set-lru-expire!
|
|
lru-has?
|
|
lru-add!
|
|
lru-count
|
|
lru-empty?
|
|
lru->list
|
|
lru-clear
|
|
)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Struct
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define-struct lru*
|
|
(cache
|
|
compare
|
|
[max-count #:mutable]
|
|
[expire-in-seconds #:mutable]
|
|
sem
|
|
)
|
|
)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Supporting functions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define (max-count? c)
|
|
(and (integer? c) (> c 0)))
|
|
|
|
(define (larger-equal0? c)
|
|
(and (integer? c) (>= c 0)))
|
|
|
|
(define (cmp-procedure? cmp-f)
|
|
(procedure? cmp-f))
|
|
|
|
(define (expire? e)
|
|
(or (eq? e #f) (max-count? e)))
|
|
|
|
(define (find-and-bubble count max-count e cache cmp expire-s current-s el)
|
|
(if (or (null? cache)
|
|
(>= count max-count))
|
|
'()
|
|
(let ((front (car cache)))
|
|
(if (and (not (eq? expire-s #f))
|
|
(< (cadr front) expire-s))
|
|
(find-and-bubble count max-count e (cdr cache) cmp expire-s current-s el)
|
|
(if (cmp e (car front))
|
|
(find-and-bubble count max-count e (cdr cache) cmp expire-s current-s (car front))
|
|
(cons front
|
|
(find-and-bubble (+ count 1) max-count
|
|
e (cdr cache) cmp expire-s current-s el))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(define (find cache cmp-f el)
|
|
(if (null? cache)
|
|
#f
|
|
(if (cmp-f el (caar cache))
|
|
#t
|
|
(find (cdr cache) cmp-f el)
|
|
)
|
|
)
|
|
)
|
|
|
|
(define (cleanup-expired lru)
|
|
(if (eq? (lru*-expire-in-seconds lru) #f)
|
|
'done
|
|
(let ((expire-s (- (current-seconds) (lru*-expire-in-seconds lru)))
|
|
(lcache (unbox (lru*-cache lru))))
|
|
(letrec ((f (λ (cache)
|
|
(if (null? cache)
|
|
'()
|
|
(if (< (cadar cache) expire-s)
|
|
(f (cdr cache))
|
|
(cons (car cache) (f (cdr cache))))))))
|
|
(set-box! (lru*-cache lru) (f lcache)))
|
|
'cleaned)))
|
|
|
|
(define (find-and/or-add lru el)
|
|
(let ((ncache (find-and-bubble
|
|
1 (lru*-max-count lru)
|
|
el (unbox (lru*-cache lru))
|
|
(lru*-compare lru)
|
|
(if (eq? (lru*-expire-in-seconds lru) #f)
|
|
#f
|
|
(- (current-seconds) (lru*-expire-in-seconds lru)))
|
|
(current-seconds)
|
|
#f)))
|
|
(set-box! (lru*-cache lru) (cons (list el (current-seconds)) ncache)))
|
|
lru)
|
|
|
|
(define-syntax with-lock
|
|
(syntax-rules ()
|
|
((_ lru code ...)
|
|
(dynamic-wind
|
|
(λ () (semaphore-wait (lru*-sem lru)))
|
|
(λ () (begin code ...))
|
|
(λ () (semaphore-post (lru*-sem lru)))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Supporting functions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define/contract (make-lru max-count #:cmp [compare equal?] #:expire [expire-in-seconds #f])
|
|
(->* (max-count?) (#:cmp cmp-procedure? #:expire expire?) lru*?)
|
|
(let ((sem (make-semaphore 1)))
|
|
(make-lru* (box '()) compare max-count expire-in-seconds sem)))
|
|
|
|
(define/contract (lru-has? lru el)
|
|
(-> lru*? any/c boolean?)
|
|
(with-lock lru
|
|
(cleanup-expired lru)
|
|
(find (unbox (lru*-cache lru)) (lru*-compare lru) el)
|
|
)
|
|
)
|
|
|
|
(define/contract (lru-add! lru el)
|
|
(-> lru*? any/c lru*?)
|
|
(with-lock lru
|
|
(find-and/or-add lru el)
|
|
lru))
|
|
|
|
(define/contract (lru-clear lru)
|
|
(-> lru*? lru*?)
|
|
(with-lock lru
|
|
(set-box! (lru*-cache lru) '())
|
|
lru))
|
|
|
|
(define/contract (lru->list lru #:with-expire [we #f])
|
|
(->* (lru*?) (#:with-expire boolean?) list?)
|
|
(with-lock lru
|
|
(cleanup-expired lru)
|
|
(let ((c (current-seconds)))
|
|
(map (λ (e)
|
|
(if we
|
|
(list (car e) (- c (cadr e)))
|
|
(car e))) (unbox (lru*-cache lru))))
|
|
)
|
|
)
|
|
|
|
(define/contract (lru-empty? l)
|
|
(-> lru*? boolean?)
|
|
(= (lru-count l) 0))
|
|
|
|
(define/contract (lru-count lru)
|
|
(-> lru*? larger-equal0?)
|
|
(with-lock lru
|
|
(cleanup-expired lru)
|
|
(length (unbox (lru*-cache lru))))
|
|
)
|
|
|
|
(define (lru? l)
|
|
(lru*? l))
|
|
|
|
(define/contract (lru-max-count l)
|
|
(-> lru*? max-count?)
|
|
(with-lock l (lru*-max-count l)))
|
|
|
|
(define/contract (lru-expire l)
|
|
(-> lru*? expire?)
|
|
(with-lock l (lru*-expire-in-seconds l)))
|
|
|
|
(define/contract (set-lru-max-count! l c)
|
|
(-> lru*? max-count? lru*?)
|
|
(with-lock l
|
|
(set-lru*-max-count! l c)
|
|
l))
|
|
|
|
(define/contract (set-lru-expire! l e)
|
|
(-> lru*? expire? lru*?)
|
|
(with-lock l
|
|
(set-lru*-expire-in-seconds! l e)
|
|
l))
|
|
|
|
(define/contract (lru-expires? l)
|
|
(-> lru*? boolean?)
|
|
(with-lock l
|
|
(let ((e (lru*-expire-in-seconds l)))
|
|
(if e
|
|
(and (integer? e) (> e 0))
|
|
#f))))
|
|
|
|
|
|
|