source: trunk/libdjvu/atomic.cpp @ 280

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

DJVU plugin: djvulibre updated to version 3.5.22

  • Property svn:eol-style set to native
File size: 10.1 KB
Line 
1/* -*- C -*-
2// -------------------------------------------------------------------
3// MiniLock - a quick mostly user space lock
4// Copyright (c) 2008  Leon Bottou. All rights reserved
5//
6// Permission is hereby granted, free of charge, to any person obtaining
7// a copy of this software and associated documentation files (the
8// "Software"), to deal in the Software without restriction, including
9// without limitation the rights to use, copy, modify, merge, publish,
10// distribute, sublicense, and/or sell copies of the Software, and to
11// permit persons to whom the Software is furnished to do so, subject to
12// the following conditions:
13//
14// The above copyright notice and this permission notice shall be
15// included in all copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24//
25// ------------------------------------------------------------------- */
26
27#ifdef HAVE_CONFIG_H
28# include "config.h"
29#endif
30
31#include <stdlib.h>
32#include <assert.h>
33#include "atomic.h"
34#if defined(WIN32)
35# include <windows.h>
36#endif
37
38#include "GThreads.h"
39// #include <pthread.h>
40// #include <QMutex>
41// #include <QWaitCondition>
42
43#define OBEY_HAVE_INTEL_ATOMIC_BUILTINS 1
44
45
46/* ============================================================
47// PART1 - THE WAITING .
48// This part must define the four macros MUTEX_ENTER,
49// MUTEX_LEAVE, COND_WAIT and COND_WAKEALL working
50// on a single monitor in a way that is consistent with
51// the pthread semantics.
52*/
53
54
55#if defined(WIN32)
56# define USE_WINDOWS_WAIT 1
57#elif defined(__cplusplus) && defined(_GTHREADS_H_)
58# define USE_GTHREAD_WAIT 1
59#elif defined(__cplusplus) && defined(QMUTEX_H)
60# define USE_QT4_WAIT 1
61#elif defined(PTHREAD_MUTEX_INITIALIZER)
62# define USE_PTHREAD_WAIT 1
63#endif
64
65
66#if USE_PTHREAD_WAIT
67static pthread_mutex_t ptm = PTHREAD_MUTEX_INITIALIZER;
68static pthread_cond_t  ptc = PTHREAD_COND_INITIALIZER;
69# define MUTEX_ENTER  pthread_mutex_lock(&ptm)
70# define MUTEX_LEAVE  pthread_mutex_unlock(&ptm)
71# define COND_WAIT    pthread_cond_wait(&ptc,&ptm)
72# define COND_WAKEALL pthread_cond_broadcast(&ptc)
73#endif
74
75
76#if USE_GTHREAD_WAIT
77static GMonitor m;
78# define MUTEX_ENTER  m.enter()
79# define MUTEX_LEAVE  m.leave()
80# define COND_WAIT    m.wait()
81# define COND_WAKEALL m.broadcast()
82#endif
83
84
85#if USE_QT4_WAIT
86static QMutex qtm;
87static QWaitCondition qtc;
88# define MUTEX_ENTER  qtm.lock()
89# define MUTEX_LEAVE  qtm.unlock()
90# define COND_WAIT    qtc.wait(&qtm)
91# define COND_WAKEALL qtc.wakeAll()
92#endif
93
94
95#if USE_WINDOWS_WAIT
96static LONG ini = 0;
97static CRITICAL_SECTION cs;
98static HANDLE ev = 0;
99static void mutex_enter()
100{
101  if (!InterlockedExchange(&ini, 1))
102    {
103      InitializeCriticalSection(&cs);
104      ev = CreateEvent(NULL, TRUE, FALSE, NULL);
105      assert(ev);
106    }
107  EnterCriticalSection(&cs);
108}
109static void cond_wait()
110{
111  ResetEvent(&ev);
112  LeaveCriticalSection(&cs);
113  WaitForSingleObject(ev, INFINITE);
114  EnterCriticalSection(&cs);
115}
116# define MUTEX_ENTER mutex_enter()
117# define MUTEX_LEAVE LeaveCriticalSection(&cs)
118# define COND_WAIT   cond_wait()
119# define COND_WAKEALL SetEvent(ev)
120#endif
121
122#if ! defined(COND_WAKEALL) || ! defined(COND_WAIT)
123# error "Could not select suitable waiting code"
124#endif
125
126
127/* ============================================================
128// PART2 - ATOMIC PRIMITIVES
129// This part should define very fast SYNC_XXX and SYNC_REL
130// macros that perform atomic operations.
131// Intel builtins functions are very nice.
132// Windows interlocked functions are nice.
133// Otherwise we have to use assembly code.
134// When these are not defined we simply use
135// the monitor macros to implement
136// slow replacement functions.
137*/
138
139
140#ifndef OBEY_HAVE_INTEL_ATOMIC_BUILTINS
141# if defined(__INTEL_COMPILER)
142#  define USE_INTEL_ATOMIC_BUILTINS 1
143# elif defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__>= 1)
144#  define USE_INTEL_ATOMIC_BUILTINS 1
145# endif
146#endif
147#if HAVE_INTEL_ATOMIC_BUILTINS
148# define USE_INTEL_ATOMIC_BUILTINS 1
149#elif defined(WIN32)
150# define USE_WIN32_INTERLOCKED 1
151#elif defined(__GNUC__) && defined(__i386__)
152# define USE_GCC_I386_ASM 1
153#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__amd64__))
154# define USE_GCC_I386_ASM 1
155#elif defined(__GNUC__) && (defined(__ppc__) || defined(__powerpc__))
156# define USE_GCC_PPC_ASM 1
157#endif
158
159
160#if USE_INTEL_ATOMIC_BUILTINS && !HAVE_SYNC
161# define SYNC_ACQ(l)     (! __sync_lock_test_and_set(l, 1))
162# define SYNC_REL(l)     (__sync_lock_release(l))
163# define SYNC_INC(l)     (__sync_add_and_fetch(l, 1))
164# define SYNC_DEC(l)     (__sync_add_and_fetch(l, -1))
165# define SYNC_CAS(l,o,n) (__sync_bool_compare_and_swap(l,o,n))
166# define HAVE_SYNC 1
167#endif
168
169
170#if USE_WIN32_INTERLOCKED && !HAVE_SYNC
171# define SYNC_ACQ(l)                                    \
172  (!InterlockedExchange((LONG volatile *)(l),1))
173# if defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64)
174#  define SYNC_REL(l)                           \
175  (InterlockedExchange((LONG volatile *)(l),0))
176# else
177#  define SYNC_REL(l)                           \
178  (*(int volatile *)(l)=0)
179# endif
180# define SYNC_INC(l)                            \
181  (InterlockedIncrement((LONG volatile *)(l)))
182# define SYNC_DEC(l)                            \
183  (InterlockedDecrement((LONG volatile *)(l)))
184# define SYNC_CAS(l,o,n)                                        \
185  (InterlockedCompareExchange((LONG volatile *)(l),n,o)==(o))
186# define HAVE_SYNC 1
187#endif
188
189
190#if USE_GCC_I386_ASM && !HAVE_SYNC
191static int xchgl(int volatile *atomic, int newval) 
192{
193  int oldval;
194  __asm__ __volatile__ ("xchgl %0, %1"
195                        : "=r" (oldval), "+m" (*atomic)
196                        : "0" (newval), "m" (*atomic)); 
197  return oldval; 
198}
199static int xaddl(int volatile *atomic, int add) 
200{
201  int val; /* This works for the 486 and later */
202  __asm__ __volatile__("lock; xaddl %0, %1" 
203                       : "=r" (val), "+m" (*atomic) 
204                       : "m" (*atomic), "0" (add) );
205  return val;
206}
207static int cmpxchglf(int volatile *atomic, int oldval, int newval)
208{
209  int ret;
210  __asm __volatile ("lock; cmpxchgl %2, %1\n"
211                    "sete %%al; movzbl %%al,%0"
212                    : "=a" (ret), "=m" (*atomic)
213                    : "r" (newval), "m" (*atomic), "0" (oldval));
214  return ret;
215}
216# define SYNC_ACQ(l)     (! xchgl(l,1))
217# define SYNC_REL(l)     (*(int volatile *)l = 0)
218# define SYNC_INC(l)     (xaddl(l, 1) + 1)
219# define SYNC_DEC(l)     (xaddl(l, -1) - 1)
220# define SYNC_CAS(l,o,n) (cmpxchglf(l,o,n))
221# define HAVE_SYNC 1
222#endif
223
224
225#if USE_GCC_PPC_ASM && !HAVE_SYNC
226static int xchg_acq(int volatile *atomic, int newval) 
227{
228  int oldval;
229  __asm __volatile ("1: lwarx   %0,0,%2\n"
230                    "   stwcx.  %3,0,%2\n"
231                    "   bne-    1b\n"
232                    "   isync"
233                    : "=&r" (oldval), "+m" (*atomic)
234                    : "b" (atomic), "r" (newval), "m" (*atomic)
235                    : "cr0", "memory");
236  return oldval;
237}
238static void st_rel(int volatile *atomic, int newval)
239{
240  __asm __volatile ("sync" ::: "memory");
241  *atomic = newval;
242}
243static int addlx(int volatile *atomic, int add) 
244{
245  int val;
246  __asm __volatile ("1: lwarx  %0,0,%2\n"
247                    "   add    %0,%0,%3\n"
248                    "   stwcx. %0,0,%2\n"
249                    "   bne-   1b"
250                    : "=&b" (val), "+m" (*atomic) 
251                    : "b" (atomic), "r" (add), "m" (*atomic)           
252                    : "cr0", "memory");
253  return val;
254}
255static int cmpxchgl(int volatile *atomic, int oldval, int newval)
256{
257  int ret;
258  __asm __volatile ("   sync\n"
259                    "1: lwarx  %0,0,%1\n"
260                    "   cmpw   %0,%2\n"
261                    "   bne    2f\n"
262                    "   stwcx. %3,0,%1\n"
263                    "   bne-   1b\n"
264                    "2: isync"
265                    : "=&r" (ret)
266                    : "b" (atomic), "r" (oldval), "r" (newval)
267                    : "cr0", "memory");
268  return ret;
269}
270# define SYNC_ACQ(l)     (!xchg_acq(l,1))
271# define SYNC_REL(l)     (st_rel(l,0))
272# define SYNC_INC(l)     (addlx(l, 1))
273# define SYNC_DEC(l)     (addlx(l, -1))
274# define SYNC_CAS(l,o,n) (cmpxchgl(l,o,n)==o)
275# define HAVE_SYNC 1
276#endif
277
278
279/* ============================================================
280// PART3 - THE IMPLEMENTATION
281*/
282
283#if HAVE_SYNC
284
285/* We have fast synchronization */
286
287int volatile nwaiters = 0;
288int volatile dummy;
289
290int 
291atomicAcquire(int volatile *lock)
292{
293  return SYNC_ACQ(lock);
294}
295   
296void 
297atomicAcquireOrSpin(int volatile *lock)
298{
299  int spin = 16;
300  while (spin >= 0 && ! SYNC_ACQ(lock))
301    spin -= 1;
302  if (spin < 0)
303    {
304      MUTEX_ENTER;
305      nwaiters += 1;
306      while (! SYNC_ACQ(lock))
307        COND_WAIT;
308      nwaiters -= 1;
309      MUTEX_LEAVE;
310    }
311}
312
313void 
314atomicRelease(int volatile *lock)
315{
316  SYNC_REL(lock);
317  if (nwaiters > 0)
318    {
319      MUTEX_ENTER;
320      if (nwaiters > 0)
321        COND_WAKEALL;
322      MUTEX_LEAVE;
323    }
324}
325
326int
327atomicIncrement(int volatile *var)
328{
329  return SYNC_INC(var);
330}
331
332int 
333atomicDecrement(int volatile *var)
334{
335  return SYNC_DEC(var);
336}
337
338int 
339atomicCompareAndSwap(int volatile *var, int oldval, int newval)
340{
341  return SYNC_CAS(var,oldval,newval);
342}
343
344
345
346#else
347
348int 
349atomicAcquire(int volatile *lock)
350{
351  int tmp;
352  MUTEX_ENTER;
353  if ((tmp = !*lock))
354    *lock = 1;
355  MUTEX_LEAVE;
356  return tmp;
357}
358
359void 
360atomicAcquireOrSpin(int volatile *lock)
361{
362  MUTEX_ENTER;
363  while (*lock)
364    COND_WAIT;
365  *lock = 1;
366  MUTEX_LEAVE;
367}
368
369void 
370atomicRelease(int volatile *lock)
371{
372  MUTEX_ENTER;
373  *lock = 0;
374  COND_WAKEALL;
375  MUTEX_LEAVE;
376}
377
378int
379atomicIncrement(int volatile *var)
380{
381  int res;
382  MUTEX_ENTER;
383  res = ++(*var);
384  MUTEX_LEAVE;
385  return res;
386}
387
388int 
389atomicDecrement(int volatile *var)
390{
391  int res;
392  MUTEX_ENTER;
393  res = --(*var);
394  MUTEX_LEAVE;
395  return res;
396}
397
398int 
399atomicCompareAndSwap(int volatile *var, int oldval, int newval)
400{
401  int ret;
402  MUTEX_ENTER;
403  ret = *var;
404  if (ret == oldval)
405    *var = newval;
406  MUTEX_LEAVE;
407  return (ret == oldval);
408}
409
410#endif  /* HAVE_SYNC */
411
412
Note: See TracBrowser for help on using the repository browser.