Warning, /frameworks/threadweaver/examples/HelloInternet.in.md is written in an unsupported language. File is not indexed.
0001 ## Doing things in a Sequence 0002 0003 The time when an application starts, especially one that needs 0004 to load quite some data, is usually one of contention. Translations 0005 need to be loaded and resources like icons and images initialized. As 0006 the application matures, more and more of such tasks are piled on to 0007 it. It will have to check for updates from a server, and load a 0008 greeting of the day to the user. Eventually, the application will 0009 take ages to load, users will tweet about how they are making coffee 0010 while it comes up, and the programmers will start to find a solution. 0011 0012 The application will come up a lot faster if it defers as many 0013 tasks as possible while it creates and shows the user interface, and 0014 also takes as many as possible of the startup tasks of an application 0015 off the main thread. The main thread is the one that runs when 0016 `main()` is entered, and in which the user interface lives. Everything 0017 that slows down or intermittendly blocks the main thread may be 0018 experienced by the user as the user interface being sluggish or 0019 hung. This is a common use case where concurrent programming can 0020 help. 0021 0022 But ... this is also one of the examples where standard thread pools 0023 fail. The startup tasks commonly need to be done in a certain order 0024 and are of different priority, and also should not be all tackled by 0025 the application process at the same time. 0026 For example, the applications icons and translations may be needed 0027 first and urgently, where the information on available updates can 0028 still be processed a couple of seconds later. There are ways around 0029 this that are rather cumbersome, like using timers to queue up some 0030 tasks later or using chains of functions that queue up new tasks when 0031 one group is done. The following example will illustrate some aspects 0032 of how ThreadWeaver comes with the necessary tools to specify the 0033 order of tasks, on application startup and otherwise. The following 0034 `main()`[^4] function allocates a main widget and an object of type 0035 `ViewController` that takes care of the startup tasks. 0036 0037 @@snippet(HelloInternet/main.cpp,hellointernet-main,cpp) 0038 0039 The example application shows an image that it eventually loads from 0040 the network, and a caption for it. In the constructor of 0041 `ViewController`, the startup operations need to be kicked off. The 0042 operations in this example are 0043 0044 * to load a placeholder image that is shown while the application 0045 loads the image and caption from the network, 0046 * to load the post that contains the caption, but only the URL of the 0047 image to show, 0048 * and then, once the image URL is known, to finally load the image 0049 from the network and display it. 0050 0051 The application's user interface will be shown right away, even before 0052 step 1 has been completed. Let's assume that the three steps need to 0053 be done in order, not in parallel. 0054 0055 The important aspect is to do as little as possible in the 0056 constructor, considering that it is called from the main 0057 thread. Creating jobs and queueing them is not expensive however, so 0058 the constructor focuses on that and then returns. 0059 0060 @@snippet(HelloInternet/ViewController.cpp,hellointernet-sequence,cpp) 0061 0062 Remember the assumption that the three startup steps have to be 0063 performed in order. The new thing here is that instead of queueing 0064 individual jobs, the constructor creates a `Sequence`, and then adds 0065 jobs to that. A sequence has the jobs performed by the thread pool in 0066 the order they have been added to it. The jobs each simply call a 0067 member function of `ViewController` when being 0068 executed. ThreadWeaver's execution logic guarantees that the next job 0069 is only executed after the previous one has been finished. Because of 0070 that, only one of the member functions will be called at a time, and 0071 they will be called in the order the jobs have been added to the 0072 sequence. 0073 0074 Since only one of the member functions will be called at a time, there 0075 is no need for further synchronization of access to the member 0076 variables of `ViewController`. This raises the question of how the 0077 controller submits new captions, statuses and images to the main 0078 widget. It would be a mistake to simply call member functions of the 0079 main widget from the methods of `ViewController`, since these are 0080 executed from a worker thread. The controller submits update by using 0081 Qt signals that are connected to corresponding slots in the main 0082 widget. The parameters of the signals are passed by value, not by 0083 reference or pointers, making use of the implicit sharing built into 0084 Qt to avoid copying. This approach relies on the fact that the 0085 reference counting of Qt's implicit-sharing mechanism is thread safe. 0086 0087 @@snippet(HelloInternet/ViewController.cpp,hellointernet-loadresource,cpp) 0088 0089 The method `loadPlaceholderFromResource()` implements the first step, 0090 to load an image from a resource that acts as a place holder until the 0091 real images has been downloaded. It cheats to appear busy by first 0092 sleeping for a short while. While it does so, the user interface will 0093 already appear to the user, with a blank background. It then emits a 0094 signal to make the main widget show a status message that indicates 0095 the program is downloading the post. 0096 0097 The method is called from the worker thread that executes the job, not 0098 the main thread. When the signal is emitted, Qt notices that sender and 0099 receiver are not in the same thread at the time, and sends the signal 0100 asynchronously. The receiver will not be called from the thread 0101 executing `loadPlaceholderFromResource()`, instead it will be invoked 0102 from the event loop of the main thread. That means there is no shared 0103 data between the controller and the main widget for processing the 0104 signal, and no further serialization of access to the QString variable 0105 holding the status text is necessary. 0106 0107 Once the method returns and the job executing it completes, the next 0108 job of the sequence will be unlocked. This causes the method 0109 `loadPostFromTumblr()` to be executed by a worker thread. This method 0110 illustrates the convenience built into Qt to process data present in 0111 Open Standard formats (XML, in this case), even though this won't be 0112 discussed here in detail.[^5] If processing the data turns out to be 0113 expensive, the user interface will not be blocked by it, since it 0114 is not performed by the main thread. 0115 0116 @@snippet(HelloInternet/ViewController.cpp,hellointernet-loadpost,cpp) 0117 0118 In case an error occurs, the method invokes another method called 0119 `error()`. `error()` indicates the problem to the user by setting a 0120 status messages in the main widget. But it also apparently aborts the 0121 execution of the sequence, as the code assumes it does not continue 0122 after calling it. 0123 0124 @@snippet(HelloInternet/ViewController.cpp,hellointernet-error,cpp) 0125 0126 `error()` shows a different placeholder image, and emits the status 0127 message to the main widget. It then raises an exception of type 0128 `ThreadWeaver::JobFailed`, which will be caught by the worker thread 0129 executing the current job. The worker thread sets the status of 0130 the job to a failed state. Specific to sequences (because only 0131 sequences know the order of the execution of their elements), this 0132 will cause the sequence to abort the execution of it's remaining 0133 elements. Raising the exception will abort the processing of the job, 0134 but not terminate the worker thread. Leaking any other type of 0135 exception than `ThreadWeaver::Exception` from the `run()` method of a 0136 job is considered a runtime error. The exception will not be caught by 0137 the worker thread, and the application will terminate. 0138 0139 The example illustrates the steps necessary to perform concurrent 0140 operations in a certain order. It also shows how a specialized 0141 object (`ViewController`, in this case) can handle the data shared 0142 between the sequential operations, how to submit data and status 0143 information back to the user interface, and how to signal error 0144 conditions from job execution. 0145 0146 ![Hello Internet](screenshots/HelloInternet.png "The HelloInternet 0147 example, after downloading the post") 0148 0149 [^4]: See `examples/HelloInternet` in the ThreadWeaver repository. 0150 [^5]: The example uses the 0151 [Tumblr API version 1](https://www.tumblr.com/docs/en/api/v1).