#lang scheme/unit
(require (planet untyped/unlib:3/list)
(planet untyped/unlib:3/parameter)
"../base.ss"
"era-sig.ss"
"transaction-sig.ss")
(import era^)
(export transaction^)
(define-struct frame (parent [copies #:mutable] [rolled-back? #:mutable]) #:transparent)
(define current-frame (make-parameter #f (make-guard frame? "frame")))
(define enable-transaction-backups?
(make-parameter #t (make-guard boolean? "boolean")))
(define (call-with-transaction-frame thunk)
(define frame (create-frame))
(define complete? #f)
(call-with-continuation-barrier
(lambda ()
(dynamic-wind
void
(lambda ()
(parameterize ([current-frame frame])
(begin0 (thunk)
(set! complete? #t))))
(lambda ()
(unless complete?
(roll-back-transaction-frame! frame)))))))
(define (store-transaction-backup! struct)
(when (enable-transaction-backups?)
(let ([frame (current-frame)])
(when (and frame (not (copy-stored? frame struct)))
(hash-set! (frame-copies frame) struct (copy-persistent-struct struct))))))
(define (frame-depth frame)
(if (frame-parent frame)
(add1 (frame-depth (frame-parent frame)))
0))
(define (create-frame)
(make-frame (current-frame) (make-hasheq) #f))
(define (copy-stored? frame struct)
(and (hash-ref (frame-copies frame) struct (lambda () #f)) #t))
(define (roll-back-transaction-frame! frame)
(if (frame-rolled-back? frame)
(raise-exn exn:fail:snooze
(format "Transaction frame already rolled back: ~s" frame))
(let ([copies (frame-copies frame)]) (set-frame-rolled-back?! frame #t)
(hash-for-each (frame-copies frame) update-persistent-struct-from-copy!))))