1 /*
2  * Copyright (C) 2010 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include <Wt/WApplication.h>
7 #include <Wt/WContainerWidget.h>
8 #include <Wt/WPushButton.h>
9 #include <Wt/WProgressBar.h>
10 
11 #include <iostream>
12 #include <thread>
13 #include <chrono>
14 
15 using namespace Wt;
16 
17 /*
18  * This is a minimal server push example, which is used to update the GUI
19  * while a big work is computing in another thread.
20  *
21  * This example grabs the userinterface UpdateLock to directly modify
22  * an application's user-interface from a worker thread. This works
23  * fine for a thread that was created and is owned by a single
24  * application, doing work involving only that application.
25  *
26  * In more complex scenarios, it may be easier to use WServer::post()
27  * to post an event to a session. This approach is illustrated in the
28  * simplechat example.
29  */
30 class BigWorkWidget : public WContainerWidget
31 {
32 public:
BigWorkWidget()33   BigWorkWidget()
34     : WContainerWidget()
35   {
36     startButton_ = this->addWidget(std::make_unique<WPushButton>("Start"));
37     startButton_->clicked().connect(startButton_, &WPushButton::disable);
38     startButton_->clicked().connect(this, &BigWorkWidget::startBigWork);
39     startButton_->setMargin(2);
40 
41     progress_ = this->addWidget(std::make_unique<WProgressBar>());
42     progress_->setInline(false);
43     progress_->setMinimum(0);
44     progress_->setMaximum(20);
45     progress_->setMargin(2);
46   }
47 
~BigWorkWidget()48   virtual ~BigWorkWidget() {
49     if (workThread_.get_id() != std::this_thread::get_id() &&
50 	workThread_.joinable())
51       workThread_.join();
52   }
53 
54 private:
55   WPushButton *startButton_;
56   WProgressBar *progress_;
57 
58   std::thread workThread_;
59 
startBigWork()60   void startBigWork() {
61     WApplication *app = WApplication::instance();
62 
63     // Enable server push
64     app->enableUpdates(true);
65 
66     if (workThread_.joinable())
67       workThread_.join();
68     workThread_
69       = std::thread(std::bind(&BigWorkWidget::doBigWork, this, app));
70 
71     progress_->setValue(0);
72     startButton_->setText("Busy...");
73   }
74 
75   /*
76    * This function runs from another thread.
77    *
78    * From within this thread, we cannot use WApplication::instance(),
79    * since that use thread-local storage. We can only access
80    * WApplication::instance() after we have grabbed its update-lock.
81    */
doBigWork(WApplication * app)82   void doBigWork(WApplication *app)
83   {
84     for (unsigned i = 0; i < 20; ++i) {
85       // Do 50 ms of hard work.
86       std::this_thread::sleep_for(std::chrono::milliseconds(50));
87 
88       // Get the application update lock to update the user-interface
89       // with a progress indication.
90       WApplication::UpdateLock uiLock(app);
91       if (uiLock) {
92         progress_->setValue(i + 1);
93         app->triggerUpdate();
94       } else
95         return;
96     }
97 
98     WApplication::UpdateLock uiLock(app);
99 
100     if (uiLock) {
101       startButton_->enable();
102       startButton_->setText("Again!");
103 
104       app->triggerUpdate();
105 
106       // Disable server push
107       app->enableUpdates(false);
108     } else
109       return;
110   }
111 };
112 
createApplication(const WEnvironment & env)113 std::unique_ptr<WApplication> createApplication(const WEnvironment& env)
114 {
115   std::unique_ptr<WApplication> app = std::make_unique<WApplication>(env);
116   app->setCssTheme("polished");
117   app->root()->addWidget(std::make_unique<BigWorkWidget>());
118 
119   return app;
120 }
121 
main(int argc,char ** argv)122 int main(int argc, char **argv)
123 {
124   return WRun(argc, argv, &createApplication);
125 }
126