• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kviewshell

GThreads.cpp

Go to the documentation of this file.
00001 //C-  -*- C++ -*-
00002 //C- -------------------------------------------------------------------
00003 //C- DjVuLibre-3.5
00004 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
00005 //C- Copyright (c) 2001  AT&T
00006 //C-
00007 //C- This software is subject to, and may be distributed under, the
00008 //C- GNU General Public License, Version 2. The license should have
00009 //C- accompanied the software or you may obtain a copy of the license
00010 //C- from the Free Software Foundation at http://www.fsf.org .
00011 //C-
00012 //C- This program is distributed in the hope that it will be useful,
00013 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //C- GNU General Public License for more details.
00016 //C- 
00017 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
00018 //C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech 
00019 //C- Software authorized us to replace the original DjVu(r) Reference 
00020 //C- Library notice by the following text (see doc/lizard2002.djvu):
00021 //C-
00022 //C-  ------------------------------------------------------------------
00023 //C- | DjVu (r) Reference Library (v. 3.5)
00024 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
00025 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
00026 //C- | 6,058,214 and patents pending.
00027 //C- |
00028 //C- | This software is subject to, and may be distributed under, the
00029 //C- | GNU General Public License, Version 2. The license should have
00030 //C- | accompanied the software or you may obtain a copy of the license
00031 //C- | from the Free Software Foundation at http://www.fsf.org .
00032 //C- |
00033 //C- | The computer code originally released by LizardTech under this
00034 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
00035 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
00036 //C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
00037 //C- | non-exclusive license to make, use, sell, or otherwise dispose of 
00038 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
00039 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
00040 //C- | General Public License.   This grant only confers the right to 
00041 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
00042 //C- | the extent such infringement is reasonably necessary to enable 
00043 //C- | recipient to make, have made, practice, sell, or otherwise dispose 
00044 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
00045 //C- | any greater extent that may be necessary to utilize further 
00046 //C- | modifications or combinations.
00047 //C- |
00048 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
00049 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
00050 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
00051 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
00052 //C- +------------------------------------------------------------------
00053 // 
00054 // $Id: GThreads.cpp,v 1.15 2004/04/21 14:54:43 leonb Exp $
00055 // $Name: release_3_5_15 $
00056 
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063 
00064 // This file defines machine independent classes
00065 // for running and synchronizing threads.
00066 // - Author: Leon Bottou, 01/1998
00067 
00068 // From: Leon Bottou, 1/31/2002
00069 // Almost unchanged by Lizardtech.
00070 // GSafeFlags should go because it not as safe as it claims.
00071 
00072 #include "GThreads.h"
00073 #include "GException.h"
00074 #include "DjVuMessageLite.h"
00075 #include <stdlib.h>
00076 #include <stdio.h>
00077 
00078 // ----------------------------------------
00079 // Consistency check
00080 
00081 #if THREADMODEL!=NOTHREADS
00082 #ifdef USE_EXCEPTION_EMULATION
00083 #warning "Compiler must support thread safe exceptions"
00084 #endif //USE_EXCEPTION_EMULATION
00085 #if defined(__GNUC__)
00086 #if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=8))
00087 #warning "GCC 2.8 exceptions are not thread safe."
00088 #warning "Use properly configured EGCS-1.1 or greater."
00089 #endif // (__GNUC__<2 ...
00090 #endif // defined(__GNUC__)
00091 #endif // THREADMODEL!=NOTHREADS
00092 
00093 #ifndef _DEBUG
00094 #if defined(DEBUG) 
00095 #define _DEBUG /* */
00096 #elif DEBUGLVL >= 1
00097 #define _DEBUG /* */
00098 #endif
00099 #endif
00100 
00101 #if THREADMODEL==WINTHREADS
00102 # include <process.h>
00103 #endif
00104 #if THREADMODEL==COTHREADS
00105 # include <setjmp.h>
00106 # include <string.h>
00107 # include <unistd.h>
00108 # include <sys/types.h>
00109 # include <sys/time.h>
00110 #endif
00111 
00112 
00113 #ifdef HAVE_NAMESPACES
00114 namespace DJVU {
00115 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00116 }
00117 #endif
00118 #endif
00119 
00120 
00121 // ----------------------------------------
00122 // NOTHREADS
00123 // ----------------------------------------
00124 
00125 #if THREADMODEL==NOTHREADS
00126 int
00127 GThread::create( void (*entry)(void*), void *arg)
00128 {
00129   (*entry)(arg);
00130   return 0;
00131 }
00132 #endif
00133 
00134 
00135 // ----------------------------------------
00136 // WIN32 IMPLEMENTATION
00137 // ----------------------------------------
00138 
00139 #if THREADMODEL==WINTHREADS
00140 
00141 static unsigned __stdcall 
00142 start(void *arg)
00143 {
00144   GThread *gt = (GThread*)arg;
00145   try 
00146     {
00147       G_TRY
00148         {
00149           gt->xentry( gt->xarg );
00150         }
00151       G_CATCH(ex)
00152         {
00153           ex.perror();
00154           DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
00155 #ifdef _DEBUG
00156           abort();
00157 #endif
00158         }
00159       G_ENDCATCH;
00160     }
00161   catch(...)
00162     {
00163       DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
00164 #ifdef _DEBUG
00165       abort();
00166 #endif
00167     }
00168   return 0;
00169 }
00170 
00171 GThread::GThread(int stacksize)
00172   : hthr(0), thrid(0), xentry(0), xarg(0)
00173 {
00174 }
00175 
00176 GThread::~GThread()
00177 {
00178   if (hthr)
00179     CloseHandle(hthr);
00180   hthr = 0;
00181   thrid = 0;
00182 }
00183 
00184 int  
00185 GThread::create(void (*entry)(void*), void *arg)
00186 {
00187   if (hthr)
00188     return -1;
00189   xentry = entry;
00190   xarg = arg;
00191   unsigned uthread = 0;
00192   hthr = (HANDLE)_beginthreadex(NULL, 0, start, (void*)this, 0, &uthread);
00193   thrid = (DWORD) uthread;
00194   if (hthr)
00195     return 0;
00196   return -1;
00197 }
00198 
00199 void 
00200 GThread::terminate()
00201 {
00202   OutputDebugString("Terminating thread.\n");
00203   if (hthr)
00204     TerminateThread(hthr,0);
00205 }
00206 
00207 int
00208 GThread::yield()
00209 {
00210   Sleep(0);
00211   return 0;
00212 }
00213 
00214 void *
00215 GThread::current()
00216 {
00217   return (void*) GetCurrentThreadId();
00218 }
00219 
00220 struct thr_waiting {
00221   struct thr_waiting *next;
00222   struct thr_waiting *prev;
00223   BOOL   waiting;
00224   HANDLE gwait;
00225 };
00226 
00227 GMonitor::GMonitor()
00228   : ok(0), count(1), head(0), tail(0)
00229 {
00230   InitializeCriticalSection(&cs);
00231   locker = GetCurrentThreadId();
00232   ok = 1;
00233 }
00234 
00235 GMonitor::~GMonitor()
00236 {
00237   ok = 0;
00238   EnterCriticalSection(&cs);
00239   for (struct thr_waiting *w=head; w; w=w->next)
00240     SetEvent(w->gwait);
00241   LeaveCriticalSection(&cs);
00242   DeleteCriticalSection(&cs); 
00243 }
00244 
00245 void 
00246 GMonitor::enter()
00247 {
00248   DWORD self = GetCurrentThreadId();
00249   if (count>0 || self!=locker)
00250     {
00251       if (ok)
00252         EnterCriticalSection(&cs);
00253       locker = self;
00254       count = 1;
00255     }
00256   count -= 1;
00257 }
00258 
00259 void 
00260 GMonitor::leave()
00261 {
00262   DWORD self = GetCurrentThreadId();
00263   if (ok && (count>0 || self!=locker))
00264     G_THROW( ERR_MSG("GThreads.not_acq_broad") );
00265   count += 1;
00266   if (count > 0)
00267     {
00268       count = 1;
00269       if (ok)
00270         LeaveCriticalSection(&cs);
00271     }
00272 }
00273 
00274 void
00275 GMonitor::signal()
00276 {
00277   if (ok)
00278     {
00279       DWORD self = GetCurrentThreadId();
00280       if (count>0 || self!=locker)
00281         G_THROW( ERR_MSG("GThreads.not_acq_signal") );
00282       for (struct thr_waiting *w=head; w; w=w->next)
00283         if (w->waiting) 
00284           {
00285             SetEvent(w->gwait);
00286             w->waiting = FALSE;
00287             break; // Only one thread is allowed to run!
00288           }
00289     }
00290 }
00291 
00292 void
00293 GMonitor::broadcast()
00294 {
00295   if (ok)
00296     {
00297       DWORD self = GetCurrentThreadId();
00298       if (count>0 || self!=locker)
00299         G_THROW( ERR_MSG("GThreads.not_acq_broad") );
00300       for (struct thr_waiting *w=head; w; w=w->next)
00301         if (w->waiting)
00302             {
00303               SetEvent(w->gwait);
00304               w->waiting = FALSE;
00305             }
00306     }
00307 }
00308 
00309 void
00310 GMonitor::wait()
00311 {
00312   // Check state
00313   DWORD self = GetCurrentThreadId();
00314   if (count>0 || self!=locker)
00315     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
00316   // Wait
00317   if (ok)
00318     {
00319       // Prepare wait record
00320       struct thr_waiting waitrec;
00321       waitrec.waiting = TRUE;
00322       waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL);
00323       waitrec.next = 0;
00324       waitrec.prev = tail;
00325       // Link wait record (protected by critical section)
00326       *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; 
00327       *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec;
00328       // Start wait
00329       int sav_count = count;
00330       count = 1;
00331       LeaveCriticalSection(&cs);
00332       WaitForSingleObject(waitrec.gwait,INFINITE);
00333       // Re-acquire
00334       EnterCriticalSection(&cs);
00335       count = sav_count;
00336       locker = self;
00337       // Unlink wait record
00338       *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev;
00339       *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next;
00340       CloseHandle(waitrec.gwait);
00341     }
00342 }
00343 
00344 void
00345 GMonitor::wait(unsigned long timeout) 
00346 {
00347   // Check state
00348   DWORD self = GetCurrentThreadId();
00349   if (count>0 || self!=locker)
00350     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
00351   // Wait
00352   if (ok)
00353     {
00354       // Prepare wait record
00355       struct thr_waiting waitrec;
00356       waitrec.waiting = TRUE;
00357       waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL);
00358       waitrec.next = 0;
00359       waitrec.prev = tail;
00360       // Link wait record (protected by critical section)
00361       *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec;
00362       *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; 
00363       // Start wait
00364       int sav_count = count;
00365       count = 1;
00366       LeaveCriticalSection(&cs);
00367       WaitForSingleObject(waitrec.gwait,timeout);
00368       // Re-acquire
00369       EnterCriticalSection(&cs);
00370       count = sav_count;
00371       locker = self;
00372       // Unlink wait record
00373       *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev;
00374       *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next;
00375       CloseHandle(waitrec.gwait);
00376     }
00377 }
00378 
00379 #endif
00380 
00381 
00382 
00383 // ----------------------------------------
00384 // MACTHREADS IMPLEMENTATION (from Praveen)
00385 // ----------------------------------------
00386 
00387 #if THREADMODEL==MACTHREADS
00388 
00389 // Doubly linked list of waiting threads
00390 struct thr_waiting {
00391   struct thr_waiting *next;     // ptr to next waiting thread record
00392   struct thr_waiting *prev;     // ptr to ptr to this waiting thread
00393   unsigned long thid;           // id of waiting thread
00394   int *wchan;                   // cause of the wait
00395 };
00396 static struct thr_waiting *first_waiting_thr = 0;
00397 static struct thr_waiting *last_waiting_thr = 0;
00398 
00399 
00400 // Stops current thread. 
00401 // Argument ``self'' must be current thread id.
00402 // Assumes ``ThreadBeginCritical'' has been called before.
00403 static void
00404 macthread_wait(ThreadID self, int *wchan)
00405 {
00406   // Prepare and link wait record
00407   struct thr_waiting wait; // no need to malloc :-)
00408   wait.thid = self;
00409   wait.wchan = wchan;
00410   wait.next = 0;
00411   wait.prev = last_waiting_thr;
00412   *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = &wait;
00413   *(wait.next ? &wait.next->prev : &last_waiting_thr ) = &wait;
00414   // Leave critical section and start waiting.
00415   (*wchan)++;
00416   SetThreadStateEndCritical(self, kStoppedThreadState, kNoThreadID);
00417   // The Apple documentation says that the above call reschedules a new
00418   // thread.  Therefore it will only return when the thread wakes up.
00419   ThreadBeginCritical();
00420   (*wchan)--;
00421   // Unlink wait record
00422   *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = wait.next;
00423   *(wait.next ? &wait.next->prev : &last_waiting_thr ) = wait.prev;
00424   // Returns from the wait.
00425 }
00426 
00427 // Wakeup one thread or all threads waiting on cause wchan
00428 static void
00429 macthread_wakeup(int *wchan, int onlyone)
00430 {
00431   if (*wchan == 0)
00432     return;
00433   for (struct thr_waiting *q=first_waiting_thr; q; q=q->next)
00434     if (q->wchan == wchan) {
00435       // Found a waiting thread
00436       q->wchan = 0;
00437       SetThreadState(q->thid, kReadyThreadState, kNoThreadID);
00438       if (onlyone)
00439         return;
00440     }
00441 }
00442 
00443 GThread::GThread(int stacksize) 
00444   : thid(kNoThreadID), xentry(0), xarg(0)
00445 {
00446 }
00447 
00448 GThread::~GThread(void)
00449 {
00450   thid = kNoThreadID;
00451 }
00452 
00453 pascal void *
00454 GThread::start(void *arg)
00455 {
00456   GThread *gt = (GThread*)arg;
00457   try 
00458     {
00459       G_TRY
00460         {
00461           (gt->xentry)(gt->xarg);
00462         }
00463       G_CATCH(ex)
00464         {
00465           ex.perror();
00466           DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
00467 #ifdef _DEBUG
00468           abort();
00469 #endif
00470         }
00471       G_ENDCATCH;
00472     }
00473   catch(...)
00474     {
00475       DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
00476 #ifdef _DEBUG
00477       abort();
00478 #endif
00479     }
00480   return 0;
00481 }
00482 
00483 int
00484 GThread::create(void (*entry)(void*), void *arg)
00485 {
00486   if (xentry || thid!=kNoThreadID)
00487     return -1;
00488   xentry = entry;
00489   xarg = arg;
00490   int err = NewThread( kCooperativeThread, GThread::start , this, 0,
00491                        kCreateIfNeeded, (void**)nil, &thid );
00492   if( err != noErr )
00493     return err;
00494   return 0;
00495 }
00496 
00497 void
00498 GThread::terminate()
00499 {
00500   if (thid != kNoThreadID) {
00501     DisposeThread( thid, NULL, false );
00502     thid = kNoThreadID;
00503   }
00504 }
00505 
00506 int
00507 GThread::yield()
00508 {
00509   YieldToAnyThread();
00510   return 0;
00511 }
00512 
00513 void*
00514 GThread::current()
00515 {
00516   unsigned long thid = kNoThreadID;
00517   GetCurrentThread(&thid);
00518   return (void*) thid;
00519 }
00520 
00521 
00522 // GMonitor implementation
00523 GMonitor::GMonitor() 
00524   : ok(0), count(1), locker(0), wlock(0), wsig(0)
00525 {
00526   locker = kNoThreadID;
00527   ok = 1;
00528 }
00529 
00530 GMonitor::~GMonitor() 
00531 {
00532   ok = 0;
00533   ThreadBeginCritical();
00534   macthread_wakeup(&wsig, 0);
00535   macthread_wakeup(&wlock, 0);
00536   ThreadEndCritical();
00537   YieldToAnyThread();
00538 }
00539 
00540 void 
00541 GMonitor::enter() 
00542 {
00543   ThreadID self;
00544   GetCurrentThread(&self);
00545   ThreadBeginCritical();
00546   if (count>0 || self!=locker)
00547     {
00548       while (ok && count<=0)
00549         macthread_wait(self, &wlock);
00550       count = 1;
00551       locker = self;
00552     }
00553   count -= 1;
00554   ThreadEndCritical();
00555 }
00556 
00557 void 
00558 GMonitor::leave() 
00559 {
00560   ThreadID self;
00561   GetCurrentThread(&self);
00562   if (ok && (count>0 || self!=locker))
00563     G_THROW( ERR_MSG("GThreads.not_acq_leave") );
00564   ThreadBeginCritical();
00565   if (++count > 0)
00566     macthread_wakeup(&wlock, 1);
00567   ThreadEndCritical();
00568 }
00569 
00570 void 
00571 GMonitor::signal() 
00572 {
00573   ThreadID self;
00574   GetCurrentThread(&self);
00575   if (count>0 || self!=locker)
00576     G_THROW( ERR_MSG("GThreads.not_acq_signal") );
00577   ThreadBeginCritical();
00578   macthread_wakeup(&wsig, 1);
00579   ThreadEndCritical();
00580 }
00581 
00582 void 
00583 GMonitor::broadcast() 
00584 {
00585   ThreadID self;
00586   GetCurrentThread(&self);
00587   if (count>0 || self!=locker)
00588     G_THROW( ERR_MSG("GThreads.not_acq_broad") );
00589   ThreadBeginCritical();
00590   macthread_wakeup(&wsig, 0);
00591   ThreadEndCritical();
00592 }
00593 
00594 void 
00595 GMonitor::wait() 
00596 {
00597   // Check state
00598   ThreadID self;
00599   GetCurrentThread(&self);
00600   if (count>0 || locker!=self)
00601     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
00602   // Wait
00603   if (ok)
00604     {
00605       // Atomically release monitor and wait
00606       ThreadBeginCritical();
00607       int sav_count = count;
00608       count = 1;
00609       macthread_wakeup(&wlock, 1);
00610       macthread_wait(self, &wsig);
00611       // Re-acquire
00612       while (ok && count<=0)
00613         macthread_wait(self, &wlock);
00614       count = sav_count;
00615       locker = self;
00616       ThreadEndCritical();
00617     }
00618 }
00619 
00620 void 
00621 GMonitor::wait(unsigned long timeout) 
00622 {
00623   // Timeouts are not used for anything important.
00624   // Just ignore the timeout and wait the regular way.
00625   wait();
00626 }
00627 
00628 #endif
00629 
00630 
00631 
00632 // ----------------------------------------
00633 // POSIXTHREADS IMPLEMENTATION
00634 // ----------------------------------------
00635 
00636 #if THREADMODEL==POSIXTHREADS
00637 
00638 #if defined(CMA_INCLUDE)
00639 #define DCETHREADS
00640 #define pthread_key_create pthread_keycreate
00641 #else
00642 #define pthread_mutexattr_default  NULL
00643 #define pthread_condattr_default   NULL
00644 #endif
00645 
00646 
00647 void *
00648 GThread::start(void *arg)
00649 {
00650   GThread *gt = (GThread*)arg;
00651 #ifdef DCETHREADS
00652 #ifdef CANCEL_ON
00653   pthread_setcancel(CANCEL_ON);
00654   pthread_setasynccancel(CANCEL_ON);
00655 #endif
00656 #else // !DCETHREADS
00657 #ifdef PTHREAD_CANCEL_ENABLE
00658   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
00659 #endif
00660 #ifdef PTHREAD_CANCEL_ASYNCHRONOUS
00661   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
00662 #endif
00663 #endif
00664   // Catch exceptions
00665 #ifdef __EXCEPTIONS
00666   try 
00667     {
00668 #endif 
00669       G_TRY
00670         {
00671           (gt->xentry)(gt->xarg);
00672         }
00673       G_CATCH(ex)
00674         {
00675           ex.perror();
00676           DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
00677 #ifdef _DEBUG
00678           abort();
00679 #endif
00680         }
00681       G_ENDCATCH;
00682 #ifdef __EXCEPTIONS
00683     }
00684   catch(...)
00685     {
00686           DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
00687 #ifdef _DEBUG
00688       abort();
00689 #endif
00690     }
00691 #endif
00692   return 0;
00693 }
00694 
00695 
00696 // GThread
00697 
00698 GThread::GThread(int stacksize) : 
00699   hthr(0), xentry(0), xarg(0)
00700 {
00701 }
00702 
00703 GThread::~GThread()
00704 {
00705   hthr = 0;
00706 }
00707 
00708 int  
00709 GThread::create(void (*entry)(void*), void *arg)
00710 {
00711   if (xentry || xarg)
00712     return -1;
00713   xentry = entry;
00714   xarg = arg;
00715 #ifdef DCETHREADS
00716   int ret = pthread_create(&hthr, pthread_attr_default, GThread::start, (void*)this);
00717   if (ret >= 0)
00718     pthread_detach(hthr);
00719 #else
00720   pthread_attr_t attr;
00721   pthread_attr_init(&attr);
00722   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00723   int ret = pthread_create(&hthr, &attr, start, (void*)this);
00724   pthread_attr_destroy(&attr);
00725 #endif
00726   return ret;
00727 }
00728 
00729 void 
00730 GThread::terminate()
00731 {
00732   if (xentry || xarg)
00733     pthread_cancel(hthr);
00734 }
00735 
00736 int
00737 GThread::yield()
00738 {
00739 #ifdef DCETHREADS
00740   pthread_yield();
00741 #else
00742   // should use sched_yield() when available.
00743   static struct timeval timeout = { 0, 0 };
00744   ::select(0, 0,0,0, &timeout);
00745 #endif
00746   return 0;
00747 }
00748 
00749 void*
00750 GThread::current()
00751 {
00752   pthread_t self = pthread_self();
00753 #if defined(pthread_getunique_np)
00754   return (void*) pthread_getunique_np( & self );
00755 #elif defined(cma_thread_get_unique)
00756   return (void*) cma_thread_get_unique( & self );  
00757 #else
00758   return (void*) self;
00759 #endif
00760 }
00761 
00762 // -- GMonitor
00763 
00764 GMonitor::GMonitor()
00765   : ok(0), count(1), locker(0)
00766 {
00767   // none of this should be necessary ... in theory.
00768 #ifdef PTHREAD_MUTEX_INITIALIZER
00769   static pthread_mutex_t tmutex=PTHREAD_MUTEX_INITIALIZER;
00770   memcpy(&mutex,&tmutex,sizeof(mutex));
00771 #endif
00772 #ifdef PTHREAD_COND_INITIALIZER
00773   static pthread_cond_t tcond=PTHREAD_COND_INITIALIZER;
00774   memcpy(&cond,&tcond,sizeof(cond));
00775 #endif
00776   // standard
00777   pthread_mutex_init(&mutex, pthread_mutexattr_default);
00778   pthread_cond_init(&cond, pthread_condattr_default); 
00779   locker = pthread_self();
00780   ok = 1;
00781 }
00782 
00783 GMonitor::~GMonitor()
00784 {
00785   ok = 0;
00786   pthread_cond_destroy(&cond);
00787   pthread_mutex_destroy(&mutex); 
00788 }
00789 
00790 
00791 void 
00792 GMonitor::enter()
00793 {
00794   pthread_t self = pthread_self();
00795   if (count>0 || !pthread_equal(locker, self))
00796     {
00797       if (ok)
00798         pthread_mutex_lock(&mutex);
00799       locker = self;
00800       count = 1;
00801     }
00802   count -= 1;
00803 }
00804 
00805 void 
00806 GMonitor::leave()
00807 {
00808   pthread_t self = pthread_self();
00809   if (ok && (count>0 || !pthread_equal(locker, self)))
00810     G_THROW( ERR_MSG("GThreads.not_acq_broad") );
00811   count += 1;
00812   if (count > 0)
00813     {
00814       count = 1;
00815       if (ok)
00816         pthread_mutex_unlock(&mutex);
00817     }
00818 }
00819 
00820 void
00821 GMonitor::signal()
00822 {
00823   if (ok)
00824     {
00825       pthread_t self = pthread_self();
00826       if (count>0 || !pthread_equal(locker, self))
00827         G_THROW( ERR_MSG("GThreads.not_acq_signal") );
00828       pthread_cond_signal(&cond);
00829     }
00830 }
00831 
00832 void
00833 GMonitor::broadcast()
00834 {
00835   if (ok)
00836     {
00837       pthread_t self = pthread_self();
00838       if (count>0 || !pthread_equal(locker, self))
00839         G_THROW( ERR_MSG("GThreads.not_acq_broad") );
00840       pthread_cond_broadcast(&cond);
00841     }
00842 }
00843 
00844 void
00845 GMonitor::wait()
00846 {
00847   // Check
00848   pthread_t self = pthread_self();
00849   if (count>0 || !pthread_equal(locker, self))
00850     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
00851   // Wait
00852   if (ok)
00853     {
00854       // Release
00855       int sav_count = count;
00856       count = 1;
00857       // Wait
00858       pthread_cond_wait(&cond, &mutex);
00859       // Re-acquire
00860       count = sav_count;
00861       locker = self;
00862     }      
00863 }
00864 
00865 void
00866 GMonitor::wait(unsigned long timeout) 
00867 {
00868   // Check
00869   pthread_t self = pthread_self();
00870   if (count>0 || !pthread_equal(locker, self))
00871     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
00872   // Wait
00873   if (ok)
00874     {
00875       // Release
00876       int sav_count = count;
00877       count = 1;
00878       // Wait
00879       struct timeval  abstv;
00880       struct timespec absts;
00881       gettimeofday(&abstv, NULL); // grrr
00882       absts.tv_sec = abstv.tv_sec + timeout/1000;
00883       absts.tv_nsec = abstv.tv_usec*1000  + (timeout%1000)*1000000;
00884       if (absts.tv_nsec > 1000000000) {
00885         absts.tv_nsec -= 1000000000;
00886         absts.tv_sec += 1;
00887       }
00888       pthread_cond_timedwait(&cond, &mutex, &absts);
00889       // Re-acquire
00890       count = sav_count;
00891       locker = self;
00892     }      
00893 }
00894 
00895 #endif
00896 
00897 
00898 
00899 // ----------------------------------------
00900 // CUSTOM COOPERATIVE THREADS
00901 // ----------------------------------------
00902 
00903 #if THREADMODEL==COTHREADS
00904 
00905 #ifndef __GNUG__
00906 #error "COTHREADS require G++"
00907 #endif
00908 #if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=90))
00909 #warning "COTHREADS require EGCS-1.1.1 with Leon's libgcc patch."
00910 #warning "You may have trouble with thread-unsafe exceptions..."
00911 #define NO_LIBGCC_HOOKS
00912 #endif
00913 
00914 // -------------------------------------- constants
00915 
00916 // Minimal stack size
00917 #define MINSTACK   (32*1024)
00918 // Default stack size
00919 #define DEFSTACK   (127*1024)
00920 // Maxtime between checking fdesc (ms)
00921 #define MAXFDWAIT    (200)
00922 // Maximum time to wait in any case
00923 #define MAXWAIT (60*60*1000)
00924 // Maximum penalty for hog task (ms)
00925 #define MAXPENALTY (1000)
00926 // Trace task switches
00927 #undef COTHREAD_TRACE
00928 #undef COTHREAD_TRACE_VERBOSE
00929 
00930 // -------------------------------------- context switch code
00931 
00932 struct mach_state { 
00933   jmp_buf buf; 
00934 };
00935 
00936 static void
00937 mach_switch(mach_state *st1, mach_state *st2)
00938 { 
00939 #if #cpu(sparc)
00940   asm("ta 3"); // save register windows
00941 #endif
00942   if (! setjmp(st1->buf))
00943     longjmp(st2->buf, 1);
00944 }
00945 
00946 static void 
00947 mach_start(mach_state *st1, void *pc, char *stacklo, char *stackhi)
00948 { 
00949 #if #cpu(sparc)
00950   asm("ta 3"); // save register windows
00951 #endif
00952   if (! setjmp(st1->buf))
00953     {
00954       // The following code must perform two tasks:
00955       // -- set stack pointer to a proper value between #stacklo# and #stackhi#.
00956       // -- branch to or call the function at address #pc#.
00957       // This function never returns ... so there is no need to save anything
00958 #if #cpu(mips)
00959       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
00960       asm volatile ("move $sp,%0\n\t"  // set new stack pointer
00961                     "move $25,%1\n\t"  // call subroutine via $25
00962                     "jal  $25\n\t"     // call subroutine via $25
00963                     "nop"              // delay slot
00964                     : : "r" (sp), "r" (pc) );
00965 #elif #cpu(i386)
00966       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
00967       asm volatile ("movl %0,%%esp\n\t" // set new stack pointer
00968                     "call *%1"          // call function
00969                     : : "r" (sp), "r" (pc) );
00970 #elif #cpu(sparc)
00971       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
00972       asm volatile ("ta 3\n\t"          // saving the register windows will not hurt.
00973                     "mov %0,%%sp\n\t"   // set new stack pointer
00974                     "call %1,0\n\t"     // call function
00975                     "nop"               // delay slot
00976                     : : "r" (sp), "r" (pc) );
00977 #elif #cpu(hppa)
00978       char *sp = (char*)(((unsigned long)stacklo+128+255) & ~0xff);
00979       asm volatile("copy %0,%%sp\n\t"       // set stack pointer
00980                    "copy %1,%%r22\n\t"      // set call address
00981                    ".CALL\n\t"              // call pseudo instr (why?)
00982                    "bl $$dyncall,%%r31\n\t" // call 
00983                    "copy %%r31,%%r2"        // delay slot ???
00984                    : : "r" (sp), "r" (pc) );
00985 #elif #cpu(alpha)
00986       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
00987       asm volatile ("bis $31,%0,$30\n\t"  // set new stack pointer
00988                     "bis $31,%1,$27\n\t"  // load function pointer
00989                     "jsr $26,($27),0"     // call function
00990                     : : "r" (sp), "r" (pc) );
00991 #elif #cpu(powerpc)
00992       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
00993       asm volatile ("mr 1,%0\n\t"         // set new stack pointer
00994                     "mr 0,%1\n\t"         // load func pointer into r0
00995                     "mtlr 0\n\t"          // load link register with r0
00996                     "blrl"                // branch
00997                     : : "r" (sp), "r" (pc) );
00998 #elif #cpu(m68k) && defined(COTHREAD_UNTESTED)
00999       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
01000       asm volatile ("move%.l %0,%Rsp\n\t" // set new stack pointer
01001                     "jmp %a1"             // branch to address %1
01002                     : : "r" (sp), "a" (pc) );
01003 #elif #cpu(arm) && defined(COTHREAD_UNTESTED)
01004       char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
01005       asm volatile ("mov%?\t%|sp, %0\n\t" // set new stack pointer
01006                     "mov%?\t%|pc, %1"     // branch to address %1
01007                     : : "r" (sp), "r" (pc) );
01008 #else
01009 #error "COTHREADS not supported on this machine."
01010 #error "Try -DTHREADMODEL=NOTHREADS."
01011 #endif
01012       // We should never reach this point
01013       abort();
01014       // Note that this call to abort() makes sure
01015       // that function mach_start() is compiled as a non-leaf
01016       // function. It is indeed a non-leaf function since the
01017       // piece of assembly code calls a function, but the compiler
01018       // would not know without the call to abort() ...
01019     }
01020 }
01021 
01022 #ifdef CHECK
01023 // This code can be used to verify that task switching works.
01024 char stack[16384];
01025 mach_state st1, st2;
01026 void th2() {
01027   puts("2b"); mach_switch(&st2, &st1);
01028   puts("4b"); mach_switch(&st2, &st1);
01029   puts("6b"); mach_switch(&st2, &st1);
01030 }
01031 void th2relay() {
01032   th2(); puts("ooops\n");
01033 }
01034 void th1() {
01035   mach_start(&st1, (void*)th2relay, stack, stack+sizeof(stack));
01036   puts("3a"); mach_switch(&st1, &st2);
01037   puts("5a"); mach_switch(&st1, &st2);
01038 }
01039 int main() { 
01040   puts("1a"); th1(); puts("6a"); 
01041 }
01042 #endif
01043 
01044 
01045 
01046 // -------------------------------------- select
01047 
01048 struct coselect {
01049   int nfds;
01050   fd_set rset;
01051   fd_set wset;
01052   fd_set eset;
01053 };
01054 
01055 static void 
01056 coselect_merge(coselect *dest, coselect *from)
01057 {
01058   int i;
01059   int nfds = from->nfds;
01060   if (nfds > dest->nfds)
01061     dest->nfds = nfds;
01062   for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->rset)) FD_SET(i, &dest->rset);
01063   for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->wset)) FD_SET(i, &dest->wset);
01064   for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->eset)) FD_SET(i, &dest->eset);
01065 }
01066 
01067 static int
01068 coselect_test(coselect *c)
01069 {
01070   static timeval tmzero = {0,0};
01071   fd_set copyr = c->rset;
01072   fd_set copyw = c->wset;
01073   fd_set copye = c->eset;
01074   return select(c->nfds, &copyr, &copyw, &copye, &tmzero);
01075 }
01076 
01077 
01078 // -------------------------------------- cotask
01079 
01080 class GThread::cotask {
01081 public:
01082 #ifndef NO_LIBGCC_HOOKS
01083   cotask(const int xstacksize,void *);
01084 #else
01085   cotask(const int xstacksize);
01086 #endif
01087   ~cotask();
01088   class GThread::cotask *next;
01089   class GThread::cotask *prev;
01090   // context
01091   mach_state regs;
01092   // stack information
01093   char *stack;
01094   GPBuffer<char> gstack;
01095   int stacksize;
01096   // timing information
01097   unsigned long over;
01098   // waiting information
01099   void *wchan;
01100   coselect *wselect;
01101   unsigned long *maxwait;
01102   // delete after termination
01103   bool autodelete;
01104   // egcs exception support
01105 #ifndef NO_LIBGCC_HOOKS
01106   void *ehctx;
01107 #endif
01108 };
01109 
01110 #ifndef NO_LIBGCC_HOOKS
01111 GThread::cotask::cotask(const int xstacksize, void *xehctx)
01112 #else
01113 GThread::cotask::cotask(const int xstacksize)
01114 #endif
01115 : next(0), prev(0), gstack(stack,xstacksize), stacksize(xstacksize),
01116   over(0), wchan(0), wselect(0), maxwait(0), autodelete(false)
01117 #ifndef NO_LIBGCC_HOOKS
01118   ,ehctx(xehctx)
01119 #endif
01120 {
01121   memset(&regs,0,sizeof(regs));
01122 }
01123 
01124 static GThread::cotask *maintask = 0;
01125 static GThread::cotask *curtask  = 0;
01126 static GThread::cotask *autodeletetask = 0;
01127 static unsigned long globalmaxwait = 0;
01128 static void (*scheduling_callback)(int) = 0;
01129 static timeval time_base;
01130 
01131 
01132 GThread::cotask::~cotask()
01133 {
01134   gstack.resize(0);
01135 #ifndef NO_LIBGCC_HOOKS
01136   if (ehctx)
01137     free(ehctx);
01138   ehctx = 0;
01139 #endif
01140 }
01141 
01142 static void 
01143 cotask_free(GThread::cotask *task)
01144 {
01145 #ifdef COTHREAD_TRACE
01146   DjVuPrintErrorUTF8("cothreads: freeing task %p with autodelete=%d\n", 
01147           task,task->autodelete);
01148 #endif
01149   if (task!=maintask)
01150   {
01151     delete task;
01152   }
01153 }
01154 
01155 
01156 // -------------------------------------- time
01157 
01158 static unsigned long
01159 time_elapsed(int reset=1)
01160 {
01161   timeval tm;
01162   gettimeofday(&tm, NULL);
01163   long msec = (tm.tv_usec-time_base.tv_usec)/1000;
01164   unsigned long elapsed = (long)(tm.tv_sec-time_base.tv_sec)*1000 + msec;
01165   if (reset && elapsed>0)
01166     {
01167 #ifdef COTHREAD_TRACE
01168 #ifdef COTHREAD_TRACE_VERBOSE
01169       DjVuPrintErrorUTF8("cothreads: %4ld ms in task %p\n", elapsed, curtask);
01170 #endif
01171 #endif
01172       time_base.tv_sec = tm.tv_sec;
01173       time_base.tv_usec += msec*1000;
01174     }
01175   return elapsed;
01176 }
01177 
01178 
01179 // -------------------------------------- scheduler
01180 
01181 static int
01182 cotask_yield()
01183 {
01184   // ok
01185   if (! maintask)
01186     return 0;
01187   // get elapsed time and return immediately when it is too small
01188   unsigned long elapsed = time_elapsed();
01189   if (elapsed==0 && curtask->wchan==0 && curtask->prev && curtask->next)
01190     return 0;
01191   // adjust task running time
01192   curtask->over += elapsed;
01193   if (curtask->over > MAXPENALTY)
01194     curtask->over = MAXPENALTY;
01195   // start scheduling
01196  reschedule:
01197   // try unblocking tasks
01198   GThread::cotask *n = curtask->next;
01199   GThread::cotask *q = n;
01200   do 
01201     { 
01202       if (q->wchan)
01203         {
01204       if (q->maxwait && *q->maxwait<=elapsed) 
01205             {
01206               *q->maxwait = 0;
01207               q->wchan=0; 
01208               q->maxwait=0; 
01209               q->wselect=0; 
01210             }
01211           else if (q->wselect && globalmaxwait<=elapsed && coselect_test(q->wselect))
01212             {
01213               q->wchan=0;
01214               if (q->maxwait)
01215                 *q->maxwait -= elapsed;
01216               q->maxwait = 0; 
01217               q->wselect=0; 
01218             }
01219           if (q->maxwait)
01220             *q->maxwait -= elapsed;
01221         }
01222       q = q->next;
01223     } 
01224   while (q!=n);
01225   // adjust globalmaxwait
01226   if (globalmaxwait < elapsed)
01227     globalmaxwait = MAXFDWAIT;
01228   else
01229     globalmaxwait -= elapsed;
01230   // find best candidate
01231   static int count;
01232   unsigned long best = MAXPENALTY + 1;
01233   GThread::cotask *r = 0;
01234   count = 0;
01235   q = n;
01236   do 
01237     { 
01238       if (! q->wchan)
01239         {
01240           count += 1;
01241           if (best > q->over)
01242             {
01243               r = q;
01244               best = r->over;
01245             } 
01246         }
01247       q = q->next;
01248     } 
01249   while (q != n);
01250   // found
01251   if (count > 0)
01252     {
01253       // adjust over 
01254       q = n;
01255       do 
01256         { 
01257           q->over = (q->over>best ? q->over-best : 0);
01258           q = q->next;
01259         } 
01260       while (q != n);
01261       // Switch
01262       if (r != curtask)
01263         {
01264 #ifdef COTHREAD_TRACE
01265           DjVuPrintErrorUTF8("cothreads: ----- switch to %p [%ld]\n", r, best);
01266 #endif
01267           GThread::cotask *old = curtask;
01268           curtask = r;
01269           mach_switch(&old->regs, &curtask->regs);
01270         }
01271       // handle autodelete
01272       if (autodeletetask && autodeletetask->autodelete) 
01273         cotask_free(autodeletetask);
01274       autodeletetask = 0;
01275       // return 
01276       if (count == 1)
01277         return 1;
01278       return 0;
01279     }
01280   // No task ready
01281   count = 0;
01282   unsigned long minwait = MAXWAIT;
01283   coselect allfds;
01284   allfds.nfds = 1;
01285   FD_ZERO(&allfds.rset);
01286   FD_ZERO(&allfds.wset);
01287   FD_ZERO(&allfds.eset);
01288   q = n;
01289   do 
01290     {
01291       if (q->maxwait || q->wselect)
01292         count += 1;
01293       if (q->maxwait && *q->maxwait<minwait)
01294         minwait = *q->maxwait;
01295       if (q->wselect)
01296         coselect_merge(&allfds, q->wselect);
01297       q = q->next;
01298     } 
01299   while (q != n);
01300   // abort on deadlock
01301   if (count == 0) {
01302     DjVuMessageLite::perror( ERR_MSG("GThreads.panic") );
01303     abort();
01304   }
01305   // select
01306   timeval tm;
01307   tm.tv_sec = minwait/1000;
01308   tm.tv_usec = 1000*(minwait-1000*tm.tv_sec);
01309   select(allfds.nfds,&allfds.rset, &allfds.wset, &allfds.eset, &tm);
01310   // reschedule
01311   globalmaxwait = 0;
01312   elapsed = time_elapsed();
01313   goto reschedule;
01314 }
01315 
01316 
01317 static void
01318 cotask_terminate(GThread::cotask *task)
01319 {
01320 #ifdef COTHREAD_TRACE
01321   DjVuPrintErrorUTF8("cothreads: terminating task %p\n", task);
01322 #endif
01323   if (task && task!=maintask)
01324     {
01325       if (task->prev && task->next)
01326         {
01327           if (scheduling_callback)
01328             (*scheduling_callback)(GThread::CallbackTerminate);
01329           task->prev->next = task->next;
01330           task->next->prev = task->prev;
01331           // mark task as terminated
01332           task->prev = 0; 
01333           // self termination
01334           if (task == curtask)
01335             {
01336               if (task->autodelete)
01337                 autodeletetask = task;
01338               cotask_yield();
01339             }
01340         }
01341     }
01342 }
01343 
01344 
01345 static void
01346 cotask_wakeup(void *wchan, int onlyone)
01347 {
01348   if (maintask && curtask)
01349     {
01350       GThread::cotask *n = curtask->next;
01351       GThread::cotask *q = n;
01352       do 
01353         { 
01354           if (q->wchan == wchan)
01355             {
01356               q->wchan=0; 
01357               q->maxwait=0; 
01358               q->wselect=0; 
01359               q->over = 0;
01360           if (onlyone)
01361         return;
01362             }
01363           q = q->next;
01364         } 
01365       while (q!=n);
01366     }
01367 }
01368 
01369 
01370 // -------------------------------------- select / get_select
01371 
01372 static int
01373 cotask_select(int nfds, 
01374               fd_set *rfds, fd_set *wfds, fd_set *efds,
01375               struct timeval *tm)
01376 {
01377   // bypass
01378   if (maintask==0 || (tm && tm->tv_sec==0 && tm->tv_usec<1000))
01379     return select(nfds, rfds, wfds, efds, tm);
01380   // copy parameters
01381   unsigned long maxwait = 0;
01382   coselect parm;
01383   // set waiting info
01384   curtask->wchan = (void*)&parm;
01385   if (rfds || wfds || efds)
01386     {
01387       parm.nfds = nfds;
01388       if (rfds) { parm.rset=*rfds; } else { FD_ZERO(&parm.rset); }
01389       if (wfds) { parm.wset=*wfds; } else { FD_ZERO(&parm.wset); }
01390       if (efds) { parm.eset=*efds; } else { FD_ZERO(&parm.eset); }
01391       curtask->wselect = &parm;
01392     }
01393   if (tm) 
01394     {
01395       maxwait = time_elapsed(0) + tm->tv_sec*1000 + tm->tv_usec/1000;
01396       curtask->maxwait = &maxwait;
01397     }
01398   // reschedule
01399   cotask_yield();
01400   // call select to update masks
01401   if (tm)
01402     {
01403       tm->tv_sec = maxwait/1000;
01404       tm->tv_usec = 1000*(maxwait-1000*tm->tv_sec);
01405     }
01406   static timeval tmzero = {0,0};
01407   return select(nfds, rfds, wfds, efds, &tmzero);
01408 }
01409 
01410 
01411 static void 
01412 cotask_get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, 
01413                   unsigned long &timeout)
01414 {
01415   int ready = 1;
01416   unsigned long minwait = MAXWAIT;
01417   unsigned long elapsed = time_elapsed(0);
01418   coselect allfds;
01419   allfds.nfds=0;
01420   FD_ZERO(&allfds.rset);
01421   FD_ZERO(&allfds.wset);
01422   FD_ZERO(&allfds.eset);
01423   if (curtask)
01424     {
01425       GThread::cotask *q=curtask->next;
01426       while (q != curtask)
01427         {
01428           ready++;
01429           if (q->wchan)
01430             {
01431               if (q->wselect) 
01432                 coselect_merge(&allfds, q->wselect);
01433               if (q->maxwait && *q->maxwait<minwait)
01434                 minwait = *q->maxwait;
01435               ready--;
01436             }
01437           q = q->next;
01438         }
01439     }
01440   timeout = 0;
01441   nfds=allfds.nfds;
01442   *rfds=allfds.rset;
01443   *wfds=allfds.wset;
01444   *efds=allfds.eset;
01445   if (ready==1 && minwait>elapsed)
01446     timeout = minwait-elapsed;
01447 }
01448 
01449 
01450 
01451 // -------------------------------------- libgcc hook
01452 
01453 #ifndef NO_LIBGCC_HOOKS
01454 // These are exported by Leon's patched version of libgcc.a
01455 // Let's hope that the egcs people will include the patch in
01456 // the distributions.
01457 extern "C" 
01458 {
01459   extern void* (*__get_eh_context_ptr)(void);
01460   extern void* __new_eh_context(void);
01461 }
01462 
01463 // This function is called via the pointer __get_eh_context_ptr
01464 // by the internal mechanisms of egcs.  It must return the 
01465 // per-thread event handler context.  This is necessary to
01466 // implement thread safe exceptions on some machine and/or 
01467 // when flag -fsjlj-exception is set.
01468 static void *
01469 cotask_get_eh_context()
01470 {
01471   if (curtask)
01472     return curtask->ehctx;
01473   else if (maintask)
01474     return maintask->ehctx;
01475   DjVuMessageLite::perror( ERR_MSG("GThreads.co_panic") );
01476   abort();
01477 }
01478 #endif
01479 
01480 
01481 
01482 // -------------------------------------- GThread
01483 
01484 void 
01485 GThread::set_scheduling_callback(void (*call)(int))
01486 {
01487   if (scheduling_callback)
01488     G_THROW( ERR_MSG("GThreads.dupl_callback") );
01489   scheduling_callback = call;
01490 }
01491 
01492 
01493 GThread::GThread(int stacksize)
01494   : task(0), xentry(0), xarg(0)
01495 {
01496   // check argument
01497   if (stacksize < 0)
01498     stacksize = DEFSTACK;
01499   if (stacksize < MINSTACK)
01500     stacksize = MINSTACK;
01501   // initialization
01502   if (! maintask)
01503     {
01504 #ifndef NO_LIBGCC_HOOKS
01505       static GThread::cotask comaintask(0,(*__get_eh_context_ptr)());
01506       __get_eh_context_ptr = cotask_get_eh_context;
01507 #else
01508       static GThread::cotask comaintask(0);
01509 #endif
01510       maintask = &comaintask;
01511 //      memset(maintask, 0, sizeof(GThread::cotask));
01512       maintask->next = maintask;
01513       maintask->prev = maintask;
01514       gettimeofday(&time_base,NULL);
01515       curtask = maintask;
01516     }
01517   // allocation
01518 #ifndef NO_LIBGCC_HOOKS
01519   task = new GThread::cotask(stacksize,__new_eh_context());
01520 #else
01521   task = new GThread::cotask(stacksize);
01522 #endif
01523 }
01524 
01525 
01526 GThread::~GThread()
01527 {
01528   if (task && task!=maintask)
01529   {
01530     if (task->prev) // running
01531       task->autodelete = true;
01532     else
01533       cotask_free(task);
01534     task = 0;
01535   }
01536 }
01537 
01538 #if __GNUC__ >= 3
01539 # if __GNUC_MINOR__ >= 4
01540 #  define noinline __attribute__((noinline,used))
01541 # elif __GNUC_MINOR >= 2
01542 #  define noinline __attribute__((noinline))
01543 # endif
01544 #endif
01545 #ifndef noinline
01546 # define noinline 
01547 #endif
01548 
01549 static noinline void startone(void);
01550 static noinline void starttwo(GThread *thr);
01551 static GThread * volatile starter;
01552 
01553 static void
01554 startone(void)
01555 {
01556   GThread *thr = starter;
01557   mach_switch(&thr->task->regs, &curtask->regs);
01558   // Registers may still contain an improper pointer
01559   // to the exception context.  We should neither 
01560   // register cleanups nor register handlers.
01561   starttwo(thr);
01562   abort();
01563 }
01564 
01565 static void 
01566 starttwo(GThread *thr)
01567 {
01568   // Hopefully this function reacquires 
01569   // an exception context pointer. Therefore
01570   // we can register the exception handlers.
01571   // It is placed after ``startone'' to avoid inlining.
01572 #ifdef __EXCEPTIONS
01573   try 
01574     {
01575 #endif 
01576       G_TRY
01577         {
01578           thr->xentry( thr->xarg );
01579         }
01580       G_CATCH(ex)
01581         {
01582           ex.perror();
01583           DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
01584 #ifdef _DEBUG
01585           abort();
01586 #endif
01587         }
01588       G_ENDCATCH;
01589 #ifdef __EXCEPTIONS
01590     }
01591   catch(...)
01592     {
01593           DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
01594 #ifdef _DEBUG
01595       abort();
01596 #endif
01597     }
01598 #endif 
01599   cotask_terminate(curtask);
01600   GThread::yield();
01601   // Do not add anything below this line!
01602   // Nothing should reach it anyway.
01603   abort();
01604 }
01605 
01606 int 
01607 GThread::create(void (*entry)(void*), void *arg)
01608 {
01609   if (task->next || task->prev)
01610     return -1;
01611   xentry = entry;
01612   xarg = arg;
01613   task->wchan = 0;
01614   task->next = curtask;
01615   task->prev = curtask->prev;
01616   task->next->prev = task;
01617   task->prev->next = task;
01618   GThread::cotask *old = curtask;
01619   starter = this;
01620   mach_start(&old->regs, (void*)startone, 
01621              task->stack, task->stack+task->stacksize);
01622   if (scheduling_callback)
01623     (*scheduling_callback)(CallbackCreate);
01624   return 0;
01625 }
01626 
01627 
01628 void 
01629 GThread::terminate()
01630 {
01631   if (task && task!=maintask)
01632     cotask_terminate(task);
01633 }
01634 
01635 int
01636 GThread::yield()
01637 {
01638   return cotask_yield();
01639 }
01640 
01641 int 
01642 GThread::select(int nfds, 
01643                 fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
01644                 struct timeval *timeout)
01645 {
01646   return cotask_select(nfds, readfds, writefds, exceptfds, timeout);
01647 }
01648 
01649 void
01650 GThread::get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, 
01651                     unsigned long &timeout)
01652 {
01653   cotask_get_select(nfds, rfds, wfds, efds, timeout);
01654 }
01655 
01656 inline void *
01657 GThread::current()
01658 {
01659   if (curtask && curtask!=maintask)
01660     return (void*)curtask;
01661   return (void*)0;
01662 }
01663 
01664 
01665 // -------------------------------------- GMonitor
01666 
01667 GMonitor::GMonitor()
01668   : count(1), locker(0), wlock(0), wsig(0)
01669 {
01670   locker = 0;
01671   ok = 1;
01672 }
01673 
01674 GMonitor::~GMonitor()
01675 {
01676   ok = 0;
01677   cotask_wakeup((void*)&wsig, 0);
01678   cotask_wakeup((void*)&wlock, 0);    
01679   cotask_yield();
01680   // Because we know how the scheduler works, we know that this single call to
01681   // yield will run all unblocked tasks and given them the chance to leave the
01682   // scope of the monitor object.
01683 }
01684 
01685 void 
01686 GMonitor::enter()
01687 {
01688   void *self = GThread::current();
01689   if (count>0 || self!=locker)
01690     {
01691       while (ok && count<=0)
01692         {
01693           curtask->wchan = (void*)&wlock;
01694           wlock++;
01695           cotask_yield();
01696           wlock--;
01697         }
01698       count = 1;
01699       locker = self;
01700     }
01701   count -= 1;
01702 }
01703 
01704 void 
01705 GMonitor::leave()
01706 {
01707   void *self = GThread::current();
01708   if (ok && (count>0 || self!=locker))
01709     G_THROW( ERR_MSG("GThreads.not_acq_leave") );
01710   if (++count > 0 && wlock > 0)
01711     cotask_wakeup((void*)&wlock, 1);
01712 }
01713 
01714 void
01715 GMonitor::signal()
01716 {
01717   void *self = GThread::current();
01718   if (count>0 || self!=locker)
01719     G_THROW( ERR_MSG("GThreads.not_acq_signal") );
01720   if (wsig > 0)
01721     {
01722       cotask_wakeup((void*)&wsig, 1);
01723       if (scheduling_callback)
01724         (*scheduling_callback)(GThread::CallbackUnblock);
01725     }
01726 }
01727 
01728 void
01729 GMonitor::broadcast()
01730 {
01731   void *self = GThread::current();
01732   if (count>0 || self!=locker)
01733     G_THROW( ERR_MSG("GThreads.not_acq_broad") );
01734   if (wsig > 0)
01735     {
01736       cotask_wakeup((void*)&wsig, 0);
01737       if (scheduling_callback)
01738         (*scheduling_callback)(GThread::CallbackUnblock);
01739     }
01740 }
01741 
01742 void
01743 GMonitor::wait()
01744 {
01745   // Check state
01746   void *self = GThread::current();
01747   if (count>0 || locker!=self)
01748     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
01749   // Wait
01750   if (ok)
01751     {
01752       // Atomically release monitor and wait
01753       int sav_count = count;
01754       count = 1;
01755       curtask->wchan = (void*)&wsig;
01756       cotask_wakeup((void*)&wlock, 1);
01757       wsig++;
01758       cotask_yield();
01759       wsig--;
01760       // Re-acquire
01761       while (ok && count <= 0)
01762         {
01763           curtask->wchan = (void*)&wlock;
01764           wlock++;
01765           cotask_yield();
01766           wlock--;
01767         }
01768       count = sav_count;
01769       locker = self;
01770     }
01771 }
01772 
01773 void
01774 GMonitor::wait(unsigned long timeout) 
01775 {
01776   // Check state
01777   void *self = GThread::current();
01778   if (count>0 || locker!=self)
01779     G_THROW( ERR_MSG("GThreads.not_acq_wait") );
01780   // Wait
01781   if (ok)
01782     {
01783       // Atomically release monitor and wait
01784       int sav_count = count;
01785       count = 1;
01786       unsigned long maxwait = time_elapsed(0) + timeout;
01787       curtask->maxwait = &maxwait;
01788       curtask->wchan = (void*)&wsig;
01789       cotask_wakeup((void*)&wlock, 1);
01790       wsig++;
01791       cotask_yield();
01792       wsig--;
01793       // Re-acquire
01794       while (ok && count<=0)
01795         {
01796           curtask->wchan = (void*)&wlock;
01797           wlock++;
01798           cotask_yield();
01799           wlock--;
01800         }
01801       count = sav_count;
01802       locker = self;
01803     }
01804 }
01805 
01806 #endif
01807 
01808 
01809 
01810 
01811 // ----------------------------------------
01812 // GSAFEFLAGS 
01813 // ----------------------------------------
01814 
01815 
01816 
01817 GSafeFlags &
01818 GSafeFlags::operator=(long xflags)
01819 {
01820    enter();
01821    if (flags!=xflags)
01822    {
01823       flags=xflags;
01824       broadcast();
01825    }
01826    leave();
01827    return *this;
01828 }
01829 
01830 GSafeFlags::operator long(void) const
01831 {
01832    long f;
01833    ((GSafeFlags *) this)->enter();
01834    f=flags;
01835    ((GSafeFlags *) this)->leave();
01836    return f;
01837 }
01838 
01839 bool
01840 GSafeFlags::test_and_modify(long set_mask, long clr_mask,
01841                 long set_mask1, long clr_mask1)
01842 {
01843    enter();
01844    if ((flags & set_mask)==set_mask &&
01845        (~flags & clr_mask)==clr_mask)
01846    {
01847       long new_flags=flags;
01848       new_flags|=set_mask1;
01849       new_flags&=~clr_mask1;
01850       if (new_flags!=flags)
01851       {
01852      flags=new_flags;
01853      broadcast();
01854       }
01855       leave();
01856       return true;
01857    }
01858    leave();
01859    return false;
01860 }
01861 
01862 void
01863 GSafeFlags::wait_and_modify(long set_mask, long clr_mask,
01864                 long set_mask1, long clr_mask1)
01865 {
01866    enter();
01867    while((flags & set_mask)!=set_mask ||
01868      (~flags & clr_mask)!=clr_mask) wait();
01869    long new_flags=flags;
01870    new_flags|=set_mask1;
01871    new_flags&=~clr_mask1;
01872    if (flags!=new_flags)
01873    {
01874       flags=new_flags;
01875       broadcast();
01876    }
01877    leave();
01878 }
01879 
01880 
01881 
01882 #ifdef HAVE_NAMESPACES
01883 }
01884 # ifndef NOT_USING_DJVU_NAMESPACE
01885 using namespace DJVU;
01886 # endif
01887 #endif

kviewshell

Skip menu "kviewshell"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • kviewshell
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal