
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 -- John Meacham - ⑆repetae.net⑆john⑈

Hello John, Sunday, July 03, 2005, 11:16:30 PM, you wrote: JM> have a screen update thread, this is the only one that will make curses JM> calls. JM> everything else communicates with the screen by modifying TVars holding JM> data values like so in my own program i plan to use different approach. may it will be better for you too. 1) one thread that render screen 2) this thread receives commands via one Channel and immediately executes them 3) other threads send commands via this Channel -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Mon, Jul 04, 2005 at 12:06:11PM +0400, Bulat Ziganshin wrote:
Hello John,
Sunday, July 03, 2005, 11:16:30 PM, you wrote:
JM> have a screen update thread, this is the only one that will make curses JM> calls. JM> everything else communicates with the screen by modifying TVars holding JM> data values like so
in my own program i plan to use different approach. may it will be better for you too.
1) one thread that render screen 2) this thread receives commands via one Channel and immediately executes them 3) other threads send commands via this Channel
Yeah, that is basically what I am doing now. it does keep all the curses calls properly ordered, but doesn't solve the problem of creating the appropriate sequence of commands in the first place :) Basically I want a 'view/model' split. the model will be a bunch of TVars I can modify (perhaps in atomic groups) and the view will automatically efficiently redraw and respond to the changes. John -- John Meacham - ⑆repetae.net⑆john⑈
participants (2)
-
Bulat Ziganshin
-
John Meacham