~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/pbms/src/cslib/CSThread.cc

Merged vcol stuff.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
2
 
 *
3
 
 * PrimeBase Media Stream for MySQL
4
 
 *
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.
9
 
 *
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.
14
 
 *
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
18
 
 *
19
 
 * Original author: Paul McCullagh (H&G2JCtL)
20
 
 * Continued development: Barry Leslie
21
 
 *
22
 
 * 2007-05-20
23
 
 *
24
 
 * CORE SYSTEM:
25
 
 * A independently running thread.
26
 
 *
27
 
 */
28
 
 
29
 
#include "CSConfig.h"
30
 
 
31
 
#ifdef OS_WINDOWS
32
 
#include <signal.h>
33
 
//#include "uniwin.h"
34
 
#define SIGUSR1 30
35
 
#define SIGUSR2 31
36
 
 
37
 
#else
38
 
#include <signal.h>
39
 
#include <sys/signal.h>
40
 
#include <unistd.h>
41
 
#endif
42
 
#include <errno.h>
43
 
 
44
 
#include "CSGlobal.h"
45
 
#include "CSLog.h"
46
 
#include "CSException.h"
47
 
#include "CSThread.h"
48
 
#include "CSStrUtil.h"
49
 
#include "CSMemory.h"
50
 
 
51
 
/*
52
 
 * ---------------------------------------------------------------
53
 
 * SIGNAL HANDLERS
54
 
 */
55
 
 
56
 
extern "C" {
57
 
 
58
 
 
59
 
static void td_catch_signal(int sig)
60
 
{
61
 
        CSThread *self;
62
 
 
63
 
        if ((self = CSThread::getSelf())) {
64
 
                if (self->isMain()) {
65
 
                        /* The main thread will pass on a signal to all threads: */
66
 
                        if (self->myThreadList)
67
 
                                self->myThreadList->signalAllThreads(sig);
68
 
                        self->setSignalPending(sig);
69
 
                }
70
 
        }
71
 
        
72
 
}
73
 
 
74
 
static  void td_throw_signal(int sig)
75
 
{
76
 
        CSThread *self;
77
 
 
78
 
        if ((self = CSThread::getSelf())) {
79
 
                if (self->isMain()) {
80
 
                        /* The main thread will pass on a signal to all threads: */
81
 
                        if (self->myThreadList)
82
 
                                self->myThreadList->signalAllThreads(sig);
83
 
                }
84
 
                self->setSignalPending(sig);
85
 
                self->interrupted();
86
 
        }
87
 
}
88
 
 
89
 
static bool td_setup_signals(CSThread *thread)
90
 
{
91
 
#ifdef OS_WINDOWS
92
 
        return true;
93
 
#else
94
 
        struct sigaction action;
95
 
 
96
 
    sigemptyset(&action.sa_mask);
97
 
    action.sa_flags = 0;
98
 
 
99
 
    action.sa_handler = td_catch_signal;
100
 
 
101
 
        if (sigaction(SIGUSR2, &action, NULL) == -1)
102
 
                goto error_occurred;
103
 
 
104
 
    action.sa_handler = td_throw_signal;
105
 
 
106
 
        return true;
107
 
 
108
 
        error_occurred:
109
 
 
110
 
        if (thread) {
111
 
                thread->myException.initOSError(CS_CONTEXT, errno);
112
 
                thread->myException.setStackTrace(thread);
113
 
        }
114
 
        else
115
 
                CSException::throwOSError(CS_CONTEXT, errno);
116
 
        return false;
117
 
#endif
118
 
}
119
 
 
120
 
}
121
 
 
122
 
/*
123
 
 * ---------------------------------------------------------------
124
 
 * THREAD LISTS
125
 
 */
126
 
 
127
 
void CSThreadList::signalAllThreads(int sig)
128
 
{
129
 
        CSThread *ptr;
130
 
 
131
 
        enter_();
132
 
        lock_(this);
133
 
        ptr = (CSThread *) getBack();
134
 
        while (ptr) {
135
 
                if (ptr != self)
136
 
                        ptr->signal(sig);
137
 
                ptr = (CSThread *) ptr->getNextLink();
138
 
        }
139
 
        unlock_(this);
140
 
 
141
 
        exit_();
142
 
}
143
 
 
144
 
void CSThreadList::quitAllThreads()
145
 
{
146
 
        CSThread *ptr;
147
 
 
148
 
        enter_();
149
 
        lock_(this);
150
 
        
151
 
        ptr = (CSThread *) getBack();
152
 
        while (ptr) {
153
 
                if (ptr != self)
154
 
                        ptr->myMustQuit = true;
155
 
                ptr = (CSThread *) ptr->getNextLink();
156
 
        }
157
 
        
158
 
        unlock_(this);
159
 
        exit_();
160
 
}
161
 
 
162
 
void CSThreadList::stopAllThreads()
163
 
{
164
 
        CSThread *thread;
165
 
 
166
 
        enter_();
167
 
        for (;;) {
168
 
                /* Get a thread that is not self! */
169
 
                lock_(this);
170
 
                if ((thread = (CSThread *) getBack())) {
171
 
                        while (thread) {
172
 
                                if (thread != self)
173
 
                                        break;
174
 
                                thread = (CSThread *) thread->getNextLink();
175
 
                        }
176
 
                }
177
 
                if (thread)
178
 
                        thread->retain();
179
 
                unlock_(this);
180
 
                
181
 
                if (!thread)
182
 
                        break;
183
 
                        
184
 
                push_(thread);
185
 
                thread->stop();
186
 
                release_(thread);
187
 
        }
188
 
        exit_();
189
 
}
190
 
 
191
 
/*
192
 
 * ---------------------------------------------------------------
193
 
 * CSTHREAD
194
 
 */
195
 
 
196
 
void CSThread::addToList()
197
 
{
198
 
        if (myThreadList) {
199
 
                enter_();
200
 
                ASSERT(self == this);
201
 
                lock_(myThreadList);
202
 
                myThreadList->addFront(self);
203
 
                isRunning = true;
204
 
                unlock_(myThreadList);
205
 
                exit_();
206
 
        }
207
 
        else
208
 
                isRunning = true;
209
 
}
210
 
        
211
 
void CSThread::removeFromList()
212
 
{
213
 
        if (myThreadList && isRunning) {
214
 
                CSThread *myself = this; // pop_() wants to take a reference to its parameter.
215
 
                enter_();
216
 
                /* Retain the thread in order to ensure
217
 
                 * that after it is removed from the list,
218
 
                 * that it is not freed! This would make the
219
 
                 * unlock_() call invalid, because it requires
220
 
                 * on the thread.
221
 
                 */
222
 
                push_(myself);
223
 
                lock_(myThreadList);
224
 
                myThreadList->remove(RETAIN(myself));
225
 
                unlock_(myThreadList);
226
 
                pop_(myself);
227
 
                outer_();
228
 
        }
229
 
        this->release();
230
 
}
231
 
 
232
 
void *CSThread::dispatch(void *arg)
233
 
{
234
 
        CSThread                *self;
235
 
        void                    *return_data = NULL;
236
 
        int                             err;
237
 
 
238
 
        /* Get a reference to myself: */
239
 
        self = reinterpret_cast<CSThread*>(arg);
240
 
        ASSERT(self);
241
 
 
242
 
        /* Store my thread in the thread key: */
243
 
        if ((err = pthread_setspecific(CSThread::sThreadKey, self))) {
244
 
                CSException::logOSError(self, CS_CONTEXT, err);
245
 
                return NULL;
246
 
        }
247
 
 
248
 
        /*
249
 
         * Make sure the thread is not freed while we
250
 
         * are running:
251
 
         */
252
 
        self->retain();
253
 
 
254
 
        try_(a) {
255
 
                td_setup_signals(NULL);
256
 
 
257
 
                /* Add the thread to the list: */
258
 
                self->addToList();
259
 
 
260
 
                // Run the task from the correct context
261
 
                return_data = self->run();
262
 
        }
263
 
        catch_(a) {
264
 
                self->logException();
265
 
        }
266
 
        cont_(a);
267
 
 
268
 
        /*
269
 
         * Removing from the thread list will also release the thread.
270
 
         */
271
 
        self->removeFromList();
272
 
 
273
 
        // Exit the thread
274
 
        return return_data;
275
 
}
276
 
 
277
 
 
278
 
