MySQL 9.1.0
Source Code Documentation
os0once.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2014, 2024, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file include/os0once.h
29 A class that aids executing a given function exactly once in a multi-threaded
30 environment.
31
32 Created Feb 20, 2014 Vasil Dimov
33 *******************************************************/
34
35#ifndef os0once_h
36#define os0once_h
37
38#include "univ.i"
39
40#include "os0atomic.h"
41#include "ut0ut.h"
42
43/** Execute a given function exactly once in a multi-threaded environment
44or wait for the function to be executed by another thread.
45
46Example usage:
47First the user must create a control variable of type os_once::state_t and
48assign it os_once::NEVER_DONE.
49Then the user must pass this variable, together with a function to be
50executed to os_once::do_or_wait_for_done().
51
52Multiple threads can call os_once::do_or_wait_for_done() simultaneously with
53the same (os_once::state_t) control variable. The provided function will be
54called exactly once and when os_once::do_or_wait_for_done() returns then this
55function has completed execution, by this or another thread. In other words
56os_once::do_or_wait_for_done() will either execute the provided function or
57will wait for its execution to complete if it is already called by another
58thread or will do nothing if the function has already completed its execution
59earlier.
60
61This mimics pthread_once(3), but unfortunately pthread_once(3) does not
62support passing arguments to the init_routine() function. We should use
63std::call_once() when we start compiling with C++11 enabled. */
64class os_once {
65 public:
66 /** Control variables' state type */
67 typedef uint32_t state_t;
68
69 /** Not yet executed. */
70 static const state_t NEVER_DONE = 0;
71
72 /** Currently being executed by this or another thread. */
73 static const state_t IN_PROGRESS = 1;
74
75 /** Finished execution. */
76 static const state_t DONE = 2;
77
78 /** Call a given function or wait its execution to complete if it is
79 already called by another thread.
80 @param[in,out] state control variable
81 @param[in] do_func function to call
82 @param[in,out] do_func_arg an argument to pass to do_func(). */
83 static void do_or_wait_for_done(std::atomic<state_t> *state,
84 void (*do_func)(void *), void *do_func_arg) {
85 /* Avoid calling compare_exchange_strong() in the most common case. */
86 if (*state == DONE) {
87 return;
88 }
89
90 state_t never_done = NEVER_DONE;
91 if (state->compare_exchange_strong(never_done, IN_PROGRESS)) {
92 /* We are the first. Call the function. */
93
94 do_func(do_func_arg);
95
96 state_t in_progress = IN_PROGRESS;
97 const bool swapped = state->compare_exchange_strong(in_progress, DONE);
98
99 ut_a(swapped);
100 } else {
101 /* The state is not NEVER_DONE, so either it is
102 IN_PROGRESS (somebody is calling the function right
103 now or DONE (it has already been called and completed).
104 Wait for it to become DONE. */
105 for (;;) {
106 const state_t s = *state;
107
108 switch (s) {
109 case DONE:
110 return;
111 case IN_PROGRESS:
112 break;
113 case NEVER_DONE:
114 [[fallthrough]];
115 default:
116 ut_error;
117 }
118
119#ifndef UNIV_HOTBACKUP
120 UT_RELAX_CPU();
121#endif /* !UNIV_HOTBACKUP */
122 }
123 }
124 }
125};
126
127#endif /* os0once_h */
Execute a given function exactly once in a multi-threaded environment or wait for the function to be ...
Definition: os0once.h:64
static void do_or_wait_for_done(std::atomic< state_t > *state, void(*do_func)(void *), void *do_func_arg)
Call a given function or wait its execution to complete if it is already called by another thread.
Definition: os0once.h:83
static const state_t NEVER_DONE
Not yet executed.
Definition: os0once.h:70
static const state_t DONE
Finished execution.
Definition: os0once.h:76
static const state_t IN_PROGRESS
Currently being executed by this or another thread.
Definition: os0once.h:73
uint32_t state_t
Control variables' state type.
Definition: os0once.h:67
Macros for using atomics.
Version control for database, common definitions, and include files.
#define ut_error
Abort execution.
Definition: ut0dbg.h:101
#define ut_a(EXPR)
Abort execution if EXPR does not evaluate to nonzero.
Definition: ut0dbg.h:93
Various utilities.
#define UT_RELAX_CPU()
Definition: ut0ut.h:93