(module resume-unit mzscheme
(require (lib "unitsig.ss")
(lib "servlet-sig.ss" "web-server")
(lib "etc.ss"))
(provide resume@ resume^ (struct session (continuation time-stamp)))
(define-signature resume^ (with-user-logged-in set-resume-point! log-in! log-out! clear-resume-table! resume-table
send/suspend-to-user send/finish-to-user send/forward-to-user send/back-to-user resume ))
(define *R* (make-hash-table 'equal))
(define *R-lock* (make-semaphore 1))
(define current-user (make-parameter #f))
(define current-servlet-custodian (make-parameter #f))
(define previous-servlet-custodian (make-parameter #f))
(define *top-level-custodian* (current-custodian))
(define-struct session (continuation time-stamp))
(define resume@
(unit/sig resume^
(import servlet^)
(define (resume-table)
(call-with-semaphore *R-lock*
(lambda () (hash-table-map *R* cons))))
(define set-resume-point!
(opt-lambda ([user (current-user)])
(let ([vals (current-preserved-thread-cell-values)])
(let/cc k
(call-with-semaphore *R-lock*
(lambda ()
(hash-table-get *R* user
(lambda ()
(error 'set-resume-point!
(string->immutable-string
(format "no active session for user ~a" user)))))
(hash-table-put! *R* user (make-session k (current-seconds))))))
(current-preserved-thread-cell-values vals)
#f)))
(define (log-in! user)
(call-with-semaphore *R-lock*
(lambda ()
(hash-table-put! *R* user
(make-session (lambda args #f) (current-seconds)))))
(let ((session-custodian (make-custodian *top-level-custodian*)))
(current-user user)
(previous-servlet-custodian (current-custodian))
(current-servlet-custodian session-custodian)
(current-custodian session-custodian)
(void)))
(define log-out!
(opt-lambda ([user (current-user)])
(call-with-semaphore *R-lock* (lambda () (hash-table-remove! *R* user)))
(custodian-shutdown-all (current-servlet-custodian))
(current-custodian (previous-servlet-custodian))
(void)))
(define (with-user-logged-in user thunk)
(let ((session-custodian (make-custodian *top-level-custodian*)))
(parameterize ((current-user user)
(current-servlet-custodian session-custodian)
(current-custodian session-custodian))
(begin0
(thunk)
(log-out! user)))))
(define (clear-resume-table!)
(call-with-semaphore *R-lock* (lambda () (set! *R* (make-hash-table 'equal))))
(void))
(define (send-to-user sender)
(let ([sender* (lambda (proc)
(let ([vals (current-preserved-thread-cell-values)])
(begin0
(sender proc)
(current-preserved-thread-cell-values vals))))])
(opt-lambda (response [user (current-user)])
(set-resume-point! user)
(sender* response))))
(define send/suspend-to-user (send-to-user send/suspend))
(define send/finish-to-user (send-to-user send/finish))
(define send/forward-to-user (send-to-user send/forward))
(define send/back-to-user (send-to-user send/back))
(define resume
(opt-lambda (user [resume-value #t])
(let ((session (call-with-semaphore *R-lock* (lambda () (hash-table-get *R* user (lambda () #f))))))
(if session
((session-continuation session) resume-value)
#f)))))))