April 17th, 2024

Adding state to the update notification pattern, part 1

Some time ago, we looked at the update notification pattern, but in those cases, the notification carried no state.

Consider the case where you want to call a notification handler, and the handler also receives a copy of data derived from the most recent state, rather than just being called to be told that something changed and forcing them to figure out what changed.

For example, suppose you want to add autocomplete to an edit control, but calculating the autocomplete results is potentially slow, so you want to do it in the background. But while you are calculating the autocomplete results, the user might type into the edit control, and you want the final autocomplete results to reflect the most recent edit in the edit control, rather than any results from what the edit control used to contain at some point in the past.

For concreteness, I’ll use C++/WinRT, but the principle applies generally. Here’s our starting point:

winrt::fire_and_forget
EditControl::TextChanged(std::string text)
{
    auto lifetime = get_strong();

    co_await winrt::resume_background();

    std::vector<std::string> matches;
    for (auto&& candidate : FindCandidates(text)) {
        if (candidate.Verify()) {
            matches.push_back(candidate.Text());
        }
    }

    co_await winrt::resume_foreground(Dispatcher());

    SetAutocomplete(matches);
}

We hop to a background thread do calculate the autocomplete results, and then return to the UI thread to report the results.

There is a race condition here if a newer autocomplete result completes more quickly than an older one. For example, if the user types “skyp“, it may take some time to find ten things that start with “skyp”. But if the user then selects all the text and replaces it by typing “b“, it probably won’t take long to find ten things that start with “b”. You then have this problem:

UI thread Background thread 1 Background thread 2
TextChanged(“skyp”)
Continue on background thread
   
  FindCandidates(“skyp”)  
TextChanged(“b”)
Continue on background thread
â‹®
â‹®
 
  ⋮
â‹®
Build matches
â‹®
FindCandidates(“b”)
Build matches
matches = { “bob”, “boy” }
Continue on UI thread
SetAutocomplete({ “bob”, “boy” });
 
 
â‹®
matches = { “skype” });
Continue on UI thread
 
SetAutocomplete({ “skype” });
 
 
   

Even though the most recent update to the edit control was to type “b”, the autocomplete window shows the results back from when the text was “skyp”.

We’ll spend the next few days trying to fix this problem.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.