Interface response times

Started by
7 comments, last by staticVoid2 7 years, 1 month ago

Hi,

I have a problem at the moment where I have an interface which has multiple implementations but the response times of the methods of each implementation can vary significantly. For e.g:


class Interface
{
public:
   virtual int getValue() = 0;
};

The first implementation (class A) simply returns a member variable and has a very quick response time:


class A : public Interface
{
public:
   virtual int getValue() override { return this->value; }

private:
   int value;
};

However, the second implementation (class B) contacts a RESTful web service and the response time can be potentially quite long:


class B : public Interface
{
public:
   virtual int getValue() override
   {
      // Send request to server
      // Wait for response
      // return value
   }
};

My question is, in these cases where response times differ should it be the responsibility of the implementation to ensure response times are consistent or should the interface itself be wrapped up in some asynchronous call mechanism (i.e. call these methods from another thread)?

I can see pros and cons of both approaches; If the implementation had to ensure a fast response time then in the case of class B it might need to return some default/incorrect value the first time it is called (which might be acceptable). Calling the interface from a separate thread would solve this however it would fundamentally change the way you would need to interact with the interface which might get a bit messy.

Or are there are any existing design patterns or solutions for this problem which I am missing?

Thanks.

Advertisement

IMHO the interface should be honest with the use that some implementations may be async. This would involve making the interface itself present an async API. e.g. if using the polling pattern, the interface would return a result proxy, and let you attempt to retrieve a value from it. The first implementation would always succeed in retrieving a value, while the second one would return failure until the internal request completes.

I assume that in the real world, the request to server part also has a bunch of failure conditions. What do you do in this situation? Do you just return a default value to the client of the interface, or is it important for them to know that the server is offline and decide the next course of action?

IMHO the interface should be honest with the use that some implementations may be async. This would involve making the interface itself present an async API. e.g. if using the polling pattern, the interface would return a result proxy, and let you attempt to retrieve a value from it. The first implementation would always succeed in retrieving a value, while the second one would return failure until the internal request completes.

I agree, one of the reasons I had added the interface was to simplify the calling code and abstract away the details of server communication and asynchronous requests, The idea being you could just call "obj->getValue()" without thinking twice about it. It looks like the asynchronous stuff is unavoidable though.

I think the polling/promise pattern might be what I'm looking for, however I might also consider just building some utility class that will wrap up the interface and provide asynchronous methods, e.g:


InterfaceWrapper wrapper(obj);

wrapper.getValue([](int value){ /* Do something here */ });

This would let the implementation focus on just retrieving the specific value and not concern itself with the asynchronous stuff.

I assume that in the real world, the request to server part also has a bunch of failure conditions. What do you do in this situation? Do you just return a default value to the client of the interface, or is it important for them to know that the server is offline and decide the next course of action?

Most of the interface methods return a generic error code which encompasses all potential errors (of all implementations). The error handling code is treated separately from the implementations that generate the error codes, so for example if it receives a 'server off-line' error code it can treat this as an independent error state and e.g. ask the user if they would like to send an error report etc.

As mentioned above this is part of the contract of the interface but which way it should be designed depends on the usage of the data.
If it's real-time then you might want to poll the REST data you need, cache it, and add a time-stamp to the data so you know how old it is (so you could interpolate.)
If it's not real-time then just make the REST call.

- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

You might consider including the concept of readiness into your interface. A client who needs to stay responsive can ask the interface whether the result is ready yet, and can try again later if not. A client that doesn't care about staying responsive can just ask for the result and it will return when it is ready. Some futures/promises systems have this concept built-in.

I think the polling/promise pattern might be what I'm looking for, however I might also consider just building some utility class that will wrap up the interface and provide asynchronous methods

There are many examples of this, including async wrappers and coroutine wrappers in Boost, and it is fairly easy to turn a long-running synchronous function into either a poll-based or push-based asynchronous system. You can write your own, but there are many utility libraries that already have it.

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:

In Layman's terms, your interface should look like this:


class AsyncResultListener
{
    virtual void resultArrived( int value ) = 0;
};

class AsyncResult
{
    virtual bool queryIsResultReady() = 0; //If true, guarantees getResult won't stall.
    virtual int getResult() = 0; /// Will stall until value is ready.

    /// Will return immediately, calling listener->resultArrived later when results arrives.
    /// If the value has already been arrived, then calls it immediately
    virtual int scheduleResultCallback( AsyncResultListener *listener ) = 0;
};

class Interface
{
    virtual AsyncResult getResult() = 0;
};

class B : Interface
{
    virtual AsyncResult getResult();
};

As mentioned above this is part of the contract of the interface but which way it should be designed depends on the usage of the data. If it's real-time then you might want to poll the REST data you need, cache it, and add a time-stamp to the data so you know how old it is (so you could interpolate.) If it's not real-time then just make the REST call.

This is the solution I currently have implemented; I have an interface for an object which exists on a server and I have a polling thread which polls the REST service and updates the objects data internally. Apart from having to synchronize the update with all of the accessor methods it seems to work ok.

One thing I should have mentioned was that I have lots of accessor methods for this object - in the example I provided there was only one 'getValue' method so it would have been fairly easy to wrap this up in a std::async call or some other utility but I'm not sure how I would have gone about this when trying to access multiple properties very frequently.

This topic is closed to new replies.

Advertisement