Concurrency for Curses

403 views
Skip to first unread message

Rob Thornton

unread,
Nov 16, 2013, 5:32:18 PM11/16/13
to golan...@googlegroups.com
I maintain goncurses[1] and I am appealing to those of who who would be better at concurrency than myself. Almost every function in the curses library reads or writes to the underlying C data structures. Curses, and its more modern descendants, were not built with concurrency in mind. ncurses has added some built-in functions which can be used to wrap any other function in the library with a global lock/mutex but from research I've done it's not recommended to use/rely on them as there is still the potential for race conditions/data corruption (I'm not exactly sure why).

Thus, I've had two options:

1) Wrap every function in a global mutex (well over 100).
2) Instruct users to wrap any curses functions within some kind of synchronization (channel or mutex) to ensure no curses functions are ever called concurrently.

I have opted, so far, to take option 2 and do so in my own code. I am worried that wrapping certain functions, especially those which are blocking (buffered input comes to mind), will cause bottlenecks/deadlocks. Is my reasoning sound?  Is there a better way I should be handling this?

RickyS

unread,
Nov 16, 2013, 5:49:54 PM11/16/13
to golan...@googlegroups.com
What could you do concurrently or in parallel, since the codebase is still 'single-threaded'?
That is, could curses start prompting the user for input while it draws the screen?  Or something?
I would guess there is nothing you could do that was correct and reliable.
In my memory the old thing would have to wait until the i/o was done, anyway.

Perhaps you could reject any call that was not from the starting thread?
I guess the best is provide help for item 2).

Are there startups basing their product on ncurses?
It was designed originally for the vt100, hardware from 30 years ago.  The software is older than you (probably) are.
Best of luck.

Rob Thornton

unread,
Nov 16, 2013, 6:06:19 PM11/16/13
to golan...@googlegroups.com
I know of its use in a few games. It would be natural for game related events and drawing to the screen to be handled separately in a goroutine and input in another.

Amusingly, the closest date I could find for the initial release of curses was "around 1980" which makes me only slightly older than the library. ;) 

Jesse van den Kieboom

unread,
Nov 17, 2013, 3:14:40 AM11/17/13
to golan...@googlegroups.com
I only have experience with gtk+ so I'm no authority, but. All gui libraries that I know are not made to be thread safe, but should be called from the same thread it was initialized from. This works well if you instruct users about it properly because in most languages you control threads explicitly and you know how to synchronize between them. In gtk+ (and I see something similar in the go qml bindings) you usually install a callback which is called when the gui main loop gets idle. The callback is executed in the context of the main loop.

The thing with go is that threads can be created all the time and you don't even know it. go routines are such an integral part of the language, that I as a user would expected that gui libraries take care of allowing me to call their code from multiple go routines. You can check https://github.com/niemeyer/qml to see how you can do that in a relatively nice way without locking all the time.

Jsor

unread,
Nov 19, 2013, 6:29:52 PM11/19/13
to golan...@googlegroups.com
The way I solved OpenGL in a multi-threaded, concurrent environment was to have a "master" goroutine that formally owned the context, and was perpetually locked in a loop over a chan func(). While you can do any calculations or whathaveyou outside the thread/goroutine, to execute instructions on the context itself, you send a func() over a channel to the main context, and the context thread will execute it. A simple/naive implementation is just

runtime.LockOSThread()
whateverImUsing.MakeContextCurrent()

for f := range instructionChan {
    f()
}

Though often you need a bit more control than that (my rendering system uses a select statement that is set to render the scene at set intervals between executing functions, for instance), but the gist remains the same.

You do have to be aware that instructions may not be executed immediately, of course, but it works pretty well and the delay in execution is usually fairly negligible (at the very least it's not too much worse than any OTHER non-professional implementation of an asynchronous rendering system).

You may want to handle I/O separately, but with most setups it's not strictly possible (the event callbacks for the input are going to be executed in the context no matter what), but you can keep them conceptually separate if you introduce a one-way dependency. For instance, I have an input handler that tags along with my renderer as a separate subsystem, and reading the code it feels like they're mostly independent of each other, but in reality 90% of the "input handler's" job is feeding new callback registration requests to the renderer's function channel as is necessary. Most of the actual work is executed in the renderer's thread/goroutine even though the code is in the input handler. (And the rendering system is agnostic to the input handler's existence).

I don't know how much of this is immediately applicable to ncurses, or gtk+, or whatever else, but from the gist I got from the thread they seem similar in concept.

I'm sure it may be theoretically possible to do I/O concurrently, but it would probably have to be built ground-up with it in mind, not just a wrapper around an existing library. Rob wrote an article on a concurrent window system here: http://swtch.com/~rsc/thread/cws.pdf

Rob Thornton

unread,
Nov 20, 2013, 11:09:39 AM11/20/13
to Jsor, golan...@googlegroups.com
Thanks for your input Jsor. I would certainly draw a closer parallel from curses to OpenGL than a GUI toolkit. NTK, the Ncurses ToolKit, would be more like Gtk+ in it's scope. Your solution is intriguing and I'll take a closer look at it.


--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/q0bYe7CA_y4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
0 new messages