Effects in Reaction

1 view
Skip to first unread message

Sergey Schetinin

unread,
Jul 28, 2009, 3:16:09 PM7/28/09
to better...@googlegroups.com
I tried to explain why @perform has to go earlier (
http://article.gmane.org/gmane.comp.python.peak/2426/match=perform )
and now it's gone. In its place theres a ctrl.txn.effect(callable,
*args) that adds that callable to a queue to get called after the
transaction finishes and only if it finishes successfully. There's
helper decorator @effect_method that makes the wrapped methods and
functions to be queued for that effect phase when called. If there's
no txn, the call goes through immediately.

I made it so that when that queue is flushed a special "disabled"
transaction is active, which means that if the effect tries to read or
write any transactional data (read or modify cells basically) it will
throw an error. I did that because @perform rules could have read
transient values ("discrete changes" in Trellis terminology) and
effects, running outside the txn could only see those values if they
were passed to them as an argument, stored in a queue. That is, the
effect would not get that value directly from in-txn data, it's up to
@maintain that queued it to read that data and pass it to txn.effect()
or @effect_method. So I thought that it would be a good idea to make
that limitation more explicit and disable txn-reads during effect
phase.

Now there's a different issue with starting new transactions from the
effect phase -- imagine a transaction queued something to be written
to a socket, the effect tries to do that and discovers that the socket
got disconnected -- it has to report it, to set some flag which would
write to a cell which is prohibited. So to allow that to work I added
yet another queue, only available from disabled transactions --
txn.post_effect(). Effects can add something to it and when all the
effects are done, if that queue is not empty it would start a new
transaction and call the post-effect callbacks in it setting all those
"disconnected" flags and whatnot.

