1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   once_block.hpp
9  * \author Andrey Semashev
10  * \date   23.06.2010
11  *
12  * \brief  The header defines classes and macros for once-blocks.
13  */
14 
15 #ifndef BOOST_LOG_UTILITY_ONCE_BLOCK_HPP_INCLUDED_
16 #define BOOST_LOG_UTILITY_ONCE_BLOCK_HPP_INCLUDED_
17 
18 #include <boost/log/detail/config.hpp>
19 #include <boost/log/utility/unique_identifier_name.hpp>
20 #include <boost/log/detail/header.hpp>
21 
22 #ifdef BOOST_HAS_PRAGMA_ONCE
23 #pragma once
24 #endif
25 
26 #ifndef BOOST_LOG_NO_THREADS
27 
28 namespace boost {
29 
30 BOOST_LOG_OPEN_NAMESPACE
31 
32 /*!
33  * \brief A flag to detect if a code block has already been executed.
34  *
35  * This structure should be used in conjunction with the \c BOOST_LOG_ONCE_BLOCK_FLAG
36  * macro. Usage example:
37  *
38  * <code>
39  * once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT;
40  *
41  * void foo()
42  * {
43  *     BOOST_LOG_ONCE_BLOCK_FLAG(flag)
44  *     {
45  *         puts("Hello, world once!");
46  *     }
47  * }
48  * </code>
49  */
50 struct once_block_flag
51 {
52 #ifndef BOOST_LOG_DOXYGEN_PASS
53     // Do not use, implementation detail
54     enum
55     {
56         uninitialized = 0, // this must be zero, so that zero-initialized once_block_flag is equivalent to the one initialized with uninitialized
57         being_initialized,
58         initialized
59     };
60     unsigned char status;
61 #endif // BOOST_LOG_DOXYGEN_PASS
62 };
63 
64 /*!
65  * \def BOOST_LOG_ONCE_BLOCK_INIT
66  *
67  * The static initializer for \c once_block_flag.
68  */
69 #define BOOST_LOG_ONCE_BLOCK_INIT { boost::log::once_block_flag::uninitialized }
70 
71 namespace aux {
72 
73 class once_block_sentry
74 {
75 private:
76     once_block_flag& m_flag;
77 
78 public:
once_block_sentry(once_block_flag & f)79     explicit once_block_sentry(once_block_flag& f) BOOST_NOEXCEPT : m_flag(f)
80     {
81     }
82 
~once_block_sentry()83     ~once_block_sentry() BOOST_NOEXCEPT
84     {
85         if (m_flag.status != once_block_flag::initialized)
86             rollback();
87     }
88 
executed() const89     bool executed() const BOOST_NOEXCEPT
90     {
91         return (m_flag.status == once_block_flag::initialized || enter_once_block());
92     }
93 
94     BOOST_LOG_API void commit() BOOST_NOEXCEPT;
95 
96 private:
97     BOOST_LOG_API bool enter_once_block() const BOOST_NOEXCEPT;
98     BOOST_LOG_API void rollback() BOOST_NOEXCEPT;
99 
100     //  Non-copyable, non-assignable
101     BOOST_DELETED_FUNCTION(once_block_sentry(once_block_sentry const&))
102     BOOST_DELETED_FUNCTION(once_block_sentry& operator= (once_block_sentry const&))
103 };
104 
105 } // namespace aux
106 
107 BOOST_LOG_CLOSE_NAMESPACE // namespace log
108 
109 } // namespace boost
110 
111 #else // BOOST_LOG_NO_THREADS
112 
113 namespace boost {
114 
115 BOOST_LOG_OPEN_NAMESPACE
116 
117 struct once_block_flag
118 {
119     bool status;
120 };
121 
122 #define BOOST_LOG_ONCE_BLOCK_INIT { false }
123 
124 namespace aux {
125 
126 class once_block_sentry
127 {
128 private:
129     once_block_flag& m_flag;
130 
131 public:
once_block_sentry(once_block_flag & f)132     explicit once_block_sentry(once_block_flag& f) BOOST_NOEXCEPT : m_flag(f)
133     {
134     }
135 
executed() const136     bool executed() const BOOST_NOEXCEPT
137     {
138         return m_flag.status;
139     }
140 
commit()141     void commit() BOOST_NOEXCEPT
142     {
143         m_flag.status = true;
144     }
145 
146     //  Non-copyable, non-assignable
147     BOOST_DELETED_FUNCTION(once_block_sentry(once_block_sentry const&))
148     BOOST_DELETED_FUNCTION(once_block_sentry& operator= (once_block_sentry const&))
149 };
150 
151 } // namespace aux
152 
153 BOOST_LOG_CLOSE_NAMESPACE // namespace log
154 
155 } // namespace boost
156 
157 #endif // BOOST_LOG_NO_THREADS
158 
159 #ifndef BOOST_LOG_DOXYGEN_PASS
160 
161 #define BOOST_LOG_ONCE_BLOCK_FLAG_INTERNAL(flag_var, sentry_var)\
162     for (boost::log::aux::once_block_sentry sentry_var((flag_var));\
163         BOOST_UNLIKELY(!sentry_var.executed()); sentry_var.commit())
164 
165 // NOTE: flag_var deliberately doesn't have an initializer so that it is zero-initialized at the static initialization stage
166 #define BOOST_LOG_ONCE_BLOCK_INTERNAL(flag_var, sentry_var)\
167     static boost::log::once_block_flag flag_var;\
168     BOOST_LOG_ONCE_BLOCK_FLAG_INTERNAL(flag_var, sentry_var)
169 
170 #endif // BOOST_LOG_DOXYGEN_PASS
171 
172 /*!
173  * \def BOOST_LOG_ONCE_BLOCK_FLAG(flag_var)
174  *
175  * Begins a code block to be executed only once, with protection against thread concurrency.
176  * User has to provide the flag variable that controls whether the block has already
177  * been executed.
178  */
179 #define BOOST_LOG_ONCE_BLOCK_FLAG(flag_var)\
180     BOOST_LOG_ONCE_BLOCK_FLAG_INTERNAL(\
181         flag_var,\
182         BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_once_block_sentry_))
183 
184 /*!
185  * \def BOOST_LOG_ONCE_BLOCK()
186  *
187  * Begins a code block to be executed only once, with protection against thread concurrency.
188  */
189 #define BOOST_LOG_ONCE_BLOCK()\
190     BOOST_LOG_ONCE_BLOCK_INTERNAL(\
191         BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_once_block_flag_),\
192         BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_once_block_sentry_))
193 
194 #include <boost/log/detail/footer.hpp>
195 
196 #endif // BOOST_LOG_UTILITY_ONCE_BLOCK_HPP_INCLUDED_
197