1/*
2 * PROGRAM: Client/Server Common Code
3 * MODULE: fb_atomic.h
4 * DESCRIPTION: Atomic counters
5 *
6 * The contents of this file are subject to the Initial
7 * Developer's Public License Version 1.0 (the "License");
8 * you may not use this file except in compliance with the
9 * License. You may obtain a copy of the License at
10 * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
11 *
12 * Software distributed under the License is distributed AS IS,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
14 * See the License for the specific language governing rights
15 * and limitations under the License.
16 *
17 * The Original Code was created by Nickolay Samofatov
18 * for the Firebird Open Source RDBMS project.
19 *
20 * Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com>
21 * and all contributors signed below.
22 *
23 * All Rights Reserved.
24 * Contributor(s): ______________________________________.
25 *
26 */
27
28#ifndef CLASSES_FB_ATOMIC_H
29#define CLASSES_FB_ATOMIC_H
30
31// Here we define an atomic type, which is to be used in interface for all OS
32#if SIZEOF_VOID_P == 8
33typedef SINT64 AtomicType;
34#else
35typedef SLONG AtomicType;
36#endif
37
38// IMPORTANT !
39// Most of the interlocked functions returns "old" value of operand (except of
40// InterlockedIncrement and InterlockedDecrement on Windows) and this is correct
41// as "old" value impossible to restore from "new" value and operation parameters
42// and "new" value could be changed at time when code looks at it.
43// This (returning of original value) is not how operators such as "=", "+=",
44// "&=" etc, usually works. Therefore overloaded operators in AtomicCounter class
45// are void and all of them have corresponding equivalent functions returning
46// "old" value of operand.
47// The only exceptions from this rule is unary increment and decrement (for
48// historical reasons). Use it with care ! If one need old value of just
49// incremented atomic variable he should use exchangeAdd, not operator++.
50
51#if defined(WIN_NT)
52
53#include <windows.h>
54#include <intrin.h>
55
56namespace Firebird {
57
58// Win95 is not supported unless compiled conditionally and
59// redirected to generic version below
60class PlatformAtomicCounter
61{
62public:
63#ifdef _WIN64
64 typedef LONGLONG counter_type;
65#else
66 typedef LONG counter_type;
67#endif
68
69 explicit PlatformAtomicCounter(counter_type val = 0) : counter(val) {}
70 ~PlatformAtomicCounter() {}
71
72 // returns old value
73 counter_type exchangeAdd(counter_type val)
74 {
75#ifdef _WIN64
76 return _InterlockedExchangeAdd64(&counter, val);
77#else
78 return _InterlockedExchangeAdd(&counter, val);
79#endif
80 }
81
82 bool compareExchange(counter_type oldVal, counter_type newVal)
83 {
84#ifdef _WIN64
85 return (_InterlockedCompareExchange64(&counter, newVal, oldVal) == oldVal);
86#else
87 return (_InterlockedCompareExchange(&counter, newVal, oldVal) == oldVal);
88#endif
89 }
90
91 // returns old value
92 counter_type setValue(counter_type val)
93 {
94#ifdef _WIN64
95 return _InterlockedExchange64(&counter, val);
96#else
97 return _InterlockedExchange(&counter, val);
98#endif
99 }
100
101protected:
102#ifndef MINGW
103 volatile
104#endif
105 counter_type counter;
106};
107
108
109class PlatformAtomicPointer
110{
111public:
112 explicit PlatformAtomicPointer(void* val = NULL) : pointer(val) {}
113 ~PlatformAtomicPointer() {}
114
115 void* platformValue() const { return (void*) pointer; }
116
117 // returns old value
118 void* platformSetValue(void* val)
119 {
120#ifdef _WIN64
121 return _InterlockedExchangePointer((volatile PVOID*) &pointer, val);
122#else
123 //InterlockedExchangePointer((volatile PVOID*)&pointer, val);
124 return (void*) _InterlockedExchange((LONG volatile*) &pointer, (LONG) val);
125#endif
126 }
127
128 bool platformCompareExchange(void* oldVal, void* newVal)
129 {
130#ifdef _WIN64
131 return (_InterlockedCompareExchangePointer(&pointer, newVal, oldVal) == oldVal);
132#else
133 //return (InterlockedCompareExchangePointer((PVOID volatile*) &pointer, newVal, oldVal) == oldVal);
134 return ((PVOID)(LONG_PTR) _InterlockedCompareExchange((LONG volatile*) &pointer, (LONG) newVal, (LONG) oldVal)) == oldVal;
135#endif
136 }
137
138private:
139 void* volatile pointer;
140};
141
142} // namespace Firebird
143
144#elif defined(AIX)
145
146#if SIZEOF_VOID_P != 8
147#error: Only 64-bit mode supported on AIX
148#endif
149
150#include <sys/atomic_op.h>
151
152namespace Firebird {
153
154// AIX version - uses AIX atomic API
155class PlatformAtomicCounter
156{
157public:
158 typedef long counter_type;
159
160 explicit PlatformAtomicCounter(AtomicType value = 0) : counter(value) {}
161 ~PlatformAtomicCounter() {}
162
163 counter_type exchangeAdd(AtomicType value)
164 {
165 return fetch_and_addlp(&counter, static_cast<unsigned long>(value));
166 }
167
168 counter_type value() const { return counter; }
169
170 bool compareExchange(counter_type oldVal, counter_type newVal)
171 {
172 return compare_and_swaplp(&counter, &oldVal, newVal);
173 }
174
175 counter_type setValue(AtomicType val)
176 {
177 counter_type old;
178 do
179 {
180 old = counter;
181 } while (!compare_and_swaplp(&counter, &old, val));
182 return old;
183 }
184
185protected:
186 counter_type counter;
187};
188
189#define ATOMIC_FLUSH_CACHE 1
190inline void FlushCache()
191{
192 asm_sync();
193}
194inline void WaitForFlushCache()
195{
196 asm_isync();
197}
198
199} // namespace Firebird
200
201#elif defined(HPUX) && defined(HAVE_ATOMIC_H)
202
203#include <atomic.h>
204
205namespace Firebird {
206
207// HPUX version - uses atomic API library
208class PlatformAtomicCounter
209{
210public:
211 typedef uint64_t counter_type;
212
213 explicit PlatformAtomicCounter(counter_type value = 0) : counter(value) {}
214 ~PlatformAtomicCounter() {}
215
216 counter_type exchangeAdd(counter_type value)
217 {
218 counter_type old = counter;
219 do
220 {
221 old = counter;
222 errno = 0;
223 } while (atomic_cas_64(&counter, old, old + value) != old);
224 return old;
225 }
226
227 bool compareExchange(counter_type oldVal, counter_type newVal)
228 {
229 return atomic_cas_64(&counter, oldVal, newVal) == oldVal;
230 }
231
232 counter_type setValue(counter_type val)
233 {
234 return atomic_swap_64(&counter, val);
235 }
236
237protected:
238 volatile counter_type counter;
239};
240
241} // namespace Firebird
242
243#elif defined(SOLARIS) && defined(HAVE_ATOMIC_H)
244
245#include <atomic.h>
246
247extern "C"
248{
249extern void membar_flush(void);
250extern void membar_wait(void);
251}
252
253namespace Firebird {
254
255// Solaris version - uses Solaris atomic_ops
256class PlatformAtomicCounter
257{
258public:
259 typedef ulong_t counter_type;
260 typedef long delta_type;
261
262 explicit PlatformAtomicCounter(counter_type value = 0) : counter(value) {}
263 ~PlatformAtomicCounter() {}
264
265 counter_type exchangeAdd(delta_type value)
266 {
267 return atomic_add_long_nv(&counter, value);
268 }
269
270 counter_type setValue(delta_type value)
271 {
272 return atomic_swap_ulong(&counter, value);
273 }
274
275 bool compareExchange(counter_type oldVal, counter_type newVal)
276 {
277 TODO: implement method for Solaris
278 }
279
280protected:
281 volatile counter_type counter;
282};
283
284#define ATOMIC_FLUSH_CACHE 1
285inline void FlushCache()
286{
287 membar_flush();
288}
289inline void WaitForFlushCache()
290{
291 membar_wait();
292}
293
294} // namespace Firebird
295
296
297#elif defined(__SUNPRO_CC) && !defined(HAVE_ATOMIC_H) && defined(__sparc)
298// Solaris 9 does not have nice <atomic.h> header file, so we provide
299// an assembly language, Sun Studio Pro only, implementation.
300
301// assembly versions of fetch_and_add_il, compare_and_swap_il,
302// are in fb_atomic_*.il
303
304// No GNU CC implementation on Solaris 9 is planned!
305extern "C"
306{
307 extern int fetch_and_add_il(volatile unsigned int* word_addr, int value);
308 extern boolean_t compare_and_swap_il(volatile unsigned int* word_addr,
309 unsigned int* old_val_addr, unsigned int new_val);
310}
311
312namespace Firebird {
313
314class PlatformAtomicCounter
315{
316public:
317 typedef uint_t counter_type;
318 typedef int delta_type;
319
320 explicit PlatformAtomicCounter(counter_type value = 0) : counter(value) {}
321 ~PlatformAtomicCounter() {}
322
323 counter_type exchangeAdd(delta_type value)
324 {
325 return fetch_and_add_il(&counter, value);
326 }
327
328 counter_type setValue(delta_type value)
329 {
330 counter_type old;
331 do
332 {
333 old = counter;
334 } while (!compare_and_swap_il(&counter, &old, value));
335 return old;
336 }
337
338 bool compareExchange(counter_type oldVal, counter_type newVal)
339 {
340 return compare_and_swap_il(&counter, &oldVal, newVal);
341 }
342
343protected:
344 counter_type counter;
345};
346
347} // namespace Firebird
348
349#elif defined(__GNUC__) //&& (defined(i386) || defined(I386) || defined(_M_IX86) || defined(AMD64) || defined(__x86_64__))
350
351namespace Firebird {
352
353class PlatformAtomicCounter
354{
355public:
356 typedef AtomicType counter_type;
357
358 explicit PlatformAtomicCounter(AtomicType value = 0) : counter(value) {}
359 ~PlatformAtomicCounter() {}
360
361 AtomicType exchangeAdd(AtomicType val)
362 {
363 return __sync_fetch_and_add(&counter, val);
364 }
365
366 AtomicType setValue(AtomicType val)
367 {
368 // This may require special processing at least on HPPA because:
369 // Many targets have only minimal support for such locks, and do not support a full exchange operation.
370 // In this case, a target may support reduced functionality here by which the only valid value to store
371 // is the immediate constant 1. The exact value actually stored in *ptr is implementation defined.
372#ifdef DEFINE_ACCORDING_IMPLEMENTATION_SPECIFIC
373 counter_type old;
374 do
375 {
376 old = counter;
377 } while (!__sync_bool_compare_and_swap(&counter, old, val));
378 return old;
379#else
380 return __sync_lock_test_and_set(&counter, val);
381#endif
382 }
383
384 bool compareExchange(counter_type oldVal, counter_type newVal)
385 {
386 return __sync_bool_compare_and_swap(&counter, oldVal, newVal);
387 }
388
389protected:
390 volatile counter_type counter;
391};
392
393class PlatformAtomicPointer
394{
395public:
396 explicit PlatformAtomicPointer(void* val = NULL) : pointer(val) {}
397 ~PlatformAtomicPointer() {}
398
399 void* platformValue() const { return (void*)pointer; }
400
401 void* platformSetValue(void* val)
402 {
403#ifdef DEFINE_ACCORDING_IMPLEMENTATION_SPECIFIC
404 void* old;
405 do
406 {
407 old = pointer;
408 } while (!__sync_bool_compare_and_swap(&pointer, old, val));
409 return old;
410#else
411 return __sync_lock_test_and_set(&pointer, val);
412#endif
413 }
414
415 bool platformCompareExchange(void* oldVal, void* newVal)
416 {
417 return __sync_bool_compare_and_swap(&pointer, oldVal, newVal);
418 }
419
420private:
421 void* volatile pointer;
422};
423
424} // namespace Firebird
425
426#elif defined(HAVE_AO_COMPARE_AND_SWAP_FULL) && !defined(HPUX)
427
428// Sometimes in the future it can become the best choice for all platforms.
429// Currently far not CPUs/OSs/compilers are supported well.
430// Therefore use it as last chance to build successfully.
431
432// wbo 2009-10 - We tested libatomic_ops against Firebird 2.5/HPUX and the
433// atomic implementation was incomplete on IA-64/HPPA
434
435extern "C" {
436#define AO_REQUIRE_CAS
437#include <atomic_ops.h>
438}
439
440namespace Firebird {
441
442class PlatformAtomicCounter
443{
444public:
445 typedef AO_t counter_type; // AO_t should match maximum native 'int' type
446
447 explicit PlatformAtomicCounter(counter_type value = 0)
448 : counter(value)
449 {
450#ifdef DEV_BUILD
451 // check that AO_t size is as we expect (can't fb_assert here)
452
453 if (sizeof(AtomicType) != sizeof(AO_t) || sizeof(void*) != sizeof(AO_t))
454 abort();
455#endif
456 }
457
458 ~PlatformAtomicCounter()
459 {
460 }
461
462 AtomicType exchangeAdd(AtomicType value)
463 {
464 counter_type old;
465 do
466 {
467 old = counter;
468 } while (!AO_compare_and_swap_full(&counter, old, old + counter_type(value)));
469 return AtomicType(old);
470 }
471
472 AtomicType setValue(AtomicType val)
473 {
474 counter_type old;
475 do
476 {
477 old = counter;
478 } while (!AO_compare_and_swap_full(&counter, old, counter_type(val)));
479 return AtomicType(old);
480 }
481
482 bool compareExchange(counter_type oldVal, counter_type newVal)
483 {
484 return AO_compare_and_swap_full(&counter, oldVal, newVal);
485 }
486
487protected:
488 counter_type counter;
489};
490
491class PlatformAtomicPointer
492{
493public:
494 explicit PlatformAtomicPointer(void* val = NULL)
495 : pointer((AO_t) val)
496 {
497#ifdef DEV_BUILD
498 // check that AO_t size is as we expect (can't fb_assert here)
499 if (sizeof(void*) != sizeof(AO_t))
500 abort();
501#endif
502 }
503
504 ~PlatformAtomicPointer()
505 {
506 }
507
508 void* platformValue() const
509 {
510 return (void*) pointer;
511 }
512
513 void* platformSetValue(void* val)
514 {
515 AO_t old;
516 do
517 {
518 old = pointer;
519 } while (!AO_compare_and_swap_full(&pointer, old, (AO_T) val));
520 return (void*) old;
521 }
522
523 bool platformCompareExchange(void* oldVal, void* newVal)
524 {
525 return AO_compare_and_swap_full(&pointer, (AO_t)oldVal, (AO_t)newVal);
526 }
527
528private:
529 AO_t pointer;
530};
531
532
533} // namespace Firebird
534
535#else
536
537#error AtomicCounter: implement appropriate code for your platform!
538
539#endif
540
541// platform-independent code
542
543namespace Firebird {
544
545class AtomicCounter : public PlatformAtomicCounter
546{
547public:
548 explicit AtomicCounter(counter_type value = 0) : PlatformAtomicCounter(value) {}
549 ~AtomicCounter() {}
550
551 counter_type value() const { return counter; }
552
553 // returns old value
554 counter_type exchangeBitAnd(counter_type val)
555 {
556 while (true)
557 {
558 volatile counter_type oldVal = counter;
559
560 if (compareExchange(oldVal, oldVal & val))
561 return oldVal;
562 }
563 }
564
565 // returns old value
566 counter_type exchangeBitOr(counter_type val)
567 {
568 while (true)
569 {
570 volatile counter_type oldVal = counter;
571
572 if (compareExchange(oldVal, oldVal | val))
573 return oldVal;
574 }
575 }
576
577 // returns old value
578 counter_type exchangeGreater(counter_type val)
579 {
580 while (true)
581 {
582 volatile counter_type oldVal = counter;
583
584 if (oldVal >= val)
585 return oldVal;
586
587 if (compareExchange(oldVal, val))
588 return oldVal;
589 }
590 }
591
592 operator counter_type () const
593 {
594 return value();
595 }
596
597 void operator =(counter_type val)
598 {
599 setValue(val);
600 }
601
602 // returns new value !
603 counter_type operator ++()
604 {
605 return exchangeAdd(1) + 1;
606 }
607
608 // returns new value !
609 counter_type operator --()
610 {
611 return exchangeAdd(-1) - 1;
612 }
613
614 void operator +=(counter_type val)
615 {
616 exchangeAdd(val);
617 }
618
619 void operator -=(counter_type val)
620 {
621 exchangeAdd(-val);
622 }
623
624 void operator &=(counter_type val)
625 {
626 exchangeBitAnd(val);
627 }
628
629 void operator |=(counter_type val)
630 {
631 exchangeBitOr(val);
632 }
633
634};
635
636template <typename T>
637class AtomicPointer : public PlatformAtomicPointer
638{
639public:
640 explicit AtomicPointer(T* val = NULL) : PlatformAtomicPointer(val) {}
641 ~AtomicPointer() {}
642
643 T* value() const
644 {
645 return (T*)platformValue();
646 }
647
648 void setValue(T* val)
649 {
650 platformSetValue(val);
651 }
652
653 bool compareExchange(T* oldVal, T* newVal)
654 {
655 return platformCompareExchange(oldVal, newVal);
656 }
657
658 operator T* () const
659 {
660 return value();
661 }
662
663 T* operator ->() const
664 {
665 return value();
666 }
667
668 void operator =(T* val)
669 {
670 setValue(val);
671 }
672};
673
674#ifndef ATOMIC_FLUSH_CACHE
675inline void FlushCache() { }
676inline void WaitForFlushCache() { }
677#endif
678
679} // namespace Firebird
680
681#endif // CLASSES_FB_ATOMIC_H
682