1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2014, Oracle and/or its affiliates. All Rights Reserved. |
4 | |
5 | This program is free software; you can redistribute it and/or modify it under |
6 | the terms of the GNU General Public License as published by the Free Software |
7 | Foundation; version 2 of the License. |
8 | |
9 | This program is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License along with |
14 | this program; if not, write to the Free Software Foundation, Inc., |
15 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
16 | |
17 | *****************************************************************************/ |
18 | |
19 | /**************************************************//** |
20 | @file include/os0once.h |
21 | A class that aids executing a given function exactly once in a multi-threaded |
22 | environment. |
23 | |
24 | Created Feb 20, 2014 Vasil Dimov |
25 | *******************************************************/ |
26 | |
27 | #ifndef os0once_h |
28 | #define os0once_h |
29 | |
30 | #include "univ.i" |
31 | |
32 | #include "ut0ut.h" |
33 | #include "my_cpu.h" |
34 | |
35 | /** Execute a given function exactly once in a multi-threaded environment |
36 | or wait for the function to be executed by another thread. |
37 | |
38 | Example usage: |
39 | First the user must create a control variable of type os_once::state_t and |
40 | assign it os_once::NEVER_DONE. |
41 | Then the user must pass this variable, together with a function to be |
42 | executed to os_once::do_or_wait_for_done(). |
43 | |
44 | Multiple threads can call os_once::do_or_wait_for_done() simultaneously with |
45 | the same (os_once::state_t) control variable. The provided function will be |
46 | called exactly once and when os_once::do_or_wait_for_done() returns then this |
47 | function has completed execution, by this or another thread. In other words |
48 | os_once::do_or_wait_for_done() will either execute the provided function or |
49 | will wait for its execution to complete if it is already called by another |
50 | thread or will do nothing if the function has already completed its execution |
51 | earlier. |
52 | |
53 | This mimics pthread_once(3), but unfortunatelly pthread_once(3) does not |
54 | support passing arguments to the init_routine() function. We should use |
55 | std::call_once() when we start compiling with C++11 enabled. */ |
56 | class os_once { |
57 | public: |
58 | /** Control variables' state type */ |
59 | typedef ib_uint32_t state_t; |
60 | |
61 | /** Not yet executed. */ |
62 | static const state_t NEVER_DONE = 0; |
63 | |
64 | /** Currently being executed by this or another thread. */ |
65 | static const state_t IN_PROGRESS = 1; |
66 | |
67 | /** Finished execution. */ |
68 | static const state_t DONE = 2; |
69 | |
70 | /** Call a given function or wait its execution to complete if it is |
71 | already called by another thread. |
72 | @param[in,out] state control variable |
73 | @param[in] do_func function to call |
74 | @param[in,out] do_func_arg an argument to pass to do_func(). */ |
75 | static |
76 | void |
77 | do_or_wait_for_done( |
78 | volatile state_t* state, |
79 | void (*do_func)(void*), |
80 | void* do_func_arg) |
81 | { |
82 | int32 oldval = NEVER_DONE; |
83 | |
84 | /* Avoid calling my_atomic_cas32() in the most common case. */ |
85 | if (*state == DONE) { |
86 | return; |
87 | } |
88 | |
89 | if (my_atomic_cas32((int32*) state, &oldval, IN_PROGRESS)) { |
90 | /* We are the first. Call the function. */ |
91 | |
92 | do_func(do_func_arg); |
93 | |
94 | my_atomic_store32((int32*) state, DONE); |
95 | } else { |
96 | /* The state is not NEVER_DONE, so either it is |
97 | IN_PROGRESS (somebody is calling the function right |
98 | now or DONE (it has already been called and completed). |
99 | Wait for it to become DONE. */ |
100 | for (;;) { |
101 | const state_t s = *state; |
102 | |
103 | switch (s) { |
104 | case DONE: |
105 | return; |
106 | case IN_PROGRESS: |
107 | break; |
108 | case NEVER_DONE: |
109 | /* fall through */ |
110 | default: |
111 | ut_error; |
112 | } |
113 | |
114 | MY_RELAX_CPU(); |
115 | } |
116 | } |
117 | } |
118 | }; |
119 | |
120 | #endif /* os0once_h */ |
121 | |