aboutsummaryrefslogtreecommitdiffstats
path: root/include/distortos/internal/synchronization/CallOnceControlBlock.hpp
blob: 1684faf017c7f952ec827e4a988438e612fab0ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
 * \file
 * \brief CallOnceControlBlock class header
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_

#include "estd/invoke.hpp"
#include "estd/TypeErasedFunctor.hpp"

#include <sys/features.h>

namespace distortos
{

/// GCC 4.9 is needed for CallOnceControlBlock::operator()() function - and thus for OnceFlag and callOnce() - earlier
/// versions don't support parameter pack expansion in lambdas
#define DISTORTOS_CALLONCE_SUPPORTED	__GNUC_PREREQ(4, 9)

#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

namespace internal
{

class ThreadList;

/// CallOnceControlBlock class implements functionality of OnceFlag class and callOnce()
/// \note This class requires GCC 4.9.
class CallOnceControlBlock
{
public:

	/**
	 * \brief CallOnceControlBlock's constructor
	 */

	constexpr CallOnceControlBlock() :
			blockedList_{},
			done_{}
	{

	}

	/**
	 * \brief CallOnceControlBlock's function call operator
	 *
	 * Does nothing if any function was already called for this object. In other case provided function and arguments
	 * are wrapped in a type-erased functor and passed to callOnceImplementation().
	 *
	 * \tparam Function is the function object that will be executed
	 * \tparam Args are the arguments for \a Function
	 *
	 * \param [in] function is the function object that will be executed
	 * \param [in] args are arguments for \a function
	 */

	template<typename Function, typename... Args>
	void operator()(Function&& function, Args&&... args);

private:

	/// Functor is a type-erased interface for functors which execute bounded function with bounded arguments
	class Functor : public estd::TypeErasedFunctor<void()>
	{

	};

	/**
	 * \brief BoundedFunctor is a type-erased Functor which calls its bounded functor
	 *
	 * \tparam F is the type of bounded functor
	 */

	template<typename F>
	class BoundedFunctor : public Functor
	{
	public:

		/**
		 * \brief BoundedFunctor's constructor
		 *
		 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct
		 * internal bounded functor
		 */

		constexpr explicit BoundedFunctor(F&& boundedFunctor) :
				boundedFunctor_{std::move(boundedFunctor)}
		{

		}

		/**
		 * \brief BoundedFunctor's function call operator
		 *
		 * Calls the bounded functor.
		 */

		void operator()() const override
		{
			boundedFunctor_();
		}

	private:

		/// bounded functor
		F boundedFunctor_;
	};

	/**
	 * \brief Helper factory function to make BoundedFunctor object with deduced template arguments
	 *
	 * \tparam F is the type of bounded functor
	 *
	 * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct internal
	 * bounded functor
	 *
	 * \return BoundedFunctor object with deduced template arguments
	 */

	template<typename F>
	constexpr static BoundedFunctor<F> makeBoundedFunctor(F&& boundedFunctor)
	{
		return BoundedFunctor<F>{std::move(boundedFunctor)};
	}

	/**
	 * \brief Implements callOnce() using type-erased functor.
	 *
	 * Does nothing if any function was already called for this object. If the function is currently being executed, but
	 * not yet done, then the calling thread is blocked. In other case the function is executed and - after it is done -
	 * all blocked threads are unblocked.
	 *
	 * \param [in] functor is a reference to functor which will execute bounded function with bounded arguments
	 */

	void callOnceImplementation(const Functor& functor);

	/// pointer to stack-allocated list of ThreadControlBlock objects blocked on associated OnceFlag
	ThreadList* blockedList_;

	/// tells whether any function was already called for this object (true) or not (false)
	bool done_;
};

template<typename Function, typename... Args>
void CallOnceControlBlock::operator()(Function&& function, Args&&... args)
{
	if (done_ == true)	// function already executed?
		return;

	const auto functor = makeBoundedFunctor(
			[&function, &args...]()
			{
				estd::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
			});
	callOnceImplementation(functor);
}

}	// namespace internal

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_