As Hodgman mentioned, buffering is the way to go if you need thread-safety and immediate readback availability. Although IMO it's hardly necessary for everything. There's basically three types of state in your UI class:
1) data that functions as part of some immediate dependency (eg window extents, which you calculate, then use to construct the window and may need to be able to query as early as within the new window's own constructor, eg in order to create child windows (although my suggestion would be to defer any actual branching into an init function instead of the constructor)). Since window position and extents can be queried both together and individually, it makes sense to store these as low/high dwords of a single qword, which is updated atomically.
2) data that realistically needs to be available as soon as possible, but no sooner than "when ready". This data makes up the bulk of state in all UI classes and in practice carries no clear meaning in terms of "immediately available" (at least not in a multithreaded environment). In simplest cases these are things like a button caption, trackbar value or a list of items in a listbox. I generally wrap the setter in some control-wide critical section, which can stall the calling thread, but as multithreaded updates to a single control should be rare at best and most controls need to redraw only when they change, will hardly ever generate any excessive contention. I find this simpler and less cumbersome than a truly asynchronous message-based system (eg something that WinAPI uses), which requires a lot more data duplication. If you controls change a lot and need to be drawn without delay, then you might still need to set up a swap buffer for the data. Generally speaking, the UI is an input interface and and output interface, not a data exchange interface. If the source of the change is the user, then the update will be as fast as the input hardware. If the source of the change is some internal process, then it likely lives on a different timescale than humans are used to anyway.
3) variables that function as an "exchange intermediary" between the UI and your application's internals. These are mostly useful if your application is running in real time and the internal code polls for updates instead of waiting for messages. In many cases these simplify your UI backend, but require extra care if you need to update them both internally and from the UI. I have two primary controls that take advantage of these - the checkbox and the trackbar, which allow binding an internal pointer as the shared value holder (a float* or a bool*, respectively). Dragging the slider or toggling the checkbox state then changes the attached float directly. As the lifetime and access of the control variable is directly managed by some internal class, this categorically prevents the variable from being written to directly (although you can still use the UI element itself as a point of synchronization) and needs extra care on the part of the programmer to ensure that the UI control has a shorter lifetime than the owner of the variable. Basically this is the fastest way to update any variable, although it is inherently unsafe. This can be particularly useful for instance when writing a synthesizer whose controls can have automation attached to them internally, but can be overriden by direct user input, or when using sliders to control parameters of some internal class that are read every frame.
Using a synchronization primitive without a proxy to update control data keeps it as up to date as possible, but requires the draw thread to stall while an update is under way. For most controls in most situations this is a non-issue. If you need maximum responsiveness on the part of the UI, then the easiest way is to buffer updates and process them asynchronously from a task queue. Personally, I don't see practical necessity for something like this unless you're updating a treeview containing an extensive directory tree that needs to be scanned from disk, or some such. In which case it might be better to specialize the class in the first place and temporarily create a separate thread to do the heavy lifting. Then again my code isn't meant as an all-purpose OS, which can run wholly asynchronously.
Here are some recommendation, which I've personally found to work quite nicely in my own UI code:
- buffer the following data: window extents, position and flags (eg visibility). Update them as soon as the backbuffer is updated. In the meantime return buffered values.
- optionally buffer the backbuffer information in some form or fashion (since, if you're using a single-threaded graphics API, the actual backbuffer can only be created in the draw thread). So defer the command, but be ready to stall if a query is made for the backbuffer itself. In a more practical example, it may be non-trivial if a window is created in thread A, posts the TASK_CREATE_BACKBUFFER command, but then ends up drawing in AND validating itself before the backbuffer is actually ready (resulting in a blank window). This is especially true if you're creating the backbuffer asynchronously on the graphics device and it may not be ready till some arbitrary amount of time has passed - a common case on most current hardware.
- don't bother buffering small updates, but wrap the setter and draw code in some form of synchronization primitive to keep things from crashing and avoid outputting garbage during redraw.
- if you need direct control over an internal value with the smallest possible latency, perform direct updates to a shared variable with the provision that the variable is read-only from outside the UI class or is updated exclusively via the UI class.
- if you do need to write a control that literally takes more than a few tens of milliseconds to update and needs to be super responsive, write a special wrapper that either pumps a message queue and bakes it into small hot-swappable chunks or creates its own thread to do the actual work asynchronously and updates the control in small enough chunks that the updates remain transparent to the user. As outlined above, I've only ever needed to do this with a directory tree scanner and in that case a dedicated low-priority thread worked out quite nicely.
So, to recap - if there's one thing I've learnt about internal responsiveness in a UI framework it's this: don't use your UI class as a data exchange proxy. It is much more important to leave a superficial responsive impression to the end user than have your stuff update 2 ms sooner internally. :wink: