1
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh (H&G2JCtL)
20
* Continued development: Barry Leslie
25
* A independently running thread.
39
#include <sys/signal.h>
46
#include "CSException.h"
48
#include "CSStrUtil.h"
51
#define PBMS_THREAD_SIG SIGUSR1
53
* ---------------------------------------------------------------
60
static void td_catch_signal(int sig)
64
if ((self = CSThread::getSelf())) {
66
/* The main thread will pass on a signal to all threads: */
67
if (self->myThreadList)
68
self->myThreadList->signalAllThreads(sig);
69
self->setSignalPending(sig);
75
static void td_throw_signal(int sig)
79
if ((self = CSThread::getSelf())) {
81
/* The main thread will pass on a signal to all threads: */
82
if (self->myThreadList)
83
self->myThreadList->signalAllThreads(sig);
85
self->setSignalPending(sig);
90
static bool td_setup_signals(CSThread *thread)
95
struct sigaction action;
97
sigemptyset(&action.sa_mask);
100
action.sa_handler = td_catch_signal;
102
if (sigaction(PBMS_THREAD_SIG, &action, NULL) == -1)
105
action.sa_handler = td_throw_signal;
112
thread->myException.initOSError(CS_CONTEXT, errno);
113
thread->myException.setStackTrace(thread);
116
CSException::throwOSError(CS_CONTEXT, errno);
124
* ---------------------------------------------------------------
128
void CSThreadList::signalAllThreads(int sig)
134
ptr = (CSThread *) getBack();
138
ptr = (CSThread *) ptr->getNextLink();
145
void CSThreadList::quitAllThreads()
152
ptr = (CSThread *) getBack();
155
ptr->myMustQuit = true;
156
ptr = (CSThread *) ptr->getNextLink();
163
void CSThreadList::stopAllThreads()
169
/* Get a thread that is not self! */
171
if ((thread = (CSThread *) getBack())) {
175
thread = (CSThread *) thread->getNextLink();
193
* ---------------------------------------------------------------
197
void CSThread::addToList()
201
ASSERT(self == this);
203
myThreadList->addFront(self);
205
unlock_(myThreadList);
212
void CSThread::removeFromList()
214
if (myThreadList && isRunning) {
215
CSThread *myself = this; // pop_() wants to take a reference to its parameter.
217
/* Retain the thread in order to ensure
218
* that after it is removed from the list,
219
* that it is not freed! This would make the
220
* unlock_() call invalid, because it requires
225
myThreadList->remove(RETAIN(myself));
226
unlock_(myThreadList);
233
void *CSThread::dispatch(void *arg)
236
void *return_data = NULL;
239
/* Get a reference to myself: */
240
self = reinterpret_cast<CSThread*>(arg);
243
/* Store my thread in the thread key: */
244
if ((err = pthread_setspecific(CSThread::sThreadKey, self))) {
245
CSException::logOSError(self, CS_CONTEXT, err);
250
* Make sure the thread is not freed while we
256
td_setup_signals(NULL);
258
/* Add the thread to the list: */
261
// Run the task from the correct context
262
return_data = self->run();
265
self->logException();
270
* Removing from the thread list will also release the thread.
272
self->removeFromList();
282
static void *dispatch_wrapper(void *arg)
284
return CSThread::dispatch(arg);
289
void *CSThread::run()
296
void CSThread::start(bool detached)
300
err = pthread_create(&iThread, NULL, dispatch_wrapper, (void *) this);
302
CSException::throwOSError(CS_CONTEXT, err);
304
/* Check if the thread is still alive,
305
* so we don't hang forever.
307
if (pthread_kill(iThread, 0))
312
isDetached = detached;
314
pthread_detach(iThread);
317
void CSThread::stop()
323
void *CSThread::join()
325
void *return_data = NULL;
330
while (isRunning && !pthread_kill(iThread, 0))
332
} else if ((err = pthread_join(iThread, &return_data))) {
333
CSException::throwOSError(CS_CONTEXT, err);
336
return_(return_data);
339
void CSThread::setSignalPending(unsigned int sig)
342
/* The terminate signal takes priority: */
343
signalPending = SIGTERM;
344
else if (!signalPending)
345
/* Otherwise, first signal wins... */
349
void CSThread::signal(unsigned int sig)
351
#ifndef OS_WINDOWS // Currently you cannot signal threads on windows.
354
setSignalPending(sig);
355
if ((err = pthread_kill(iThread, PBMS_THREAD_SIG)))
357
/* Ignore the error if the process does not exist! */
358
if (err != ESRCH) /* No such process */
359
CSException::throwOSError(CS_CONTEXT, err);
364
void CSThread::throwSignal()
368
if ((sig = signalPending) && !ignoreSignals) {
370
CSException::throwSignal(CS_CONTEXT, sig);
374
bool CSThread::isMain()
380
* -----------------------------------------------------------------------
381
* THROWING EXCEPTIONS
385
* When an exception is .
388
void CSThread::releaseObjects(CSReleasePtr top)
392
while (relTop > top) {
393
/* Remove and release or unlock the object on the top of the stack: */
395
switch(relTop->r_type) {
396
case CS_RELEASE_OBJECT:
397
if ((obj = relTop->x.r_object))
400
case CS_RELEASE_MUTEX:
401
if (relTop->x.r_mutex)
402
relTop->x.r_mutex->unlock();
404
case CS_RELEASE_POOLED:
405
if (relTop->x.r_pooled)
406
relTop->x.r_pooled->returnToPool();
410
cs_free(relTop->x.r_mem);
412
case CS_RELEASE_OBJECT_PTR:
413
if ((relTop->x.r_objectPtr) && (obj = *(relTop->x.r_objectPtr)))
420
/* Throw an already registered error: */
421
void CSThread::throwException()
423
/* Record the stack trace: */
424
if (this->jumpDepth > 0 && this->jumpDepth <= CS_JUMP_STACK_SIZE) {
426
* As recommended by Barry:
427
* release the objects before we jump!
428
* This has the advantage that the stack context is still
429
* valid when the resources are released.
431
releaseObjects(this->jumpEnv[this->jumpDepth-1].jb_res_top);
433
/* Then do the longjmp: */
434
longjmp(this->jumpEnv[this->jumpDepth-1].jb_buffer, 1);
438
void CSThread::logStack(int depth, const char *msg)
440
char buffer[CS_EXC_CONTEXT_SIZE +1];
442
CSL.log(this, CSLog::Trace, msg);
444
for (int i= callTop-1; i>=0 && depth; i--, depth--) {
445
cs_format_context(CS_EXC_CONTEXT_SIZE, buffer,
446
callStack[i].cs_func, callStack[i].cs_file, callStack[i].cs_line);
447
strcat(buffer, "\n");
448
CSL.log(this, CSLog::Trace, buffer);
453
void CSThread::logException()
455
myException.log(this);
459
* This function is called when an exception is caught.
460
* It restores the function call top and frees
461
* any resource allocated by lower levels.
463
void CSThread::caught()
465
/* Restore the call top: */
466
this->callTop = this->jumpEnv[this->jumpDepth].jb_call_top;
469
* Release all all objects that were pushed after
470
* this jump position was set:
472
releaseObjects(this->jumpEnv[this->jumpDepth].jb_res_top);
476
* ---------------------------------------------------------------
480
pthread_key_t CSThread::sThreadKey;
481
bool CSThread::isUp = false;
483
bool CSThread::startUp()
488
if ((err = pthread_key_create(&sThreadKey, NULL))) {
489
CSException::logOSError(CS_CONTEXT, errno);
496
void CSThread::shutDown()
501
bool CSThread::attach(CSThread *thread)
506
CSException::logOSError(CS_CONTEXT, ENOMEM);
510
if (!setSelf(thread))
513
/* Now we are ready to receive signals: */
514
if (!td_setup_signals(thread))
522
void CSThread::detach(CSThread *thread)
524
ASSERT(!getSelf() || getSelf() == thread);
525
thread->removeFromList();
527
pthread_setspecific(sThreadKey, NULL);
530
CSThread* CSThread::getSelf()
532
CSThread* self = NULL;
534
if ((!isUp) || !(self = (CSThread*) pthread_getspecific(sThreadKey)))
535
return (CSThread*) NULL;
538
/* PMC - Problem is, if this is called when releasing a
539
* thread, then we have the reference count equal to
541
if (self && !self->iRefCount) {
542
pthread_setspecific(sThreadKey, NULL);
543
CSException::throwAssertion(CS_CONTEXT, "Freed self pointer referenced.");
551
bool CSThread::setSelf(CSThread *self)
556
self->iThread = pthread_self();
558
/* Store my thread in the thread key: */
559
if ((err = pthread_setspecific(sThreadKey, self))) {
560
self->myException.initOSError(CS_CONTEXT, err);
561
self->myException.setStackTrace(self);
566
pthread_setspecific(sThreadKey, NULL);
570
/* timeout is in milliseconds */
571
void CSThread::sleep(unsigned long timeout)
574
usleep(timeout * 1000);
580
int cs_assert(const char *func, const char *file, int line, const char *message)
582
CSException::throwAssertion(func, file, line, message);
586
int cs_hope(const char *func, const char *file, int line, const char *message)
590
e.initAssertion(func, file, line, message);
596
CSThread *CSThread::newThread(CSString *name, ThreadRunFunc run_func, CSThreadList *list)
601
if (!(thd = new CSThread(list))) {
603
CSException::throwOSError(CS_CONTEXT, ENOMEM);
605
thd->threadName = name;
606
thd->iRunFunc = run_func;
610
CSThread *CSThread::newCSThread()
612
CSThread *thd = NULL;
614
if (!(thd = new CSThread(NULL))) {
615
CSException::throwOSError(CS_CONTEXT, ENOMEM);
622
* ---------------------------------------------------------------
626
CSDaemon::CSDaemon(time_t wait_time, CSThreadList *list):
629
myWaitTime(wait_time),
635
CSDaemon::CSDaemon(CSThreadList *list):
644
void CSDaemon::try_Run(CSThread *self, const bool c_must_sleep)
647
bool must_sleep = c_must_sleep; // This done to avoid longjmp() clobber.
648
while (!myMustQuit) {
652
suspendedWait(myWaitTime);
659
must_sleep = doWork();
663
if (!handleException())
669
void *CSDaemon::run()
671
bool must_sleep = false;
675
myMustQuit = !initializeWork();
677
while (!myMustQuit) {
678
try_Run(self, must_sleep);
682
/* Prevent signals from going off in completeWork! */
683
ignoreSignals = true;
685
return_(completeWork());
688
bool CSDaemon::doWork()
695
bool CSDaemon::handleException()
702
void CSDaemon::wakeup()
707
void CSDaemon::stop()
715
void CSDaemon::suspend()
720
while (!iSuspended && !myMustQuit)
728
void CSDaemon::resume()
732
if (iSuspendCount > 0)
739
void CSDaemon::suspended()
741
if (!iSuspendCount || myMustQuit) {
747
while (iSuspendCount && !myMustQuit) {
756
void CSDaemon::suspendedWait()
764
void CSDaemon::suspendedWait(time_t milli_sec)
775
* ---------------------------------------------------------------