[squeak-dev] Call-cc [was: GUI threads (was: Replacing or enhancing or supplementing Morphic))

Vanessa Freudenberg vanessa at codefrau.net
Fri Mar 3 19:51:34 UTC 2023


Might be worth revisiting some of Tweak's ideas. That was a UI for Squeak
inspired by Morphic but with much nicer coding semantics.

The thing I loved most about it was that you could write code as if it was
the only thing running. It was handling concurrency behind the scenes.

Stuff like this animation:

    0 to: 100 do: [ :x |
         object x: x.
         self wait: 20]

or this event handler

    downEvent := object waitFor: #mouseDown.
    [ evt := object waitFor: #mouseMove or: #mouseUp.
      evt type = #mouseUp] whileFalse:
         [ ... do something (even blocking...) ]

(I forgot the exact syntax)

Tweak suffered from low debuggability (there were race conditions that were
really hard to get right), and perceived tie-in to Croquet (even though it
could be used by itself). But when it worked it was magical.

Vanessa

On Fri, Mar 3, 2023 at 8:35 AM Frank Shearar <frank.shearar at gmail.com>
wrote:

>
>
> On Fri, 3 Mar 2023 at 07:41, Eliot Miranda <eliot.miranda at gmail.com>
> wrote:
>
>>
>> On Mar 2, 2023, at 11:54 PM, rabbit <rabbit at callistohouse.org> wrote:
>>
>>
>> 
>> Does a block closure preserve its creating context?
>>
>>
>> Yes, but not the creating context’s continuation.
>>
>> Walking the future tree, we could collect the back tree of creating
>> contexts to establish an historical tree. I say tree because reactor #1 was
>> then: immediately after causal send leading to the exception, and the
>> promise got passed around and another belly cat sent a then: for reactor
>> #2, both in the same promise’s pendingSends queue. Therefore the historical
>> view of a future tree is also a tree. So the eventual debugger has a future
>> / historical toggle. And we debug the tree and not the stack.
>>
>>
>> This is a good idea and could be fine efficiently with the current VM
>> architecture. (*) Promises would be created with the equivalent of
>> “call-cc”, (*) scheme’s call-with-current-continuation.  I’ve discussed
>> this with Andreas, Avi Bryant, Colin Putney, and others over the years. It
>> would elevate the level of comprehensibility in future/promise based
>> systems significantly.
>>
>
> As long as "call-cc" means "delimited continuations", yup yup. (See
> https://okmij.org/ftp/continuations/against-callcc.html and
> https://okmij.org/ftp/continuations/undelimited.html#delim-vs-undelim)
>
> frank
>
>
>> Further, because the exception system is first-class, handler search
>> could be extended from the promise’s stack to its enclosing continuation’s
>> stack, which would make managing errors in promises much easier; one would
>> not have to establish an error handler where each promise is created, but
>> could establish a single error handler in the thread that creates promises
>> (etc, this can nest if desired/so implemented).
>>
>>
>> Yet, reactor #1 could also have been sent outside the causal send
>> context. So we may still want to hold the causal context in the promise /
>> eMsgSend object.
>>
>>
>> (*) an implementation note for those looking fir a juicy VM project. The
>> essence of call-cc is stack duplication. C.f. the explicit stack
>> duplication in Seaside’s delimited continuations. When call-cc occurs
>> effectively the entire stack is duplicated so that both the creating and
>> the created thread can continue. (for promises this allows us to see where
>> the promise originates long after the creating thread has continued and
>> returned from where it created the promise, which is why this helps with
>> debugging).
>>
>> I am told that in efficient  scheme implementations stack duplication is
>> made lazy. Essentially a flag is set in an activation record that does
>> call-cc that is checked at return time. This causes the activation to be
>> preserved for the call-cc continuation and set for the activation record
>> being returned into, lazily duplicating only those activations that are
>> actually returned from. Neat.
>>
>> In the Cog VM, executable activation records (contexts) live in a special
>> form on small stack pages in the stack zone. This is done both to avoid
>> having to create context objects until unavoidable (home contexts for
>> blocks, reifying the contexts stack for the debugger, etc), abc to allow
>> the JIT to optimize sends using conventional call instructions.  Each stack
>> page is large enough ti hold about 40 normal sized activations (& only one
>> of two huge activations).  Having the equivalent of call-cc duplicate the
>> entire stack page, and propagate down the “preserve/duplicate me” flag to
>> the next page, could be extremely efficient, avoiding the relatively
>> expensive return-time check of scheme implementations.
>>
>> Anybody seriously considering building in Squeak above Cog things like a
>> massively parallel compute architecture, or making things like Teatime (in
>> Croquet) much easier to debug, would do well to take such an approach.
>>
>>
>> BTW, Jecek’s explanation of the concurrency architecture in Morphic is
>> masterful and should at least be copied into the help browser.  Thank you,
>> Jecel.
>>
>>
>> 🐰
>>
>>>> Have a good one; keep it, light.
>> Kindly, rabbit . .. … ‘…^,^ 🐇🐇🐇
>>
>> Sent from Callisto House mobile - Ganymede
>> :: decentralized mobile homeless solutions ::
>>
>>
>>
>>
>> On Fri, Mar 3, 2023 at 02:42, rabbit <rabbit at callistohouse.org> wrote:
>>
>> This was a little confusing my apologies.
>>
>> This example
>>
>>      (anObject eventual identityHash * 10)
>>
>> I incorrectly labeled promise chaining. It’s more properly called in the
>> literature as pipelining, where we send a follow on DNU message to
>> anENearPromise. Let’s break it down
>>
>> anObject eventual returns anENear
>>
>> Sending identityHash schedules it’s EventualMessageSend, in the Vat, and
>> reteruns anENearPromise.
>>
>> Immediately following this promised send, we eventually send a * 10. This
>> stores it’s EventualMessageSend in the ENearPromise’s pendingSends queue
>> and returns anENearPromise #2.
>>
>> This is Promise Pipelining.
>>
>> In my opinion right now it is more convenient to develop in a synchronous
>> manner in Squeak because the debugger in its current state is better suited
>> for that. It does not support you very well with Promises, deferred UI
>> messages, spawned processes and the like... And when some of the tools that
>> you are used to do not support you well anymore, it does not feel good.
>>
>> Yes, indeed. I’ve been thinking about this. Deep in a send tree, an
>> exception occurs. We would like to see a stack representation of some kind.
>> What kind?
>>
>> First guess is the stack of sending contexts leading to the exceptional
>> send. However, good Eventual Sending Style would see calling sends to
>> eventuality send its work and exit. Therefore, it is highly likely there is
>> no stack available.
>>
>> Is it feasible to store sends leading down to the exceptional send? This
>> is a disconnect that could seriously affect our ability to
>> debug effectively.
>>
>> So what sort of stack could we build? As this only happens when
>> debugging, we could afford to seek the stack for the EventualMessageSend
>> activating the immediate stack. Store the ENearPromise that corresponds to
>> the eMsgSend’s resolver withe the eMsgSend. Then when the activating
>> eMsgSend is found, access the promise’s pendingSends and collect the
>> registered reactors from calls to the promise #then: / #whenResolved: /
>> #whenRejected:. They themselves have promises with pendingSends, so a tree
>> of future sends could be established. These should all get #smashed: from
>> the exceptional send, once resumed into the future.
>>
>> Andso, it seems to me we could struggle to produce an historical stack,
>> but we could provide a futuristic promise tree.
>>
>> I’m thinking we really need the historical stack. For example an argument
>> is provided that fails. Where did this argument come from. We need to
>> backup and regenerate the argument. Without a historical stack I don’t see
>> how that works.
>>
>> Therefore, I think we may need to preserve the sending contexts. What are
>> the implications to do this? I don’t know.
>>
>> 🐰
>>
>> On Wed, Mar 1, 2023 at 15:23, rabbit <rabbit at callistohouse.org> wrote:
>>
>> Let’s make better Turbinado Sugar for our café con leche! The #* is
>> sent to the result promise from #identityHash’s esend. This promise’s
>> #doesNotUnderstand: redirectMessage: of the ‘* 10’ and returns the 2nd
>> promise. The argument to show: cannot be an eventual ref, at this time, so
>> a then: with a reactor is required. No promise chaining will work.
>>
>>     (anObject eventual identityHash * 10)
>>         then: [:codeMag |
>>             Transcript cr;
>>                 show: ‘code order of magnitude = ‘, codeMag].
>>
>> 🐰
>>
>>>> Have a good one; keep it, light.
>> Kindly, rabbit . .. … ‘…^,^ 🐇🐇🐇
>>
>> Sent from Callisto House mobile - Ganymede
>> :: decentralized mobile homeless solutions ::
>>
>>
>>
>>
>> On Wed, Mar 1, 2023 at 14:52, rabbit <rabbit at callistohouse.org> wrote:
>>
>>
>>
>> On Wed, Mar 1, 2023 at 14:18, Jakob Reschke <jakres+squeak at gmail.com>
>> wrote:
>>
>> Event-loop based UI development is usually a good strategy. It is how
>> Windows works at the foundations... I know that this will sound rather like
>> an argument against it for some people. ;-P
>>
>> lol! Certainement! Yet they built the Midori eventual sending OS [1].
>>
>> The question is whether it is convenient to program like that. All parts
>> involved must account for the asynchronicity. It may be more convenient to
>> just send a Smalltalk message rather than using addDeferredUiMessage: and
>> wrapping your intent in a block, but the code that comes after that line
>> looks different, too.
>>
>> Yes indeed. The EST: Eventual Sending Style.
>>
>>     ((an object eventual send: #identityHash)
>>         then: [:code | code * 10])
>>             then: [:codeMag | Transcript cr; show: ‘code order of
>> magnitude = ‘, codeMag].
>>
>> Looking into sugar, with ESqueak, where EAbstract>>#doesNotUnderstand:
>> eventually sends the msg like a trampoline:
>>
>>     (anObject eventual identityHash
>>         >>: [:code | code * 10]
>>         >>: [:codeMag | Transcript cr; show: ‘code order of magnitude =
>> ‘, codeMag].
>>
>> Where the as yet unimplemented ENearPromise>>#>>:>>: would be
>>
>>     ^ (self then: reactor1)
>>         then: reactor2.
>>
>> Of course instead of a block the reactor could be (an anonymous) subclass
>> of WhenResolvedReactor and set a closure object in initialize.
>>
>> Especially if you need a result/response to continue your overall
>> computation.
>>
>> I was thinking of a possible marking of a closure as #immediate to turn
>> all eventual sending within as immediate calls.
>>
>> You may be inclined to use Promises there, where you would not do that
>> otherwise, for example.
>>
>> Right.
>>
>> In my opinion right now it is more convenient to develop in a synchronous
>> manner in Squeak because the debugger in its current state is better suited
>> for that. It does not support you very well with Promises, deferred UI
>> messages, spawned processes and the like... And when some of the tools that
>> you are used to do not support you well anymore, it does not feel good.
>>
>> An excellent observation, Jakob! Tool support for eventual sending would
>> be a must, specifically the debugger.
>>
>> I think this exposes the coordination problem, I guess we could call it.
>> How does one intrepid souk debug control flow among independent event
>> processes?
>>
>> Given success with these issue, does it make sense to keep eventual
>> sending as a candidate?
>>
>> Would it not allow controller / view / model interactions to be thought
>> about clearly?
>>
>> Thanks for your consideration.
>>
>> 🐰
>>
>>
>>
>> Am Mi., 1. März 2023 um 19:40 Uhr schrieb rabbit <
>> rabbit at callistohouse.org>:
>>
>>> Awesome! Jecel, I’m quite curious to hear your thoughts on eventual
>>> sending. EVERYBODIES thoughts, really. Is event-loop eventual sending a
>>> feasible style / function for UI development?
>>>
>>> 🐰
>>>
>>>>>> Have a good one; keep it, light.
>>> Kindly, rabbit . .. … ‘…^,^ 🐇🐇🐇
>>>
>>> Sent from Callisto House mobile - Ganymede
>>> :: decentralized mobile homeless solutions ::
>>>
>>>
>>>
>>>
>>> On Wed, Mar 1, 2023 at 13:00, Jecel Assumpcao Jr <jecel at merlintec.com>
>>> wrote:
>>>
>>> David T. Lewis wrote on Tue, 28 Feb 2023 20:39:54 -0500
>>> > In MVC, each Controller has its own Process, and those processes are
>>> > scheduled when needed as windows are activated. In Morphic, there is
>>> > a single UI process.
>>>
>>> True, but misleading. Just like the VM has a single OS thread that it
>>> multiplexes between image level processes, the single Morphic thread
>>> pretends to run each morph in parallel at a known rate by repeated
>>> sending #step to them.
>>>
>>> Because we know that only when #step for one morph is finished that
>>> #step will be sent to the next morph, we have been programming as it
>>> morphs did not execute in parallel even though that is what the model
>>> was supposed to be. That is why we get in trouble when things happen
>>> outside of the GUI scheduler.
>>>
>>> In Self you have have more than one Morphic window and each gets its own
>>> GUI thread, but they are unlikely to share the same objects so we don't
>>> notice the problem. You can also have multiple windows in different
>>> machines viewing the same "world" and in that case they all share a
>>> single GUI thread.
>>>
>>> -- Jecel
>>>
>>>
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20230303/d0140e14/attachment-0001.html>


More information about the Squeak-dev mailing list