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