extern "C"
279
 
{
280
 
 
281
 
static void *dispatch_wrapper(void *arg)
282
 
{
283
 
        return CSThread::dispatch(arg);
284
 
}
285
 
 
286
 
}
287
 
 
288
 
void *CSThread::run()
289
 
{
290
 
        if (iRunFunc)
291
 
                return iRunFunc();
292
 
        return NULL;
293
 
}
294
 
 
295
 
void CSThread::start(bool detached)
296
 
{
297
 
        int err;
298
 
 
299
 
        err = pthread_create(&iThread, NULL, dispatch_wrapper, (void *) this);
300
 
        if (err)
301
 
                CSException::throwOSError(CS_CONTEXT, err);
302
 
        while (!isRunning) {
303
 
                /* Check if the thread is still alive,
304
 
                 * so we don't hang forever.
305
 
                 */
306
 
                if (pthread_kill(iThread, 0))
307
 
                        break;
308
 
                usleep(10);
309
 
        }
310
 
        
311
 
        isDetached = detached;
312
 
        if (detached)
313
 
                pthread_detach(iThread);
314
 
}
315
 
 
316
 
void CSThread::stop()
317
 
{
318
 
        signal(SIGTERM);
319
 
        join();
320
 
}
321
 
 
322
 
void *CSThread::join()
323
 
{
324
 
        void    *return_data = NULL;
325
 
        int             err;
326
 
 
327
 
        enter_();
328
 
        if (isDetached) {
329
 
                while (isRunning && !pthread_kill(iThread, 0)) 
330
 
                        usleep(100);
331
 
        } else if ((err = pthread_join(iThread, &return_data))) {
332
 
                CSException::throwOSError(CS_CONTEXT, err);
333
 
        }
334
 
 
335
 
        return_(return_data);
336
 
}
337
 
 
338
 
void CSThread::setSignalPending(unsigned int sig)
339
 
{
340
 
        if (sig == SIGTERM)
341
 
                /* The terminate signal takes priority: */
342
 
                signalPending = SIGTERM;
343
 
        else if (!signalPending)
344
 
                /* Otherwise, first signal wins... */
345
 
                signalPending = sig;
346
 
}
347
 
 
348
 
void CSThread::signal(unsigned int sig)
349
 
{
350
 
#ifndef OS_WINDOWS // Currently you cannot signal threads on windows.
351
 
        int err;
352
 
 
353
 
        setSignalPending(sig);
354
 
        if ((err = pthread_kill(iThread, SIGUSR2)))
355
 
        {
356
 
                /* Ignore the error if the process does not exist! */
357
 
                if (err != ESRCH) /* No such process */
358
 
                        CSException::throwOSError(CS_CONTEXT, err);
359
 
        }
360
 
#endif
361
 
}
362
 
 
363
 
void CSThread::throwSignal()
364
 
{
365
 
        int sig;
366
 
 
367
 
        if ((sig = signalPending) && !ignoreSignals) {
368
 
                signalPending = 0;
369
 
                CSException::throwSignal(CS_CONTEXT, sig);
370
 
        }
371
 
}
372
 
 
373
 
bool CSThread::isMain()
374
 
{
375
 
        return iIsMain;
376
 
}
377
 
 
378
 
/*
379
 
 * -----------------------------------------------------------------------
380
 
 * THROWING EXCEPTIONS
381
 
 */
382
 
 
383
 
/* 
384
 
 * When an exception is .
385
 
 */
386
 
 
387
 
void CSThread::releaseObjects(CSReleasePtr top)
388
 
{
389
 
        CSObject *obj;
390
 
 
391
 
        while (relTop > top) {
392
 
                /* Remove and release or unlock the object on the top of the stack: */
393
 
                relTop--;
394
 
                switch(relTop->r_type) {
395
 
                        case CS_RELEASE_OBJECT:
396
 
                                if ((obj = relTop->x.r_object))
397
 
                                        obj->release();
398
 
                                break;
399
 
                        case CS_RELEASE_MUTEX:
400
 
                                if (relTop->x.r_mutex)
401
 
                                        relTop->x.r_mutex->unlock();
402
 
                                break;
403
 
                        case CS_RELEASE_POOLED:
404
 
                                if (relTop->x.r_pooled)
405
 
                                        relTop->x.r_pooled->returnToPool();
406
 
                                break;
407
 
                        case CS_RELEASE_MEM:
408
 
                                if (relTop->x.r_mem)
409
 
                                        cs_free(relTop->x.r_mem);
410
 
                                break;
411
 
                        case CS_RELEASE_OBJECT_PTR:
412
 
                                if ((relTop->x.r_objectPtr) && (obj = *(relTop->x.r_objectPtr)))
413
 
                                        obj->release();
414
 
                                break;
415
 
                }
416
 
        }
417
 
}
418
 
 
419
 
/* Throw an already registered error: */
420
 
void CSThread::throwException()
421
 
{
422
 
        /* Record the stack trace: */
423
 
        if (this->jumpDepth > 0 && this->jumpDepth <= CS_JUMP_STACK_SIZE) {
424
 
                /*
425
 
                 * As recommended by Barry:
426
 
                 * release the objects before we jump!
427
 
                 * This has the advantage that the stack context is still
428
 
                 * valid when the resources are released.
429
 
                 */
430
 
                releaseObjects(this->jumpEnv[this->jumpDepth-1].jb_res_top);
431
 
 
432
 
                /* Then do the longjmp: */
433
 
                longjmp(this->jumpEnv[this->jumpDepth-1].jb_buffer, 1);
434
 
        }
435
 
}
436
 
 
437
 
void CSThread::logStack(int depth, const char *msg)
438
 
{
439
 
        char buffer[CS_EXC_CONTEXT_SIZE +1];
440
 
        CSL.lock();
441
 
        CSL.log(this, CSLog::Trace, msg);
442
 
        
443
 
        for (int i= callTop-1; i>=0 && depth; i--, depth--) {
444
 
                cs_format_context(CS_EXC_CONTEXT_SIZE, buffer,
445
 
                        callStack[i].cs_func, callStack[i].cs_file, callStack[i].cs_line);
446
 
                strcat(buffer, "\n");
447
 
                CSL.log(this, CSLog::Trace, buffer);
448
 
        }
449
 
        CSL.unlock();
450
 
}
451
 
 
452
 
void CSThread::logException()
453
 
{
454
 
        myException.log(this);
455
 
}
456
 
 
457
 
/*
458
 
 * This function is called when an exception is caught.
459
 
 * It restores the function call top and frees
460
 
 * any resource allocated by lower levels.
461
 
 */
462
 
void CSThread::caught()
463
 
{
464
 
        /* Restore the call top: */
465
 
        this->callTop = this->jumpEnv[this->jumpDepth].jb_call_top;
466
 
 
467
 
        /* 
468
 
         * Release all all objects that were pushed after
469
 
         * this jump position was set:
470
 
         */
471
 
        releaseObjects(this->jumpEnv[this->jumpDepth].jb_res_top);
472
 
}
473
 
 
474
 
/*
475
 
 * ---------------------------------------------------------------
476
 
 * STATIC METHODS
477
 
 */
478
 
 
479
 
pthread_key_t   CSThread::sThreadKey;
480
 
bool                    CSThread::isUp = false;
481
 
 
482
 
bool CSThread::startUp()
483
 
{
484
 
        int err;
485
 
 
486
 
        isUp = false;
487
 
        if ((err = pthread_key_create(&sThreadKey, NULL))) {
488
 
                CSException::logOSError(CS_CONTEXT, errno);
489
 
        } else
490
 
                isUp = true;
491
 
                
492
 
        return isUp;
493
 
}
494
 
 
495
 
void CSThread::shutDown()
496
 
{
497
 
        isUp = false;
498
 
}
499
 
 
500
 
bool CSThread::attach(CSThread *thread)
501
 
{
502
 
        ASSERT(!getSelf());
503
 
        
504
 
        if (!thread) {
505
 
                CSException::logOSError(CS_CONTEXT, ENOMEM);
506
 
                return false;
507
 
        }
508
 
 
509
 
        if (!setSelf(thread))
510
 
                return false;
511
 
 
512
 
        /* Now we are ready to receive signals: */
513
 
        if (!td_setup_signals(thread))
514
 
                return false;
515
 
 
516
 
        thread->addToList();
517
 
        thread->retain();
518
 
        return true;
519
 
}
520
 
 
521
 
