2020-04-27 18:27:48 +00:00
|
|
|
#lang syndicate/test-implementation
|
2019-05-07 11:07:30 +00:00
|
|
|
;;
|
|
|
|
;; In principle, we might like that actors within a turn should
|
|
|
|
;; respond to assertions currently in the database *as modified by
|
|
|
|
;; previous pending actions of the actor*. However, this doesn't
|
2020-04-27 18:27:48 +00:00
|
|
|
;; currently work. Not only does the syndicate
|
2019-05-07 11:07:30 +00:00
|
|
|
;; implementation not do this, the old syndicate implementation
|
|
|
|
;; doesn't do it either, and there's also a theoretical problem: Say
|
|
|
|
;; we had been asserting `'x`, and within a single turn, we retract it
|
|
|
|
;; and establish a facet responding to it. Should the new facet react
|
|
|
|
;; to `'x`? The answer can only be "maybe", because our own assertion
|
|
|
|
;; of `'x` may not be the only one in the dataspace. Therefore,
|
|
|
|
;; perhaps it's best just to keep the current situation, or its ideal
|
|
|
|
;; reflection perhaps, which is: within-turn actions should execute in
|
|
|
|
;; context of the dataspace state as it was at the beginning of the
|
|
|
|
;; turn. I'm not 100% sure whether the current implementation actually
|
|
|
|
;; provides this guarantee, but it must be quite close since it seems
|
|
|
|
;; to hold for the examples I've tried.
|
|
|
|
;;
|
|
|
|
;; This line of thinking is in reaction to an infelicity I noticed
|
|
|
|
;; while working on the client/server/federation code. The following
|
|
|
|
;; code, on connection drop, runs through the `boot-connection` loop
|
|
|
|
;; *twice* instead of just once, because the retraction of
|
|
|
|
;; `server-connected` hasn't had time to actually be reflected in the
|
|
|
|
;; dataspace by the time the reaction to `server-connected` is
|
|
|
|
;; (re)established. The `server-connected` record is withdrawn on the
|
|
|
|
;; next turn, leading to a second `boot-connection`, and the system
|
|
|
|
;; stabilises here because `server-connected` is no longer present.
|
|
|
|
;;
|
|
|
|
;; (let boot-connection ()
|
|
|
|
;; (define root-facet (current-facet))
|
|
|
|
;; (log-info "boot-connection ~v ~a" (current-facet) (facet-live? (current-facet)))
|
|
|
|
;;
|
|
|
|
;; (reassert-on (tcp-connection id (tcp-address host port))
|
|
|
|
;; (retracted (tcp-accepted id))
|
|
|
|
;; (asserted (tcp-rejected id _)))
|
|
|
|
;;
|
|
|
|
;; (during (tcp-accepted id)
|
|
|
|
;; (on-start (log-info "+tcp-accepted ~v ~a" (current-facet) id))
|
|
|
|
;; (on-stop (log-info "-tcp-accepted ~v ~a" (current-facet) id))
|
|
|
|
;; (assert (server-connected address))
|
|
|
|
;; (define accumulate! (packet-accumulator (lambda (p) (send! (server-packet address p)))))
|
|
|
|
;; (on (message (tcp-in id $bs)) (accumulate! bs)))
|
|
|
|
;;
|
|
|
|
;; (during (server-connected address)
|
|
|
|
;; (on-start (log-info "+server-connected ~v ~a" (current-facet) address))
|
|
|
|
;; (on-stop (log-info "-server-connected ~v ~a" (current-facet) address))
|
|
|
|
;; ;; (on-start (send! (tcp-out id (encode (Connect scope)))))
|
|
|
|
;; (generic-client-session-facet address (lambda (x) (send! (tcp-out id (encode x)))))
|
|
|
|
;; (on-stop (log-info "---- ~v" (current-facet))
|
|
|
|
;; (stop-facet root-facet
|
|
|
|
;; (log-info "!!!! ~v" (current-facet))
|
|
|
|
;; (react (boot-connection))))))
|
|
|
|
;;
|
|
|
|
;; The following test case is simplified but analogous to the problem
|
|
|
|
;; I noticed. Note particularly the `(flush!)` call, which is needed
|
|
|
|
;; to force the retraction of `'y` through to the dataspace and back
|
|
|
|
;; to the actor, so that the next `retry` doesn't improperly react to
|
|
|
|
;; a soon-to-be-retracted assertion of `'y`.
|
|
|
|
|
|
|
|
(test-case
|
|
|
|
[
|
|
|
|
(spawn* (until (asserted (observe 'x)))
|
|
|
|
(send! 'x))
|
|
|
|
|
|
|
|
;; (spawn (on (asserted $anything) (printf "+ ~a\n" anything))
|
|
|
|
;; (on (retracted $anything) (printf "- ~a\n" anything))
|
|
|
|
;; (on (message $anything) (printf "! ~a\n" anything)))
|
|
|
|
|
|
|
|
(spawn (let retry ((n 0))
|
|
|
|
(cond [(>= n 10) (printf "Exceeded count\n")]
|
|
|
|
[else
|
|
|
|
(define f (current-facet))
|
|
|
|
;; (during 'x (assert 'y))
|
|
|
|
(on (message 'x)
|
|
|
|
(react (assert 'y)
|
|
|
|
(on (asserted (observe 'go))
|
|
|
|
(send! 'go))))
|
|
|
|
(during ($ val 'y)
|
|
|
|
(on-start (printf "+++++ ~a\n" val))
|
|
|
|
(on-stop (printf "----- ~a\n" val))
|
|
|
|
(on (message 'go)
|
|
|
|
(printf "going\n")
|
|
|
|
(stop-current-facet))
|
|
|
|
(on-stop
|
|
|
|
(printf "stopping\n")
|
|
|
|
(stop-facet f
|
|
|
|
(printf "calling retry\n")
|
|
|
|
(flush!) ;; !!!! Crucial!
|
|
|
|
(react (retry (+ n 1))))))])))
|
|
|
|
]
|
|
|
|
no-crashes
|
|
|
|
(expected-output (list "+++++ y"
|
|
|
|
"going"
|
|
|
|
"----- y"
|
|
|
|
;;
|
|
|
|
;; (If we comment out the `(flush!)` above,
|
|
|
|
;; the following additional events appear
|
|
|
|
;; here, reflecting the unwanted reaction to
|
|
|
|
;; the doomed `'y` assertion:)
|
|
|
|
;;
|
|
|
|
;; "stopping"
|
|
|
|
;; "calling retry"
|
|
|
|
;; "+++++ y"
|
|
|
|
;; "----- y"
|
|
|
|
;;
|
|
|
|
"stopping"
|
|
|
|
"calling retry")))
|