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