void CSThread::detach(CSThread *thread)
522
 
{
523
 
        ASSERT(!getSelf() || getSelf() == thread);
524
 
        thread->removeFromList();
525
 
        thread->release();
526
 
        pthread_setspecific(sThreadKey, NULL);
527
 
}
528
 
 
529
 
CSThread* CSThread::getSelf()
530
 
{
531
 
        CSThread* self = NULL;
532
 
        
533
 
        if ((!isUp) || !(self = (CSThread*) pthread_getspecific(sThreadKey)))
534
 
                return (CSThread*) NULL;
535
 
                
536
 
#ifdef DEBUG
537
 
        /* PMC - Problem is, if this is called when releasing a
538
 
         * thread, then we have the reference count equal to
539
 
         * zero.
540
 
        if (self && !self->iRefCount) {
541
 
                pthread_setspecific(sThreadKey, NULL);
542
 
                CSException::throwAssertion(CS_CONTEXT, "Freed self pointer referenced.");
543
 
        }
544
 
        */
545
 
#endif
546
 
 
547
 
        return self;
548
 
}
549
 
 
550
 
bool CSThread::setSelf(CSThread *self)
551
 
{
552
 
        int err;
553
 
 
554
 
        if (self) {
555
 
                self->iThread = pthread_self();
556
 
 
557
 
                /* Store my thread in the thread key: */
558
 
                if ((err = pthread_setspecific(sThreadKey, self))) {
559
 
                        self->myException.initOSError(CS_CONTEXT, err);
560
 
                        self->myException.setStackTrace(self);
561
 
                        return false;
562
 
                }
563
 
        }
564
 
        else
565
 
                pthread_setspecific(sThreadKey, NULL);
566
 
        return true;
567
 
}
568
 
 
569
 
/* timeout is in milliseconds */
570
 
void CSThread::sleep(unsigned long timeout)
571
 
{
572
 
        enter_();
573
 
        usleep(timeout * 1000);
574
 
        self->interrupted();
575
 
        exit_();
576
 
}
577
 
 
578
 
#ifdef DEBUG
579
 
int cs_assert(const char *func, const char *file, int line, const char *message)
580
 
{
581
 
        CSException::throwAssertion(func, file, line, message);
582
 
        return 0;
583
 
}
584
 
 
585
 
int cs_hope(const char *func, const char *file, int line, const char *message)
586
 
{
587
 
        CSException e;
588
 
                
589
 
        e.initAssertion(func, file, line, message);
590
 
        e.log(NULL);
591
 
        return 0;
592
 
}
593
 
#endif
594
 
 
595
 
CSThread *CSThread::newThread(CSString *name, ThreadRunFunc run_func, CSThreadList *list)
596
 
{
597
 
        CSThread *thd;
598
 
 
599
 
        enter_();
600
 
        if (!(thd = new CSThread(list))) {
601
 
                name->release();
602
 
                CSException::throwOSError(CS_CONTEXT, ENOMEM);
603
 
        }
604
 
        thd->threadName = name;
605
 
        thd->iRunFunc = run_func;
606
 
        return_(thd);
607
 
}
608
 
 
609
 
CSThread *CSThread::newCSThread()
610
 
{
611
 
        CSThread *thd = NULL;
612
 
 
613
 
        if (!(thd = new CSThread(NULL))) {
614
 
                CSException::throwOSError(CS_CONTEXT, ENOMEM);
615
 
        }
616
 
        
617
 
        return thd;
618
 
}
619
 
 
620
 
/*
621
 
 * ---------------------------------------------------------------
622
 
 * DAEMON THREADS
623
 
 */
624
 
 
625
 
CSDaemon::CSDaemon(time_t wait_time, CSThreadList *list):
626
 
CSThread(list),
627
 
CSSync(),
628
 
myWaitTime(wait_time),
629
 
iSuspended(false),
630
 
iSuspendCount(0)
631
 
{
632
 
}
633
 
 
634
 
CSDaemon::CSDaemon(CSThreadList *list):
635
 
CSThread(list),
636
 
