source: trunk/libdjvu/GThreads.cpp @ 15

Last change on this file since 15 was 15, checked in by Eugene Romanenko, 15 years ago

needed libs update

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