summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu/GThreads.h
blob: 2235e2b6116ea54f65da3c5c83027448d483e1aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
//C-  -*- C++ -*-
//C- -------------------------------------------------------------------
//C- DjVuLibre-3.5
//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
//C- Copyright (c) 2001  AT&T
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- This program is distributed in the hope that it will be useful,
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//C- GNU General Public License for more details.
//C- 
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech 
//C- Software authorized us to replace the original DjVu(r) Reference 
//C- Library notice by the following text (see doc/lizard2002.djvu):
//C-
//C-  ------------------------------------------------------------------
//C- | DjVu (r) Reference Library (v. 3.5)
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
//C- | 6,058,214 and patents pending.
//C- |
//C- | This software is subject to, and may be distributed under, the
//C- | GNU General Public License, Version 2. The license should have
//C- | accompanied the software or you may obtain a copy of the license
//C- | from the Free Software Foundation at http://www.fsf.org .
//C- |
//C- | The computer code originally released by LizardTech under this
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
//C- | ORIGINAL CODE."  Subject to any third party intellectual property
//C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
//C- | non-exclusive license to make, use, sell, or otherwise dispose of 
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
//C- | General Public License.   This grant only confers the right to 
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
//C- | the extent such infringement is reasonably necessary to enable 
//C- | recipient to make, have made, practice, sell, or otherwise dispose 
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
//C- | any greater extent that may be necessary to utilize further 
//C- | modifications or combinations.
//C- |
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C- +------------------------------------------------------------------
// 
// $Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $
// $Name: release_3_5_15 $

#ifndef _GTHREADS_H_
#define _GTHREADS_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma interface
#endif


/** @name GThreads.h

    Files #"GThreads.h"# and #"GThreads.cpp"# implement common entry points
    for multithreading on multiple platforms.  Each execution thread is
    represented by an instance of class \Ref{GThread}.  Synchronization is
    provided by class \Ref{GMonitor} which implements a monitor (C.A.R Hoare,
    Communications of the ACM, 17(10), 1974).

    The value of compiler symbol #THREADMODEL# selects an appropriate
    implementation for these classes. The current implementation supports
    the following values:
    \begin{description}
    \item[-DTHREADMODEL=NOTHREADS] Dummy implementation.  This is a
          good choice when the multithreading features are not required,
          because it minimizes the portability problems.  This is currently
          the default when compiling under Unix.
    \item[-DTHREADMODEL=WINTHREADS] Windows implementation.
          This is the default when compiling under Windows.
    \item[-DTHREADMODEL=MACTHREADS] Macintosh implementation,
          which is based on the MacOS cooperative model. The current 
          implementation does not yet fully support synchronization.
          This is the default when compiling under MacOS.
    \item[-DTHREADMODEL=POSIXTHREADS] Posix implementation.
          This implementation also supports DCE threads. The behavior of
          the code is subject to the quality of the system implementation of
          Posix threads.
    \item[-DTHREADMODEL=COTHREADS] Custom cooperative threads.
          These custom threads do not redefine system calls. Before executing
          a potentially blocking system function, each thread must explicitly
          check whether it is going to block and yield control explicitly if
          this is the case.  This code must be compiled with a patched version
          of egcs-1.1.1 \URL{http://egcs.cygnus.com}. The patch addresses
          exception thread-safety and is provided in #"@Tools/libgcc2.c.diff"#.
          Once you get the right compiler, this implementation is remarkably
          compact and portable. A variety of processors are supported,
          including mips, intel, sparc, hppa, and alpha.
    \item[-DTHREADMODEL=JRITHREADS] Java implementation hooks.
          Multi-threading within a Netscape plugin can be tricky.  A simple
          idea however consists of implementing the threading primitives in
          Java and to access them using JRI.  The classes just contain a
          JRIGlobalRef.  This is not a real implementation since everything
          (Java code, native functions, stubs, exception thread safety) must
          be addressed by the plugin source code. Performance may be a serious
          issue.
    \end{description}
    
    {\bf Portability}: The simultaneous use of threads and exceptions caused a
    lot of portability headaches under Unix.  We eventually decided to
    implement the COTHREADS cooperative threads (because preemptive threads
    have more problems) and to patch EGCS in order to make exception handling
    COTHREAD-safe. 

    @memo
    Portable threads
    @author
    L\'eon Bottou <leonb@research.att.com> -- initial implementation.\\
    Praveen Guduru <praveen@sanskrit.lz.att.com> -- mac implementation.

// From: Leon Bottou, 1/31/2002
// Almost unchanged by Lizardtech.
// GSafeFlags should go because it not as safe as it claims.

    @version
    #$Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */
//@{


#include "DjVuGlobal.h"
#include "GException.h"

#define NOTHREADS     0
#define COTHREADS     1
#define JRITHREADS    2
#define POSIXTHREADS  10
#define WINTHREADS    11
#define MACTHREADS    12

// Known platforms
#ifndef THREADMODEL
#if defined(WIN32)
#define THREADMODEL WINTHREADS
#endif
#if defined(macintosh)
#define THREADMODEL MACTHREADS
#endif
#endif

// Exception emulation is not thread safe
#ifdef USE_EXCEPTION_EMULATION
#undef  THREADMODEL
#define THREADMODEL NOTHREADS
#endif
// Default is nothreads
#ifndef THREADMODEL
#define THREADMODEL NOTHREADS
#endif

// ----------------------------------------
// INCLUDES

#if THREADMODEL==WINTHREADS
#ifndef _WINDOWS_
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
#endif

#if THREADMODEL==MACTHREADS
#include <threads.h>
#endif

#if THREADMODEL==POSIXTHREADS
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#undef TRY
#undef CATCH
#define _CMA_NOWRAPPERS_
#include <pthread.h>
#endif

#if THREADMODEL==JRITHREADS
#include "jri.h"
#endif

#if THREADMODEL==COTHREADS
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#endif


// ----------------------------------------
// PORTABLE CLASSES


#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif



/** Thread class.  A multithreaded process is composed of a main execution
    thread and of several secondary threads.  Each secondary thread is
    represented by a #GThread# object.  The amount of memory required for the
    stack of a secondary thread is defined when the #GThread# object is
    constructed.  The execution thread is started when function
    \Ref{GThread::create} is called.  The destructor of class GThread waits
    until the thread terminanes.  Note that the execution can be terminated at
    any time (with possible prejudice) by calling \Ref{GThread::terminate}.

    Several static member functions control the thread scheduler.  Function
    \Ref{GThread::yield} relinquishes the processor to another thread.
    Function \Ref{GThread::select} (#COTHREADS# only) provides a thread-aware
    replacement for the well-known unix system call #select#.  

    {\bf Note} --- Both the copy constructor and the copy operator are declared
    as private members. It is therefore not possible to make multiple copies
    of instances of this class, as implied by the class semantic. */

class GThread {
public:
  /** Constructs a new thread object.  Memory is allocated for the
      thread, but the thread is not started. 
      Argument #stacksize# is used by the #COTHREADS# model only for
      specifying the amount of memory needed for the processor stack. A
      negative value will be replaced by a suitable default value of 128Kb.
      A minimum value of 32Kb is silently enforced. */
  GThread(int stacksize = -1);
  /** Destructor.  Destroying the thread object while the thread is running is
      perfectly ok since it only destroys the thread identifier.  Execution
      will continue without interference. */
  ~GThread();
  /** Starts the thread. The new thread executes function #entry# with
      argument #arg#.  The thread terminates when the function returns.  A
      thread cannot be restarted after its termination. You must create a new
      #GThread# object. */
  int  create(void (*entry)(void*), void *arg);
  /** Terminates a thread with extreme prejudice. The thread is removed from
      the scheduling list.  Execution terminates regardless of the execution
      status of the thread function. Automatic variables may or may not be
      destroyed. This function must be considered as a last resort since
      memory may be lost. */
  void terminate();
  /** Causes the current thread to relinquish the processor.  The scheduler
      selects a thread ready to run and transfers control to that thread.  The
      actual effect of #yield# heavily depends on the selected implementation.
      Function #yield# usually returns zero when the execution of the current
      thread is resumed.  It may return a positive number when it can
      determine that the current thread will remain the only runnable thread
      for some time.  You may then call function \Ref{get_select} to
      obtain more information. */
  static int yield();
  /** Returns a value which uniquely identifies the current thread. */
  static void *current();

#if THREADMODEL==WINTHREADS
private:
  HANDLE hthr;
  DWORD  thrid;
#elif THREADMODEL==MACTHREADS
private:
  unsigned long thid;
  static pascal void *start(void *arg);
#elif THREADMODEL==POSIXTHREADS
private:
  pthread_t hthr;
  static void *start(void *arg);
#elif THREADMODEL==JRITHREADS
private:
  JRIGlobalRef obj;
#elif THREADMODEL==COTHREADS
  friend class GMonitor;
public:
  class cotask;
  class cotask *task;
  /** Replaces system call #select# (COTHREADS only).  The #COTHREADS# model
      does not redefine system function.  System functions therefore can
      potentially block the whole process (instead of blocking the current
      thread only) because the system is not aware of the #COTHREADS#
      scheduler.  The function #GThread::select# is a #COTHREADS#-aware
      replacement for the well known system function #select#.  You can also
      use #GThread::select# for making sure that calls to system functions
      will not block the entire process, as demonstrated below:
      \begin{verbatim}
      int 
      gthread_read(int fd, void *buffer, size_t len) 
      {
        fd_set rdset; 
        FD_ZERO(&rdset); 
        FD_SET(fd, &rdset);
        GThread::select(fd+1, &rdset, 0, 0, 0);
        return read(fd, buffer, len);
      }
      \end{verbatim} */
  static int select(int nfds, fd_set*, fd_set*, fd_set*, struct timeval*);
  /** Provide arguments for system call #select# (COTHREADS only). It may be
      appropriate to call the real system call #select# if the current thread
      is the only thread ready to run.  Other threads however may wake up when
      certain file descriptors are ready or when a certain delay expires.
      Function #get_select# returns this information by filling the three
      usual file descriptor sets (similar to the arguments of system call
      #select#).  It also returns a timeout #timeout# expressed in
      milliseconds.  Note that this timeout is zero is the current thread is
      not the sole thread ready to run. */
  static void get_select(int &nfds, fd_set*, fd_set*, fd_set*, unsigned long &timeout);
  /** Install hooks in the scheduler (COTHREADS only).  The hook function
      #call# is called when a new thread is created (argument is
      #GThread::CallbackCreate#), when a thread terminates (argument is
      #GThread::CallbackTerminate#), or when thread is unblocked (argument is
      #GThread::CallbackUnblock#).  This callback can be useful in certain GUI
      toolkits where the most convenient method for scheduling the threads
      consists in setting a timer event that calls \Ref{GThread::yield}.  */
  static void set_scheduling_callback(void (*call)(int));
  enum { CallbackCreate, CallbackTerminate, CallbackUnblock };

#endif
public:
  // Should be considered as private
  void (*xentry)(void*);
  void  *xarg;
private:
  // Disable default members
  GThread(const GThread&);
  GThread& operator=(const GThread&);
};


/** Monitor class.  Monitors have been first described in (C.A.R Hoare,
    Communications of the ACM, 17(10), 1974).  This mechanism provides the
    basic mutual exclusion (mutex) and thread notification facilities
    (condition variables).
    
    Only one thread can own the monitor at a given time.  Functions
    \Ref{enter} and \Ref{leave} can be used to acquire and release the
    monitor. This mutual exclusion provides an efficient way to protect
    segment of codes ({\em critical sections}) which should not be
    simultaneously executed by two threads. Class \Ref{GMonitorLock} provides
    a convenient way to do this effectively.
    
    When the thread owning the monitor calls function \Ref{wait}, the monitor
    is released and the thread starts waiting until another thread calls
    function \Ref{signal} or \Ref{broadcast}.  When the thread wakes-up, it
    re-acquires the monitor and function #wait# returns.  Since the signaling
    thread must acquire the monitor before calling functions #signal# and
    #broadcast#, the signaled thread will not be able to re-acquire the
    monitor until the signaling thread(s) releases the monitor.
    
    {\bf Note} --- Both the copy constructor and the copy operator are declared
    as private members. It is therefore not possible to make multiple copies
    of instances of this class, as implied by the class semantic. */

class GMonitor
{
public:
  GMonitor();
  ~GMonitor();
  /** Enters the monitor.  If the monitor is acquired by another thread this
      function waits until the monitor is released.  The current thread then
      acquires the monitor.  Calls to #enter# and #leave# may be nested. */
  void enter();
  /** Leaves the monitor.  The monitor counts how many times the current
      thread has entered the monitor.  Function #leave# decrement this count.
      The monitor is released when this count reaches zero.  An exception is
      thrown if this function is called by a thread which does not own the
      monitor. */
  void leave();
  /** Waits until the monitor is signaled.  The current thread atomically
      releases the monitor and waits until another thread calls function
      #signal# or #broadcast#.  Function #wait# then re-acquires the monitor
      and returns.  An exception is thrown if this function is called by a
      thread which does not own the monitor. */
  void wait();
  /** Waits until the monitor is signaled or a timeout is reached.  The
      current thread atomically releases the monitor and waits until another
      thread calls function #signal# or #broadcast# or a maximum of #timeout#
      milliseconds.  Function #wait# then re-acquires the monitor and returns.
      An exception is thrown if this function is called by a thread which does
      not own the monitor. */
  void wait(unsigned long timeout);
  /** Signals one waiting thread.  Function #signal# wakes up at most one of
      the waiting threads for this monitor.  An exception is thrown if this
      function is called by a thread which does not own the monitor. */
  void signal();
  /** Signals all waiting threads. Function #broadcast# wakes up all the
      waiting threads for this monitor.  An exception is thrown if this
      function is called by a thread which does not own the monitor. */
  void broadcast();
private:
#if THREADMODEL==WINTHREADS
  int ok;
  int count;
  DWORD locker;
  CRITICAL_SECTION cs;
  struct thr_waiting *head;
  struct thr_waiting *tail;
#elif THREADMODEL==MACTHREADS
  int ok;
  int count;
  unsigned long locker;
  int wlock;
  int wsig;
#elif THREADMODEL==POSIXTHREADS
  int ok;
  int count;
  pthread_t locker;
  pthread_mutex_t mutex;
  pthread_cond_t cond;
#elif THREADMODEL==COTHREADS
  int ok;
  int count;
  void *locker;
  int wlock;
  int wsig;
#elif THREADMODEL==JRITHREADS
  JRIGlobalRef obj;
#endif  
private:
  // Disable default members
  GMonitor(const GMonitor&);
  GMonitor& operator=(const GMonitor&);
};




// ----------------------------------------
// NOTHREADS INLINES

#if THREADMODEL==NOTHREADS
inline GThread::GThread(int) {}
inline GThread::~GThread(void) {}
inline void GThread::terminate() {}
inline int GThread::yield() { return 0; }
inline void* GThread::current() { return 0; }
inline GMonitor::GMonitor() {}
inline GMonitor::~GMonitor() {}
inline void GMonitor::enter() {}
inline void GMonitor::leave() {}
inline void GMonitor::wait() {}
inline void GMonitor::wait(unsigned long) {}
inline void GMonitor::signal() {}
inline void GMonitor::broadcast() {}
#endif // NOTHREADS


// ----------------------------------------
// SCOPE LOCK


/** Wrapper for mutually exclusive code.
    This class locks a specified critical section (see \Ref{GCriticalSection})
    at construction time and unlocks it at destruction time. It provides a
    convenient way to take advantage of the C++ implicit destruction of
    automatic variables in order to make sure that the monitor is
    released when exiting the protected code.  The following code will release
    the monitor when the execution thread leaves the protected scope, either
    because the protected code has executed successfully, or because an
    exception was thrown.
    \begin{verbatim}
      {      -- protected scope
         static GMonitor theMonitor;
         GMonitorLock lock(&theMonitor)
         ... -- protected code
      }
    \end{verbatim} 
    This construct will do nothing when passed a null pointer.
*/
class GMonitorLock 
{
private:
  GMonitor *gsec;
public:
  /** Constructor. Enters the monitor #gsec#. */
  GMonitorLock(GMonitor *gsec) : gsec(gsec) 
    { if (gsec) gsec->enter(); };
  /** Destructor. Leaves the associated monitor. */
  ~GMonitorLock() 
    { if (gsec) gsec->leave(); };
};



// ----------------------------------------
// GSAFEFLAGS (not so safe)


