glib: Rewrite gatomic.[ch]
[platform/upstream/glib.git] / glib / gatomic.c
1 /*
2  * Copyright © 2011 Ryan Lortie
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17  * USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gatomic.h"
25
26 /**
27  * SECTION:atomic_operations
28  * @title: Atomic Operations
29  * @short_description: basic atomic integer and pointer operations
30  * @see_also: #GMutex
31  *
32  * The following is a collection of compiler macros to provide atomic
33  * access to integer and pointer-sized values.
34  *
35  * The macros that have 'int' in the name will operate on pointers to
36  * #gint and #guint.  The macros with 'pointer' in the name will operate
37  * on pointers to any pointer-sized value, including #gsize.  There is
38  * no support for 64bit operations on platforms with 32bit pointers
39  * because it is not generally possible to perform these operations
40  * atomically.
41  *
42  * The get, set and exchange operations for integers and pointers
43  * nominally operate on #gint and #gpointer, respectively.  Of the
44  * arithmetic operations, the 'add' operation operates on (and returns)
45  * signed integer values (#gint and #gssize) and the 'and', 'or', and
46  * 'xor' operations operate on (and return) unsigned integer values
47  * (#guint and #gsize).
48  *
49  * All of the operations act as a full compiler and (where appropriate)
50  * hardware memory barrier.  Acquire and release or producer and
51  * consumer barrier semantics are not available through this API.
52  *
53  * On GCC, these macros are implemented using GCC intrinsic operations.
54  * On non-GCC compilers they will evaluate to function calls to
55  * functions implemented by GLib.
56  *
57  * If GLib itself was compiled with GCC then these functions will again
58  * be implemented by the GCC intrinsics.  On Windows without GCC, the
59  * interlocked API is used to implement the functions.
60  *
61  * With non-GCC compilers on non-Windows systems, the functions are
62  * currently incapable of implementing true atomic operations --
63  * instead, they fallback to holding a global lock while performing the
64  * operation.  This provides atomicity between the threads of one
65  * process, but not between separate processes.  For this reason, one
66  * should exercise caution when attempting to use these options on
67  * shared memory regions.
68  *
69  * It is very important that all accesses to a particular integer or
70  * pointer be performed using only this API and that different sizes of
71  * operation are not mixed or used on overlapping memory regions.  Never
72  * read or assign directly from or to a value -- always use this API.
73  *
74  * For simple reference counting purposes you should use
75  * g_atomic_int_inc() and g_atomic_int_dec_and_test().  Other uses that
76  * fall outside of simple reference counting patterns are prone to
77  * subtle bugs and occasionally undefined behaviour.  It is also worth
78  * noting that since all of these operations require global
79  * synchronisation of the entire machine, they can be quite slow.  In
80  * the case of performing multiple atomic operations it can often be
81  * faster to simply acquire a mutex lock around the critical area,
82  * perform the operations normally and then release the lock.
83  **/
84
85 #ifdef G_ATOMIC_OP_USE_GCC_BUILTINS
86
87 #ifndef __GNUC__
88 #error Using GCC builtin atomic ops, but not compiling with GCC?
89 #endif
90
91 /**
92  * g_atomic_int_get:
93  * @atomic: a pointer to a #gint or #guint
94  *
95  * Gets the current value of @atomic.
96  *
97  * This call acts as a full compiler and hardware memory barrier (before
98  * the get).
99  *
100  * Returns: the value of the integer
101  *
102  * Since: 2.4
103  **/
104 gint
105 (g_atomic_int_get) (volatile gint *atomic)
106 {
107   return g_atomic_int_get (atomic);
108 }
109
110 /**
111  * g_atomic_int_set:
112  * @atomic: a pointer to a #gint or #guint
113  * @newval: a new value to store
114  *
115  * Sets the value of @atomic to @newval.
116  *
117  * This call acts as a full compiler and hardware memory barrier (after
118  * the set).
119  *
120  * Since: 2.4
121  **/
122 void
123 (g_atomic_int_set) (volatile gint *atomic,
124                     gint           newval)
125 {
126   g_atomic_int_set (atomic, newval);
127 }
128
129 /**
130  * g_atomic_int_inc:
131  * @atomic: a pointer to a #gint or #guint
132  *
133  * Increments the value of @atomic by 1.
134  *
135  * This call acts as a full compiler and hardware memory barrier.
136  *
137  * Since: 2.4
138  **/
139 void
140 (g_atomic_int_inc) (volatile gint *atomic)
141 {
142   g_atomic_int_inc (atomic);
143 }
144
145 /**
146  * g_atomic_int_dec_and_test:
147  * @atomic: a pointer to a #gint or #guint
148  *
149  * Decrements the value of @atomic by 1.
150  *
151  * This call acts as a full compiler and hardware memory barrier.
152  *
153  * Returns: %TRUE if the resultant value is zero
154  *
155  * Since: 2.4
156  **/
157 gboolean
158 (g_atomic_int_dec_and_test) (volatile gint *atomic)
159 {
160   return g_atomic_int_dec_and_test (atomic);
161 }
162
163 /**
164  * g_atomic_int_compare_and_exchange:
165  * @atomic: a pointer to a #gint or #guint
166  * @oldval: the value to compare with
167  * @newval: the value to conditionally replace with
168  *
169  * Compares @atomic to @oldval and, if equal, sets it to @newval.  If
170  * @atomic was not equal to @oldval then no change occurs.
171  *
172  * This compare and exchange is done atomically.
173  *
174  * This call acts as a full compiler and hardware memory barrier.
175  *
176  * Returns: %TRUE if the exchange took place
177  *
178  * Since: 2.4
179  **/
180 gboolean
181 (g_atomic_int_compare_and_exchange) (volatile gint *atomic,
182                                      gint           oldval,
183                                      gint           newval)
184 {
185   return g_atomic_int_compare_and_exchange (atomic, oldval, newval);
186 }
187
188 /**
189  * g_atomic_int_add:
190  * @atomic: a pointer to a #gint or #guint
191  * @val: the value to add
192  *
193  * Atomically adds @val to the value of @atomic.
194  *
195  * This call acts as a full compiler and hardware memory barrier.
196  *
197  * Returns: the value of @atomic before the add, signed
198  *
199  * Since: 2.4
200  **/
201 gint
202 (g_atomic_int_add) (volatile gint *atomic,
203                     gint           val)
204 {
205   return g_atomic_int_add (atomic, val);
206 }
207
208 /**
209  * g_atomic_int_and:
210  * @atomic: a pointer to a #gint or #guint
211  * @val: the value to 'and'
212  *
213  * Performs an atomic bitwise 'and' of the value of @atomic and @val,
214  * storing the result back in @atomic.
215  *
216  * This call acts as a full compiler and hardware memory barrier.
217  *
218  * Returns: the value of @atomic before the operation, unsigned
219  *
220  * Since: 2.30
221  **/
222 guint
223 (g_atomic_int_and) (volatile guint *atomic,
224                     guint           val)
225 {
226   return g_atomic_int_and (atomic, val);
227 }
228
229 /**
230  * g_atomic_int_or:
231  * @atomic: a pointer to a #gint or #guint
232  * @val: the value to 'or'
233  *
234  * Performs an atomic bitwise 'or' of the value of @atomic and @val,
235  * storing the result back in @atomic.
236  *
237  * This call acts as a full compiler and hardware memory barrier.
238  *
239  * Returns: the value of @atomic before the operation, unsigned
240  *
241  * Since: 2.30
242  **/
243 guint
244 (g_atomic_int_or) (volatile guint *atomic,
245                    guint           val)
246 {
247   return g_atomic_int_or (atomic, val);
248 }
249
250 /**
251  * g_atomic_int_xor:
252  * @atomic: a pointer to a #gint or #guint
253  * @val: the value to 'xor'
254  *
255  * Performs an atomic bitwise 'xor' of the value of @atomic and @val,
256  * storing the result back in @atomic.
257  *
258  * This call acts as a full compiler and hardware memory barrier.
259  *
260  * Returns: the value of @atomic before the operation, unsigned
261  *
262  * Since: 2.30
263  **/
264 guint
265 (g_atomic_int_xor) (volatile guint *atomic,
266                     guint           val)
267 {
268   return g_atomic_int_xor (atomic, val);
269 }
270
271
272 /**
273  * g_atomic_pointer_get:
274  * @atomic: a pointer to a #gpointer-sized value
275  *
276  * Gets the current value of @atomic.
277  *
278  * This call acts as a full compiler and hardware memory barrier (before
279  * the get).
280  *
281  * Returns: the value of the pointer
282  *
283  * Since: 2.4
284  **/
285 gpointer
286 (g_atomic_pointer_get) (volatile void *atomic)
287 {
288   return g_atomic_pointer_get ((volatile gpointer *) atomic);
289 }
290
291 /**
292  * g_atomic_pointer_set:
293  * @atomic: a pointer to a #gpointer-sized value
294  * @newval: a new value to store
295  *
296  * Sets the value of @atomic to @newval.
297  *
298  * This call acts as a full compiler and hardware memory barrier (after
299  * the set).
300  *
301  * Since: 2.4
302  **/
303 void
304 (g_atomic_pointer_set) (volatile void *atomic,
305                         gpointer       newval)
306 {
307   g_atomic_pointer_set ((volatile gpointer *) atomic, newval);
308 }
309
310 /**
311  * g_atomic_pointer_compare_and_exchange:
312  * @atomic: a pointer to a #gpointer-sized value
313  * @oldval: the value to compare with
314  * @newval: the value to conditionally replace with
315  *
316  * Compares @atomic to @oldval and, if equal, sets it to @newval.  If
317  * @atomic was not equal to @oldval then no change occurs.
318  *
319  * This compare and exchange is done atomically.
320  *
321  * This call acts as a full compiler and hardware memory barrier.
322  *
323  * Returns: %TRUE if the exchange took place
324  *
325  * Since: 2.4
326  **/
327 gboolean
328 (g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
329                                          gpointer       oldval,
330                                          gpointer       newval)
331 {
332   return g_atomic_pointer_compare_and_exchange ((volatile gpointer *) atomic,
333                                                 oldval, newval);
334 }
335
336 /**
337  * g_atomic_pointer_add:
338  * @atomic: a pointer to a #gpointer-sized value
339  * @val: the value to add
340  *
341  * Atomically adds @val to the value of @atomic.
342  *
343  * This call acts as a full compiler and hardware memory barrier.
344  *
345  * Returns: the value of @atomic before the add, signed
346  *
347  * Since: 2.30
348  **/
349 gssize
350 (g_atomic_pointer_add) (volatile void *atomic,
351                         gssize         val)
352 {
353   return g_atomic_pointer_add ((volatile gpointer *) atomic, val);
354 }
355
356 /**
357  * g_atomic_pointer_and:
358  * @atomic: a pointer to a #gpointer-sized value
359  * @val: the value to 'and'
360  *
361  * Performs an atomic bitwise 'and' of the value of @atomic and @val,
362  * storing the result back in @atomic.
363  *
364  * This call acts as a full compiler and hardware memory barrier.
365  *
366  * Returns: the value of @atomic before the operation, unsigned
367  *
368  * Since: 2.30
369  **/
370 gsize
371 (g_atomic_pointer_and) (volatile void *atomic,
372                         gsize          val)
373 {
374   return g_atomic_pointer_and ((volatile gpointer *) atomic, val);
375 }
376
377 /**
378  * g_atomic_pointer_or:
379  * @atomic: a pointer to a #gpointer-sized value
380  * @val: the value to 'or'
381  *
382  * Performs an atomic bitwise 'or' of the value of @atomic and @val,
383  * storing the result back in @atomic.
384  *
385  * This call acts as a full compiler and hardware memory barrier.
386  *
387  * Returns: the value of @atomic before the operation, unsigned
388  *
389  * Since: 2.30
390  **/
391 gsize
392 (g_atomic_pointer_or) (volatile void *atomic,
393                        gsize          val)
394 {
395   return g_atomic_pointer_or ((volatile gpointer *) atomic, val);
396 }
397
398 /**
399  * g_atomic_pointer_xor:
400  * @atomic: a pointer to a #gpointer-sized value
401  * @val: the value to 'xor'
402  *
403  * Performs an atomic bitwise 'xor' of the value of @atomic and @val,
404  * storing the result back in @atomic.
405  *
406  * This call acts as a full compiler and hardware memory barrier.
407  *
408  * Returns: the value of @atomic before the operation, unsigned
409  *
410  * Since: 2.30
411  **/
412 gsize
413 (g_atomic_pointer_xor) (volatile void *atomic,
414                         gsize          val)
415 {
416   return g_atomic_pointer_xor ((volatile gpointer *) atomic, val);
417 }
418
419 #elif defined (G_PLATFORM_WIN32)
420
421 /*
422  * http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx
423  */
424 gint
425 (g_atomic_int_get) (volatile gint *atomic)
426 {
427   MemoryBarrier ();
428   return *atomic;
429 }
430
431 void
432 (g_atomic_int_set) (volatile gint *atomic,
433                     gint           newval)
434 {
435   *atomic = newval;
436   MemoryBarrier ();
437 }
438
439 void
440 (g_atomic_int_inc) (volatile gint *atomic)
441 {
442   InterlockedIncrement (atomic);
443 }
444
445 gboolean
446 (g_atomic_int_dec_and_test) (volatile gint *atomic)
447 {
448   return InterlockedDecrement (atomic) == 0;
449 }
450
451 gboolean
452 (g_atomic_int_compare_and_exchange) (volatile gint *atomic,
453                                      gint           oldval,
454                                      gint           newval)
455 {
456   return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
457 }
458
459 gint
460 (g_atomic_int_add) (volatile gint *atomic,
461                     gint           val)
462 {
463   return InterlockedExchangeAdd (atomic, val);
464 }
465
466 guint
467 (g_atomic_int_and) (volatile guint *atomic,
468                     guint           val)
469 {
470   return InterlockedAnd (atomic, val);
471 }
472
473 guint
474 (g_atomic_int_or) (volatile guint *atomic,
475                    guint           val)
476 {
477   return InterlockedOr (atomic, val);
478 }
479
480 guint
481 (g_atomic_int_xor) (volatile guint *atomic,
482                     guint           val)
483 {
484   return InterlockedXor (atomic, val);
485 }
486
487
488 gpointer
489 (g_atomic_pointer_get) (volatile void *atomic)
490 {
491   volatile gpointer *ptr = atomic;
492
493   MemoryBarrier ();
494   return *ptr;
495 }
496
497 void
498 (g_atomic_pointer_set) (volatile void *atomic,
499                         gpointer       newval)
500 {
501   volatile gpointer *ptr = atomic;
502
503   *ptr = newval;
504   MemoryBarrier ();
505 }
506
507 gboolean
508 (g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
509                                          gpointer       oldval,
510                                          gpointer       newval)
511 {
512   return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
513 }
514
515 gssize
516 (g_atomic_pointer_add) (volatile void *atomic,
517                         gssize         val)
518 {
519 #if GLIB_SIZEOF_VOID_P == 8
520   return InterlockedExchangeAdd64 (atomic, val);
521 #else
522   return InterlockedExchangeAdd (atomic, val);
523 #endif
524 }
525
526 gsize
527 (g_atomic_pointer_and) (volatile void *atomic,
528                         gsize          val)
529 {
530 #if GLIB_SIZEOF_VOID_P == 8
531   return InterlockedAnd64 (atomic, val);
532 #else
533   return InterlockedAnd (atomic, val);
534 #endif
535 }
536
537 gsize
538 (g_atomic_pointer_or) (volatile void *atomic,
539                        gsize          val)
540 {
541 #if GLIB_SIZEOF_VOID_P == 8
542   return InterlockedOr64 (atomic, val);
543 #else
544   return InterlockedOr (atomic, val);
545 #endif
546 }
547
548 gsize
549 (g_atomic_pointer_xor) (volatile void *atomic,
550                         gsize          val)
551 {
552 #if GLIB_SIZEOF_VOID_P == 8
553   return InterlockedXor64 (atomic, val);
554 #else
555   return InterlockedXor (atomic, val);
556 #endif
557 }
558
559 #else
560
561 #include "gthread.h"
562
563 static GStaticMutex g_atomic_lock;
564
565 gint
566 (g_atomic_int_get) (volatile gint *atomic)
567 {
568   gint value;
569
570   g_static_mutex_lock (&g_atomic_lock);
571   value = *atomic;
572   g_static_mutex_unlock (&g_atomic_lock);
573
574   return value;
575 }
576
577 void
578 (g_atomic_int_set) (volatile gint *atomic,
579                     gint           value)
580 {
581   g_static_mutex_lock (&g_atomic_lock);
582   *atomic = value;
583   g_static_mutex_unlock (&g_atomic_lock);
584 }
585
586 void
587 (g_atomic_int_inc) (volatile gint *atomic)
588 {
589   g_static_mutex_lock (&g_atomic_lock);
590   (*atomic)++;
591   g_static_mutex_unlock (&g_atomic_lock);
592 }
593
594 gboolean
595 (g_atomic_int_dec_and_test) (volatile gint *atomic)
596 {
597   gboolean is_zero;
598
599   g_static_mutex_lock (&g_atomic_lock);
600   is_zero = --(*atomic) == 0;
601   g_static_mutex_unlock (&g_atomic_lock);
602
603   return is_zero;
604 }
605
606 gboolean
607 (g_atomic_int_compare_and_exchange) (volatile gint *atomic,
608                                      gint           oldval,
609                                      gint           newval)
610 {
611   gboolean success;
612
613   g_static_mutex_lock (&g_atomic_lock);
614
615   if ((success = (*atomic == oldval)))
616     *atomic = newval;
617
618   g_static_mutex_unlock (&g_atomic_lock);
619
620   return success;
621 }
622
623 gint
624 (g_atomic_int_add) (volatile gint *atomic,
625                     gint           val)
626 {
627   gint oldval;
628
629   g_static_mutex_lock (&g_atomic_lock);
630   oldval = *atomic;
631   *atomic = oldval + val;
632   g_static_mutex_unlock (&g_atomic_lock);
633
634   return oldval;
635 }
636
637 guint
638 (g_atomic_int_and) (volatile guint *atomic,
639                     guint           val)
640 {
641   guint oldval;
642
643   g_static_mutex_lock (&g_atomic_lock);
644   oldval = *atomic;
645   *atomic = oldval & val;
646   g_static_mutex_unlock (&g_atomic_lock);
647
648   return oldval;
649 }
650
651 guint
652 (g_atomic_int_or) (volatile guint *atomic,
653                    guint           val)
654 {
655   guint oldval;
656
657   g_static_mutex_lock (&g_atomic_lock);
658   oldval = *atomic;
659   *atomic = oldval | val;
660   g_static_mutex_unlock (&g_atomic_lock);
661
662   return oldval;
663 }
664
665 guint
666 (g_atomic_int_xor) (volatile guint *atomic,
667                     guint           val)
668 {
669   guint oldval;
670
671   g_static_mutex_lock (&g_atomic_lock);
672   oldval = *atomic;
673   *atomic = oldval ^ val;
674   g_static_mutex_unlock (&g_atomic_lock);
675
676   return oldval;
677 }
678
679
680 gpointer
681 (g_atomic_pointer_get) (volatile void *atomic)
682 {
683   volatile gpointer *ptr = atomic;
684   gpointer value;
685
686   g_static_mutex_lock (&g_atomic_lock);
687   value = *ptr;
688   g_static_mutex_unlock (&g_atomic_lock);
689
690   return value;
691 }
692
693 void
694 (g_atomic_pointer_set) (volatile void *atomic,
695                         gpointer       newval)
696 {
697   volatile gpointer *ptr = atomic;
698
699   g_static_mutex_lock (&g_atomic_lock);
700   *ptr = newval;
701   g_static_mutex_unlock (&g_atomic_lock);
702 }
703
704 gboolean
705 (g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
706                                          gpointer       oldval,
707                                          gpointer       newval)
708 {
709   volatile gpointer *ptr = atomic;
710   gboolean success;
711
712   g_static_mutex_lock (&g_atomic_lock);
713
714   if ((success = (*ptr == oldval)))
715     *ptr = newval;
716
717   g_static_mutex_unlock (&g_atomic_lock);
718
719   return success;
720 }
721
722 gssize
723 (g_atomic_pointer_add) (volatile void *atomic,
724                         gssize         val)
725 {
726   volatile gssize *ptr = atomic;
727   gssize oldval;
728
729   g_static_mutex_lock (&g_atomic_lock);
730   oldval = *ptr;
731   *ptr = oldval + val;
732   g_static_mutex_unlock (&g_atomic_lock);
733
734   return oldval;
735 }
736
737 gsize
738 (g_atomic_pointer_and) (volatile void *atomic,
739                         gsize          val)
740 {
741   volatile gsize *ptr = atomic;
742   gsize oldval;
743
744   g_static_mutex_lock (&g_atomic_lock);
745   oldval = *ptr;
746   *ptr = oldval & val;
747   g_static_mutex_unlock (&g_atomic_lock);
748
749   return oldval;
750 }
751
752 gsize
753 (g_atomic_pointer_or) (volatile void *atomic,
754                        gsize          val)
755 {
756   volatile gsize *ptr = atomic;
757   gsize oldval;
758
759   g_static_mutex_lock (&g_atomic_lock);
760   oldval = *ptr;
761   *ptr = oldval | val;
762   g_static_mutex_unlock (&g_atomic_lock);
763
764   return oldval;
765 }
766
767 gsize
768 (g_atomic_pointer_xor) (volatile void *atomic,
769                         gsize          val)
770 {
771   volatile gsize *ptr = atomic;
772   gsize oldval;
773
774   g_static_mutex_lock (&g_atomic_lock);
775   oldval = *ptr;
776   *ptr = oldval ^ val;
777   g_static_mutex_unlock (&g_atomic_lock);
778
779   return oldval;
780 }
781
782 #endif
783
784 /**
785  * g_atomic_int_exchange_and_add:
786  * @atomic: a pointer to a #gint
787  * @val: the value to add
788  *
789  * This function existed before g_atomic_int_add() returned the prior
790  * value of the integer (which it now does).  It is retained only for
791  * compatibility reasons.  Don't use this function in new code.
792  *
793  * Returns: the value of @atomic before the add, signed
794  * Since: 2.4
795  * Deprecated: 2.30: Use g_atomic_int_add() instead.
796  **/
797 gint
798 g_atomic_int_exchange_and_add (volatile gint *atomic,
799                                gint           val)
800 {
801   return (g_atomic_int_add) (atomic, val);
802 }