From eaade6e4fd7cfaae52f583e6f978a830950fd28e Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Sun, 7 Aug 2016 19:44:57 -0400 Subject: [PATCH] Treat synthetic patches differently wrt previousKnowledge --- js/src/actor.js | 17 ++++---- racket/syndicate/actor.rkt | 30 +++++++------- .../actor/example-synthetic-patch.rkt | 41 +++++++++++++++++++ 3 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 racket/syndicate/examples/actor/example-synthetic-patch.rkt diff --git a/js/src/actor.js b/js/src/actor.js index 28efb3d..709fbfb 100644 --- a/js/src/actor.js +++ b/js/src/actor.js @@ -70,7 +70,7 @@ Actor.prototype.handleEvent = function(e) { throw new Error('Syndicate: pendingActions must not be nonempty at start of handleEvent'); } this.facets.forEach(function (f) { - withCurrentFacet(actor, f, function () { f.handleEvent(e); }); + withCurrentFacet(actor, f, function () { f.handleEvent(e, false); }); }); this.quiesce(); }; @@ -160,10 +160,10 @@ function withCurrentFacet(actor, facet, f) { return result; } -Facet.prototype.handleEvent = function(e) { +Facet.prototype.handleEvent = function(e, isSynthetic) { var facet = this; facet.endpoints.forEach(function(endpoint) { - endpoint.handlerFn.call(facet.fields, e); + endpoint.handlerFn.call(facet.fields, e, isSynthetic); }); }; @@ -218,7 +218,7 @@ Facet.prototype.onEvent = function(priority, case 'asserted': /* fall through */ case 'retracted': - return this.addEndpoint(new Endpoint(subscriptionFn, function(e) { + return this.addEndpoint(new Endpoint(subscriptionFn, function(e, isSynthetic) { if (e.type === 'stateChange') { var proj = projectionFn.call(facet.fields); var spec = Patch.prependAtMeta(proj.assertion, proj.metalevel); @@ -230,7 +230,7 @@ Facet.prototype.onEvent = function(priority, var shouldTerminate = isTerminal; objects.forEach(function (o) { var instantiated = Trie.instantiateProjection(spec, o); - if (facet.interestWas(eventType, instantiated)) { + if (facet.interestWas(eventType, instantiated, isSynthetic)) { if (shouldTerminate) { shouldTerminate = false; facet.terminate(); @@ -264,9 +264,10 @@ Facet.prototype.onEvent = function(priority, } }; -Facet.prototype.interestWas = function(assertedOrRetracted, pat) { +Facet.prototype.interestWas = function(assertedOrRetracted, pat, isSyntheticEvent) { function orStar(a, b) { return (a || b); } - var oldExists = Trie.matchValue(this.actor.previousKnowledge, pat, false, orStar); + var previousKnowledge = isSyntheticEvent ? Trie.emptyTrie : this.actor.previousKnowledge; + var oldExists = Trie.matchValue(previousKnowledge, pat, false, orStar); var newExists = Trie.matchValue(this.actor.knowledge, pat, false, orStar); switch (assertedOrRetracted) { case 'asserted': @@ -310,7 +311,7 @@ Facet.prototype.completeBuild = function() { facet.initBlocks.forEach(function(b) { b.call(facet.fields); }); }); var initialEvent = _Dataspace.stateChange(new Patch.Patch(facet.actor.knowledge, Trie.emptyTrie)); - withCurrentFacet(this.actor, facet, function () { facet.handleEvent(initialEvent); }); + withCurrentFacet(this.actor, facet, function () { facet.handleEvent(initialEvent, true); }); }; Facet.prototype.terminate = function() { diff --git a/racket/syndicate/actor.rkt b/racket/syndicate/actor.rkt index f70055d..2eaeff3 100644 --- a/racket/syndicate/actor.rkt +++ b/racket/syndicate/actor.rkt @@ -397,7 +397,7 @@ (define (on-event* where proc #:priority [priority *normal-priority*]) (add-endpoint! where (lambda () patch-empty) - (lambda (e) + (lambda (e _synthetic?) (schedule-script! #:priority priority #f (lambda () (proc e)))))) (define-syntax (on stx) @@ -620,19 +620,20 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Syntax-time support -(define (interests-pre-and-post-patch pat) +(define (interests-pre-and-post-patch pat synthetic?) (define (or* x y) (or x y)) (define a (current-actor-state)) - (define old (trie-lookup (actor-state-previous-knowledge a) pat #f #:wildcard-union or*)) + (define previous-knowledge (if synthetic? trie-empty (actor-state-previous-knowledge a))) + (define old (trie-lookup previous-knowledge pat #f #:wildcard-union or*)) (define new (trie-lookup (actor-state-knowledge a) pat #f #:wildcard-union or*)) (values old new)) -(define (interest-just-appeared-matching? pat) - (define-values (old new) (interests-pre-and-post-patch pat)) +(define (interest-just-appeared-matching? pat synthetic?) + (define-values (old new) (interests-pre-and-post-patch pat synthetic?)) (and (not old) new)) -(define (interest-just-disappeared-matching? pat) - (define-values (old new) (interests-pre-and-post-patch pat)) +(define (interest-just-disappeared-matching? pat synthetic?) + (define-values (old new) (interests-pre-and-post-patch pat synthetic?)) (and old (not new))) (define-for-syntax (analyze-asserted/retracted outer-expr-stx @@ -654,7 +655,7 @@ (lambda () (if #,when-pred-stx (core:sub #,pat) patch-empty)) - (lambda (e) + (lambda (e synthetic?) (core:match-event e [(? #,event-predicate-stx p) (define proj #,proj-stx) @@ -670,7 +671,7 @@ #,(let ((entry-handler-stx (quasisyntax/loc script-stx (let ((instantiated (instantiate-projection proj entry))) - (and (#,change-detector-stx instantiated) + (and (#,change-detector-stx instantiated synthetic?) (schedule-script! #:priority #,priority-stx #,(if terminal? #'#t #'#f) @@ -722,7 +723,7 @@ (lambda () (if #,when-pred-stx (core:sub #,pat) patch-empty)) - (lambda (e) + (lambda (e _synthetic?) (core:match-event e [(core:message body) (define capture-vals @@ -933,7 +934,8 @@ [field-descriptors (current-field-descriptors)]))))) (facet-handle-event! fid (lookup-facet fid) - (patch (actor-state-knowledge (current-actor-state)) trie-empty)) + (patch (actor-state-knowledge (current-actor-state)) trie-empty) + #t) (when (and (facet-live? fid) parent-fid (not (facet-live? parent-fid))) (terminate-facet! fid))) @@ -1067,13 +1069,13 @@ (current-pending-actions '()) (current-pending-scripts (make-empty-pending-scripts))] (for [((fid f) (in-hash (actor-state-facets a)))] - (facet-handle-event! fid f e)) + (facet-handle-event! fid f e #f)) (run-scripts!)))) -(define (facet-handle-event! fid f e) +(define (facet-handle-event! fid f e synthetic?) (with-current-facet fid (facet-field-descriptors f) #f (for [(ep (in-hash-values (facet-endpoints f)))] - ((endpoint-handler-fn ep) e)))) + ((endpoint-handler-fn ep) e synthetic?)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Script suspend-and-resume. diff --git a/racket/syndicate/examples/actor/example-synthetic-patch.rkt b/racket/syndicate/examples/actor/example-synthetic-patch.rkt new file mode 100644 index 0000000..d1278cc --- /dev/null +++ b/racket/syndicate/examples/actor/example-synthetic-patch.rkt @@ -0,0 +1,41 @@ +#lang syndicate/actor +;; Illustrates a (now fixed) bug where creating a facet interested in something +;; already known didn't properly trigger the assertion-handler. +;; +;; Symptomatic output: +;; +;; +outer "first" +;; +show +;; -show +;; -outer "first" +;; +outer "second" +;; +;; Correct output: +;; +;; +outer "first" +;; +show +;; -show +;; -outer "first" +;; +outer "second" +;; +show +;; +;; Should eventually be turned into some kind of test case. + +(struct outer (v) #:prefab) +(struct show () #:prefab) + +(actor (react (field [v "first"]) + (assert (outer (v))) + (assert (show)) + (on (message 2) + (v "second")))) + +(actor (react (on-start (send! 1)) + (during (outer $v) + (on-start (log-info "+outer ~v" v)) + (on-stop (log-info "-outer ~v" v)) + (during (show) + (on-start (log-info "+show")) + (on-stop (log-info "-show")))) + (on (message 1) + (send! 2))))