CSSync(),
637
 
myWaitTime(0),
638
 
iSuspended(false),
639
 
iSuspendCount(0)
640
 
{
641
 
}
642
 
 
643
 
void CSDaemon::try_Run(CSThread *self, const bool c_must_sleep)
644
 
{
645
 
        try_(a) {
646
 
                 bool must_sleep = c_must_sleep; // This done to avoid longjmp() clobber.
647
 
                while (!myMustQuit) {
648
 
                        if (must_sleep) {
649
 
                                lock_(this);
650
 
                                if (myWaitTime)
651
 
                                        suspendedWait(myWaitTime);
652
 
                                else
653
 
                                        suspendedWait();
654
 
                                unlock_(this);
655
 
                                if (myMustQuit)
656
 
                                        break;
657
 
                        }
658
 
                        must_sleep = doWork();
659
 
                }
660
 
        }
661
 
        catch_(a) {
662
 
                if (!handleException())
663
 
                        myMustQuit = true;
664
 
        }
665
 
        cont_(a);
666
 
}
667
 
 
668
 
void *CSDaemon::run()
669
 
{
670
 
        bool must_sleep = false;
671
 
 
672
 
        enter_();
673
 
 
674
 
        myMustQuit = !initializeWork();
675
 
 
676
 
        while  (!myMustQuit) {
677
 
                try_Run(self, must_sleep);
678
 
                must_sleep = true;
679
 
        }
680
 
 
681
 
        /* Prevent signals from going off in completeWork! */
682
 
        ignoreSignals = true;
683
 
 
684
 
        return_(completeWork());
685
 
}
686
 
 
687
 
bool CSDaemon::doWork()
688
 
{
689
 
        if (iRunFunc)
690
 
                (void) iRunFunc();
691
 
        return true;
692
 
}
693
 
 
694
 
bool CSDaemon::handleException()
695
 
{
696
 
        if (!myMustQuit)
697
 
                logException();
698
 
        return true;
699
 
}
700
 
 
701
 
void CSDaemon::wakeup()
702
 
{
703
 
        CSSync::wakeup();
704
 
}
705
 
 
706
 
void CSDaemon::stop()
707
 
{
708
 
        myMustQuit = true;
709
 
        wakeup();
710
 
        signal(SIGTERM);
711
 
        join();
712
 
}
713
 
 
714
 
void CSDaemon::suspend()
715
 
{
716
 
        enter_();
717
 
        lock_(this);
718
 
        iSuspendCount++;
719
 
        while (!iSuspended && !myMustQuit)
720
 
                wait(500);
721
 
        if (!iSuspended)
722
 
                iSuspendCount--;
723
 
        unlock_(this);
724
 
        exit_();
725
 
}
726
 
 
727
 
void CSDaemon::resume()
728
 
{
729
 
        enter_();
730
 
        lock_(this);
731
 
        if (iSuspendCount > 0)
732
 
                iSuspendCount--;
733
 
        wakeup();
734
 
        unlock_(this);
735
 
        exit_();
736
 
}
737
 
 
738
 
void CSDaemon::suspended()
739
 
{
740
 
        if (!iSuspendCount || myMustQuit) {
741
 
                iSuspended = false;
742
 
                return;
743
 
        }
744
 
        enter_();
745
 
        lock_(this);
746
 
        while (iSuspendCount && !myMustQuit) {
747
 
                iSuspended = true;
748
 
                wait(500);
749
 
        }
750
 
        iSuspended = false;
751
 
        unlock_(this);
752
 
        exit_();
753
 
}
754
 
 
755
 
void CSDaemon::suspendedWait()
756
 
{
757
 
        iSuspended = true;
758
 
        wait();
759
 
        if (iSuspendCount)
760
 
                suspended();
761
 
}
762
 
 
763
 
void CSDaemon::suspendedWait(time_t milli_sec)
764
 
{
765
 
        iSuspended = true;
766
 
        wait(milli_sec);
767
 
        if (iSuspendCount)
768
 
                suspended();
769
 
        else
770
 
                iSuspended = false;
771
 
}
772
 
 
773
 
/*
774
 
 * ---------------------------------------------------------------
775
 
 * THREAD POOLS
776
 
 */
777
 
 
778