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