| 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 | |