[threadpool] Fix race on runtime shutdown (#4263)
[mono.git] / mono / utils / refcount.h
1
2 #ifndef __MONO_UTILS_REFCOUNT_H__
3 #define __MONO_UTILS_REFCOUNT_H__
4
5 #include <glib.h>
6 #include <config.h>
7
8 #include "atomic.h"
9
10 /*
11  * Mechanism for ref-counting which tries to be as user-friendly as possible. Instead of being a wrapper around
12  * user-provided data, it is embedded into the user data.
13  *
14  * This introduces some constraints on the MonoRefCount field:
15  *  - it needs to be called "ref"
16  *  - it cannot be a pointer
17  */
18
19 typedef struct {
20         guint32 ref;
21         void (*destructor) (gpointer data);
22 } MonoRefCount;
23
24 #define mono_refcount_init(v,destructor) do { mono_refcount_initialize (&(v)->ref, (destructor)); } while (0)
25 #define mono_refcount_inc(v) (mono_refcount_increment (&(v)->ref),(v))
26 #define mono_refcount_tryinc(v) (mono_refcount_tryincrement (&(v)->ref))
27 #define mono_refcount_dec(v) do { mono_refcount_decrement (&(v)->ref); } while (0)
28
29 static inline void
30 mono_refcount_initialize (MonoRefCount *refcount, void (*destructor) (gpointer data))
31 {
32         refcount->ref = 1;
33         refcount->destructor = destructor;
34 }
35
36 static inline gboolean
37 mono_refcount_tryincrement (MonoRefCount *refcount)
38 {
39         guint32 oldref, newref;
40
41         g_assert (refcount);
42
43         do {
44                 oldref = refcount->ref;
45                 if (oldref == 0)
46                         return FALSE;
47
48                 newref = oldref + 1;
49         } while (InterlockedCompareExchange ((gint32*) &refcount->ref, newref, oldref) != oldref);
50
51         return TRUE;
52 }
53
54 static inline void
55 mono_refcount_increment (MonoRefCount *refcount)
56 {
57         if (!mono_refcount_tryincrement (refcount))
58                 g_error ("%s: cannot increment a ref with value 0", __func__);
59 }
60
61 static inline void
62 mono_refcount_decrement (MonoRefCount *refcount)
63 {
64         guint32 oldref, newref;
65
66         g_assert (refcount);
67
68         do {
69                 oldref = refcount->ref;
70                 if (oldref == 0)
71                         g_error ("%s: cannot decrement a ref with value 0", __func__);
72
73                 newref = oldref - 1;
74         } while (InterlockedCompareExchange ((gint32*) &refcount->ref, newref, oldref) != oldref);
75
76         if (newref == 0 && refcount->destructor)
77                 refcount->destructor ((gpointer) refcount);
78 }
79
80 #endif /* __MONO_UTILS_REFCOUNT_H__ */