STM help

Simon Marlow simonmar at microsoft.com
Mon Jul 4 08:15:29 EDT 2005


The problem seems to boil down to this:

  - write a thread that performs an IO action whenever the contents
    of a set of TVars changes.

Providing this functionality as part of STM seems to be wrong, because
it relies on a particular implementation of STM (or a simulation of that
behaviour).

It would be difficult to incorporate this in the semantics, because the
semantics doesn't currenly say anything about the change-detecting
optimisation - so the behaviour of the new combinator would have to be
specified explicitly.

Another purely STM solution that you didn't mention is this:

  - have one TVar that records the "something changed" condition; every
    write to a TVar in the widget tree also sets this flag to true;
    the updater thread waits for the "something changed" flag to
    become True, calculates the screen redraw, then sets the flag to
    False again.

  - The difficulty then is optimising away the redraws when only hidden
parts
    of the widget tree have changed - you could do this by having a
"visible"
    flag on every widget, set by the updater thread.  If an update
occurs to a
    hidden widget, the changed flag is not set.  If a widget changes
such that
    it might have become visible, it must set the changed flag.

I know this is an extra TVar per widget and more operations for each
update, but I think it's the right thing.  There are no unnecessary
traversals of the widget tree, and redraws happen no more often than
necessary.

Cheers,
	Simon

On 03 July 2005 20:17, John Meacham wrote:

> I am attempting to use the STM to rewrite the screen handling code in
> ginsu, which frankly, I am a little embarassed of :)
> 
> The basic needs are:
> - fully concurrent
> - all curses calls must go through a single thread (otherwise it gets
>   real complicated)
> - atomic updates to multiple parts of the screen at once.
> - efficient update mechanism, screen _must not_ be needlessly redrawn.
> 
> 
> So, my idea was basically,
> 
> have a screen update thread, this is the only one that will make
> curses 
> calls.
> everything else communicates with the screen by modifying TVars
> holding 
> data values like so
> 
> data Widget = HBox { subWidgets :: [TVar Widget] } | Text { body ::
> TVar String } 
> 
> and the screen is represented by a top level 'TVar Widget'
> 
> 
> 
> This gives the first three requirements very nicely, you can
> atomically 
> update several widgets or tex boxes, and everything is very concurrent
> in a good way.
> 
> The problem comes in when trying to implement an efficient update
> mechanism, the problem is that I can't figure out how to wait on only
> the relevant changed TVars.
> 
> The structure of my curses thread would look like (essentially):
> 
> 
> cursesThread head = do
>         atomically $ do
>                 tv <- readTVar head
>                 doit <- render tv  -- returns an IO action
>                                       which draws the screen,
>                                       calling readTVar on subwidgets.
>                 return doit
>         doit
>         -- What to put here to block on relevant changes?
>         cursesThread head
> 
> The problem is that many TVars may contribute to the final screen, one
> can change a TVar several widgets down and the screen should be
> updated. 
> In addition, the screen should not be redrawn if a TVar for an
> offscreen 
> or hidden widget changes. other threads should be able to update state
> without worrying about the UI.
> 
> Now, i can think of a couple things, neither of which I like very
> much. 
> 
> - create a wrapper monad around STM that accumulates a list of TVars
>   read. This is just unappealing for a variety of reasons.
> - have render return some sort of keys to compare values too and retry
>   if they are all equal to their old values. this would
>   double the work, one pass to draw it, another pass to figure out we
>   need to block. it would also really complicate the code.
> 
> 
> Now, the other reason these options are undesirable, is that the STM
> mechanism collects the exact information I need. AFAIK there is just
> no 
> way to get at it. the STM must keep a log of all TVars read in order
> to 
> know what to wait on when retrying. What I need is a way to somehow
> atomically commit (assuming the transaction succeeds) and then be able
> to block on the depended on TVars
> 
> 
> This could be done with the addition of a simple primitive:
> 
> - | behave exactly like 'atomically' except after the transaction
>   succeeeds, run the action on the result, and then block until
>   one of the TVars read by the transaction changes.
> 
> atomicallyBlock :: STM a -> (a -> IO b) ->  IO b
> 
> 
> I think this could be very useful in general, but perhaps there is
> something I am not seeing? I am still figuring out idioms to use with
> STM.
> 
> 
>         John



More information about the Glasgow-haskell-users mailing list