1.. _GUI_Thread:
2
3GUI Thread
4==========
5
6.. container:: section
7
8
9   .. rubric:: Problem
10      :class: sectiontitle
11
12   A user interface thread must remain responsive to user requests, and
13   must not get bogged down in long computations.
14
15
16.. container:: section
17
18
19   .. rubric:: Context
20      :class: sectiontitle
21
22   Graphical user interfaces often have a dedicated thread ("GUI
23   thread") for servicing user interactions. The thread must remain
24   responsive to user requests even while the application has long
25   computations running. For example, the user might want to press a
26   "cancel" button to stop the long running computation. If the GUI
27   thread takes part in the long running computation, it will not be
28   able to respond to user requests.
29
30
31.. container:: section
32
33
34   .. rubric:: Forces
35      :class: sectiontitle
36
37   -  The GUI thread services an event loop.
38
39
40   -  The GUI thread needs to offload work onto other threads without
41      waiting for the work to complete.
42
43
44   -  The GUI thread must be responsive to the event loop and not become
45      dedicated to doing the offloaded work.
46
47
48.. container:: section
49
50
51   .. rubric:: Related
52      :class: sectiontitle
53
54   -  Non-Preemptive Priorities
55   -  Local Serializer
56
57
58.. container:: section
59
60
61   .. rubric:: Solution
62      :class: sectiontitle
63
64   The GUI thread offloads the work by firing off a task to do it using
65   method ``task_arena::enqueue`` of a ``task_arena`` instance.
66   When finished, the task posts an event to the GUI thread to indicate that the work is done.
67   The semantics of ``enqueue`` cause the task to eventually run on a worker thread
68   distinct from the calling thread.
69
70   The following figure sketches the communication paths. Items in black are executed
71   by the GUI thread; items in blue are executed by another thread.
72
73   |image0|
74
75.. container:: section
76
77
78   .. rubric:: Example
79      :class: sectiontitle
80
81   The example is for the Microsoft Windows\* operating systems, though
82   similar principles apply to any GUI using an event loop idiom. For
83   each event, the GUI thread calls a user-defined function ``WndProc`` to process an event.
84
85
86   ::
87
88
89      // Event posted from enqueued task when it finishes its work.
90      const UINT WM_POP_FOO = WM_USER+0;
91
92
93      // Queue for transmitting results from enqueued task to GUI thread.
94      oneapi::tbb::concurrent_queue<Foo>ResultQueue;
95
96
97      // GUI thread's private copy of most recently computed result.
98      Foo CurrentResult;
99       
100
101      LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
102         switch(msg) {
103             case WM_COMMAND:
104                 switch (LOWORD(wParam)) {
105                     case IDM_LONGRUNNINGWORK:
106                         // User requested a long computation. Delegate it to another thread.
107                         LaunchLongRunningWork(hWnd);
108                         break;
109                     case IDM_EXIT:
110                         DestroyWindow(hWnd);
111                         break;
112                     default:
113                         return DefWindowProc(hWnd, msg, wParam, lParam);
114                 }
115                 break;
116             case WM_POP_FOO:
117                 // There is another result in ResultQueue for me to grab.
118                 ResultQueue.try_pop(CurrentResult);
119                 // Update the window with the latest result.
120                 RedrawWindow( hWnd, NULL, NULL, RDW_ERASE|RDW_INVALIDATE );
121                 break;
122             case WM_PAINT:
123                 Repaint the window using CurrentResult
124                 break;
125             case WM_DESTROY:
126                 PostQuitMessage(0);
127                 break;
128             default:
129                 return DefWindowProc( hWnd, msg, wParam, lParam );
130         }
131         return 0;
132      }
133
134
135   The GUI thread processes long computations as follows:
136
137
138   #. The GUI thread calls ``LongRunningWork``, which hands off the work
139      to a worker thread and immediately returns.
140
141
142   #. The GUI thread continues servicing the event loop. If it has to
143      repaint the window, it uses the value of\ ``CurrentResult``, which
144      is the most recent ``Foo`` that it has seen.
145
146
147   When a worker finishes the long computation, it pushes the result
148   into ResultQueue, and sends a message WM_POP_FOO to the GUI thread.
149
150
151   #. The GUI thread services a ``WM_POP_FOO`` message by popping an
152      item from ResultQueue into CurrentResult. The ``try_pop`` always
153      succeeds because there is exactly one ``WM_POP_FOO`` message for
154      each item in ``ResultQueue``.
155
156
157   Routine ``LaunchLongRunningWork`` creates a function task and launches it
158   using method ``task_arena::enqueue``.
159
160   ::
161
162
163      class LongTask {
164         HWND hWnd;
165         void operator()() {
166             Do long computation
167             Foo x = result of long computation
168             ResultQueue.push( x );
169             // Notify GUI thread that result is available.
170             PostMessage(hWnd,WM_POP_FOO,0,0);
171         }
172      public:
173         LongTask( HWND hWnd_ ) : hWnd(hWnd_) {}
174      };
175
176      void LaunchLongRunningWork( HWND hWnd ) {
177         oneapi::tbb::task_arena a;
178         a.enqueue(LongTask(hWnd));
179      }
180
181
182   It is essential to use method ``task_arena::enqueue`` here.
183   Even though, an explicit ``task_arena`` instance is created,
184   the method ``enqueue`` ensures that the function task eventually executes when resources permit,
185   even if no thread explicitly waits on the task. In contrast, ``oneapi::tbb::task_group::run`` may
186   postpone execution of the function task until it is explicitly waited upon with the ``oneapi::tbb::task_group::wait``.
187
188   The example uses a ``concurrent_queue`` for workers to communicate
189   results back to the GUI thread. Since only the most recent result
190   matters in the example, and alternative would be to use a shared
191   variable protected by a mutex. However, doing so would block the
192   worker while the GUI thread was holding a lock on the mutex, and vice
193   versa. Using ``concurrent_queue`` provides a simple robust solution.
194
195   If two long computations are in flight, there is a chance that the
196   first computation completes after the second one. If displaying the
197   result of the most recently requested computation is important, then
198   associate a request serial number with the computation. The GUI
199   thread can pop from ``ResultQueue`` into a temporary variable, check
200   the serial number, and update ``CurrentResult`` only if doing so
201   advances the serial number.
202
203.. |image0| image:: Images/image007a.jpg
204   :width: 400px
205   :height: 150px
206