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 |
33 | typedef SINT64 AtomicType; |
34 | #else |
35 | typedef 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 | |
56 | namespace Firebird { |
57 | |
58 | // Win95 is not supported unless compiled conditionally and |
59 | // redirected to generic version below |
60 | class PlatformAtomicCounter |
61 | { |
62 | public: |
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 | |
101 | protected: |
102 | #ifndef MINGW |
103 | volatile |
104 | #endif |
105 | counter_type counter; |
106 | }; |
107 | |
108 | |
109 | class PlatformAtomicPointer |
110 | { |
111 | public: |
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 | |
138 | private: |
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 | |
152 | namespace Firebird { |
153 | |
154 | // AIX version - uses AIX atomic API |
155 | class PlatformAtomicCounter |
156 | { |
157 | public: |
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 | |
185 | protected: |
186 | counter_type counter; |
187 | }; |
188 | |
189 | #define ATOMIC_FLUSH_CACHE 1 |
190 | inline void FlushCache() |
191 | { |
192 | asm_sync(); |
193 | } |
194 | inline 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 | |
205 | namespace Firebird { |
206 | |
207 | // HPUX version - uses atomic API library |
208 | class PlatformAtomicCounter |
209 | { |
210 | public: |
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 | |
237 | protected: |
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 | |
247 | extern "C" |
248 | { |
249 | extern void membar_flush(void); |
250 | extern void membar_wait(void); |
251 | } |
252 | |
253 | namespace Firebird { |
254 | |
255 | // Solaris version - uses Solaris atomic_ops |
256 | class PlatformAtomicCounter |
257 | { |
258 | public: |
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 | |
280 | protected: |
281 | volatile counter_type counter; |
282 | }; |
283 | |
284 | #define ATOMIC_FLUSH_CACHE 1 |
285 | inline void FlushCache() |
286 | { |
287 | membar_flush(); |
288 | } |
289 | inline 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! |
305 | extern "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 | |
312 | namespace Firebird { |
313 | |
314 | class PlatformAtomicCounter |
315 | { |
316 | public: |
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 | |
343 | protected: |
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 | |
351 | namespace Firebird { |
352 | |
353 | class PlatformAtomicCounter |
354 | { |
355 | public: |
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 | |
389 | protected: |
390 | volatile counter_type counter; |
391 | }; |
392 | |
393 | class PlatformAtomicPointer |
394 | { |
395 | public: |
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 | |
420 | private: |
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 | |
435 | extern "C" { |
436 | #define AO_REQUIRE_CAS |
437 | #include <atomic_ops.h> |
438 | } |
439 | |
440 | namespace Firebird { |
441 | |
442 | class PlatformAtomicCounter |
443 | { |
444 | public: |
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 | |
487 | protected: |
488 | counter_type counter; |
489 | }; |
490 | |
491 | class PlatformAtomicPointer |
492 | { |
493 | public: |
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 | |
528 | private: |
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 | |
543 | namespace Firebird { |
544 | |
545 | class AtomicCounter : public PlatformAtomicCounter |
546 | { |
547 | public: |
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 | |
636 | template <typename T> |
637 | class AtomicPointer : public PlatformAtomicPointer |
638 | { |
639 | public: |
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 |
675 | inline void FlushCache() { } |
676 | inline void WaitForFlushCache() { } |
677 | #endif |
678 | |
679 | } // namespace Firebird |
680 | |
681 | #endif // CLASSES_FB_ATOMIC_H |
682 | |