Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / mono-lazy-init.h
1 /**
2  * \file
3  * Lazy initialization and cleanup utilities
4  *
5  * Authors: Ludovic Henry <ludovic@xamarin.com>
6  *
7  * Copyright 2015 Xamarin, Inc. (www.xamarin.com)
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10
11 #ifndef __MONO_LAZY_INIT_H__
12 #define __MONO_LAZY_INIT_H__
13
14 #include <glib.h>
15
16 #include <config.h>
17
18 #include "atomic.h"
19 #include "mono-threads.h"
20 #include "mono-memory-model.h"
21
22 /*
23  * These functions should be used if you want some form of lazy initialization. You can have a look at the
24  * threadpool for a more detailed example.
25  *
26  * The idea is that a module can be in 5 different states:
27  *  - not initialized: it is the first state it starts in
28  *  - initializing/initialized: whenever we need this module for the first time, we need to initialize it: allocate
29  *     memory, launch background thread, etc. To achieve this, we have a module specific function (let's call it
30  *     initialize)
31  *  - cleaning/cleaned: when we want to clean this module specific data up, then we need to clean it up: deallocate
32  *     memory, wait for background threads to finish, etc. As for the initialization process, we need a module specific
33  *     function (let's call it cleanup)
34  *
35  * The switch from one state to the other can only happen in the following ways:
36  *  - not initialized
37  *  - not initialized -> initializing -> initialized
38  *  - not initialized -> cleaned
39  *  - not initialized -> initializing -> initialized -> cleaning -> cleaned
40  *
41  * The initialize and cleanup functions are guaranteed to:
42  *  - be each called once and only once
43  *  - not be called concurrently (either 2+ initialize or 2+ cleanup, either initialize and cleanup)
44  */
45
46 typedef gint32 mono_lazy_init_t;
47
48 enum {
49         MONO_LAZY_INIT_STATUS_NOT_INITIALIZED,
50         MONO_LAZY_INIT_STATUS_INITIALIZING,
51         MONO_LAZY_INIT_STATUS_INITIALIZED,
52         MONO_LAZY_INIT_STATUS_CLEANING,
53         MONO_LAZY_INIT_STATUS_CLEANED,
54 };
55
56 static inline gboolean
57 mono_lazy_initialize (mono_lazy_init_t *lazy_init, void (*initialize) (void))
58 {
59         gint32 status;
60
61         g_assert (lazy_init);
62
63         status = *lazy_init;
64
65         if (status >= MONO_LAZY_INIT_STATUS_INITIALIZED)
66                 return status == MONO_LAZY_INIT_STATUS_INITIALIZED;
67         if (status == MONO_LAZY_INIT_STATUS_INITIALIZING
68              || InterlockedCompareExchange (lazy_init, MONO_LAZY_INIT_STATUS_INITIALIZING, MONO_LAZY_INIT_STATUS_NOT_INITIALIZED)
69                  != MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
70         ) {
71                 while (*lazy_init == MONO_LAZY_INIT_STATUS_INITIALIZING)
72                         mono_thread_info_yield ();
73                 g_assert (InterlockedRead (lazy_init) >= MONO_LAZY_INIT_STATUS_INITIALIZED);
74                 return status == MONO_LAZY_INIT_STATUS_INITIALIZED;
75         }
76
77         initialize ();
78
79         mono_atomic_store_release (lazy_init, MONO_LAZY_INIT_STATUS_INITIALIZED);
80         return TRUE;
81 }
82
83 static inline void
84 mono_lazy_cleanup (mono_lazy_init_t *lazy_init, void (*cleanup) (void))
85 {
86         gint32 status;
87
88         g_assert (lazy_init);
89
90         status = *lazy_init;
91
92         if (status == MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
93              && InterlockedCompareExchange (lazy_init, MONO_LAZY_INIT_STATUS_CLEANED, MONO_LAZY_INIT_STATUS_NOT_INITIALIZED)
94                  == MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
95         ) {
96                 return;
97         }
98         if (status == MONO_LAZY_INIT_STATUS_INITIALIZING) {
99                 while ((status = *lazy_init) == MONO_LAZY_INIT_STATUS_INITIALIZING)
100                         mono_thread_info_yield ();
101         }
102
103         if (status == MONO_LAZY_INIT_STATUS_CLEANED)
104                 return;
105         if (status == MONO_LAZY_INIT_STATUS_CLEANING
106              || InterlockedCompareExchange (lazy_init, MONO_LAZY_INIT_STATUS_CLEANING, MONO_LAZY_INIT_STATUS_INITIALIZED)
107                  != MONO_LAZY_INIT_STATUS_INITIALIZED
108         ) {
109                 while (*lazy_init == MONO_LAZY_INIT_STATUS_CLEANING)
110                         mono_thread_info_yield ();
111                 g_assert (InterlockedRead (lazy_init) == MONO_LAZY_INIT_STATUS_CLEANED);
112                 return;
113         }
114
115         cleanup ();
116
117         mono_atomic_store_release (lazy_init, MONO_LAZY_INIT_STATUS_CLEANED);
118 }
119
120 static inline gboolean
121 mono_lazy_is_initialized (mono_lazy_init_t *lazy_init)
122 {
123         g_assert (lazy_init);
124         return InterlockedRead (lazy_init) == MONO_LAZY_INIT_STATUS_INITIALIZED;
125 }
126
127 #endif