Imported Upstream version 3.2.2
[debian/gnuradio] / omnithread / solaris.cc
1 //                              Package : omnithread
2 // omnithread/solaris.cc        Created : 7/94 tjr
3 //
4 //    Copyright (C) 1994-1999 AT&T Laboratories Cambridge
5 //
6 //    This file is part of the omnithread library
7 //
8 //    The omnithread library is free software; you can redistribute it and/or
9 //    modify it under the terms of the GNU Library General Public
10 //    License as published by the Free Software Foundation; either
11 //    version 2 of the License, or (at your option) any later version.
12 //
13 //    This library is distributed in the hope that it will be useful,
14 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //    Library General Public License for more details.
17 //
18 //    You should have received a copy of the GNU Library General Public
19 //    License along with this library; if not, write to the Free
20 //    Software Foundation, Inc., 51 Franklin Street, Boston, MA  
21 //    02110-1301, USA
22 //
23 //
24 // Implementation of OMNI thread abstraction for solaris threads.
25 //
26
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <gnuradio/omnithread.h>
30
31 #define DB(x) // x 
32 // #include <iostream> or #include <iostream.h> if DB is on.
33
34 #define THROW_ERRORS(x) { int rc = (x); \
35                           if (rc != 0) throw omni_thread_fatal(rc); }
36
37
38
39 ///////////////////////////////////////////////////////////////////////////
40 //
41 // Mutex
42 //
43 ///////////////////////////////////////////////////////////////////////////
44
45
46 omni_mutex::omni_mutex(void)
47 {
48     THROW_ERRORS(mutex_init(&sol_mutex, USYNC_THREAD, 0));
49 }
50
51 omni_mutex::~omni_mutex(void)
52 {
53     THROW_ERRORS(mutex_destroy(&sol_mutex));
54 }
55
56 void
57 omni_mutex::lock(void)
58 {
59     THROW_ERRORS(mutex_lock(&sol_mutex));
60 }
61
62 void
63 omni_mutex::unlock(void)
64 {
65     THROW_ERRORS(mutex_unlock(&sol_mutex));
66 }
67
68
69
70 ///////////////////////////////////////////////////////////////////////////
71 //
72 // Condition variable
73 //
74 ///////////////////////////////////////////////////////////////////////////
75
76
77 omni_condition::omni_condition(omni_mutex* m) : mutex(m)
78 {
79     THROW_ERRORS(cond_init(&sol_cond, USYNC_THREAD, 0));
80 }
81
82 omni_condition::~omni_condition(void)
83 {
84     THROW_ERRORS(cond_destroy(&sol_cond));
85 }
86
87 void
88 omni_condition::wait(void)
89 {
90     THROW_ERRORS(cond_wait(&sol_cond, &mutex->sol_mutex));
91 }
92
93 int
94 omni_condition::timedwait(unsigned long secs, unsigned long nanosecs)
95 {
96     timespec rqts = { secs, nanosecs };
97
98  again:
99     int rc = cond_timedwait(&sol_cond, &mutex->sol_mutex, &rqts);
100
101     if (rc == 0)
102         return 1;
103
104     if (rc == EINTR)
105         goto again;
106
107     if (rc == ETIME)
108         return 0;
109
110     throw omni_thread_fatal(rc);
111 }
112
113 void
114 omni_condition::signal(void)
115 {
116     THROW_ERRORS(cond_signal(&sol_cond));
117 }
118
119 void
120 omni_condition::broadcast(void)
121 {
122     THROW_ERRORS(cond_broadcast(&sol_cond));
123 }
124
125
126
127 ///////////////////////////////////////////////////////////////////////////
128 //
129 // Counting semaphore
130 //
131 ///////////////////////////////////////////////////////////////////////////
132
133
134 omni_semaphore::omni_semaphore(unsigned int initial)
135 {
136     THROW_ERRORS(sema_init(&sol_sem, initial, USYNC_THREAD, NULL));
137 }
138
139 omni_semaphore::~omni_semaphore(void)
140 {
141     THROW_ERRORS(sema_destroy(&sol_sem));
142 }
143
144 void
145 omni_semaphore::wait(void)
146 {
147     THROW_ERRORS(sema_wait(&sol_sem));
148 }
149
150 void
151 omni_semaphore::post(void)
152 {
153     THROW_ERRORS(sema_post(&sol_sem));
154 }
155
156
157
158 ///////////////////////////////////////////////////////////////////////////
159 //
160 // Thread
161 //
162 ///////////////////////////////////////////////////////////////////////////
163
164
165 //
166 // Static variables
167 //
168
169 int omni_thread::init_t::count = 0;
170
171 omni_mutex* omni_thread::next_id_mutex;
172 int omni_thread::next_id = 0;
173
174 static thread_key_t self_key;
175
176 static size_t stack_size = 0;
177
178 //
179 // Initialisation function (gets called before any user code).
180 //
181
182 omni_thread::init_t::init_t(void)
183 {
184     if (count++ != 0)   // only do it once however many objects get created.
185         return;
186
187     DB(cerr << "omni_thread::init: solaris implementation initialising\n");
188
189     THROW_ERRORS(thr_keycreate(&self_key, NULL));
190
191     next_id_mutex = new omni_mutex;
192
193     //
194     // Create object for this (i.e. initial) thread.
195     //
196
197     omni_thread* t = new omni_thread;
198
199     t->_state = STATE_RUNNING;
200
201     t->sol_thread = thr_self();
202
203     DB(cerr << "initial thread " << t->id() << " sol_thread " << t->sol_thread
204        << endl);
205
206     THROW_ERRORS(thr_setspecific(self_key, (void*)t));
207
208     THROW_ERRORS(thr_setprio(t->sol_thread, sol_priority(PRIORITY_NORMAL)));
209 }
210
211
212 //
213 // Wrapper for thread creation.
214 //
215
216 extern "C" void*
217 omni_thread_wrapper(void* ptr)
218 {
219     omni_thread* me = (omni_thread*)ptr;
220
221     DB(cerr << "omni_thread::wrapper: thread " << me->id()
222        << " started\n");
223
224     THROW_ERRORS(thr_setspecific(self_key, me));
225
226     //
227     // Now invoke the thread function with the given argument.
228     //
229
230     if (me->fn_void != NULL) {
231         (*me->fn_void)(me->thread_arg);
232         omni_thread::exit();
233     }
234
235     if (me->fn_ret != NULL) {
236         void* return_value = (*me->fn_ret)(me->thread_arg);
237         omni_thread::exit(return_value);
238     }
239
240     if (me->detached) {
241         me->run(me->thread_arg);
242         omni_thread::exit();
243     } else {
244         void* return_value = me->run_undetached(me->thread_arg);
245         omni_thread::exit(return_value);
246     }
247
248     // should never get here.
249
250     return NULL;
251 }
252
253
254 //
255 // Constructors for omni_thread - set up the thread object but don't
256 // start it running.
257 //
258
259 // construct a detached thread running a given function.
260
261 omni_thread::omni_thread(void (*fn)(void*), void* arg, priority_t pri)
262 {
263     common_constructor(arg, pri, 1);
264     fn_void = fn;
265     fn_ret = NULL;
266 }
267
268 // construct an undetached thread running a given function.
269
270 omni_thread::omni_thread(void* (*fn)(void*), void* arg, priority_t pri)
271 {
272     common_constructor(arg, pri, 0);
273     fn_void = NULL;
274     fn_ret = fn;
275 }
276
277 // construct a thread which will run either run() or run_undetached().
278
279 omni_thread::omni_thread(void* arg, priority_t pri)
280 {
281     common_constructor(arg, pri, 1);
282     fn_void = NULL;
283     fn_ret = NULL;
284 }
285
286 // common part of all constructors.
287
288 void
289 omni_thread::common_constructor(void* arg, priority_t pri, int det)
290 {
291     _state = STATE_NEW;
292     _priority = pri;
293
294     next_id_mutex->lock();
295     _id = next_id++;
296     next_id_mutex->unlock();
297
298     thread_arg = arg;
299     detached = det;     // may be altered in start_undetached()
300
301     _dummy       = 0;
302     _values      = 0;
303     _value_alloc = 0;
304     // sol_thread is set up in initialisation routine or start().
305 }
306
307
308 //
309 // Destructor for omni_thread.
310 //
311
312 omni_thread::~omni_thread(void)
313 {
314     DB(cerr << "destructor called for thread " << id() << endl);
315     if (_values) {
316         for (key_t i=0; i < _value_alloc; i++) {
317             if (_values[i]) {
318                 delete _values[i];
319             }
320         }
321         delete [] _values;
322     }
323 }
324
325
326 //
327 // Start the thread
328 //
329
330 void
331 omni_thread::start(void)
332 {
333     long flags = 0;
334
335     if (detached)
336         flags |= THR_DETACHED;
337
338     omni_mutex_lock l(mutex);
339
340     if (_state != STATE_NEW)
341         throw omni_thread_invalid();
342
343     THROW_ERRORS(thr_create(0, stack_size, omni_thread_wrapper, (void*)this, flags,
344                             &sol_thread));
345
346     _state = STATE_RUNNING;
347
348     THROW_ERRORS(thr_setprio(sol_thread, sol_priority(_priority)));
349 }
350
351
352 //
353 // Start a thread which will run the member function run_undetached().
354 //
355
356 void
357 omni_thread::start_undetached(void)
358 {
359     if ((fn_void != NULL) || (fn_ret != NULL))
360         throw omni_thread_invalid();
361
362     detached = 0;
363     start();
364 }
365
366
367 //
368 // join - simply check error conditions & call thr_join.
369 //
370
371 void
372 omni_thread::join(void** status)
373 {
374     mutex.lock();
375
376     if ((_state != STATE_RUNNING) && (_state != STATE_TERMINATED)) {
377         mutex.unlock();
378         throw omni_thread_invalid();
379     }
380
381     mutex.unlock();
382
383     if (this == self())
384         throw omni_thread_invalid();
385
386     if (detached)
387         throw omni_thread_invalid();
388
389     DB(cerr << "omni_thread::join: doing thr_join\n");
390
391     THROW_ERRORS(thr_join(sol_thread, (thread_t *)NULL, status));
392
393     DB(cerr << "omni_thread::join: thr_join succeeded\n");
394
395     delete this;
396 }
397
398
399 //
400 // Change this thread's priority.
401 //
402
403 void
404 omni_thread::set_priority(priority_t pri)
405 {
406     omni_mutex_lock l(mutex);
407
408     if (_state != STATE_RUNNING)
409         throw omni_thread_invalid();
410
411     _priority = pri;
412
413     THROW_ERRORS(thr_setprio(sol_thread, sol_priority(pri)));
414 }
415
416
417 //
418 // create - construct a new thread object and start it running.  Returns thread
419 // object if successful, null pointer if not.
420 //
421
422 // detached version
423
424 omni_thread*
425 omni_thread::create(void (*fn)(void*), void* arg, priority_t pri)
426 {
427     omni_thread* t = new omni_thread(fn, arg, pri);
428
429     t->start();
430
431     return t;
432 }
433
434 // undetached version
435
436 omni_thread*
437 omni_thread::create(void* (*fn)(void*), void* arg, priority_t pri)
438 {
439     omni_thread* t = new omni_thread(fn, arg, pri);
440
441     t->start();
442
443     return t;
444 }
445
446
447 //
448 // exit() _must_ lock the mutex even in the case of a detached thread.  This is
449 // because a thread may run to completion before the thread that created it has
450 // had a chance to get out of start().  By locking the mutex we ensure that the
451 // creating thread must have reached the end of start() before we delete the
452 // thread object.  Of course, once the call to start() returns, the user can
453 // still incorrectly refer to the thread object, but that's their problem.
454 //
455
456 void
457 omni_thread::exit(void* return_value)
458 {
459     omni_thread* me = self();
460
461     if (me)
462       {
463         me->mutex.lock();
464
465         me->_state = STATE_TERMINATED;
466
467         me->mutex.unlock();
468
469         DB(cerr << "omni_thread::exit: thread " << me->id() << " detached "
470            << me->detached << " return value " << return_value << endl);
471
472         if (me->detached)
473           delete me;
474       }
475     else
476       {
477         DB(cerr << "omni_thread::exit: called with a non-omnithread. Exit quietly." << endl);
478       }
479
480     thr_exit(return_value);
481 }
482
483
484 omni_thread*
485 omni_thread::self(void)
486 {
487     omni_thread* me;
488
489     THROW_ERRORS(thr_getspecific(self_key, (void**)&me));
490
491     if (!me) {
492       // This thread is not created by omni_thread::start because it
493       // doesn't has a class omni_thread instance attached to its key.
494       DB(cerr << "omni_thread::self: called with a non-ominthread. NULL is returned." << endl);
495     }
496
497     return me;
498 }
499
500
501 void
502 omni_thread::yield(void)
503 {
504     thr_yield();
505 }
506
507
508 void
509 omni_thread::sleep(unsigned long secs, unsigned long nanosecs)
510 {
511     timespec rqts = { secs, nanosecs };
512     timespec remain;
513     while (nanosleep(&rqts, &remain)) {
514       if (errno == EINTR) {
515         rqts.tv_sec  = remain.tv_sec;
516         rqts.tv_nsec = remain.tv_nsec;
517         continue;
518       }
519       else
520         throw omni_thread_fatal(errno);
521     }
522 }
523
524
525 void
526 omni_thread::get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
527                       unsigned long rel_sec, unsigned long rel_nsec)
528 {
529     timespec abs;
530     clock_gettime(CLOCK_REALTIME, &abs);
531     abs.tv_nsec += rel_nsec;
532     abs.tv_sec += rel_sec + abs.tv_nsec / 1000000000;
533     abs.tv_nsec = abs.tv_nsec % 1000000000;
534     *abs_sec = abs.tv_sec;
535     *abs_nsec = abs.tv_nsec;
536 }
537
538
539 int
540 omni_thread::sol_priority(priority_t pri)
541 {
542     switch (pri) {
543
544     case PRIORITY_LOW:
545         return 0;
546
547     case PRIORITY_NORMAL:
548         return 1;
549
550     case PRIORITY_HIGH:
551         return 2;
552     }
553
554     throw omni_thread_invalid();
555 }
556
557
558 void
559 omni_thread::stacksize(unsigned long sz)
560 {
561   stack_size = sz;
562 }
563
564 unsigned long
565 omni_thread::stacksize()
566 {
567   return stack_size;
568 }
569
570
571 //
572 // Dummy thread
573 //
574
575 #error This dummy thread code is not tested. It might work if you're lucky.
576
577 class omni_thread_dummy : public omni_thread {
578 public:
579   inline omni_thread_dummy() : omni_thread()
580   {
581     _dummy = 1;
582     _state = STATE_RUNNING;
583     sol_thread = thr_self();
584     THROW_ERRORS(thr_setspecific(self_key, (void*)this));
585   }
586   inline ~omni_thread_dummy()
587   {
588     THROW_ERRORS(thr_setspecific(self_key, 0));
589   }
590 };
591
592 omni_thread*
593 omni_thread::create_dummy()
594 {
595   if (omni_thread::self())
596     throw omni_thread_invalid();
597
598   return new omni_thread_dummy;
599 }
600
601 void
602 omni_thread::release_dummy()
603 {
604   omni_thread* self = omni_thread::self();
605   if (!self || !self->_dummy)
606     throw omni_thread_invalid();
607
608   omni_thread_dummy* dummy = (omni_thread_dummy*)self;
609   delete dummy;
610 }
611
612
613 #define INSIDE_THREAD_IMPL_CC
614 #include "threaddata.cc"
615 #undef INSIDE_THREAD_IMPL_CC