aboutsummaryrefslogtreecommitdiffstats
path: root/include/distortos/internal/synchronization/MessageQueueBase.hpp
blob: e0bd4e514f665c8dc3ce15fe165b0db6e1a149b3 (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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/**
 * \file
 * \brief MessageQueueBase 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_MESSAGEQUEUEBASE_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MESSAGEQUEUEBASE_HPP_

#include "distortos/Semaphore.hpp"

#include "distortos/internal/synchronization/QueueFunctor.hpp"
#include "distortos/internal/synchronization/SemaphoreFunctor.hpp"

#include "estd/SortedIntrusiveForwardList.hpp"

#include <memory>

namespace distortos
{

namespace internal
{

/// MessageQueueBase class implements basic functionality of MessageQueue template class
class MessageQueueBase
{
public:

	/// entry in the MessageQueueBase
	struct Entry
	{
		/**
		 * \brief Entry's constructor
		 *
		 * \param [in] priorityy is the priority of the entry
		 * \param [in] storagee is the storage for the entry
		 */

		constexpr Entry(const uint8_t priorityy, void* const storagee) :
				node{},
				priority{priorityy},
				storage{storagee}
		{

		}

		/// node for intrusive forward list
		estd::IntrusiveForwardListNode node;

		/// priority of the entry
		uint8_t priority;

		/// storage for the entry
		void* storage;
	};

	/// type of uninitialized storage for Entry
	using EntryStorage = typename std::aligned_storage<sizeof(Entry), alignof(Entry)>::type;

	/// unique_ptr (with deleter) to EntryStorage[]
	using EntryStorageUniquePointer = std::unique_ptr<EntryStorage[], void(&)(EntryStorage*)>;

	/**
	 * type of uninitialized storage for value
	 *
	 * \tparam T is the type of data in queue
	 */

	template<typename T>
	using ValueStorage = typename std::aligned_storage<sizeof(T), alignof(T)>::type;

	/// unique_ptr (with deleter) to storage
	using ValueStorageUniquePointer = std::unique_ptr<void, void(&)(void*)>;

	/// functor which gives descending priority order of elements on the list
	struct DescendingPriority
	{
		/**
		 * \brief DescendingPriority's constructor
		 */

		constexpr DescendingPriority()
		{

		}

		/**
		 * \brief DescendingPriority's function call operator
		 *
		 * \param [in] left is the object on the left side of comparison
		 * \param [in] right is the object on the right side of comparison
		 *
		 * \return true if left's priority is less than right's priority
		 */

		bool operator()(const Entry& left, const Entry& right) const
		{
			return left.priority < right.priority;
		}
	};

	/// type of entry list
	using EntryList = estd::SortedIntrusiveForwardList<DescendingPriority, Entry, &Entry::node>;

	/// type of free entry list
	using FreeEntryList = EntryList::UnsortedIntrusiveForwardList;

	/**
	 * \brief InternalFunctor is a type-erased interface for functors which execute common code of pop() and push()
	 * operations.
	 *
	 * The functor will be called by MessageQueueBase internals with references to \a entryList_ and \a freeEntryList_.
	 * It should perform common actions and execute the QueueFunctor passed from callers.
	 */

	class InternalFunctor : public estd::TypeErasedFunctor<void(EntryList&, FreeEntryList&)>
	{

	};

	/**
	 * \brief MessageQueueBase's constructor
	 *
	 * \param [in] entryStorageUniquePointer is a rvalue reference to EntryStorageUniquePointer with storage for queue
	 * entries (sufficiently large for \a maxElements EntryStorage objects) and appropriate deleter
	 * \param [in] valueStorageUniquePointer is a rvalue reference to ValueStorageUniquePointer with storage for queue
	 * elements (sufficiently large for \a maxElements, each \a elementSize bytes long) and appropriate deleter
	 * \param [in] elementSize is the size of single queue element, bytes
	 * \param [in] maxElements is the number of elements in \a entryStorage array and valueStorage memory block
	 */

	MessageQueueBase(EntryStorageUniquePointer&& entryStorageUniquePointer,
			ValueStorageUniquePointer&& valueStorageUniquePointer, size_t elementSize, size_t maxElements);

	/**
	 * \brief MessageQueueBase's destructor
	 */

	~MessageQueueBase();

	/**
	 * \brief Implementation of pop() using type-erased functor
	 *
	 * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_
	 * \param [out] priority is a reference to variable that will be used to return priority of popped value
	 * \param [in] functor is a reference to QueueFunctor which will execute actions related to popping - it will get a
	 * pointer to storage with element
	 *
	 * \return zero if element was popped successfully, error code otherwise:
	 * - error codes returned by \a waitSemaphoreFunctor's operator() call;
	 * - error codes returned by Semaphore::post();
	 */

	int pop(const SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, const QueueFunctor& functor);

	/**
	 * \brief Implementation of push() using type-erased functor
	 *
	 * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_
	 * \param [in] priority is the priority of new element
	 * \param [in] functor is a reference to QueueFunctor which will execute actions related to pushing - it will get a
	 * pointer to storage for element
	 *
	 * \return zero if element was pushed successfully, error code otherwise:
	 * - error codes returned by \a waitSemaphoreFunctor's operator() call;
	 * - error codes returned by Semaphore::post();
	 */

	int push(const SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, const QueueFunctor& functor);

private:

	/**
	 * \brief Implementation of pop() and push() using type-erased internal functor
	 *
	 * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a waitSemaphore
	 * \param [in] internalFunctor is a reference to InternalFunctor which will execute actions related to
	 * popping/pushing
	 * \param [in] waitSemaphore is a reference to semaphore that will be waited for, \a popSemaphore_ for pop(), \a
	 * pushSemaphore_ for push()
	 * \param [in] postSemaphore is a reference to semaphore that will be posted after the operation, \a pushSemaphore_
	 * for pop(), \a popSemaphore_ for push()
	 *
	 * \return zero if operation was successful, error code otherwise:
	 * - error codes returned by \a waitSemaphoreFunctor's operator() call;
	 * - error codes returned by Semaphore::post();
	 */

	int popPush(const SemaphoreFunctor& waitSemaphoreFunctor, const InternalFunctor& internalFunctor,
			Semaphore& waitSemaphore, Semaphore& postSemaphore);

	/// semaphore guarding access to "pop" functions - its value is equal to the number of available elements
	Semaphore popSemaphore_;

	/// semaphore guarding access to "push" functions - its value is equal to the number of free slots
	Semaphore pushSemaphore_;

	/// storage for queue entries
	const EntryStorageUniquePointer entryStorageUniquePointer_;

	/// storage for queue elements
	const ValueStorageUniquePointer valueStorageUniquePointer_;

	/// list of available entries, sorted in descending order of priority
	EntryList entryList_;

	/// list of "free" entries
	FreeEntryList freeEntryList_;
};

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MESSAGEQUEUEBASE_HPP_