1 /*
2 * The Sciter Engine of Terra Informatica Software, Inc.
3 * http://sciter.com
4 *
5 * The code and information provided "as-is" without
6 * warranty of any kind, either expressed or implied.
7 *
8 * (C) 2003-2015, Terra Informatica Software, Inc.
9 */
10
11 /*
12 * Asynchronous GUI Task Queue.
13 * Use these primitives when you need to run code in GUI thread.
14 */
15
16
17 #if !defined(__SCITER_THREADS_H__)
18 #define __SCITER_THREADS_H__
19
20 #if defined(WINDOWS)
21 #include <specstrings.h>
22 #include <windows.h>
23 #include <stdio.h>
24 #include <assert.h>
25 #include <functional>
26
27 #ifndef THIS_HINSTANCE
28 EXTERN_C IMAGE_DOS_HEADER __ImageBase;
29 #define THIS_HINSTANCE ((HINSTANCE)&__ImageBase)
30 #endif
31
32
34
35 namespace sync {
36
37 class mutex
38 {
39 CRITICAL_SECTION cs;
40 public:
41 void lock() { EnterCriticalSection(&cs); }
42 void unlock() { LeaveCriticalSection(&cs); }
43 mutex() { InitializeCriticalSection(&cs); }
44 ~
mutex() { DeleteCriticalSection(&cs); }
45 };
46
47 class critical_section {
49 public:
50 critical_section(
mutex& guard) : m(guard) { m.lock(); }
51 ~critical_section() { m.unlock(); }
52 };
53
55 {
56 HANDLE h;
57 event() { h = CreateEvent(NULL, FALSE, FALSE, NULL); }
58 ~
event() { CloseHandle(h); }
59 void signal() { SetEvent(h); }
60 bool wait(unsigned int ms = INFINITE) { return WaitForSingleObject(h, ms) == WAIT_OBJECT_0; }
61 private:
63 event& operator=(
const event& );
64 };
65
66 inline void yield() { Sleep(0); }
67 inline void sleep(
unsigned ms) { Sleep(ms); }
68
69 class gui_thread_ctx
70 {
71 HHOOK _hook;
72
73 typedef std::function<void(void)> gui_block;
74
75 static DWORD thread_id()
76 {
77 static DWORD _thread_id = ::GetCurrentThreadId();
78 return _thread_id;
79 }
80 static UINT message()
81 {
82 static UINT _message = ::RegisterWindowMessage( TEXT("GUI-THREAD-EXEC_RQ"));
83 return _message;
84 }
85
86 void install_hook()
87 {
88 message(); // force message to be registered
89 // setup the WH_GETMESSAGE hook.
90 // Using hooks here allows this mechanism to work even under modal dialogs.
91 _hook = ::SetWindowsHookEx(WH_GETMESSAGE,&exec_hook,THIS_HINSTANCE, thread_id());
92 }
93 void release_hook()
94 {
95 if(_hook) ::UnhookWindowsHookEx(_hook);
96 }
97
98 // message hook to handle WM_EXEC in GUI thread
99 static LRESULT CALLBACK exec_hook(int code, WPARAM wParam, LPARAM lParam )
100 {
101 MSG* pmsg = reinterpret_cast<MSG*>(lParam);
102 if(wParam == PM_REMOVE && pmsg->message == message())
103 {
105 gui_block* pf = reinterpret_cast<gui_block*>(pmsg->lParam);
106 (*pf)(); // execute the block in this GUI thread
107 pe->
signal();
// signal that we've done with it 108 // this will resume execution of worker thread.
109 }
110 return CallNextHookEx(0,code, wParam,lParam);
111 }
112
113 public:
114 gui_thread_ctx() { install_hook(); }
115 ~gui_thread_ctx() { release_hook(); }
116
117 // this function is called from worker threads to
118 // execute the gui_block in GUI thread
119 static void exec( gui_block code )
120 {
121 event evt;
122 PostThreadMessage(thread_id(), message(), WPARAM(&evt),LPARAM(&code));
123 evt.wait(); // suspend worker thread until GUI will execute the block.
124 }
125
126 };
127
128 #define GUI_CODE_START sciter::sync::gui_thread_ctx::exec([&]() {
129 #define GUI_CODE_END });
130
131 }
132
133 template<typename F, typename P>
134 class thread_ctx
135 {
136 F _f;
137 P _p;
138 static DWORD WINAPI ThreadProc(LPVOID lpData)
139 {
140 thread_ctx* self = reinterpret_cast<thread_ctx*>(lpData);
141 try {
142 self->_f(self->_p);
143 }
144 catch(...) {
145 assert(false);
146 }
147 delete self;
148 return 0;
149 }
150 thread_ctx(const F& f, const P& p): _f(f),_p(p) {
151
152 DWORD dwThreadID;
153 HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, this, 0, &dwThreadID);
154 assert(hThread != NULL);
155 ::CloseHandle(hThread);
156 }
157 public:
158 static void start( const F& f, const P& p )
159 {
160 new thread_ctx<F,P>(f,p);
161 }
162 };
163
164 template<typename F, typename P>
165 inline void thread( F f, P p )
166 {
167 thread_ctx<F,P>::start(f,p);
168 }
169
170 }
171
172 #else
173
174 #include <mutex>
175 #include <condition_variable>
176 #include <thread>
177 #include <chrono>
178
180
182
183 class mutex :
public std::recursive_mutex
184 {
186 typedef std::recursive_mutex super;
189 public:
191 //void lock() { super::lock(); }
192 //void unlock() { super::unlock(); }
193 };
194
196 {
197 typedef std::lock_guard<mutex> super;
200
201 public:
204 };
205
207 {
208 //typedef std::condition_variable super;
210 event& operator=(
const event&) =
delete;
211
212 std::condition_variable_any _var;
213 std::mutex _mtx;
214 public:
217 _var.notify_one();
218 }
220 std::unique_lock<mutex> lock(m);
221 _var.wait(lock);
222 }
223 void wait(
unsigned int ms =
unsigned(-1)) {
224 std::unique_lock<std::mutex> lock(_mtx);
225 if( ms == unsigned(-1) )
226 _var.wait(lock);
227 else
228 _var.wait_for(lock, std::chrono::milliseconds(ms));
229 }
230
231 };
232
233 inline void sleep(uint ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }
235
236 }
237 template<typename F, typename P>
239 {
241 }
242
243 }
244
245 #endif
246
247 #endif // __SCITER_THREADS_H__
critical_section(mutex &m)
void wait(unsigned int ms=unsigned(-1))