/** A thread safe class representing a set of flags. The flags are protected
    by \Ref{GMonitor}, which is attempted to be locked whenever somebody
    accesses the flags. One can modify the class contents using one of
    two functions: \Ref{test_and_modify}() and \Ref{wait_and_modify}().
    Both of them provide atomic operation of testing (first) and modification
    (second). The flags remain locked between the moment of testing and
    modification, which guarantees, that their state cannot be changed in
    between of these operations. */
class GSafeFlags : public GMonitor
{
private:
   volatile long flags;
public:
      /// Constructs #GSafeFlags# object.
   GSafeFlags(long flags=0);

      /** Assignment operator. Will also wake up threads waiting for the
	  flags to change. */
   GSafeFlags & operator=(long flags);

      /** Returns the value of the flags */
   operator long(void) const;
      /** Modifies the flags by ORing them with the provided tqmask. A broadcast
	  will be sent after the modification is done. */
   GSafeFlags &	operator|=(long tqmask);
      /** Modifies the flags by ANDing them with the provided tqmask. A broadcast
	  will be sent after the modification is done. */
   GSafeFlags &	operator&=(long tqmask);

      /** If all bits mentioned in #set_tqmask# are set in the flags and all
	  bits mentioned in #clr_tqmask# are cleared in the flags, it sets all
	  bits from #set_tqmask1# in the flags, clears all flags from
	  #clr_tqmask1# in the flags and returns #TRUE#. Otherwise returns
	  #FALSE#. */
   bool	test_and_modify(long set_tqmask, long clr_tqmask,
			long set_tqmask1, long clr_tqmask1);

      /** Waits until all bits mentioned in #set_tqmask# are set in the flags
	  and all bits mentioned in #clr_flags# are cleared in the flags.
	  After that it sets bits from #set_tqmask1# and clears bits from
	  #clr_tqmask1# in the flags. */
   void	wait_and_modify(long set_tqmask, long clr_tqmask,
			long set_tqmask1, long clr_tqmask1);

      /** Waits until all bits set in #set_tqmask# are set in the flags and
	  all bits mentioned in #clr_tqmask# are cleared in the flags. */
   void	wait_for_flags(long set_tqmask, long clr_tqmask=0) const;

      /** Modifies the flags by setting all bits mentioned in #set_tqmask#
	  and clearing all bits mentioned in #clr_tqmask#. If the flags have
	  actually been modified, a broadcast will be sent. */
   void	modify(long set_tqmask, long clr_tqmask);
};

inline
GSafeFlags::GSafeFlags(long xflags) 
  : flags(xflags) 
{
}

inline void
GSafeFlags::wait_for_flags(long set_tqmask, long clr_tqmask) const
{
   ((GSafeFlags *) this)->wait_and_modify(set_tqmask, clr_tqmask, 0, 0);
}

inline void
GSafeFlags::modify(long set_tqmask, long clr_tqmask)
{
   test_and_modify(0, 0, set_tqmask, clr_tqmask);
}

inline GSafeFlags &
GSafeFlags::operator|=(long tqmask)
{
   test_and_modify(0, 0, tqmask, 0);
   return *this;
}

inline GSafeFlags &
GSafeFlags::operator&=(long tqmask)
{
   test_and_modify(0, 0, 0, ~tqmask);
   return *this;
}

//@}




// ----------------------------------------
// COMPATIBILITY CLASSES


// -- these classes are no longer documented.

class GCriticalSection : protected GMonitor 
{
public:
  void lock() 
    { GMonitor::enter(); };
  void unlock() 
    { GMonitor::leave(); };
};

class GEvent : protected GMonitor 
{
private:
  int status;
public:
  GEvent() 
    : status(0) { };
  void set() 
    { if (!status) { enter(); status=1; signal(); leave(); } };
  void wait() 
    { enter(); if (!status) GMonitor::wait(); status=0; leave(); };
  void wait(int timeout) 
    { enter(); if (!status) GMonitor::wait(timeout); status=0; leave(); };
};

class GCriticalSectionLock
{
private:
  GCriticalSection *gsec;
public:
  GCriticalSectionLock(GCriticalSection *gsec) : gsec(gsec) 
    { if (gsec) gsec->lock(); };
  ~GCriticalSectionLock() 
    { if (gsec) gsec->unlock(); };
};


// ----------------------------------------

#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif
#endif //_GTHREADS_H_