Now, while that works fine, I figured that I could actually make those
queues the same -- just allow the effects to read / write txn but make
that happen in the new txn, that way effects still only get applied
after initial txn has succeeded with no possibility of retry yet they
can also report their results in a natural way. It is very similar to
how @tasks and top-level @atomic methods work -- they mix effects and
txn changes with no capability of retries and thanks to the trick I
explained earlier (
http://article.gmane.org/gmane.comp.python.peak/2453 ) they still can
happen concurrently. So I'm going to make effects happen in a regular
transaction, but not the one they got queued up from. This also meshes
nicely with newly introduced limitations on @track rule that may not
change their value more than once per txn (they still can run twice
due to dependency on transient values / computes). If there's a
cascade of effects to apply (like creating a window hierarchy) all
that can happen in a series of transactions via the txn.effect and all
the rules would still behave nicely inside those txns with txn-al
state being consistent at all time even if some effect fails at some
point.

I don't know if anyone used or uses Trellis / Reaction for something
that suffers from the hackish way Trellis handles effects, but trust
me, this is a big step forward.

--
Best Regards,
Sergey Schetinin

http://s3bk.com/ -- S3 Backup
http://word-to-html.com/ -- Word to HTML Converter

Andrew Svetlov

unread,
Jul 29, 2009, 11:55:44 AM7/29/09
to Better Python
1. I still don't understand - effect should to call txn.post_effect to
modify txn state or no? I think first one is better and more explicit.
I has problems with implicit modification inside Trellis task and it
was not easy to figure out.
2. effect can be called only from maintain or some other rule too?
From my perspective @perform can be natively translated to @track +
effect (not need maintain write access).

Sergey Schetinin

unread,
Jul 29, 2009, 2:40:54 PM7/29/09
to better...@googlegroups.com
2009/7/29 Andrew Svetlov <andrew....@gmail.com>:

>
> 1. I still don't understand - effect should to call txn.post_effect to
> modify txn state or no? I think first one is better and more explicit.

No, that was the intermediary solution. Now the queued effect() calls
are collected and after the transaction is completely finished (and
succeeded) a new transaction is immediately created where those queued
calls are executed. Those calls can modify state and reaction to those
modifications could create more effects that will be queued for yet
another cycle, it repeats until there were no effect calls in a
transaction or until a transaction fails, in the latter case only the
last transaction's changes are discarded.

> I has problems with implicit modification inside Trellis task and it
> was not easy to figure out.

I'm not sure I understand what you mean here.


> 2. effect can be called only from maintain or some other rule too?
> From my perspective @perform can be natively translated to @track +
> effect (not need maintain write access).

It can be called from non-readonly rules: from @maintain, from
top-level @atomic functions and from @task.

I don't see why you want to avoid using @maintain for effects, but
queuing effects from @track (given the fact that effects can change
the state, even if in the next txn) would break some properties. I
suppose we could have two kinds of effects, one capable of working
with state and another that runs in "disabled txn", the latter one
could probably be allowed to be queued from readonly rules, but I'm
not sure there's a need for that.

Sergey Schetinin

unread,
Jul 30, 2009, 3:36:03 AM7/30/09
to better...@googlegroups.com
I also think that with these changes we can prohibit reading then
writing from @maintain, so this would be invalid:

from mext.reaction import *

class C(Component):
v = attr(0)
@maintain
def x(self):
self.v += 1


print C().v


Instead, stuff like that would be accomplished via effects.

Andrew Svetlov

unread,
Jul 30, 2009, 8:50:55 AM7/30/09
to Better Python
Good change. Also please take a look on depended cells.
There are will not recalculated yet and contains old values.
From my perspective this reads also should generate exceptions.

>> I has problems with implicit modification inside Trellis task and it
>> was not easy to figure out.
> I'm not sure I understand what you mean here.
It related to previous. Task can write cell than read it or dependent
cells - and got old values without any errors.

Andrew Svetlov

unread,
Jul 30, 2009, 9:12:24 AM7/30/09
to Better Python
> > 2. effect can be called only from maintain or some other rule too?
> > From my perspective @perform can be natively translated to @track +
> > effect (not need maintain write access).
>
> It can be called from non-readonly rules: from @maintain, from
> top-level @atomic functions and from @task.
>
> I don't see why you want to avoid using @maintain for effects, but
> queuing effects from @track (given the fact that effects can change
> the state, even if in the next txn) would break some properties. I
> suppose we could have two kinds of effects, one capable of working
> with state and another that runs in "disabled txn", the latter one
> could probably be allowed to be queued from readonly rules, but I'm
> not sure there's a need for that.
>
I mean @perform looks like pure listener with readonly txn.
Actions executed from perform is stateless (in reaction context)
changes.
This actions cannot generate exceptions - perform is not undoable.
To update UI from mext.reaction I was need to make perform and put
cell values to UI widgets.
Of course this is trivial case not care about problems in action
executing like socket errors discribed by you.

maintain has readwrite txn and this is a bit surplus than required.
I want to have as restricted execution context as need for executing
rule.
For me `disabled txn` is good solution for this case - just make pure
non-optional listener and make syncronization inside it.

Sergey Schetinin

unread,
Jul 30, 2009, 4:18:56 PM7/30/09
to better...@googlegroups.com
2009/7/30 Andrew Svetlov <andrew....@gmail.com>:

>
>> > 2. effect can be called only from maintain or some other rule too?
>> > From my perspective @perform can be natively translated to @track +
>> > effect (not need maintain write access).
>>
>> It can be called from non-readonly rules: from @maintain, from
>> top-level @atomic functions and from @task.
>>
>> I don't see why you want to avoid using @maintain for effects, but
>> queuing effects from @track (given the fact that effects can change
>> the state, even if in the next txn) would break some properties. I
>> suppose we could have two kinds of effects, one capable of working
>> with state and another that runs in "disabled txn", the latter one
>> could probably be allowed to be queued from readonly rules, but I'm
>> not sure there's a need for that.
>>
> I mean @perform looks like pure listener with readonly txn.
> Actions executed from perform is stateless (in reaction context)
> changes.

A perform rule might access a @compute or @make which would initialize
that cell at that point (inside @perform) that cell might have a rule
that would create a component with a @maintain or a @task -- that
means that while @perform may not change state itself it still often
results in state change.

> This actions cannot generate exceptions - perform is not undoable.

Not really true, but also note that w/ concurrency transactions might
need to be retried even if they had no errors, or wrong order of
execution, simply because their reads were invalidated by another txn.
This means that no non-undoable operations may happen inside txn.

> To update UI from mext.reaction I was need to make perform and put
> cell values to UI widgets.
> Of course this is trivial case not care about problems in action
> executing like socket errors discribed by you.
>
> maintain has readwrite txn and this is a bit surplus than required.
> I want to have as restricted execution context as need for executing
> rule.
> For me `disabled txn` is good solution for this case - just make pure

> non-optional listener and make synchronization inside it.

As I said, we simply cannot allow effects *inside listeners*, the
safest way to do it would be to apply all effects with txn completely
disabled, but instead we can offer a compromise where effects can
happen inside txn but only right at its beginning -- via @task or via
txn.effect, allowing effects after that point just would break any
chance of concurrency. Still, having "pure" effects running with
disabled txn would be nice, so maybe I should add that back too.

For UI updates, I would recommend to have @maintain rules in a
baseclass that would observe overridable cells telling them what
should be put into UI, something like this:

http://code.google.com/p/trellis-fork/source/browse/trunk/demos/wx_demo5-observe.py

I imagine we could have a listener that would only read the txn, have
no value, but could create effects, and name it something like
@maintain.effect. I guess I can add that, cause it does make sense.

Andrew Svetlov

unread,
Jul 31, 2009, 9:22:15 AM7/31/09
to Better Python
> http://code.google.com/p/trellis-fork/source/browse/trunk/demos/wx_de...
>

I think I understand transaction roles good enouth. And you described
why perfom should gone away and what problems it has.
It's clean.

> I imagine we could have a listener that would only read the txn, have
> no value, but could create effects, and name it something like
> @maintain.effect. I guess I can add that, cause it does make sense.
My point is same. Trivial readonly listener what can executed effects
(perhaps with disabled-txn as you said).
Execute code with minimum required environment - maintain allows more
freedom than really required in many cases.
Reply all
Reply to author
Forward
0 new messages