iceoryx_doc  1.0.1
taco.hpp
1 // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // SPDX-License-Identifier: Apache-2.0
16 #ifndef IOX_UTILS_CONCURRENT_TACO_HPP
17 #define IOX_UTILS_CONCURRENT_TACO_HPP
18 
19 #include "iceoryx_utils/cxx/helplets.hpp"
20 #include "iceoryx_utils/cxx/optional.hpp"
21 
22 #include <atomic>
23 #include <cstdint>
24 
25 namespace iox
26 {
27 namespace concurrent
28 {
30 enum class TACOMode
31 {
33  AccecptDataFromSameContext,
35  DenyDataFromSameContext
36 };
37 
114 template <typename T, typename Context, uint32_t MaxNumberOfContext = 500>
115 class TACO
116 {
117  private:
118  struct Transaction
119  {
120  Context context{Context::END_OF_LIST};
122  };
123 
124  TACOMode m_mode;
125  // this is the index of the transaction currently available for consumption
126  std::atomic<uint32_t> m_pendingTransaction;
127 
128  static constexpr uint32_t NumberOfContext = static_cast<uint32_t>(Context::END_OF_LIST);
129  // the corresponding transaction indices for the thread context;
130  // the value of m_indicex[Context] contains the index of the m_transactions array which is owned by the context
131  // so it's save to access m_transactions[m_indices[Context]]
132  uint32_t m_indices[NumberOfContext];
133  // this is a local buffer for the transaction, one for each thread that might access the TACO
134  // and there needs to be one more element which is the one ready for consumption
135  Transaction m_transactions[NumberOfContext + 1];
136 
137  public:
140  TACO(TACOMode mode)
141  : m_mode(mode)
142  , m_pendingTransaction(NumberOfContext)
143  {
144  static_assert(std::is_enum<Context>::value, "TACO Context must be an enum class!");
145  static_assert(std::is_convertible<Context, uint32_t>::value == false,
146  "TACO Context must be an enum class, not just an enum!");
147  static_assert(std::is_same<uint32_t, typename std::underlying_type<Context>::type>::value,
148  "TACO Context underlying type must be uint32_t!");
149  static_assert(static_cast<uint32_t>(Context::END_OF_LIST) < MaxNumberOfContext,
150  "TACO exceeded max number of contexts!");
151 
152  // initially assign the indices to the corresponding contexts
153  uint32_t i = 0;
154  for (auto& index : m_indices)
155  {
156  index = i;
157  i++;
158  }
159  }
160 
161  TACO(const TACO&) = delete;
162  TACO(TACO&&) = delete;
163  TACO& operator=(const TACO&) = delete;
164  TACO& operator=(TACO&&) = delete;
165 
171  cxx::optional<T> exchange(const T& data, Context context)
172  {
173  cxx::Expects(context < Context::END_OF_LIST);
174  m_transactions[m_indices[static_cast<uint32_t>(context)]].data.emplace(data);
175  return exchange(context);
176  }
177 
182  cxx::optional<T> take(const Context context)
183  {
184  cxx::Expects(context < Context::END_OF_LIST);
185  // there is no need to set the transaction for the corresponding context to nullopt_t, the exchange function
186  // either moves the data, which leaves a nullopt_t or resets the data, which also results in a nullopt_t
187  return exchange(context);
188  }
189 
193  void store(const T& data, const Context context)
194  {
195  cxx::Expects(context < Context::END_OF_LIST);
196  exchange(data, context);
197  }
198 
199  private:
200  cxx::optional<T> exchange(const Context context)
201  {
202  auto contextIndex = static_cast<uint32_t>(context);
203  auto transactionIndexOld = m_indices[contextIndex];
204  m_transactions[transactionIndexOld].context = context;
205 
206  m_indices[contextIndex] = m_pendingTransaction.exchange(transactionIndexOld, std::memory_order_acq_rel);
207  auto transactionIndexNew = m_indices[contextIndex];
208 
209  if (m_mode == TACOMode::AccecptDataFromSameContext || m_transactions[transactionIndexNew].context != context)
210  {
211  return std::move(m_transactions[transactionIndexNew].data);
212  }
213 
214  m_transactions[transactionIndexNew].data.reset();
215  return cxx::nullopt_t();
216  }
217 };
218 } // namespace concurrent
219 } // namespace iox
220 
221 #endif // IOX_UTILS_CONCURRENT_TACO_HPP
TACO is an acronym for Thread Aware exChange Ownership. Exchanging data between thread needs some syn...
Definition: taco.hpp:116
cxx::optional< T > take(const Context context)
Definition: taco.hpp:182
void store(const T &data, const Context context)
Definition: taco.hpp:193
cxx::optional< T > exchange(const T &data, Context context)
Definition: taco.hpp:171
TACO(TACOMode mode)
Definition: taco.hpp:140
Optional implementation from the C++17 standard with C++11. The interface is analog to the C++17 stan...
Definition: optional.hpp:63
building block to easily create free function for logging in a library context
Definition: lockfree_queue.hpp:28
Helper struct which is used to signal an empty optional. It is equivalent to no value.
Definition: optional.hpp:34