source: trunk/libdjvu/GThreads.cpp @ 280

Last change on this file since 280 was 280, checked in by rbri, 12 years ago

DJVU plugin: djvulibre updated to version 3.5.22

File size: 42.8 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, either Version 2 of the license,
9//C- or (at your option) any later version. The license should have
10//C- accompanied the software or you may obtain a copy of the license
11//C- from the Free Software Foundation at http://www.fsf.org .
12//C-
13//C- This program is distributed in the hope that it will be useful,
14//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16//C- GNU General Public License for more details.
17//C-
18//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19//C- Lizardtech Software.  Lizardtech Software has authorized us to
20//C- replace the original DjVu(r) Reference Library notice by the following
21//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22//C-
23//C-  ------------------------------------------------------------------
24//C- | DjVu (r) Reference Library (v. 3.5)
25//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26//C- | The DjVu Reference Library is protected by U.S. Pat. No.
27//C- | 6,058,214 and patents pending.
28//C- |
29//C- | This software is subject to, and may be distributed under, the
30//C- | GNU General Public License, either Version 2 of the license,
31//C- | or (at your option) any later version. The license should have
32//C- | accompanied the software or you may obtain a copy of the license
33//C- | from the Free Software Foundation at http://www.fsf.org .
34//C- |
35//C- | The computer code originally released by LizardTech under this
36//C- | license and unmodified by other parties is deemed "the LIZARDTECH
37//C- | ORIGINAL CODE."  Subject to any third party intellectual property
38//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39//C- | non-exclusive license to make, use, sell, or otherwise dispose of
40//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42//C- | General Public License.   This grant only confers the right to
43//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44//C- | the extent such infringement is reasonably necessary to enable
45//C- | recipient to make, have made, practice, sell, or otherwise dispose
46//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47//C- | any greater extent that may be necessary to utilize further
48//C- | modifications or combinations.
49//C- |
50//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54//C- +------------------------------------------------------------------
55//
56// $Id: GThreads.cpp,v 1.19 2007/03/25 20:48:32 leonb Exp $
57// $Name: release_3_5_22 $
58
59#ifdef HAVE_CONFIG_H
60# include "config.h"
61#endif
62#if NEED_GNUG_PRAGMAS
63# pragma implementation
64#endif
65
66// This file defines machine independent classes
67// for running and synchronizing threads.
68// - Author: Leon Bottou, 01/1998
69
70// From: Leon Bottou, 1/31/2002
71// Almost unchanged by Lizardtech.
72// GSafeFlags should go because it not as safe as it claims.
73
74#include "GThreads.h"
75#include "GException.h"
76#include "DjVuMessageLite.h"
77#include <stdlib.h>
78#include <stdio.h>
79
80// ----------------------------------------
81// Consistency check
82
83#if THREADMODEL!=NOTHREADS
84#ifdef USE_EXCEPTION_EMULATION
85#warning "Compiler must support thread safe exceptions"
86#endif //USE_EXCEPTION_EMULATION
87#if defined(__GNUC__)
88#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=8))
89#warning "GCC 2.8 exceptions are not thread safe."
90#warning "Use properly configured EGCS-1.1 or greater."
91#endif // (__GNUC__<2 ...
92#endif // defined(__GNUC__)
93#endif // THREADMODEL!=NOTHREADS
94
95#ifndef _DEBUG
96#if defined(DEBUG)
97#define _DEBUG /* */
98#elif DEBUGLVL >= 1
99#define _DEBUG /* */
100#endif
101#endif
102
103#if THREADMODEL==WINTHREADS
104# include <process.h>
105#endif
106#if THREADMODEL==COTHREADS
107# include <setjmp.h>
108# include <string.h>
109# include <unistd.h>
110# include <sys/types.h>
111# include <sys/time.h>
112#endif
113
114
115#ifdef HAVE_NAMESPACES
116namespace DJVU {
117# ifdef NOT_DEFINED // Just to fool emacs c++ mode
118}
119#endif
120#endif
121
122
123// ----------------------------------------
124// NOTHREADS
125// ----------------------------------------
126
127#if THREADMODEL==NOTHREADS
128int
129GThread::create( void (*entry)(void*), void *arg)
130{
131  (*entry)(arg);
132  return 0;
133}
134#endif
135
136
137// ----------------------------------------
138// WIN32 IMPLEMENTATION
139// ----------------------------------------
140
141#if THREADMODEL==WINTHREADS
142
143static unsigned __stdcall 
144start(void *arg)
145{
146  GThread *gt = (GThread*)arg;
147  try 
148    {
149      G_TRY
150        {
151          gt->xentry( gt->xarg );
152        }
153      G_CATCH(ex)
154        {
155          ex.perror();
156          DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
157#ifdef _DEBUG
158          abort();
159#endif
160        }
161      G_ENDCATCH;
162    }
163  catch(...)
164    {
165      DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
166#ifdef _DEBUG
167      abort();
168#endif
169    }
170  return 0;
171}
172
173GThread::GThread(int stacksize)
174  : hthr(0), thrid(0), xentry(0), xarg(0)
175{
176}
177
178GThread::~GThread()
179{
180  if (hthr)
181    CloseHandle(hthr);
182  hthr = 0;
183  thrid = 0;
184}
185
186int 
187GThread::create(void (*entry)(void*), void *arg)
188{
189  if (hthr)
190    return -1;
191  xentry = entry;
192  xarg = arg;
193  unsigned uthread = 0;
194  hthr = (HANDLE)_beginthreadex(NULL, 0, start, (void*)this, 0, &uthread);
195  thrid = (DWORD) uthread;
196  if (hthr)
197    return 0;
198  return -1;
199}
200
201void 
202GThread::terminate()
203{
204  OutputDebugString("Terminating thread.\n");
205  if (hthr)
206    TerminateThread(hthr,0);
207}
208
209int
210GThread::yield()
211{
212  Sleep(0);
213  return 0;
214}
215
216void *
217GThread::current()
218{
219  return (void*) GetCurrentThreadId();
220}
221
222struct thr_waiting {
223  struct thr_waiting *next;
224  struct thr_waiting *prev;
225  BOOL   waiting;
226  HANDLE gwait;
227};
228
229GMonitor::GMonitor()
230  : ok(0), count(1), head(0), tail(0)
231{
232  InitializeCriticalSection(&cs);
233  locker = GetCurrentThreadId();
234  ok = 1;
235}
236
237GMonitor::~GMonitor()
238{
239  ok = 0;
240  EnterCriticalSection(&cs);
241  for (struct thr_waiting *w=head; w; w=w->next)
242    SetEvent(w->gwait);
243  LeaveCriticalSection(&cs);
244  DeleteCriticalSection(&cs); 
245}
246
247void 
248GMonitor::enter()
249{
250  DWORD self = GetCurrentThreadId();
251  if (count>0 || self!=locker)
252    {
253      if (ok)
254        EnterCriticalSection(&cs);
255      locker = self;
256      count = 1;
257    }
258  count -= 1;
259}
260
261void 
262GMonitor::leave()
263{
264  DWORD self = GetCurrentThreadId();
265  if (ok && (count>0 || self!=locker))
266    G_THROW( ERR_MSG("GThreads.not_acq_broad") );
267  count += 1;
268  if (count > 0)
269    {
270      count = 1;
271      if (ok)
272        LeaveCriticalSection(&cs);
273    }
274}
275
276void
277GMonitor::signal()
278{
279  if (ok)
280    {
281      DWORD self = GetCurrentThreadId();
282      if (count>0 || self!=locker)
283        G_THROW( ERR_MSG("GThreads.not_acq_signal") );
284      for (struct thr_waiting *w=head; w; w=w->next)
285        if (w->waiting) 
286          {
287            SetEvent(w->gwait);
288            w->waiting = FALSE;
289            break; // Only one thread is allowed to run!
290          }
291    }
292}
293
294void
295GMonitor::broadcast()
296{
297  if (ok)
298    {
299      DWORD self = GetCurrentThreadId();
300      if (count>0 || self!=locker)
301        G_THROW( ERR_MSG("GThreads.not_acq_broad") );
302      for (struct thr_waiting *w=head; w; w=w->next)
303        if (w->waiting)
304            {
305              SetEvent(w->gwait);
306              w->waiting = FALSE;
307            }
308    }
309}
310
311void
312GMonitor::wait()
313{
314  // Check state
315  DWORD self = GetCurrentThreadId();
316  if (count>0 || self!=locker)
317    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
318  // Wait
319  if (ok)
320    {
321      // Prepare wait record
322      struct thr_waiting waitrec;
323      waitrec.waiting = TRUE;
324      waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL);
325      waitrec.next = 0;
326      waitrec.prev = tail;
327      // Link wait record (protected by critical section)
328      *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; 
329      *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec;
330      // Start wait
331      int sav_count = count;
332      count = 1;
333      LeaveCriticalSection(&cs);
334      WaitForSingleObject(waitrec.gwait,INFINITE);
335      // Re-acquire
336      EnterCriticalSection(&cs);
337      count = sav_count;
338      locker = self;
339      // Unlink wait record
340      *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev;
341      *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next;
342      CloseHandle(waitrec.gwait);
343    }
344}
345
346void
347GMonitor::wait(unsigned long timeout) 
348{
349  // Check state
350  DWORD self = GetCurrentThreadId();
351  if (count>0 || self!=locker)
352    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
353  // Wait
354  if (ok)
355    {
356      // Prepare wait record
357      struct thr_waiting waitrec;
358      waitrec.waiting = TRUE;
359      waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL);
360      waitrec.next = 0;
361      waitrec.prev = tail;
362      // Link wait record (protected by critical section)
363      *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec;
364      *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; 
365      // Start wait
366      int sav_count = count;
367      count = 1;
368      LeaveCriticalSection(&cs);
369      WaitForSingleObject(waitrec.gwait,timeout);
370      // Re-acquire
371      EnterCriticalSection(&cs);
372      count = sav_count;
373      locker = self;
374      // Unlink wait record
375      *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev;
376      *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next;
377      CloseHandle(waitrec.gwait);
378    }
379}
380
381#endif
382
383
384
385// ----------------------------------------
386// MACTHREADS IMPLEMENTATION (obsolete)
387// ----------------------------------------
388
389#if THREADMODEL==MACTHREADS
390
391// Doubly linked list of waiting threads
392struct thr_waiting {
393  struct thr_waiting *next;     // ptr to next waiting thread record
394  struct thr_waiting *prev;     // ptr to ptr to this waiting thread
395  unsigned long thid;           // id of waiting thread
396  int *wchan;                   // cause of the wait
397};
398static struct thr_waiting *first_waiting_thr = 0;
399static struct thr_waiting *last_waiting_thr = 0;
400
401
402// Stops current thread.
403// Argument ``self'' must be current thread id.
404// Assumes ``ThreadBeginCritical'' has been called before.
405static void
406macthread_wait(ThreadID self, int *wchan)
407{
408  // Prepare and link wait record
409  struct thr_waiting wait; // no need to malloc :-)
410  wait.thid = self;
411  wait.wchan = wchan;
412  wait.next = 0;
413  wait.prev = last_waiting_thr;
414  *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = &wait;
415  *(wait.next ? &wait.next->prev : &last_waiting_thr ) = &wait;
416  // Leave critical section and start waiting.
417  (*wchan)++;
418  SetThreadStateEndCritical(self, kStoppedThreadState, kNoThreadID);
419  // The Apple documentation says that the above call reschedules a new
420  // thread.  Therefore it will only return when the thread wakes up.
421  ThreadBeginCritical();
422  (*wchan)--;
423  // Unlink wait record
424  *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = wait.next;
425  *(wait.next ? &wait.next->prev : &last_waiting_thr ) = wait.prev;
426  // Returns from the wait.
427}
428
429// Wakeup one thread or all threads waiting on cause wchan
430static void
431macthread_wakeup(int *wchan, int onlyone)
432{
433  if (*wchan == 0)
434    return;
435  for (struct thr_waiting *q=first_waiting_thr; q; q=q->next)
436    if (q->wchan == wchan) {
437      // Found a waiting thread
438      q->wchan = 0;
439      SetThreadState(q->thid, kReadyThreadState, kNoThreadID);
440      if (onlyone)
441        return;
442    }
443}
444
445GThread::GThread(int stacksize) 
446  : thid(kNoThreadID), xentry(0), xarg(0)
447{
448}
449
450GThread::~GThread(void)
451{
452  thid = kNoThreadID;
453}
454
455pascal void *
456GThread::start(void *arg)
457{
458  GThread *gt = (GThread*)arg;
459  try 
460    {
461      G_TRY
462        {
463          (gt->xentry)(gt->xarg);
464        }
465      G_CATCH(ex)
466        {
467          ex.perror();
468          DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
469#ifdef _DEBUG
470          abort();
471#endif
472        }
473      G_ENDCATCH;
474    }
475  catch(...)
476    {
477      DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
478#ifdef _DEBUG
479      abort();
480#endif
481    }
482  return 0;
483}
484
485int
486GThread::create(void (*entry)(void*), void *arg)
487{
488  if (xentry || thid!=kNoThreadID)
489    return -1;
490  xentry = entry;
491  xarg = arg;
492  int err = NewThread( kCooperativeThread, GThread::start , this, 0,
493                       kCreateIfNeeded, (void**)nil, &thid );
494  if( err != noErr )
495    return err;
496  return 0;
497}
498
499void
500GThread::terminate()
501{
502  if (thid != kNoThreadID) {
503    DisposeThread( thid, NULL, false );
504    thid = kNoThreadID;
505  }
506}
507
508int
509GThread::yield()
510{
511  YieldToAnyThread();
512  return 0;
513}
514
515void*
516GThread::current()
517{
518  unsigned long thid = kNoThreadID;
519  GetCurrentThread(&thid);
520  return (void*) thid;
521}
522
523
524// GMonitor implementation
525GMonitor::GMonitor() 
526  : ok(0), count(1), locker(0), wlock(0), wsig(0)
527{
528  locker = kNoThreadID;
529  ok = 1;
530}
531
532GMonitor::~GMonitor() 
533{
534  ok = 0;
535  ThreadBeginCritical();
536  macthread_wakeup(&wsig, 0);
537  macthread_wakeup(&wlock, 0);
538  ThreadEndCritical();
539  YieldToAnyThread();
540}
541
542void 
543GMonitor::enter() 
544{
545  ThreadID self;
546  GetCurrentThread(&self);
547  ThreadBeginCritical();
548  if (count>0 || self!=locker)
549    {
550      while (ok && count<=0)
551        macthread_wait(self, &wlock);
552      count = 1;
553      locker = self;
554    }
555  count -= 1;
556  ThreadEndCritical();
557}
558
559void 
560GMonitor::leave() 
561{
562  ThreadID self;
563  GetCurrentThread(&self);
564  if (ok && (count>0 || self!=locker))
565    G_THROW( ERR_MSG("GThreads.not_acq_leave") );
566  ThreadBeginCritical();
567  if (++count > 0)
568    macthread_wakeup(&wlock, 1);
569  ThreadEndCritical();
570}
571
572void 
573GMonitor::signal() 
574{
575  ThreadID self;
576  GetCurrentThread(&self);
577  if (count>0 || self!=locker)
578    G_THROW( ERR_MSG("GThreads.not_acq_signal") );
579  ThreadBeginCritical();
580  macthread_wakeup(&wsig, 1);
581  ThreadEndCritical();
582}
583
584void 
585GMonitor::broadcast() 
586{
587  ThreadID self;
588  GetCurrentThread(&self);
589  if (count>0 || self!=locker)
590    G_THROW( ERR_MSG("GThreads.not_acq_broad") );
591  ThreadBeginCritical();
592  macthread_wakeup(&wsig, 0);
593  ThreadEndCritical();
594}
595
596void 
597GMonitor::wait() 
598{
599  // Check state
600  ThreadID self;
601  GetCurrentThread(&self);
602  if (count>0 || locker!=self)
603    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
604  // Wait
605  if (ok)
606    {
607      // Atomically release monitor and wait
608      ThreadBeginCritical();
609      int sav_count = count;
610      count = 1;
611      macthread_wakeup(&wlock, 1);
612      macthread_wait(self, &wsig);
613      // Re-acquire
614      while (ok && count<=0)
615        macthread_wait(self, &wlock);
616      count = sav_count;
617      locker = self;
618      ThreadEndCritical();
619    }
620}
621
622void 
623GMonitor::wait(unsigned long timeout) 
624{
625  // Timeouts are not used for anything important.
626  // Just ignore the timeout and wait the regular way.
627  if (timeout > 0)
628    wait();
629}
630
631#endif
632
633
634
635// ----------------------------------------
636// POSIXTHREADS IMPLEMENTATION
637// ----------------------------------------
638
639#if THREADMODEL==POSIXTHREADS
640
641#if defined(CMA_INCLUDE)
642#define DCETHREADS
643#define pthread_key_create pthread_keycreate
644#else
645#define pthread_mutexattr_default  NULL
646#define pthread_condattr_default   NULL
647#endif
648
649
650void *
651GThread::start(void *arg)
652{
653  GThread *gt = (GThread*)arg;
654#ifdef DCETHREADS
655#ifdef CANCEL_ON
656  pthread_setcancel(CANCEL_ON);
657  pthread_setasynccancel(CANCEL_ON);
658#endif
659#else // !DCETHREADS
660#ifdef PTHREAD_CANCEL_ENABLE
661  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
662#endif
663#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
664  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
665#endif
666#endif
667  // Catch exceptions
668#ifdef __EXCEPTIONS
669  try 
670    {
671#endif
672      G_TRY
673        {
674          (gt->xentry)(gt->xarg);
675        }
676      G_CATCH(ex)
677        {
678          ex.perror();
679          DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
680#ifdef _DEBUG
681          abort();
682#endif
683        }
684      G_ENDCATCH;
685#ifdef __EXCEPTIONS
686    }
687  catch(...)
688    {
689          DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
690#ifdef _DEBUG
691      abort();
692#endif
693    }
694#endif
695  return 0;
696}
697
698
699// GThread
700
701GThread::GThread(int stacksize) : 
702  hthr(0), xentry(0), xarg(0)
703{
704}
705
706GThread::~GThread()
707{
708  hthr = 0;
709}
710
711int 
712GThread::create(void (*entry)(void*), void *arg)
713{
714  if (xentry || xarg)
715    return -1;
716  xentry = entry;
717  xarg = arg;
718#ifdef DCETHREADS
719  int ret = pthread_create(&hthr, pthread_attr_default, GThread::start, (void*)this);
720  if (ret >= 0)
721    pthread_detach(hthr);
722#else
723  pthread_attr_t attr;
724  pthread_attr_init(&attr);
725  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
726  int ret = pthread_create(&hthr, &attr, start, (void*)this);
727  pthread_attr_destroy(&attr);
728#endif
729  return ret;
730}
731
732void 
733GThread::terminate()
734{
735  if (xentry || xarg)
736    pthread_cancel(hthr);
737}
738
739int
740GThread::yield()
741{
742#ifdef DCETHREADS
743  pthread_yield();
744#else
745  // should use sched_yield() when available.
746  static struct timeval timeout = { 0, 0 };
747  ::select(0, 0,0,0, &timeout);
748#endif
749  return 0;
750}
751
752void*
753GThread::current()
754{
755  pthread_t self = pthread_self();
756#if defined(pthread_getunique_np)
757  return (void*) pthread_getunique_np( & self );
758#elif defined(cma_thread_get_unique)
759  return (void*) cma_thread_get_unique( & self ); 
760#else
761  return (void*) self;
762#endif
763}
764
765// -- GMonitor
766
767GMonitor::GMonitor()
768  : ok(0), count(1), locker(0)
769{
770  // none of this should be necessary ... in theory.
771#ifdef PTHREAD_MUTEX_INITIALIZER
772  static pthread_mutex_t tmutex=PTHREAD_MUTEX_INITIALIZER;
773  memcpy(&mutex,&tmutex,sizeof(mutex));
774#endif
775#ifdef PTHREAD_COND_INITIALIZER
776  static pthread_cond_t tcond=PTHREAD_COND_INITIALIZER;
777  memcpy(&cond,&tcond,sizeof(cond));
778#endif
779  // standard
780  pthread_mutex_init(&mutex, pthread_mutexattr_default);
781  pthread_cond_init(&cond, pthread_condattr_default); 
782  locker = pthread_self();
783  ok = 1;
784}
785
786GMonitor::~GMonitor()
787{
788  ok = 0;
789  pthread_cond_destroy(&cond);
790  pthread_mutex_destroy(&mutex); 
791}
792
793
794void 
795GMonitor::enter()
796{
797  pthread_t self = pthread_self();
798  if (count>0 || !pthread_equal(locker, self))
799    {
800      if (ok)
801        pthread_mutex_lock(&mutex);
802      locker = self;
803      count = 1;
804    }
805  count -= 1;
806}
807
808void 
809GMonitor::leave()
810{
811  pthread_t self = pthread_self();
812  if (ok && (count>0 || !pthread_equal(locker, self)))
813    G_THROW( ERR_MSG("GThreads.not_acq_broad") );
814  count += 1;
815  if (count > 0)
816    {
817      count = 1;
818      if (ok)
819        pthread_mutex_unlock(&mutex);
820    }
821}
822
823void
824GMonitor::signal()
825{
826  if (ok)
827    {
828      pthread_t self = pthread_self();
829      if (count>0 || !pthread_equal(locker, self))
830        G_THROW( ERR_MSG("GThreads.not_acq_signal") );
831      pthread_cond_signal(&cond);
832    }
833}
834
835void
836GMonitor::broadcast()
837{
838  if (ok)
839    {
840      pthread_t self = pthread_self();
841      if (count>0 || !pthread_equal(locker, self))
842        G_THROW( ERR_MSG("GThreads.not_acq_broad") );
843      pthread_cond_broadcast(&cond);
844    }
845}
846
847void
848GMonitor::wait()
849{
850  // Check
851  pthread_t self = pthread_self();
852  if (count>0 || !pthread_equal(locker, self))
853    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
854  // Wait
855  if (ok)
856    {
857      // Release
858      int sav_count = count;
859      count = 1;
860      // Wait
861      pthread_cond_wait(&cond, &mutex);
862      // Re-acquire
863      count = sav_count;
864      locker = self;
865    }     
866}
867
868void
869GMonitor::wait(unsigned long timeout) 
870{
871  // Check
872  pthread_t self = pthread_self();
873  if (count>0 || !pthread_equal(locker, self))
874    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
875  // Wait
876  if (ok)
877    {
878      // Release
879      int sav_count = count;
880      count = 1;
881      // Wait
882      struct timeval  abstv;
883      struct timespec absts;
884      gettimeofday(&abstv, NULL); // grrr
885      absts.tv_sec = abstv.tv_sec + timeout/1000;
886      absts.tv_nsec = abstv.tv_usec*1000  + (timeout%1000)*1000000;
887      if (absts.tv_nsec > 1000000000) {
888        absts.tv_nsec -= 1000000000;
889        absts.tv_sec += 1;
890      }
891      pthread_cond_timedwait(&cond, &mutex, &absts);
892      // Re-acquire
893      count = sav_count;
894      locker = self;
895    }     
896}
897
898#endif
899
900
901
902// ----------------------------------------
903// CUSTOM COOPERATIVE THREADS
904// ----------------------------------------
905
906#if THREADMODEL==COTHREADS
907
908#ifndef __GNUG__
909#error "COTHREADS require G++"
910#endif
911#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=90))
912#warning "COTHREADS require EGCS-1.1.1 with Leon's libgcc patch."
913#warning "You may have trouble with thread-unsafe exceptions..."
914#define NO_LIBGCC_HOOKS
915#endif
916
917// -------------------------------------- constants
918
919// Minimal stack size
920#define MINSTACK   (32*1024)
921// Default stack size
922#define DEFSTACK   (127*1024)
923// Maxtime between checking fdesc (ms)
924#define MAXFDWAIT    (200)
925// Maximum time to wait in any case
926#define MAXWAIT (60*60*1000)
927// Maximum penalty for hog task (ms)
928#define MAXPENALTY (1000)
929// Trace task switches
930#undef COTHREAD_TRACE
931#undef COTHREAD_TRACE_VERBOSE
932
933// -------------------------------------- context switch code
934
935struct mach_state { 
936  jmp_buf buf; 
937};
938
939static void
940mach_switch(mach_state *st1, mach_state *st2)
941{ 
942#if #cpu(sparc)
943  asm("ta 3"); // save register windows
944#endif
945  if (! setjmp(st1->buf))
946    longjmp(st2->buf, 1);
947}
948
949static void 
950mach_start(mach_state *st1, void *pc, char *stacklo, char *stackhi)
951{ 
952#if #cpu(sparc)
953  asm("ta 3"); // save register windows
954#endif
955  if (! setjmp(st1->buf))
956    {
957      // The following code must perform two tasks:
958      // -- set stack pointer to a proper value between #stacklo# and #stackhi#.
959      // -- branch to or call the function at address #pc#.
960      // This function never returns ... so there is no need to save anything
961#if #cpu(mips)
962      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
963      asm volatile ("move $sp,%0\n\t"  // set new stack pointer
964                    "move $25,%1\n\t"  // call subroutine via $25
965                    "jal  $25\n\t"     // call subroutine via $25
966                    "nop"              // delay slot
967                    : : "r" (sp), "r" (pc) );
968#elif #cpu(i386)
969      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
970      asm volatile ("movl %0,%%esp\n\t" // set new stack pointer
971                    "call *%1"          // call function
972                    : : "r" (sp), "r" (pc) );
973#elif #cpu(sparc)
974      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
975      asm volatile ("ta 3\n\t"          // saving the register windows will not hurt.
976                    "mov %0,%%sp\n\t"   // set new stack pointer
977                    "call %1,0\n\t"     // call function
978                    "nop"               // delay slot
979                    : : "r" (sp), "r" (pc) );
980#elif #cpu(hppa)
981      char *sp = (char*)(((unsigned long)stacklo+128+255) & ~0xff);
982      asm volatile("copy %0,%%sp\n\t"       // set stack pointer
983                   "copy %1,%%r22\n\t"      // set call address
984                   ".CALL\n\t"              // call pseudo instr (why?)
985                   "bl $$dyncall,%%r31\n\t" // call
986                   "copy %%r31,%%r2"        // delay slot ???
987                   : : "r" (sp), "r" (pc) );
988#elif #cpu(alpha)
989      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
990      asm volatile ("bis $31,%0,$30\n\t"  // set new stack pointer
991                    "bis $31,%1,$27\n\t"  // load function pointer
992                    "jsr $26,($27),0"     // call function
993                    : : "r" (sp), "r" (pc) );
994#elif #cpu(powerpc)
995      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
996      asm volatile ("mr 1,%0\n\t"         // set new stack pointer
997                    "mr 0,%1\n\t"         // load func pointer into r0
998                    "mtlr 0\n\t"          // load link register with r0
999                    "blrl"                // branch
1000                    : : "r" (sp), "r" (pc) );
1001#elif #cpu(m68k) && defined(COTHREAD_UNTESTED)
1002      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
1003      asm volatile ("move%.l %0,%Rsp\n\t" // set new stack pointer
1004                    "jmp %a1"             // branch to address %1
1005                    : : "r" (sp), "a" (pc) );
1006#elif #cpu(arm) && defined(COTHREAD_UNTESTED)
1007      char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff);
1008      asm volatile ("mov%?\t%|sp, %0\n\t" // set new stack pointer
1009                    "mov%?\t%|pc, %1"     // branch to address %1
1010                    : : "r" (sp), "r" (pc) );
1011#else
1012#error "COTHREADS not supported on this machine."
1013#error "Try -DTHREADMODEL=NOTHREADS."
1014#endif
1015      // We should never reach this point
1016      abort();
1017      // Note that this call to abort() makes sure
1018      // that function mach_start() is compiled as a non-leaf
1019      // function. It is indeed a non-leaf function since the
1020      // piece of assembly code calls a function, but the compiler
1021      // would not know without the call to abort() ...
1022    }
1023}
1024
1025#ifdef CHECK
1026// This code can be used to verify that task switching works.
1027char stack[16384];
1028mach_state st1, st2;
1029void th2() {
1030  puts("2b"); mach_switch(&st2, &st1);
1031  puts("4b"); mach_switch(&st2, &st1);
1032  puts("6b"); mach_switch(&st2, &st1);
1033}
1034void th2relay() {
1035  th2(); puts("ooops\n");
1036}
1037void th1() {
1038  mach_start(&st1, (void*)th2relay, stack, stack+sizeof(stack));
1039  puts("3a"); mach_switch(&st1, &st2);
1040  puts("5a"); mach_switch(&st1, &st2);
1041}
1042int main() { 
1043  puts("1a"); th1(); puts("6a"); 
1044}
1045#endif
1046
1047
1048
1049// -------------------------------------- select
1050
1051struct coselect {
1052  int nfds;
1053  fd_set rset;
1054  fd_set wset;
1055  fd_set eset;
1056};
1057
1058static void 
1059coselect_merge(coselect *dest, coselect *from)
1060{
1061  int i;
1062  int nfds = from->nfds;
1063  if (nfds > dest->nfds)
1064    dest->nfds = nfds;
1065  for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->rset)) FD_SET(i, &dest->rset);
1066  for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->wset)) FD_SET(i, &dest->wset);
1067  for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->eset)) FD_SET(i, &dest->eset);
1068}
1069
1070static int
1071coselect_test(coselect *c)
1072{
1073  static timeval tmzero = {0,0};
1074  fd_set copyr = c->rset;
1075  fd_set copyw = c->wset;
1076  fd_set copye = c->eset;
1077  return select(c->nfds, &copyr, &copyw, &copye, &tmzero);
1078}
1079
1080
1081// -------------------------------------- cotask
1082
1083class GThread::cotask {
1084public:
1085#ifndef NO_LIBGCC_HOOKS
1086  cotask(const int xstacksize,void *);
1087#else
1088  cotask(const int xstacksize);
1089#endif
1090  ~cotask();
1091  class GThread::cotask *next;
1092  class GThread::cotask *prev;
1093  // context
1094  mach_state regs;
1095  // stack information
1096  char *stack;
1097  GPBuffer<char> gstack;
1098  int stacksize;
1099  // timing information
1100  unsigned long over;
1101  // waiting information
1102  void *wchan;
1103  coselect *wselect;
1104  unsigned long *maxwait;
1105  // delete after termination
1106  bool autodelete;
1107  // egcs exception support
1108#ifndef NO_LIBGCC_HOOKS
1109  void *ehctx;
1110#endif
1111};
1112
1113#ifndef NO_LIBGCC_HOOKS
1114GThread::cotask::cotask(const int xstacksize, void *xehctx)
1115#else
1116GThread::cotask::cotask(const int xstacksize)
1117#endif
1118: next(0), prev(0), gstack(stack,xstacksize), stacksize(xstacksize),
1119  over(0), wchan(0), wselect(0), maxwait(0), autodelete(false)
1120#ifndef NO_LIBGCC_HOOKS
1121  ,ehctx(xehctx)
1122#endif
1123{
1124  memset(&regs,0,sizeof(regs));
1125}
1126
1127static GThread::cotask *maintask = 0;
1128static GThread::cotask *curtask  = 0;
1129static GThread::cotask *autodeletetask = 0;
1130static unsigned long globalmaxwait = 0;
1131static void (*scheduling_callback)(int) = 0;
1132static timeval time_base;
1133
1134
1135GThread::cotask::~cotask()
1136{
1137  gstack.resize(0);
1138#ifndef NO_LIBGCC_HOOKS
1139  if (ehctx)
1140    free(ehctx);
1141  ehctx = 0;
1142#endif
1143}
1144
1145static void 
1146cotask_free(GThread::cotask *task)
1147{
1148#ifdef COTHREAD_TRACE
1149  DjVuPrintErrorUTF8("cothreads: freeing task %p with autodelete=%d\n", 
1150          task,task->autodelete);
1151#endif
1152  if (task!=maintask)
1153  {
1154    delete task;
1155  }
1156}
1157
1158
1159// -------------------------------------- time
1160
1161static unsigned long
1162time_elapsed(int reset=1)
1163{
1164  timeval tm;
1165  gettimeofday(&tm, NULL);
1166  long msec = (tm.tv_usec-time_base.tv_usec)/1000;
1167  unsigned long elapsed = (long)(tm.tv_sec-time_base.tv_sec)*1000 + msec;
1168  if (reset && elapsed>0)
1169    {
1170#ifdef COTHREAD_TRACE
1171#ifdef COTHREAD_TRACE_VERBOSE
1172      DjVuPrintErrorUTF8("cothreads: %4ld ms in task %p\n", elapsed, curtask);
1173#endif
1174#endif
1175      time_base.tv_sec = tm.tv_sec;
1176      time_base.tv_usec += msec*1000;
1177    }
1178  return elapsed;
1179}
1180
1181
1182// -------------------------------------- scheduler
1183
1184static int
1185cotask_yield()
1186{
1187  // ok
1188  if (! maintask)
1189    return 0;
1190  // get elapsed time and return immediately when it is too small
1191  unsigned long elapsed = time_elapsed();
1192  if (elapsed==0 && curtask->wchan==0 && curtask->prev && curtask->next)
1193    return 0;
1194  // adjust task running time
1195  curtask->over += elapsed;
1196  if (curtask->over > MAXPENALTY)
1197    curtask->over = MAXPENALTY;
1198  // start scheduling
1199 reschedule:
1200  // try unblocking tasks
1201  GThread::cotask *n = curtask->next;
1202  GThread::cotask *q = n;
1203  do 
1204    { 
1205      if (q->wchan)
1206        {
1207          if (q->maxwait && *q->maxwait<=elapsed) 
1208            {
1209              *q->maxwait = 0;
1210              q->wchan=0; 
1211              q->maxwait=0; 
1212              q->wselect=0; 
1213            }
1214          else if (q->wselect && globalmaxwait<=elapsed && coselect_test(q->wselect))
1215            {
1216              q->wchan=0;
1217              if (q->maxwait)
1218                *q->maxwait -= elapsed;
1219              q->maxwait = 0; 
1220              q->wselect=0; 
1221            }
1222          if (q->maxwait)
1223            *q->maxwait -= elapsed;
1224        }
1225      q = q->next;
1226    } 
1227  while (q!=n);
1228  // adjust globalmaxwait
1229  if (globalmaxwait < elapsed)
1230    globalmaxwait = MAXFDWAIT;
1231  else
1232    globalmaxwait -= elapsed;
1233  // find best candidate
1234  static int count;
1235  unsigned long best = MAXPENALTY + 1;
1236  GThread::cotask *r = 0;
1237  count = 0;
1238  q = n;
1239  do 
1240    { 
1241      if (! q->wchan)
1242        {
1243          count += 1;
1244          if (best > q->over)
1245            {
1246              r = q;
1247              best = r->over;
1248            } 
1249        }
1250      q = q->next;
1251    } 
1252  while (q != n);
1253  // found
1254  if (count > 0)
1255    {
1256      // adjust over
1257      q = n;
1258      do 
1259        { 
1260          q->over = (q->over>best ? q->over-best : 0);
1261          q = q->next;
1262        } 
1263      while (q != n);
1264      // Switch
1265      if (r != curtask)
1266        {
1267#ifdef COTHREAD_TRACE
1268          DjVuPrintErrorUTF8("cothreads: ----- switch to %p [%ld]\n", r, best);
1269#endif
1270          GThread::cotask *old = curtask;
1271          curtask = r;
1272          mach_switch(&old->regs, &curtask->regs);
1273        }
1274      // handle autodelete
1275      if (autodeletetask && autodeletetask->autodelete) 
1276        cotask_free(autodeletetask);
1277      autodeletetask = 0;
1278      // return
1279      if (count == 1)
1280        return 1;
1281      return 0;
1282    }
1283  // No task ready
1284  count = 0;
1285  unsigned long minwait = MAXWAIT;
1286  coselect allfds;
1287  allfds.nfds = 1;
1288  FD_ZERO(&allfds.rset);
1289  FD_ZERO(&allfds.wset);
1290  FD_ZERO(&allfds.eset);
1291  q = n;
1292  do 
1293    {
1294      if (q->maxwait || q->wselect)
1295        count += 1;
1296      if (q->maxwait && *q->maxwait<minwait)
1297        minwait = *q->maxwait;
1298      if (q->wselect)
1299        coselect_merge(&allfds, q->wselect);
1300      q = q->next;
1301    } 
1302  while (q != n);
1303  // abort on deadlock
1304  if (count == 0) {
1305    DjVuMessageLite::perror( ERR_MSG("GThreads.panic") );
1306    abort();
1307  }
1308  // select
1309  timeval tm;
1310  tm.tv_sec = minwait/1000;
1311  tm.tv_usec = 1000*(minwait-1000*tm.tv_sec);
1312  select(allfds.nfds,&allfds.rset, &allfds.wset, &allfds.eset, &tm);
1313  // reschedule
1314  globalmaxwait = 0;
1315  elapsed = time_elapsed();
1316  goto reschedule;
1317}
1318
1319
1320static void
1321cotask_terminate(GThread::cotask *task)
1322{
1323#ifdef COTHREAD_TRACE
1324  DjVuPrintErrorUTF8("cothreads: terminating task %p\n", task);
1325#endif
1326  if (task && task!=maintask)
1327    {
1328      if (task->prev && task->next)
1329        {
1330          if (scheduling_callback)
1331            (*scheduling_callback)(GThread::CallbackTerminate);
1332          task->prev->next = task->next;
1333          task->next->prev = task->prev;
1334          // mark task as terminated
1335          task->prev = 0; 
1336          // self termination
1337          if (task == curtask)
1338            {
1339              if (task->autodelete)
1340                autodeletetask = task;
1341              cotask_yield();
1342            }
1343        }
1344    }
1345}
1346
1347
1348static void
1349cotask_wakeup(void *wchan, int onlyone)
1350{
1351  if (maintask && curtask)
1352    {
1353      GThread::cotask *n = curtask->next;
1354      GThread::cotask *q = n;
1355      do 
1356        { 
1357          if (q->wchan == wchan)
1358            {
1359              q->wchan=0; 
1360              q->maxwait=0; 
1361              q->wselect=0; 
1362              q->over = 0;
1363              if (onlyone)
1364                return;
1365            }
1366          q = q->next;
1367        } 
1368      while (q!=n);
1369    }
1370}
1371
1372
1373// -------------------------------------- select / get_select
1374
1375static int
1376cotask_select(int nfds, 
1377              fd_set *rfds, fd_set *wfds, fd_set *efds,
1378              struct timeval *tm)
1379{
1380  // bypass
1381  if (maintask==0 || (tm && tm->tv_sec==0 && tm->tv_usec<1000))
1382    return select(nfds, rfds, wfds, efds, tm);
1383  // copy parameters
1384  unsigned long maxwait = 0;
1385  coselect parm;
1386  // set waiting info
1387  curtask->wchan = (void*)&parm;
1388  if (rfds || wfds || efds)
1389    {
1390      parm.nfds = nfds;
1391      if (rfds) { parm.rset=*rfds; } else { FD_ZERO(&parm.rset); }
1392      if (wfds) { parm.wset=*wfds; } else { FD_ZERO(&parm.wset); }
1393      if (efds) { parm.eset=*efds; } else { FD_ZERO(&parm.eset); }
1394      curtask->wselect = &parm;
1395    }
1396  if (tm) 
1397    {
1398      maxwait = time_elapsed(0) + tm->tv_sec*1000 + tm->tv_usec/1000;
1399      curtask->maxwait = &maxwait;
1400    }
1401  // reschedule
1402  cotask_yield();
1403  // call select to update masks
1404  if (tm)
1405    {
1406      tm->tv_sec = maxwait/1000;
1407      tm->tv_usec = 1000*(maxwait-1000*tm->tv_sec);
1408    }
1409  static timeval tmzero = {0,0};
1410  return select(nfds, rfds, wfds, efds, &tmzero);
1411}
1412
1413
1414static void 
1415cotask_get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, 
1416                  unsigned long &timeout)
1417{
1418  int ready = 1;
1419  unsigned long minwait = MAXWAIT;
1420  unsigned long elapsed = time_elapsed(0);
1421  coselect allfds;
1422  allfds.nfds=0;
1423  FD_ZERO(&allfds.rset);
1424  FD_ZERO(&allfds.wset);
1425  FD_ZERO(&allfds.eset);
1426  if (curtask)
1427    {
1428      GThread::cotask *q=curtask->next;
1429      while (q != curtask)
1430        {
1431          ready++;
1432          if (q->wchan)
1433            {
1434              if (q->wselect) 
1435                coselect_merge(&allfds, q->wselect);
1436              if (q->maxwait && *q->maxwait<minwait)
1437                minwait = *q->maxwait;
1438              ready--;
1439            }
1440          q = q->next;
1441        }
1442    }
1443  timeout = 0;
1444  nfds=allfds.nfds;
1445  *rfds=allfds.rset;
1446  *wfds=allfds.wset;
1447  *efds=allfds.eset;
1448  if (ready==1 && minwait>elapsed)
1449    timeout = minwait-elapsed;
1450}
1451
1452
1453
1454// -------------------------------------- libgcc hook
1455
1456#ifndef NO_LIBGCC_HOOKS
1457// These are exported by Leon's patched version of libgcc.a
1458// Let's hope that the egcs people will include the patch in
1459// the distributions.
1460extern "C" 
1461{
1462  extern void* (*__get_eh_context_ptr)(void);
1463  extern void* __new_eh_context(void);
1464}
1465
1466// This function is called via the pointer __get_eh_context_ptr
1467// by the internal mechanisms of egcs.  It must return the
1468// per-thread event handler context.  This is necessary to
1469// implement thread safe exceptions on some machine and/or
1470// when flag -fsjlj-exception is set.
1471static void *
1472cotask_get_eh_context()
1473{
1474  if (curtask)
1475    return curtask->ehctx;
1476  else if (maintask)
1477    return maintask->ehctx;
1478  DjVuMessageLite::perror( ERR_MSG("GThreads.co_panic") );
1479  abort();
1480}
1481#endif
1482
1483
1484
1485// -------------------------------------- GThread
1486
1487void 
1488GThread::set_scheduling_callback(void (*call)(int))
1489{
1490  if (scheduling_callback)
1491    G_THROW( ERR_MSG("GThreads.dupl_callback") );
1492  scheduling_callback = call;
1493}
1494
1495
1496GThread::GThread(int stacksize)
1497  : task(0), xentry(0), xarg(0)
1498{
1499  // check argument
1500  if (stacksize < 0)
1501    stacksize = DEFSTACK;
1502  if (stacksize < MINSTACK)
1503    stacksize = MINSTACK;
1504  // initialization
1505  if (! maintask)
1506    {
1507#ifndef NO_LIBGCC_HOOKS
1508      static GThread::cotask comaintask(0,(*__get_eh_context_ptr)());
1509      __get_eh_context_ptr = cotask_get_eh_context;
1510#else
1511      static GThread::cotask comaintask(0);
1512#endif
1513      maintask = &comaintask;
1514//      memset(maintask, 0, sizeof(GThread::cotask));
1515      maintask->next = maintask;
1516      maintask->prev = maintask;
1517      gettimeofday(&time_base,NULL);
1518      curtask = maintask;
1519    }
1520  // allocation
1521#ifndef NO_LIBGCC_HOOKS
1522  task = new GThread::cotask(stacksize,__new_eh_context());
1523#else
1524  task = new GThread::cotask(stacksize);
1525#endif
1526}
1527
1528
1529GThread::~GThread()
1530{
1531  if (task && task!=maintask)
1532  {
1533    if (task->prev) // running
1534      task->autodelete = true;
1535    else
1536      cotask_free(task);
1537    task = 0;
1538  }
1539}
1540
1541#if __GNUC__ >= 3
1542# if __GNUC_MINOR__ >= 4
1543#  define noinline __attribute__((noinline,used))
1544# elif __GNUC_MINOR >= 2
1545#  define noinline __attribute__((noinline))
1546# endif
1547#endif
1548#ifndef noinline
1549# define noinline /**/
1550#endif
1551
1552static noinline void startone(void);
1553static noinline void starttwo(GThread *thr);
1554static GThread * volatile starter;
1555
1556static void
1557startone(void)
1558{
1559  GThread *thr = starter;
1560  mach_switch(&thr->task->regs, &curtask->regs);
1561  // Registers may still contain an improper pointer
1562  // to the exception context.  We should neither
1563  // register cleanups nor register handlers.
1564  starttwo(thr);
1565  abort();
1566}
1567
1568static void 
1569starttwo(GThread *thr)
1570{
1571  // Hopefully this function reacquires
1572  // an exception context pointer. Therefore
1573  // we can register the exception handlers.
1574  // It is placed after ``startone'' to avoid inlining.
1575#ifdef __EXCEPTIONS
1576  try 
1577    {
1578#endif
1579      G_TRY
1580        {
1581          thr->xentry( thr->xarg );
1582        }
1583      G_CATCH(ex)
1584        {
1585          ex.perror();
1586          DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") );
1587#ifdef _DEBUG
1588          abort();
1589#endif
1590        }
1591      G_ENDCATCH;
1592#ifdef __EXCEPTIONS
1593    }
1594  catch(...)
1595    {
1596          DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") );
1597#ifdef _DEBUG
1598      abort();
1599#endif
1600    }
1601#endif
1602  cotask_terminate(curtask);
1603  GThread::yield();
1604  // Do not add anything below this line!
1605  // Nothing should reach it anyway.
1606  abort();
1607}
1608
1609int 
1610GThread::create(void (*entry)(void*), void *arg)
1611{
1612  if (task->next || task->prev)
1613    return -1;
1614  xentry = entry;
1615  xarg = arg;
1616  task->wchan = 0;
1617  task->next = curtask;
1618  task->prev = curtask->prev;
1619  task->next->prev = task;
1620  task->prev->next = task;
1621  GThread::cotask *old = curtask;
1622  starter = this;
1623  mach_start(&old->regs, (void*)startone, 
1624             task->stack, task->stack+task->stacksize);
1625  if (scheduling_callback)
1626    (*scheduling_callback)(CallbackCreate);
1627  return 0;
1628}
1629
1630
1631void 
1632GThread::terminate()
1633{
1634  if (task && task!=maintask)
1635    cotask_terminate(task);
1636}
1637
1638int
1639GThread::yield()
1640{
1641  return cotask_yield();
1642}
1643
1644int 
1645GThread::select(int nfds, 
1646                fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
1647                struct timeval *timeout)
1648{
1649  return cotask_select(nfds, readfds, writefds, exceptfds, timeout);
1650}
1651
1652void
1653GThread::get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, 
1654                    unsigned long &timeout)
1655{
1656  cotask_get_select(nfds, rfds, wfds, efds, timeout);
1657}
1658
1659inline void *
1660GThread::current()
1661{
1662  if (curtask && curtask!=maintask)
1663    return (void*)curtask;
1664  return (void*)0;
1665}
1666
1667
1668// -------------------------------------- GMonitor
1669
1670GMonitor::GMonitor()
1671  : count(1), locker(0), wlock(0), wsig(0)
1672{
1673  locker = 0;
1674  ok = 1;
1675}
1676
1677GMonitor::~GMonitor()
1678{
1679  ok = 0;
1680  cotask_wakeup((void*)&wsig, 0);
1681  cotask_wakeup((void*)&wlock, 0);   
1682  cotask_yield();
1683  // Because we know how the scheduler works, we know that this single call to
1684  // yield will run all unblocked tasks and given them the chance to leave the
1685  // scope of the monitor object.
1686}
1687
1688void 
1689GMonitor::enter()
1690{
1691  void *self = GThread::current();
1692  if (count>0 || self!=locker)
1693    {
1694      while (ok && count<=0)
1695        {
1696          curtask->wchan = (void*)&wlock;
1697          wlock++;
1698          cotask_yield();
1699          wlock--;
1700        }
1701      count = 1;
1702      locker = self;
1703    }
1704  count -= 1;
1705}
1706
1707void 
1708GMonitor::leave()
1709{
1710  void *self = GThread::current();
1711  if (ok && (count>0 || self!=locker))
1712    G_THROW( ERR_MSG("GThreads.not_acq_leave") );
1713  if (++count > 0 && wlock > 0)
1714    cotask_wakeup((void*)&wlock, 1);
1715}
1716
1717void
1718GMonitor::signal()
1719{
1720  void *self = GThread::current();
1721  if (count>0 || self!=locker)
1722    G_THROW( ERR_MSG("GThreads.not_acq_signal") );
1723  if (wsig > 0)
1724    {
1725      cotask_wakeup((void*)&wsig, 1);
1726      if (scheduling_callback)
1727        (*scheduling_callback)(GThread::CallbackUnblock);
1728    }
1729}
1730
1731void
1732GMonitor::broadcast()
1733{
1734  void *self = GThread::current();
1735  if (count>0 || self!=locker)
1736    G_THROW( ERR_MSG("GThreads.not_acq_broad") );
1737  if (wsig > 0)
1738    {
1739      cotask_wakeup((void*)&wsig, 0);
1740      if (scheduling_callback)
1741        (*scheduling_callback)(GThread::CallbackUnblock);
1742    }
1743}
1744
1745void
1746GMonitor::wait()
1747{
1748  // Check state
1749  void *self = GThread::current();
1750  if (count>0 || locker!=self)
1751    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
1752  // Wait
1753  if (ok)
1754    {
1755      // Atomically release monitor and wait
1756      int sav_count = count;
1757      count = 1;
1758      curtask->wchan = (void*)&wsig;
1759      cotask_wakeup((void*)&wlock, 1);
1760      wsig++;
1761      cotask_yield();
1762      wsig--;
1763      // Re-acquire
1764      while (ok && count <= 0)
1765        {
1766          curtask->wchan = (void*)&wlock;
1767          wlock++;
1768          cotask_yield();
1769          wlock--;
1770        }
1771      count = sav_count;
1772      locker = self;
1773    }
1774}
1775
1776void
1777GMonitor::wait(unsigned long timeout) 
1778{
1779  // Check state
1780  void *self = GThread::current();
1781  if (count>0 || locker!=self)
1782    G_THROW( ERR_MSG("GThreads.not_acq_wait") );
1783  // Wait
1784  if (ok)
1785    {
1786      // Atomically release monitor and wait
1787      int sav_count = count;
1788      count = 1;
1789      unsigned long maxwait = time_elapsed(0) + timeout;
1790      curtask->maxwait = &maxwait;
1791      curtask->wchan = (void*)&wsig;
1792      cotask_wakeup((void*)&wlock, 1);
1793      wsig++;
1794      cotask_yield();
1795      wsig--;
1796      // Re-acquire
1797      while (ok && count<=0)
1798        {
1799          curtask->wchan = (void*)&wlock;
1800          wlock++;
1801          cotask_yield();
1802          wlock--;
1803        }
1804      count = sav_count;
1805      locker = self;
1806    }
1807}
1808
1809#endif
1810
1811
1812
1813
1814// ----------------------------------------
1815// GSAFEFLAGS
1816// ----------------------------------------
1817
1818
1819
1820GSafeFlags &
1821GSafeFlags::operator=(long xflags)
1822{
1823   enter();
1824   if (flags!=xflags)
1825   {
1826      flags=xflags;
1827      broadcast();
1828   }
1829   leave();
1830   return *this;
1831}
1832
1833GSafeFlags::operator long(void) const
1834{
1835   long f;
1836   ((GSafeFlags *) this)->enter();
1837   f=flags;
1838   ((GSafeFlags *) this)->leave();
1839   return f;
1840}
1841
1842bool
1843GSafeFlags::test_and_modify(long set_mask, long clr_mask,
1844                            long set_mask1, long clr_mask1)
1845{
1846   enter();
1847   if ((flags & set_mask)==set_mask &&
1848       (~flags & clr_mask)==clr_mask)
1849   {
1850      long new_flags=flags;
1851      new_flags|=set_mask1;
1852      new_flags&=~clr_mask1;
1853      if (new_flags!=flags)
1854      {
1855         flags=new_flags;
1856         broadcast();
1857      }
1858      leave();
1859      return true;
1860   }
1861   leave();
1862   return false;
1863}
1864
1865void
1866GSafeFlags::wait_and_modify(long set_mask, long clr_mask,
1867                            long set_mask1, long clr_mask1)
1868{
1869   enter();
1870   while((flags & set_mask)!=set_mask ||
1871         (~flags & clr_mask)!=clr_mask) wait();
1872   long new_flags=flags;
1873   new_flags|=set_mask1;
1874   new_flags&=~clr_mask1;
1875   if (flags!=new_flags)
1876   {
1877      flags=new_flags;
1878      broadcast();
1879   }
1880   leave();
1881}
1882
1883
1884
1885#ifdef HAVE_NAMESPACES
1886}
1887# ifndef NOT_USING_DJVU_NAMESPACE
1888using namespace DJVU;
1889# endif
1890#endif
Note: See TracBrowser for help on using the repository browser.