1 /**
2 * @file concurrency.cpp
3 * Concurrency: threads, mutexes, semaphores.
4 *
5 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
6 * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
7 * @authors Copyright © 2006 Jamie Jones <jamie_jones_au@yahoo.com.au>
8 *
9 * @par License
10 * GPL: http://www.gnu.org/licenses/gpl.html
11 *
12 * <small>This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. This program is distributed in the hope that it
16 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
18 * Public License for more details. You should have received a copy of the GNU
19 * General Public License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA</small>
22 */
23
24 #include "de/concurrency.h"
25 #include <QMutex>
26 #include <QCoreApplication>
27 #include <QDebug>
28 #include <de/App>
29 #include <de/Time>
30 #include <de/Log>
31 #include <de/Garbage>
32 #include <assert.h>
33
CallbackThread(systhreadfunc_t func,void * param)34 CallbackThread::CallbackThread(systhreadfunc_t func, void *param)
35 : _callback(func), _parm(param), _returnValue(0),
36 _exitStatus(DENG_THREAD_STOPPED_NORMALLY),
37 _terminationFunc(0)
38 {
39 //qDebug() << "CallbackThread:" << this << "created.";
40
41 // Only used if the thread needs to be shut down forcibly.
42 setTerminationEnabled(true);
43
44 // Cleanup at app exit time for threads whose exit value hasn't been checked.
45 connect(qApp, SIGNAL(destroyed()), this, SLOT(deleteNow()));
46 }
47
~CallbackThread()48 CallbackThread::~CallbackThread()
49 {
50 if (isRunning())
51 {
52 //qDebug() << "CallbackThread:" << this << "forcibly stopping, deleting.";
53 terminate();
54 wait(1000);
55 }
56 else
57 {
58 //qDebug() << "CallbackThread:" << this << "deleted.";
59 }
60 }
61
deleteNow()62 void CallbackThread::deleteNow()
63 {
64 delete this;
65 }
66
run()67 void CallbackThread::run()
68 {
69 _exitStatus = DENG_THREAD_STOPPED_WITH_FORCE;
70
71 try
72 {
73 if (_callback)
74 {
75 _returnValue = _callback(_parm);
76 }
77 _exitStatus = DENG_THREAD_STOPPED_NORMALLY;
78 }
79 catch (std::exception const &error)
80 {
81 LOG_AS("CallbackThread");
82 LOG_ERROR(QString("Uncaught exception: ") + error.what());
83 _returnValue = -1;
84 _exitStatus = DENG_THREAD_STOPPED_WITH_EXCEPTION;
85 }
86
87 if (_terminationFunc)
88 {
89 _terminationFunc(_exitStatus);
90 }
91
92 Garbage_ClearForThread();
93
94 // No more log output from this thread.
95 de::Log::disposeThreadLog();
96 }
97
exitValue() const98 int CallbackThread::exitValue() const
99 {
100 return _returnValue;
101 }
102
exitStatus() const103 systhreadexitstatus_t CallbackThread::exitStatus() const
104 {
105 return _exitStatus;
106 }
107
setTerminationFunc(void (* func)(systhreadexitstatus_t))108 void CallbackThread::setTerminationFunc(void (*func)(systhreadexitstatus_t))
109 {
110 _terminationFunc = func;
111 }
112
113 /*void Sys_MarkAsMainThread(void)
114 {
115 // This is the main thread.
116 mainThreadId = Sys_CurrentThreadId();
117 }*/
118
Sys_InMainThread(void)119 dd_bool Sys_InMainThread(void)
120 {
121 return de::App::inMainThread();
122 }
123
Thread_Sleep(int milliseconds)124 void Thread_Sleep(int milliseconds)
125 {
126 de::TimeSpan::fromMilliSeconds(milliseconds).sleep();
127 }
128
Sys_StartThread(systhreadfunc_t startpos,void * parm,void (* terminationFunc)(systhreadexitstatus_t))129 thread_t Sys_StartThread(systhreadfunc_t startpos, void *parm, void (*terminationFunc)(systhreadexitstatus_t))
130 {
131 CallbackThread *t = new CallbackThread(startpos, parm);
132 t->setTerminationFunc(terminationFunc);
133 t->start();
134 return t;
135 }
136
Sys_StartThread(int (* startpos)(void *),void * parm,void (* terminationFunc)(systhreadexitstatus_t))137 thread_t Sys_StartThread(int (*startpos)(void *), void *parm, void (*terminationFunc)(systhreadexitstatus_t))
138 {
139 return Sys_StartThread(systhreadfunc_t(startpos), parm, terminationFunc);
140 }
141
Thread_KillAbnormally(thread_t handle)142 void Thread_KillAbnormally(thread_t handle)
143 {
144 QThread *t = reinterpret_cast<QThread *>(handle);
145 if (!handle)
146 {
147 t = QThread::currentThread();
148 }
149 assert(t);
150 t->terminate();
151 }
152
Thread_SetCallback(thread_t thread,void (* terminationFunc)(systhreadexitstatus_t))153 void Thread_SetCallback(thread_t thread, void (*terminationFunc)(systhreadexitstatus_t))
154 {
155 CallbackThread *t = reinterpret_cast<CallbackThread *>(thread);
156 DENG_ASSERT(t);
157 if (!t) return;
158
159 t->setTerminationFunc(terminationFunc);
160 }
161
Sys_WaitThread(thread_t handle,int timeoutMs,systhreadexitstatus_t * exitStatus)162 int Sys_WaitThread(thread_t handle, int timeoutMs, systhreadexitstatus_t *exitStatus)
163 {
164 if (!handle)
165 {
166 if (exitStatus) *exitStatus = DENG_THREAD_STOPPED_NORMALLY;
167 return 0;
168 }
169
170 CallbackThread *t = reinterpret_cast<CallbackThread *>(handle);
171 assert(static_cast<QThread *>(t) != QThread::currentThread());
172 t->wait(timeoutMs);
173 if (!t->isFinished())
174 {
175 LOG_WARNING("Thread did not stop in time, forcibly killing it.");
176 if (exitStatus) *exitStatus = DENG_THREAD_STOPPED_WITH_FORCE;
177 }
178 else
179 {
180 if (exitStatus) *exitStatus = t->exitStatus();
181 }
182 t->deleteLater(); // get rid of it
183 return t->exitValue();
184 }
185
Sys_ThreadId(thread_t handle)186 uint32_t Sys_ThreadId(thread_t handle)
187 {
188 QThread *t = reinterpret_cast<QThread *>(handle);
189 if (!t) t = QThread::currentThread();
190 return uint32_t(PTR2INT(t));
191 }
192
Sys_CurrentThreadId(void)193 uint32_t Sys_CurrentThreadId(void)
194 {
195 return Sys_ThreadId(NULL /*this thread*/);
196 }
197
198 /// @todo remove the name parameter
Sys_CreateMutex(const char *)199 mutex_t Sys_CreateMutex(const char *)
200 {
201 return new QMutex(QMutex::Recursive);
202 }
203
Sys_DestroyMutex(mutex_t handle)204 void Sys_DestroyMutex(mutex_t handle)
205 {
206 if (handle)
207 {
208 delete reinterpret_cast<QMutex *>(handle);
209 }
210 }
211
Sys_Lock(mutex_t handle)212 void Sys_Lock(mutex_t handle)
213 {
214 QMutex *m = reinterpret_cast<QMutex *>(handle);
215 assert(m != 0);
216 if (m)
217 {
218 m->lock();
219 }
220 }
221
Sys_Unlock(mutex_t handle)222 void Sys_Unlock(mutex_t handle)
223 {
224 QMutex *m = reinterpret_cast<QMutex *>(handle);
225 assert(m != 0);
226 if (m)
227 {
228 m->unlock();
229 }
230 }
231