| 1 | /**************************************************************************** | 
|---|
| 2 | ** $Id: qthread_pm.cpp 8 2005-11-16 19:36:46Z dmik $ | 
|---|
| 3 | ** | 
|---|
| 4 | ** QThread class for OS/2 | 
|---|
| 5 | ** | 
|---|
| 6 | ** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved. | 
|---|
| 7 | ** Copyright (C) 2004 Norman ASA.  Initial OS/2 Port. | 
|---|
| 8 | ** Copyright (C) 2005 netlabs.org.  Further OS/2 Development. | 
|---|
| 9 | ** | 
|---|
| 10 | ** This file is part of the kernel module of the Qt GUI Toolkit. | 
|---|
| 11 | ** | 
|---|
| 12 | ** This file may be distributed under the terms of the Q Public License | 
|---|
| 13 | ** as defined by Trolltech AS of Norway and appearing in the file | 
|---|
| 14 | ** LICENSE.QPL included in the packaging of this file. | 
|---|
| 15 | ** | 
|---|
| 16 | ** This file may be distributed and/or modified under the terms of the | 
|---|
| 17 | ** GNU General Public License version 2 as published by the Free Software | 
|---|
| 18 | ** Foundation and appearing in the file LICENSE.GPL included in the | 
|---|
| 19 | ** packaging of this file. | 
|---|
| 20 | ** | 
|---|
| 21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition | 
|---|
| 22 | ** licenses may use this file in accordance with the Qt Commercial License | 
|---|
| 23 | ** Agreement provided with the Software. | 
|---|
| 24 | ** | 
|---|
| 25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | 
|---|
| 26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | 
|---|
| 27 | ** | 
|---|
| 28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for | 
|---|
| 29 | **   information about Qt Commercial License Agreements. | 
|---|
| 30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information. | 
|---|
| 31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | 
|---|
| 32 | ** | 
|---|
| 33 | ** Contact info@trolltech.com if any conditions of this licensing are | 
|---|
| 34 | ** not clear to you. | 
|---|
| 35 | ** | 
|---|
| 36 | **********************************************************************/ | 
|---|
| 37 |  | 
|---|
| 38 | #if defined(QT_THREAD_SUPPORT) | 
|---|
| 39 |  | 
|---|
| 40 | #include "qt_os2.h" | 
|---|
| 41 |  | 
|---|
| 42 | #include "qthread.h" | 
|---|
| 43 | #include <private/qthreadinstance_p.h> | 
|---|
| 44 | #include <private/qmutexpool_p.h> | 
|---|
| 45 | #include "qthreadstorage.h" | 
|---|
| 46 |  | 
|---|
| 47 | #include <private/qcriticalsection_p.h> | 
|---|
| 48 |  | 
|---|
| 49 | #include <stdlib.h> | 
|---|
| 50 |  | 
|---|
| 51 | static QThreadInstance main_instance = { | 
|---|
| 52 | 0, { 0, &main_instance }, 0, 0, 1, 0, 0, 0 | 
|---|
| 53 | }; | 
|---|
| 54 |  | 
|---|
| 55 |  | 
|---|
| 56 | static QMutexPool *qt_thread_mutexpool = 0; | 
|---|
| 57 |  | 
|---|
| 58 | #define LocalStorage (*_threadstore()) | 
|---|
| 59 |  | 
|---|
| 60 | // since OS/2 doesn't support specifying a maximum time to wait for a thread | 
|---|
| 61 | // to end other than infininty we use a loop containing of wait intervals | 
|---|
| 62 | // until the thread has ended or the maximum time specified by the caller | 
|---|
| 63 | // has expired. | 
|---|
| 64 | static const ULONG THREAD_WAIT_INTERVAL = 1000; | 
|---|
| 65 |  | 
|---|
| 66 | /************************************************************************** | 
|---|
| 67 | ** QThreadInstance | 
|---|
| 68 | *************************************************************************/ | 
|---|
| 69 |  | 
|---|
| 70 | QThreadInstance *QThreadInstance::current() | 
|---|
| 71 | { | 
|---|
| 72 | QThreadInstance *ret = (QThreadInstance *) LocalStorage; | 
|---|
| 73 | if ( ! ret ) return &main_instance; | 
|---|
| 74 | return ret; | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | void QThreadInstance::init(unsigned int stackSize) | 
|---|
| 78 | { | 
|---|
| 79 | stacksize = stackSize; | 
|---|
| 80 | args[0] = args[1] = 0; | 
|---|
| 81 | thread_storage = 0; | 
|---|
| 82 | finished = FALSE; | 
|---|
| 83 | running = FALSE; | 
|---|
| 84 | orphan = FALSE; | 
|---|
| 85 |  | 
|---|
| 86 | thread_id = 0; | 
|---|
| 87 | waiters = 0; | 
|---|
| 88 |  | 
|---|
| 89 | // threads have not been initialized yet, do it now | 
|---|
| 90 | if ( ! qt_thread_mutexpool ) QThread::initialize(); | 
|---|
| 91 | } | 
|---|
| 92 |  | 
|---|
| 93 | void QThreadInstance::deinit() | 
|---|
| 94 | { | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | void _System QThreadInstance::start( void * _arg ) | 
|---|
| 98 | { | 
|---|
| 99 | void **arg = (void **) _arg; | 
|---|
| 100 |  | 
|---|
| 101 | LocalStorage = arg [1]; | 
|---|
| 102 |  | 
|---|
| 103 | ( (QThread *) arg[0] )->run(); | 
|---|
| 104 |  | 
|---|
| 105 | finish( (QThreadInstance *) arg[1] ); | 
|---|
| 106 |  | 
|---|
| 107 | return; | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | void QThreadInstance::finish( QThreadInstance *d ) | 
|---|
| 111 | { | 
|---|
| 112 | if ( ! d ) { | 
|---|
| 113 | #ifdef QT_CHECK_STATE | 
|---|
| 114 | qWarning( "QThread: internal error: zero data for running thread." ); | 
|---|
| 115 | #endif // QT_CHECK_STATE | 
|---|
| 116 | return; | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | QMutexLocker locker( d->mutex() ); | 
|---|
| 120 | d->running = FALSE; | 
|---|
| 121 | d->finished = TRUE; | 
|---|
| 122 | d->args[0] = d->args[1] = 0; | 
|---|
| 123 |  | 
|---|
| 124 | QThreadStorageData::finish( d->thread_storage ); | 
|---|
| 125 | d->thread_storage = 0; | 
|---|
| 126 | d->thread_id = 0; | 
|---|
| 127 |  | 
|---|
| 128 | if ( d->orphan ) { | 
|---|
| 129 | d->deinit(); | 
|---|
| 130 | delete d; | 
|---|
| 131 | } | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | QMutex *QThreadInstance::mutex() const | 
|---|
| 135 | { | 
|---|
| 136 | return qt_thread_mutexpool ? qt_thread_mutexpool->get( (void *) this ) : 0; | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | void QThreadInstance::terminate() | 
|---|
| 140 | { | 
|---|
| 141 | PTIB ptib; | 
|---|
| 142 | DosGetInfoBlocks( &ptib, NULL ); | 
|---|
| 143 | if ( ptib->tib_ptib2->tib2_ultid == thread_id ) { | 
|---|
| 144 | #ifdef QT_CHECK_STATE | 
|---|
| 145 | qWarning( "QThread: internal error: thread cannot terminate itself." ); | 
|---|
| 146 | #endif // QT_CHECK_STATE | 
|---|
| 147 | return; | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | /* | 
|---|
| 151 | delete the thread storage *after* the thread has been | 
|---|
| 152 | terminated.  we could end up deleting the thread's data while it | 
|---|
| 153 | is accessing it (but before the thread stops), which results in | 
|---|
| 154 | a crash. | 
|---|
| 155 | */ | 
|---|
| 156 | void **storage = thread_storage; | 
|---|
| 157 | thread_storage = 0; | 
|---|
| 158 |  | 
|---|
| 159 | running = FALSE; | 
|---|
| 160 | finished = TRUE; | 
|---|
| 161 | args[0] = args[1] = 0; | 
|---|
| 162 |  | 
|---|
| 163 | if ( orphan ) { | 
|---|
| 164 | deinit(); | 
|---|
| 165 | delete this; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | DosKillThread( thread_id ); | 
|---|
| 169 | thread_id = 0; | 
|---|
| 170 |  | 
|---|
| 171 | QThreadStorageData::finish( storage ); | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 |  | 
|---|
| 175 | /************************************************************************** | 
|---|
| 176 | ** QThread | 
|---|
| 177 | *************************************************************************/ | 
|---|
| 178 |  | 
|---|
| 179 | Qt::HANDLE QThread::currentThread() | 
|---|
| 180 | { | 
|---|
| 181 | PTIB ptib; | 
|---|
| 182 | DosGetInfoBlocks( &ptib, NULL ); | 
|---|
| 183 | return (Qt::HANDLE) ptib->tib_ptib2->tib2_ultid; | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 | void QThread::initialize() | 
|---|
| 187 | { | 
|---|
| 188 | if ( ! qt_global_mutexpool ) | 
|---|
| 189 | qt_global_mutexpool = new QMutexPool( TRUE, 73 ); | 
|---|
| 190 | if ( ! qt_thread_mutexpool ) | 
|---|
| 191 | qt_thread_mutexpool = new QMutexPool( FALSE, 127 ); | 
|---|
| 192 | } | 
|---|
| 193 |  | 
|---|
| 194 | void QThread::cleanup() | 
|---|
| 195 | { | 
|---|
| 196 | delete qt_global_mutexpool; | 
|---|
| 197 | delete qt_thread_mutexpool; | 
|---|
| 198 | qt_global_mutexpool = 0; | 
|---|
| 199 | qt_thread_mutexpool = 0; | 
|---|
| 200 |  | 
|---|
| 201 | QThreadInstance::finish(&main_instance); | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | void QThread::sleep( unsigned long secs ) | 
|---|
| 205 | { | 
|---|
| 206 | DosSleep( secs * 1000 ); | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | void QThread::msleep( unsigned long msecs ) | 
|---|
| 210 | { | 
|---|
| 211 | DosSleep( msecs ); | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | void QThread::usleep( unsigned long usecs ) | 
|---|
| 215 | { | 
|---|
| 216 | DosSleep( ( usecs / 1000 ) + 1 ); | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 |  | 
|---|
| 220 | void QThread::start(Priority priority) | 
|---|
| 221 | { | 
|---|
| 222 | QMutexLocker locker( d->mutex() ); | 
|---|
| 223 |  | 
|---|
| 224 | if ( d->running && !d->finished ) { | 
|---|
| 225 | #ifdef QT_CHECK_RANGE | 
|---|
| 226 | qWarning( "Thread is already running" ); | 
|---|
| 227 | #endif | 
|---|
| 228 | wait(); | 
|---|
| 229 | } | 
|---|
| 230 |  | 
|---|
| 231 | d->running = TRUE; | 
|---|
| 232 | d->finished = FALSE; | 
|---|
| 233 | d->args[0] = this; | 
|---|
| 234 | d->args[1] = d; | 
|---|
| 235 |  | 
|---|
| 236 | /* | 
|---|
| 237 | NOTE: we create the thread in the suspended state, set the | 
|---|
| 238 | priority and then resume the thread. | 
|---|
| 239 |  | 
|---|
| 240 | since threads are created with normal priority by default, we | 
|---|
| 241 | could get into a case where a thread (with priority less than | 
|---|
| 242 | NormalPriority) tries to create a new thread (also with priority | 
|---|
| 243 | less than NormalPriority), but the newly created thread preempts | 
|---|
| 244 | its 'parent' and runs at normal priority. | 
|---|
| 245 | */ | 
|---|
| 246 | int stacksize = d->stacksize ? d->stacksize : 32768; | 
|---|
| 247 | int tid = _beginthread( QThreadInstance::start, NULL, stacksize, d->args ); | 
|---|
| 248 | d->thread_id = tid != -1 ? (TID) tid : 0; | 
|---|
| 249 |  | 
|---|
| 250 | if ( !d->thread_id ) { | 
|---|
| 251 | #ifdef QT_CHECK_STATE | 
|---|
| 252 | qWarning( "Failed to create thread\n\tError code %d - %s", errno, strerror( errno ) ); | 
|---|
| 253 | #endif | 
|---|
| 254 | d->running = FALSE; | 
|---|
| 255 | d->finished = TRUE; | 
|---|
| 256 | d->args[0] = d->args[1] = 0; | 
|---|
| 257 | return; | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | ULONG prioClass = 0; | 
|---|
| 261 | LONG prioDelta = 0; | 
|---|
| 262 | switch (priority) { | 
|---|
| 263 | case IdlePriority: | 
|---|
| 264 | prioClass = PRTYC_IDLETIME; | 
|---|
| 265 | prioDelta = PRTYD_MINIMUM; | 
|---|
| 266 | break; | 
|---|
| 267 |  | 
|---|
| 268 | case LowestPriority: | 
|---|
| 269 | prioClass = PRTYC_IDLETIME; | 
|---|
| 270 | break; | 
|---|
| 271 |  | 
|---|
| 272 | case LowPriority: | 
|---|
| 273 | prioClass = PRTYC_IDLETIME; | 
|---|
| 274 | prioDelta = PRTYD_MAXIMUM; | 
|---|
| 275 | break; | 
|---|
| 276 |  | 
|---|
| 277 | case NormalPriority: | 
|---|
| 278 | prioClass = PRTYC_REGULAR; | 
|---|
| 279 | break; | 
|---|
| 280 |  | 
|---|
| 281 | case HighPriority: | 
|---|
| 282 | prioClass = PRTYC_REGULAR; | 
|---|
| 283 | prioDelta = PRTYD_MAXIMUM; | 
|---|
| 284 | break; | 
|---|
| 285 |  | 
|---|
| 286 | case HighestPriority: | 
|---|
| 287 | prioClass = PRTYC_TIMECRITICAL; | 
|---|
| 288 | break; | 
|---|
| 289 |  | 
|---|
| 290 | case TimeCriticalPriority: | 
|---|
| 291 | prioClass = PRTYC_TIMECRITICAL; | 
|---|
| 292 | prioDelta = PRTYD_MAXIMUM; | 
|---|
| 293 | break; | 
|---|
| 294 |  | 
|---|
| 295 | case InheritPriority: | 
|---|
| 296 | default: | 
|---|
| 297 | PTIB ptib; | 
|---|
| 298 | DosGetInfoBlocks( &ptib, NULL ); | 
|---|
| 299 | prioClass = (ptib->tib_ptib2->tib2_ulpri >> 8) & 0xFF; | 
|---|
| 300 | prioDelta = (ptib->tib_ptib2->tib2_ulpri) & 0xFF; | 
|---|
| 301 | break; | 
|---|
| 302 | } | 
|---|
| 303 |  | 
|---|
| 304 | APIRET rc = DosSetPriority( PRTYS_THREAD, prioClass, prioDelta, d->thread_id ); | 
|---|
| 305 | if ( rc ) { | 
|---|
| 306 | #ifdef QT_CHECK_STATE | 
|---|
| 307 | qSystemWarning( "Failed to set thread priority", rc ); | 
|---|
| 308 | #endif | 
|---|
| 309 | } | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | void QThread::start() | 
|---|
| 313 | { | 
|---|
| 314 | start(InheritPriority); | 
|---|
| 315 | } | 
|---|
| 316 |  | 
|---|
| 317 | bool QThread::wait( unsigned long time ) | 
|---|
| 318 | { | 
|---|
| 319 | PTIB ptib; | 
|---|
| 320 | DosGetInfoBlocks( &ptib, NULL ); | 
|---|
| 321 |  | 
|---|
| 322 | QMutexLocker locker(d->mutex()); | 
|---|
| 323 |  | 
|---|
| 324 | if ( d->thread_id == ptib->tib_ptib2->tib2_ultid ) { | 
|---|
| 325 | #ifdef QT_CHECK_RANGE | 
|---|
| 326 | qWarning( "Thread tried to wait on itself" ); | 
|---|
| 327 | #endif | 
|---|
| 328 | return FALSE; | 
|---|
| 329 | } | 
|---|
| 330 | if ( d->finished || !d->running ) | 
|---|
| 331 | return TRUE; | 
|---|
| 332 |  | 
|---|
| 333 | ++d->waiters; | 
|---|
| 334 | locker.mutex()->unlock(); | 
|---|
| 335 |  | 
|---|
| 336 | bool ret = TRUE; | 
|---|
| 337 | TID tid = d->thread_id; | 
|---|
| 338 | APIRET rc = 0; | 
|---|
| 339 | if (time == ULONG_MAX ) { | 
|---|
| 340 | rc = DosWaitThread( &tid, DCWW_WAIT ); | 
|---|
| 341 | } else { | 
|---|
| 342 | ULONG sleep; | 
|---|
| 343 | do { | 
|---|
| 344 | sleep = time > THREAD_WAIT_INTERVAL ? THREAD_WAIT_INTERVAL : time; | 
|---|
| 345 | DosSleep( sleep ); | 
|---|
| 346 | rc = DosWaitThread( &tid, DCWW_NOWAIT ); | 
|---|
| 347 | if ( rc != ERROR_THREAD_NOT_TERMINATED ) | 
|---|
| 348 | break; | 
|---|
| 349 | if ( d->finished || !d->running ) { | 
|---|
| 350 | rc = 0; | 
|---|
| 351 | break; | 
|---|
| 352 | } | 
|---|
| 353 | time -= sleep; | 
|---|
| 354 | } while ( time ); | 
|---|
| 355 | } | 
|---|
| 356 | if ( rc && rc != ERROR_INVALID_THREADID ) { | 
|---|
| 357 | #ifdef QT_CHECK_RANGE | 
|---|
| 358 | qSystemWarning( "Thread wait failure", rc ); | 
|---|
| 359 | #endif | 
|---|
| 360 | ret = FALSE; | 
|---|
| 361 | } | 
|---|
| 362 |  | 
|---|
| 363 | locker.mutex()->lock(); | 
|---|
| 364 | --d->waiters; | 
|---|
| 365 |  | 
|---|
| 366 | return ret; | 
|---|
| 367 | } | 
|---|
| 368 |  | 
|---|
| 369 | void QThread::exit() | 
|---|
| 370 | { | 
|---|
| 371 | QThreadInstance *d = QThreadInstance::current(); | 
|---|
| 372 |  | 
|---|
| 373 | if ( ! d ) { | 
|---|
| 374 | #ifdef QT_CHECK_STATE | 
|---|
| 375 | qWarning( "QThread::exit() called without a QThread instance." ); | 
|---|
| 376 | #endif // QT_CHECK_STATE | 
|---|
| 377 |  | 
|---|
| 378 | _endthread(); | 
|---|
| 379 | return; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | QThreadInstance::finish(d); | 
|---|
| 383 | _endthread(); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 |  | 
|---|
| 387 | #endif // QT_THREAD_SUPPORT | 
|---|