density
C++11 library for paged memory management, function queues, heterogeneous queues and lifo memory management
heter_queue.h
1 
2 // Copyright Giuseppe Campana (giu.campana@gmail.com) 2016-2018.
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 #pragma once
8 #include <density/default_allocator.h>
10 #include <density/dynamic_reference.h>
11 #include <density/runtime_type.h>
12 #include <iterator>
13 
14 namespace density
15 {
16  namespace detail
17  {
18  struct QueueControl
19  {
20  uintptr_t
21  m_next;
23  };
24 
26  enum Queue_Flags : uintptr_t
27  {
28  Queue_Busy = 1,
29  Queue_Dead =
30  2,
35  Queue_External =
36  4,
37  Queue_AllFlags = Queue_Busy | Queue_Dead | Queue_External
38  };
39  } // namespace detail
40 
199  template <typename RUNTIME_TYPE = runtime_type<>, typename ALLOCATOR_TYPE = default_allocator>
200  class heter_queue : private ALLOCATOR_TYPE
201  {
202  using ControlBlock = detail::QueueControl;
203 
205  ControlBlock * m_head;
206 
208  ControlBlock * m_tail;
209 
211  enum class PrivateType
212  {
213  };
214 
215  public:
218  constexpr static size_t min_alignment = detail::size_max(
219  detail::Queue_AllFlags + 1, alignof(ControlBlock), alignof(RUNTIME_TYPE));
220 
221  using runtime_type = RUNTIME_TYPE;
223  using allocator_type = ALLOCATOR_TYPE;
224  using pointer = value_type *;
225  using const_pointer = const value_type *;
226  using reference = value_type;
227  using const_reference = const value_type &;
228  using size_type = std::size_t;
229  using difference_type = std::ptrdiff_t;
230 
231  class const_iterator;
232 
233  private:
237  constexpr static auto s_invalid_control_block = ALLOCATOR_TYPE::page_size - 1;
238 
241  constexpr static size_t s_sizeof_ControlBlock =
242  uint_upper_align(sizeof(ControlBlock), min_alignment);
243 
246  constexpr static size_t s_sizeof_RuntimeType =
247  uint_upper_align(sizeof(RUNTIME_TYPE), min_alignment);
248 
250  constexpr static auto s_max_size_inpage = ALLOCATOR_TYPE::page_size -
251  s_sizeof_ControlBlock - s_sizeof_RuntimeType -
252  s_sizeof_ControlBlock;
253 
255  struct Allocation
256  {
257  ControlBlock * m_control_block;
258  void * m_user_storage;
259  };
260 
261  public:
263  static constexpr bool concurrent_puts = false;
264 
266  static constexpr bool concurrent_consumes = false;
267 
270  static constexpr bool concurrent_put_consumes = false;
271 
273  static constexpr bool is_seq_cst = true;
274 
275  static_assert(
276  is_power_of_2(ALLOCATOR_TYPE::page_alignment) &&
277  ALLOCATOR_TYPE::page_alignment >= ALLOCATOR_TYPE::page_size &&
278  (ALLOCATOR_TYPE::page_alignment % min_alignment) == 0,
279  "The alignment of the pages must be a power of 2, greater or equal to the size of the "
280  "pages, and a multiple of min_alignment");
281 
282  static_assert(
283  ALLOCATOR_TYPE::page_size > (min_alignment + alignof(ControlBlock)) * 4,
284  "Invalid page size");
285 
295  constexpr heter_queue() noexcept
296  : m_head(reinterpret_cast<ControlBlock *>(s_invalid_control_block)),
297  m_tail(reinterpret_cast<ControlBlock *>(s_invalid_control_block))
298  {
299  static_assert(std::is_nothrow_default_constructible<ALLOCATOR_TYPE>::value, "");
300  }
301 
312  constexpr explicit heter_queue(const ALLOCATOR_TYPE & i_source_allocator) noexcept
313  : ALLOCATOR_TYPE(i_source_allocator),
314  m_head(reinterpret_cast<ControlBlock *>(s_invalid_control_block)),
315  m_tail(reinterpret_cast<ControlBlock *>(s_invalid_control_block))
316  {
317  }
318 
329  constexpr explicit heter_queue(ALLOCATOR_TYPE && i_source_allocator) noexcept
330  : ALLOCATOR_TYPE(std::move(i_source_allocator)),
331  m_head(reinterpret_cast<ControlBlock *>(s_invalid_control_block)),
332  m_tail(reinterpret_cast<ControlBlock *>(s_invalid_control_block))
333  {
334  static_assert(std::is_nothrow_move_constructible<ALLOCATOR_TYPE>::value, "");
335  }
336 
346  heter_queue(heter_queue && i_source) noexcept
347  : ALLOCATOR_TYPE(std::move(static_cast<ALLOCATOR_TYPE &&>(i_source))),
348  m_head(i_source.m_head), m_tail(i_source.m_tail)
349  {
350  static_assert(std::is_nothrow_move_constructible<ALLOCATOR_TYPE>::value, "");
351 
352  i_source.m_tail = i_source.m_head =
353  reinterpret_cast<ControlBlock *>(s_invalid_control_block);
354  }
355 
366  heter_queue(const heter_queue & i_source)
367  : allocator_type(static_cast<const allocator_type &>(i_source)),
368  m_head(reinterpret_cast<ControlBlock *>(s_invalid_control_block)),
369  m_tail(reinterpret_cast<ControlBlock *>(s_invalid_control_block))
370  {
371  for (auto source_it = i_source.cbegin(); source_it != i_source.cend(); source_it++)
372  {
373  dyn_push_copy(source_it.complete_type(), source_it.element_ptr());
374  }
375  }
376 
389  heter_queue & operator=(heter_queue && i_source) noexcept
390  {
391  destroy();
392  m_tail = m_head = reinterpret_cast<ControlBlock *>(s_invalid_control_block);
393  swap(*this, i_source);
394  return *this;
395  }
396 
406  heter_queue & operator=(const heter_queue & i_source)
407  {
408  auto copy(i_source);
409  swap(*this, copy);
410  return *this;
411  }
412 
416  allocator_type
417  get_allocator() noexcept(std::is_nothrow_copy_constructible<allocator_type>::value)
418  {
419  return *this;
420  }
421 
425  allocator_type & get_allocator_ref() noexcept { return *this; }
426 
430  const allocator_type & get_allocator_ref() const noexcept { return *this; }
431 
435  friend void swap(
437  heter_queue<RUNTIME_TYPE, ALLOCATOR_TYPE> & i_second) noexcept
438  {
439  using std::swap;
440  swap(static_cast<ALLOCATOR_TYPE &>(i_first), static_cast<ALLOCATOR_TYPE &>(i_second));
441  swap(i_first.m_head, i_second.m_head);
442  swap(i_first.m_tail, i_second.m_tail);
443  }
444 
450  ~heter_queue() { destroy(); }
451 
458  bool empty() const noexcept
459  {
460  // the queue may contain busy or dead elements, that must be ignored
461  for (auto curr = m_head; curr != m_tail;)
462  {
463  auto const control_bits = curr->m_next & (detail::Queue_Busy | detail::Queue_Dead);
464  if (control_bits == 0) // if not busy and not dead
465  {
466  return false;
467  }
468  curr = reinterpret_cast<ControlBlock *>(curr->m_next & ~detail::Queue_AllFlags);
469  }
470  return true;
471  }
472 
480  void clear() noexcept
481  {
482  consume_operation consume;
483  while (try_start_consume(consume))
484  {
485  consume.commit();
486  }
487 
488  DENSITY_ASSERT_INTERNAL(empty());
489 
490  clean_dead_elements();
491  }
492 
514  template <typename ELEMENT_COMPLETE_TYPE = void> class put_transaction
515  {
516  static_assert(
517  std::is_same<
518  ELEMENT_COMPLETE_TYPE,
519  typename std::decay<ELEMENT_COMPLETE_TYPE>::type>::value,
520  "");
521 
522  public:
524  put_transaction() noexcept : m_queue(nullptr) {}
525 
529  put_transaction(const put_transaction &) = delete;
530 
534  put_transaction & operator=(const put_transaction &) = delete;
535 
543  template <
544  typename OTHERTYPE,
545  typename = typename std::enable_if<
546  std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value ||
547  std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
549  : m_queue(i_source.m_queue), m_put_data(i_source.m_put_data)
550  {
551  i_source.m_queue = nullptr;
552  }
553 
559  template <
560  typename OTHERTYPE,
561  typename = typename std::enable_if<
562  std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value ||
563  std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
565  {
566  if (
567  this !=
568  static_cast<void *>(
569  &i_source)) // cast to void to allow comparing pointers to unrelated types
570  {
571  if (!empty())
572  cancel();
573 
574  std::swap(m_queue, i_source.m_queue);
575  std::swap(m_put_data, i_source.m_put_data);
576  }
577  return *this;
578  }
579 
583  friend void swap(put_transaction & i_first, put_transaction & i_second) noexcept
584  {
585  using std::swap;
586  swap(i_first.m_queue, i_second.m_queue);
587  swap(i_first.m_put_data, i_second.m_put_data);
588  }
589 
612  void * raw_allocate(size_t i_size, size_t i_alignment)
613  {
614  DENSITY_ASSERT(!empty());
615  auto push_data =
616  m_queue->inplace_allocate<detail::Queue_Dead, false>(i_size, i_alignment);
617  return push_data.m_user_storage;
618  }
619 
644  template <typename INPUT_ITERATOR>
645  typename std::iterator_traits<INPUT_ITERATOR>::value_type *
646  raw_allocate_copy(INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end)
647  {
648  using ValueType = typename std::iterator_traits<INPUT_ITERATOR>::value_type;
649  static_assert(
650  std::is_trivially_destructible<ValueType>::value,
651  "raw_allocate_copy provides a raw memory in-place allocation that does not "
652  "invoke "
653  "destructors when deallocating");
654 
655  auto const count_s = std::distance(i_begin, i_end);
656  auto const count = static_cast<size_t>(count_s);
657  DENSITY_ASSUME(static_cast<decltype(count_s)>(count) == count_s);
658 
659  auto const elements = static_cast<ValueType *>(
660  raw_allocate(sizeof(ValueType) * count, alignof(ValueType)));
661  for (auto curr = elements; i_begin != i_end; ++i_begin, ++curr)
662  new (curr) ValueType(*i_begin);
663  return elements;
664  }
665 
688  template <typename INPUT_RANGE>
689  auto raw_allocate_copy(const INPUT_RANGE & i_source_range)
690  -> decltype(std::declval<put_transaction>().raw_allocate_copy(
691  std::begin(i_source_range), std::end(i_source_range)))
692  {
693  return raw_allocate_copy(std::begin(i_source_range), std::end(i_source_range));
694  }
695 
704  void commit() noexcept
705  {
706  DENSITY_ASSERT(!empty());
707  m_queue = nullptr;
708  }
709 
720  void cancel() noexcept
721  {
722  DENSITY_ASSERT(!empty());
723  cancel_put_impl(m_put_data.m_control_block);
724  m_queue = nullptr;
725  }
726 
730  bool empty() const noexcept { return m_queue == nullptr; }
731 
735  explicit operator bool() const noexcept { return m_queue != nullptr; }
736 
740  heter_queue * queue() const noexcept { return m_queue; }
741 
757  void * element_ptr() const noexcept
758  {
759  DENSITY_ASSERT(!empty());
760  return m_put_data.m_user_storage;
761  }
762 
776 #ifndef DOXYGEN_DOC_GENERATION
777  template <
778  typename EL = ELEMENT_COMPLETE_TYPE,
779  typename std::enable_if<!std::is_void<EL>::value>::type * = nullptr>
780  EL &
781 #else
782  ELEMENT_COMPLETE_TYPE &
783 #endif
784  element() const noexcept
785  {
786  return *static_cast<ELEMENT_COMPLETE_TYPE *>(element_ptr());
787  }
788 
795  const RUNTIME_TYPE & complete_type() const noexcept
796  {
797  DENSITY_ASSERT(!empty());
798  return *type_after_control(m_put_data.m_control_block);
799  }
800 
805  {
806  if (m_queue != nullptr)
807  {
808  cancel_put_impl(m_put_data.m_control_block);
809  }
810  }
811 
813  put_transaction(PrivateType, heter_queue * i_queue, Allocation i_push_data) noexcept
814  : m_queue(i_queue), m_put_data(i_push_data)
815  {
816  }
817 
818  private:
819  heter_queue * m_queue;
821  Allocation m_put_data;
822 
823  template <typename OTHERTYPE> friend class put_transaction;
824  };
825 
844  {
845  public:
849  consume_operation() noexcept : m_control(nullptr) {}
850 
854  consume_operation(const consume_operation &) = delete;
855 
859  consume_operation & operator=(const consume_operation &) = delete;
860 
864  consume_operation(consume_operation && i_source) noexcept
865  : m_queue(i_source.m_queue), m_control(i_source.m_control)
866  {
867  i_source.m_control = nullptr;
868  }
869 
874  {
875  if (this != &i_source)
876  {
877  if (!empty())
878  {
879  commit();
880  }
881  m_queue = i_source.m_queue;
882  m_control = i_source.m_control;
883  i_source.m_control = nullptr;
884  }
885  return *this;
886  }
887 
892  {
893  if (m_control != nullptr)
894  {
895  m_queue->cancel_consume_impl(m_control);
896  }
897  }
898 
902  friend void swap(consume_operation & i_first, consume_operation & i_second) noexcept
903  {
904  using std::swap;
905  swap(i_first.m_queue, i_second.m_queue);
906  swap(i_first.m_control, i_second.m_control);
907  }
908 
912  bool empty() const noexcept { return m_control == nullptr; }
913 
917  explicit operator bool() const noexcept { return m_control != nullptr; }
918 
922  heter_queue * queue() const noexcept
923  {
924  return m_control != nullptr ? m_queue : nullptr;
925  }
926 
935  void commit() noexcept
936  {
937  DENSITY_ASSERT(!empty());
938 
939  auto const & type = complete_type();
940  auto const element = element_ptr();
941  type.destroy(element);
942 
943  type.RUNTIME_TYPE::~RUNTIME_TYPE();
944 
945  m_queue->commit_consume_impl(m_control);
946  m_control = nullptr;
947  }
948 
966  void commit_nodestroy() noexcept
967  {
968  DENSITY_ASSERT(!empty());
969 
970  bool destroy_type = !std::is_trivially_destructible<RUNTIME_TYPE>::value;
971  if (destroy_type)
972  {
973  auto const & type = complete_type();
974  type.RUNTIME_TYPE::~RUNTIME_TYPE();
975  }
976 
977  m_queue->commit_consume_impl(m_control);
978  m_control = nullptr;
979  }
980 
991  void cancel() noexcept
992  {
993  DENSITY_ASSERT(!empty());
994 
995  m_queue->cancel_consume_impl(m_control);
996  m_control = nullptr;
997  }
998 
1004  const RUNTIME_TYPE & complete_type() const noexcept
1005  {
1006  DENSITY_ASSERT(!empty());
1007  return *type_after_control(m_control);
1008  }
1009 
1017  void * unaligned_element_ptr() const noexcept
1018  {
1019  DENSITY_ASSERT(!empty());
1020  return get_unaligned_element(m_control);
1021  }
1022 
1030  void * element_ptr() const noexcept
1031  {
1032  DENSITY_ASSERT(!empty());
1033  return get_element(m_control);
1034  }
1035 
1042  template <typename COMPLETE_ELEMENT_TYPE>
1043  COMPLETE_ELEMENT_TYPE & element() const noexcept
1044  {
1045  DENSITY_ASSERT(!empty() && complete_type().template is<COMPLETE_ELEMENT_TYPE>());
1046  return *static_cast<COMPLETE_ELEMENT_TYPE *>(get_element(m_control));
1047  }
1048 
1050  consume_operation(PrivateType, heter_queue * i_queue, ControlBlock * i_control) noexcept
1051  : m_queue(i_queue), m_control(i_control)
1052  {
1053  }
1054 
1056  bool start_consume_impl(PrivateType, heter_queue * i_queue)
1057  {
1058  if (m_control != nullptr)
1059  {
1060  m_queue->cancel_consume_impl(m_control);
1061  }
1062  m_queue = i_queue;
1063  m_control = i_queue->start_consume_impl();
1064  return m_control != nullptr;
1065  }
1066 
1067  private:
1068  heter_queue * m_queue;
1069  ControlBlock * m_control;
1070  };
1071 
1089  template <typename ELEMENT_TYPE> void push(ELEMENT_TYPE && i_source)
1090  {
1091  return emplace<typename std::decay<ELEMENT_TYPE>::type>(
1092  std::forward<ELEMENT_TYPE>(i_source));
1093  }
1094 
1111  template <typename ELEMENT_TYPE, typename... CONSTRUCTION_PARAMS>
1112  void emplace(CONSTRUCTION_PARAMS &&... i_construction_params)
1113  {
1114  start_emplace<ELEMENT_TYPE>(std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...)
1115  .commit();
1116  }
1117 
1134  void dyn_push(const RUNTIME_TYPE & i_type) { start_dyn_push(i_type).commit(); }
1135 
1154  void dyn_push_copy(const RUNTIME_TYPE & i_type, const void * i_source)
1155  {
1156  start_dyn_push_copy(i_type, i_source).commit();
1157  }
1158 
1177  void dyn_push_move(const RUNTIME_TYPE & i_type, void * i_source)
1178  {
1179  start_dyn_push_move(i_type, i_source).commit();
1180  }
1181 
1206  template <typename ELEMENT_TYPE>
1207  put_transaction<typename std::decay<ELEMENT_TYPE>::type>
1208  start_push(ELEMENT_TYPE && i_source)
1209  {
1210  return start_emplace<typename std::decay<ELEMENT_TYPE>::type>(
1211  std::forward<ELEMENT_TYPE>(i_source));
1212  }
1213 
1236  template <typename ELEMENT_TYPE, typename... CONSTRUCTION_PARAMS>
1237  put_transaction<ELEMENT_TYPE> start_emplace(CONSTRUCTION_PARAMS &&... i_construction_params)
1238  {
1239  auto push_data = inplace_allocate<
1240  0,
1241  true,
1242  detail::size_of<ELEMENT_TYPE>::value,
1243  alignof(ELEMENT_TYPE)>();
1244 
1245  RUNTIME_TYPE * type = nullptr;
1246  try
1247  {
1248  auto const type_storage = type_after_control(push_data.m_control_block);
1249  DENSITY_ASSUME(type_storage != nullptr);
1250  type = new (type_storage) RUNTIME_TYPE(RUNTIME_TYPE::template make<ELEMENT_TYPE>());
1251 
1252  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
1253  new (push_data.m_user_storage)
1254  ELEMENT_TYPE(std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...);
1255  }
1256  catch (...)
1257  {
1258  if (type != nullptr)
1259  type->RUNTIME_TYPE::~RUNTIME_TYPE();
1261  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
1262  0);
1263  push_data.m_control_block->m_next += detail::Queue_Dead;
1264  throw;
1265  }
1266 
1267  return put_transaction<ELEMENT_TYPE>(PrivateType(), this, push_data);
1268  }
1269 
1288  put_transaction<> start_dyn_push(const RUNTIME_TYPE & i_type)
1289  {
1290  auto push_data = inplace_allocate<0, true>(i_type.size(), i_type.alignment());
1291 
1292  RUNTIME_TYPE * type = nullptr;
1293  try
1294  {
1295  auto const type_storage = type_after_control(push_data.m_control_block);
1296  DENSITY_ASSUME(type_storage != nullptr);
1297  type = new (type_storage) RUNTIME_TYPE(i_type);
1298 
1299  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
1300  i_type.default_construct(push_data.m_user_storage);
1301  }
1302  catch (...)
1303  {
1304  if (type != nullptr)
1305  type->RUNTIME_TYPE::~RUNTIME_TYPE();
1307  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
1308  0);
1309  push_data.m_control_block->m_next += detail::Queue_Dead;
1310  throw;
1311  }
1312 
1313  return put_transaction<void>(PrivateType(), this, push_data);
1314  }
1315 
1316 
1337  put_transaction<> start_dyn_push_copy(const RUNTIME_TYPE & i_type, const void * i_source)
1338  {
1339  auto push_data = inplace_allocate<0, true>(i_type.size(), i_type.alignment());
1340 
1341  RUNTIME_TYPE * type = nullptr;
1342  try
1343  {
1344  auto const type_storage = type_after_control(push_data.m_control_block);
1345  DENSITY_ASSUME(type_storage != nullptr);
1346  type = new (type_storage) RUNTIME_TYPE(i_type);
1347 
1348  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
1349  i_type.copy_construct(push_data.m_user_storage, i_source);
1350  }
1351  catch (...)
1352  {
1353  if (type != nullptr)
1354  type->RUNTIME_TYPE::~RUNTIME_TYPE();
1356  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
1357  0);
1358  push_data.m_control_block->m_next += detail::Queue_Dead;
1359  throw;
1360  }
1361 
1362  return put_transaction<void>(PrivateType(), this, push_data);
1363  }
1364 
1385  put_transaction<> start_dyn_push_move(const RUNTIME_TYPE & i_type, void * i_source)
1386  {
1387  auto push_data = inplace_allocate<0, true>(i_type.size(), i_type.alignment());
1388 
1389  RUNTIME_TYPE * type = nullptr;
1390  try
1391  {
1392  auto const type_storage = type_after_control(push_data.m_control_block);
1393  DENSITY_ASSUME(type_storage != nullptr);
1394  type = new (type_storage) RUNTIME_TYPE(i_type);
1395 
1396  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
1397  i_type.move_construct(push_data.m_user_storage, i_source);
1398  }
1399  catch (...)
1400  {
1401  if (type != nullptr)
1402  type->RUNTIME_TYPE::~RUNTIME_TYPE();
1404  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
1405  0);
1406  push_data.m_control_block->m_next += detail::Queue_Dead;
1407  throw;
1408  }
1409 
1410  return put_transaction<void>(PrivateType(), this, push_data);
1411  }
1412 
1413 
1428  void pop() noexcept { try_start_consume().commit(); }
1429 
1440  bool try_pop() noexcept
1441  {
1442  if (auto operation = try_start_consume())
1443  {
1444  operation.commit();
1445  return true;
1446  }
1447  return false;
1448  }
1449 
1456  consume_operation try_start_consume() noexcept
1457  {
1458  return consume_operation(PrivateType(), this, start_consume_impl());
1459  }
1460 
1474  bool try_start_consume(consume_operation & i_consume) noexcept
1475  {
1476  return i_consume.start_consume_impl(PrivateType(), this);
1477  }
1478 
1479 
1501  template <typename ELEMENT_COMPLETE_TYPE = void> class reentrant_put_transaction
1502  {
1503  static_assert(
1504  std::is_same<
1505  ELEMENT_COMPLETE_TYPE,
1506  typename std::decay<ELEMENT_COMPLETE_TYPE>::type>::value,
1507  "");
1508 
1509  public:
1511  reentrant_put_transaction() noexcept : m_queue(nullptr) {}
1512 
1517 
1521  reentrant_put_transaction & operator=(const reentrant_put_transaction &) = delete;
1522 
1530  template <
1531  typename OTHERTYPE,
1532  typename = typename std::enable_if<
1533  std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value ||
1534  std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
1536  : m_queue(i_source.m_queue), m_put_data(i_source.m_put_data)
1537  {
1538  i_source.m_queue = nullptr;
1539  }
1540 
1546  template <
1547  typename OTHERTYPE,
1548  typename = typename std::enable_if<
1549  std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value ||
1550  std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
1553  {
1554  if (
1555  this !=
1556  static_cast<void *>(
1557  &i_source)) // cast to void to allow comparing pointers to unrelated types
1558  {
1559  if (!empty())
1560  cancel();
1561 
1562  std::swap(m_queue, i_source.m_queue);
1563  std::swap(m_put_data, i_source.m_put_data);
1564  }
1565  return *this;
1566  }
1567 
1571  friend void swap(
1572  reentrant_put_transaction & i_first, reentrant_put_transaction & i_second) noexcept
1573  {
1574  std::swap(i_first.m_queue, i_second.m_queue);
1575  std::swap(i_first.m_put_data, i_second.m_put_data);
1576  }
1577 
1600  void * raw_allocate(size_t i_size, size_t i_alignment)
1601  {
1602  DENSITY_ASSERT(!empty());
1603 
1604  auto push_data =
1605  m_queue->inplace_allocate<detail::Queue_Dead, false>(i_size, i_alignment);
1606  return push_data.m_user_storage;
1607  }
1608 
1633  template <typename INPUT_ITERATOR>
1634  typename std::iterator_traits<INPUT_ITERATOR>::value_type *
1635  raw_allocate_copy(INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end)
1636  {
1637  using ValueType = typename std::iterator_traits<INPUT_ITERATOR>::value_type;
1638  static_assert(
1639  std::is_trivially_destructible<ValueType>::value,
1640  "raw_allocate_copy provides a raw memory in-place allocation that does not "
1641  "invoke "
1642  "destructors when deallocating");
1643 
1644  auto const count_s = std::distance(i_begin, i_end);
1645  auto const count = static_cast<size_t>(count_s);
1646  DENSITY_ASSUME(static_cast<decltype(count_s)>(count) == count_s);
1647 
1648  auto const elements = static_cast<ValueType *>(
1649  raw_allocate(sizeof(ValueType) * count, alignof(ValueType)));
1650  for (auto curr = elements; i_begin != i_end; ++i_begin, ++curr)
1651  new (curr) ValueType(*i_begin);
1652  return elements;
1653  }
1654 
1677  template <typename INPUT_RANGE>
1678  auto raw_allocate_copy(const INPUT_RANGE & i_source_range)
1679  -> decltype(std::declval<reentrant_put_transaction>().raw_allocate_copy(
1680  std::begin(i_source_range), std::end(i_source_range)))
1681  {
1682  return raw_allocate_copy(std::begin(i_source_range), std::end(i_source_range));
1683  }
1684 
1693  void commit() noexcept
1694  {
1695  DENSITY_ASSERT(!empty());
1696  commit_reentrant_put_impl(m_put_data.m_control_block);
1697  m_queue = nullptr;
1698  }
1699 
1700 
1711  void cancel() noexcept
1712  {
1713  DENSITY_ASSERT(!empty());
1714  cancel_reentrant_put_impl(m_put_data.m_control_block);
1715  m_queue = nullptr;
1716  }
1717 
1721  bool empty() const noexcept { return m_queue == nullptr; }
1722 
1726  explicit operator bool() const noexcept { return m_queue != nullptr; }
1727 
1731  heter_queue * queue() const noexcept { return m_queue; }
1732 
1744  void * element_ptr() const noexcept
1745  {
1746  DENSITY_ASSERT(!empty());
1747  return m_put_data.m_user_storage;
1748  }
1749 
1763 #ifndef DOXYGEN_DOC_GENERATION
1764  template <
1765  typename EL = ELEMENT_COMPLETE_TYPE,
1766  typename std::enable_if<!std::is_void<EL>::value>::type * = nullptr>
1767  EL &
1768 #else
1769  ELEMENT_COMPLETE_TYPE &
1770 #endif
1771  element() const noexcept
1772  {
1773  return *static_cast<ELEMENT_COMPLETE_TYPE *>(element_ptr());
1774  }
1775 
1782  const RUNTIME_TYPE & complete_type() const noexcept
1783  {
1784  DENSITY_ASSERT(!empty());
1785  return *type_after_control(m_put_data.m_control_block);
1786  }
1787 
1792  {
1793  if (m_queue != nullptr)
1794  {
1795  cancel_reentrant_put_impl(m_put_data.m_control_block);
1796  }
1797  }
1798 
1801  PrivateType, heter_queue * i_queue, Allocation i_push_data) noexcept
1802  : m_queue(i_queue), m_put_data(i_push_data)
1803  {
1804  }
1805 
1806 
1807  private:
1808  heter_queue * m_queue = nullptr;
1809  Allocation m_put_data;
1810 
1811  template <typename OTHERTYPE> friend class reentrant_put_transaction;
1812  };
1813 
1814 
1832  {
1833  public:
1837  reentrant_consume_operation() noexcept : m_control(nullptr) {}
1838 
1843 
1847  reentrant_consume_operation & operator=(const reentrant_consume_operation &) = delete;
1848 
1853  : m_queue(i_source.m_queue), m_control(i_source.m_control)
1854  {
1855  i_source.m_control = nullptr;
1856  }
1857 
1863  {
1864  if (this != &i_source)
1865  {
1866  if (!empty())
1867  {
1868  commit();
1869  }
1870  m_queue = i_source.m_queue;
1871  m_control = i_source.m_control;
1872  i_source.m_control = nullptr;
1873  }
1874  return *this;
1875  }
1876 
1881  {
1882  if (m_control != nullptr)
1883  {
1884  m_queue->cancel_consume_impl(m_control);
1885  }
1886  }
1887 
1891  friend void swap(
1892  reentrant_consume_operation & i_first,
1893  reentrant_consume_operation & i_second) noexcept
1894  {
1895  std::swap(i_first.m_queue, i_second.m_queue);
1896  std::swap(i_first.m_control, i_second.m_control);
1897  }
1898 
1902  bool empty() const noexcept { return m_control == nullptr; }
1903 
1907  explicit operator bool() const noexcept { return m_control != nullptr; }
1908 
1912  heter_queue * queue() const noexcept
1913  {
1914  return m_control != nullptr ? m_queue : nullptr;
1915  }
1916 
1925  void commit() noexcept
1926  {
1927  DENSITY_ASSERT(!empty());
1928 
1929  auto const & type = complete_type();
1930  auto const element = element_ptr();
1931  type.destroy(element);
1932 
1933  type.RUNTIME_TYPE::~RUNTIME_TYPE();
1934 
1935  m_queue->commit_consume_impl(m_control);
1936  m_control = nullptr;
1937  }
1938 
1956  void commit_nodestroy() noexcept
1957  {
1958  DENSITY_ASSERT(!empty());
1959 
1960  bool destroy_type = !std::is_trivially_destructible<RUNTIME_TYPE>::value;
1961  if (destroy_type)
1962  {
1963  auto const & type = complete_type();
1964  type.RUNTIME_TYPE::~RUNTIME_TYPE();
1965  }
1966 
1967  m_queue->commit_consume_impl(m_control);
1968  m_control = nullptr;
1969  }
1970 
1981  void cancel() noexcept
1982  {
1983  DENSITY_ASSERT(!empty());
1984 
1985  m_queue->cancel_consume_impl(m_control);
1986  m_control = nullptr;
1987  }
1988 
1994  const RUNTIME_TYPE & complete_type() const noexcept
1995  {
1996  DENSITY_ASSERT(!empty());
1997  return *type_after_control(m_control);
1998  }
1999 
2007  void * unaligned_element_ptr() const noexcept
2008  {
2009  DENSITY_ASSERT(!empty());
2010  return get_unaligned_element(m_control);
2011  }
2012 
2020  void * element_ptr() const noexcept
2021  {
2022  DENSITY_ASSERT(!empty());
2023  return get_element(m_control);
2024  }
2025 
2032  template <typename COMPLETE_ELEMENT_TYPE>
2033  COMPLETE_ELEMENT_TYPE & element() const noexcept
2034  {
2035  DENSITY_ASSERT(!empty() && complete_type().template is<COMPLETE_ELEMENT_TYPE>());
2036  return *static_cast<COMPLETE_ELEMENT_TYPE *>(get_element(m_control));
2037  }
2038 
2041  PrivateType, heter_queue * i_queue, ControlBlock * i_control) noexcept
2042  : m_queue(i_queue), m_control(i_control)
2043  {
2044  }
2045 
2047  bool start_consume_impl(PrivateType, heter_queue * i_queue) noexcept
2048  {
2049  if (m_control != nullptr)
2050  {
2051  m_queue->cancel_consume_impl(m_control);
2052  }
2053  m_queue = i_queue;
2054  m_control = i_queue->start_consume_impl();
2055  return m_control != nullptr;
2056  }
2057 
2058  private:
2059  heter_queue * m_queue;
2060  ControlBlock * m_control;
2061  };
2062 
2068  template <typename ELEMENT_TYPE> void reentrant_push(ELEMENT_TYPE && i_source)
2069  {
2070  return reentrant_emplace<typename std::decay<ELEMENT_TYPE>::type>(
2071  std::forward<ELEMENT_TYPE>(i_source));
2072  }
2073 
2079  template <typename ELEMENT_TYPE, typename... CONSTRUCTION_PARAMS>
2080  void reentrant_emplace(CONSTRUCTION_PARAMS &&... i_construction_params)
2081  {
2082  start_reentrant_emplace<ELEMENT_TYPE>(
2083  std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...)
2084  .commit();
2085  }
2086 
2092  void reentrant_dyn_push(const RUNTIME_TYPE & i_type)
2093  {
2094  start_reentrant_dyn_push(i_type).commit();
2095  }
2096 
2102  void reentrant_dyn_push_copy(const RUNTIME_TYPE & i_type, const void * i_source)
2103  {
2104  start_reentrant_dyn_push_copy(i_type, i_source).commit();
2105  }
2106 
2112  void reentrant_dyn_push_move(const RUNTIME_TYPE & i_type, void * i_source)
2113  {
2114  start_reentrant_dyn_push_move(i_type, i_source).commit();
2115  }
2116 
2122  template <typename ELEMENT_TYPE>
2123  reentrant_put_transaction<typename std::decay<ELEMENT_TYPE>::type>
2124  start_reentrant_push(ELEMENT_TYPE && i_source)
2125  {
2126  return start_reentrant_emplace<typename std::decay<ELEMENT_TYPE>::type>(
2127  std::forward<ELEMENT_TYPE>(i_source));
2128  }
2129 
2135  template <typename ELEMENT_TYPE, typename... CONSTRUCTION_PARAMS>
2136  reentrant_put_transaction<ELEMENT_TYPE>
2137  start_reentrant_emplace(CONSTRUCTION_PARAMS &&... i_construction_params)
2138  {
2139  auto push_data = inplace_allocate<
2140  detail::Queue_Busy,
2141  true,
2142  detail::size_of<ELEMENT_TYPE>::value,
2143  alignof(ELEMENT_TYPE)>();
2144 
2145  RUNTIME_TYPE * type = nullptr;
2146  try
2147  {
2148  auto const type_storage = type_after_control(push_data.m_control_block);
2149  DENSITY_ASSUME(type_storage != nullptr);
2150  type = new (type_storage) RUNTIME_TYPE(RUNTIME_TYPE::template make<ELEMENT_TYPE>());
2151 
2152  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
2153  new (push_data.m_user_storage)
2154  ELEMENT_TYPE(std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...);
2155  }
2156  catch (...)
2157  {
2158  if (type != nullptr)
2159  type->RUNTIME_TYPE::~RUNTIME_TYPE();
2161  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2162  detail::Queue_Busy);
2163  push_data.m_control_block->m_next += detail::Queue_Dead - detail::Queue_Busy;
2164  throw;
2165  }
2166 
2167  return reentrant_put_transaction<ELEMENT_TYPE>(PrivateType(), this, push_data);
2168  }
2169 
2175  reentrant_put_transaction<> start_reentrant_dyn_push(const RUNTIME_TYPE & i_type)
2176  {
2177  auto push_data =
2178  inplace_allocate<detail::Queue_Busy, true>(i_type.size(), i_type.alignment());
2179 
2180  RUNTIME_TYPE * type = nullptr;
2181  try
2182  {
2183  auto const type_storage = type_after_control(push_data.m_control_block);
2184  DENSITY_ASSUME(type_storage != nullptr);
2185  type = new (type_storage) RUNTIME_TYPE(i_type);
2186 
2187  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
2188  i_type.default_construct(push_data.m_user_storage);
2189  }
2190  catch (...)
2191  {
2192  if (type != nullptr)
2193  type->RUNTIME_TYPE::~RUNTIME_TYPE();
2195  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2196  detail::Queue_Busy);
2197  push_data.m_control_block->m_next += detail::Queue_Dead - detail::Queue_Busy;
2198  throw;
2199  }
2200 
2201  return reentrant_put_transaction<void>(PrivateType(), this, push_data);
2202  }
2203 
2204 
2210  reentrant_put_transaction<>
2211  start_reentrant_dyn_push_copy(const RUNTIME_TYPE & i_type, const void * i_source)
2212  {
2213  auto push_data =
2214  inplace_allocate<detail::Queue_Busy, true>(i_type.size(), i_type.alignment());
2215 
2216  RUNTIME_TYPE * type = nullptr;
2217  try
2218  {
2219  auto const type_storage = type_after_control(push_data.m_control_block);
2220  DENSITY_ASSUME(type_storage != nullptr);
2221  type = new (type_storage) RUNTIME_TYPE(i_type);
2222 
2223  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
2224  i_type.copy_construct(push_data.m_user_storage, i_source);
2225  }
2226  catch (...)
2227  {
2228  if (type != nullptr)
2229  type->RUNTIME_TYPE::~RUNTIME_TYPE();
2231  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2232  detail::Queue_Busy);
2233  push_data.m_control_block->m_next += detail::Queue_Dead - detail::Queue_Busy;
2234  throw;
2235  }
2236 
2237  return reentrant_put_transaction<void>(PrivateType(), this, push_data);
2238  }
2239 
2245  reentrant_put_transaction<>
2246  start_reentrant_dyn_push_move(const RUNTIME_TYPE & i_type, void * i_source)
2247  {
2248  auto push_data =
2249  inplace_allocate<detail::Queue_Busy, true>(i_type.size(), i_type.alignment());
2250 
2251  RUNTIME_TYPE * type = nullptr;
2252  try
2253  {
2254  auto const type_storage = type_after_control(push_data.m_control_block);
2255  DENSITY_ASSUME(type_storage != nullptr);
2256  type = new (type_storage) RUNTIME_TYPE(i_type);
2257 
2258  DENSITY_ASSUME(push_data.m_user_storage != nullptr);
2259  i_type.move_construct(push_data.m_user_storage, i_source);
2260  }
2261  catch (...)
2262  {
2263  if (type != nullptr)
2264  type->RUNTIME_TYPE::~RUNTIME_TYPE();
2266  (push_data.m_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2267  detail::Queue_Busy);
2268  push_data.m_control_block->m_next += detail::Queue_Dead - detail::Queue_Busy;
2269  throw;
2270  }
2271 
2272  return reentrant_put_transaction<void>(PrivateType(), this, push_data);
2273  }
2274 
2290  void reentrant_pop() noexcept { try_start_reentrant_consume().commit(); }
2291 
2303  bool try_reentrant_pop() noexcept
2304  {
2305  if (auto operation = try_start_reentrant_consume())
2306  {
2307  operation.commit();
2308  return true;
2309  }
2310  return false;
2311  }
2312 
2313 
2322  reentrant_consume_operation try_start_reentrant_consume() noexcept
2323  {
2324  return reentrant_consume_operation(PrivateType(), this, start_consume_impl());
2325  }
2326 
2342  bool try_start_reentrant_consume(reentrant_consume_operation & i_consume) noexcept
2343  {
2344  return i_consume.start_consume_impl(PrivateType(), this);
2345  }
2346 
2347 
2348  // const_iterator
2349 
2350  class const_iterator
2351  {
2352  public:
2353  using iterator_category = std::input_iterator_tag;
2354  using runtime_type = RUNTIME_TYPE;
2356  using pointer = value_type *;
2357  using const_pointer = const value_type *;
2358  using reference = value_type &;
2359  using const_reference = const value_type &;
2360  using size_type = std::size_t;
2361  using difference_type = std::ptrdiff_t;
2362 
2363  const_iterator() noexcept = default;
2364  const_iterator(const const_iterator & i_source) noexcept
2365  : m_control(i_source.m_control), m_queue(i_source.m_queue)
2366  {
2367  new (&m_value.m_pair) value_type(m_value.m_pair);
2368  }
2369 
2370  const_iterator & operator=(const const_iterator & i_source) noexcept
2371  {
2372  m_control = i_source.m_control;
2373  m_queue = i_source.m_queue;
2374  new (&m_value.m_pair) value_type(m_value.m_pair);
2375  return *this;
2376  }
2377 
2378  const_iterator(const heter_queue * i_queue, ControlBlock * i_control) noexcept
2379  : m_control(i_control), m_queue(i_queue)
2380  {
2381  if (m_control != nullptr)
2382  {
2383  const RUNTIME_TYPE & type = *type_after_control(m_control);
2384  new (&m_value.m_pair) value_type(type, get_element(m_control));
2385  }
2386  }
2387 
2388  const value_type & operator*() const noexcept
2389  {
2390  DENSITY_ASSUME(m_control != nullptr);
2391  return m_value.m_pair;
2392  }
2393 
2394  const value_type * operator->() const noexcept
2395  {
2396  DENSITY_ASSUME(m_control != nullptr);
2397  return &m_value.m_pair;
2398  }
2399 
2400  const RUNTIME_TYPE & complete_type() const noexcept
2401  {
2402  DENSITY_ASSUME(m_control != nullptr);
2403  return m_value.m_pair.type();
2404  }
2405 
2406  const void * element_ptr() const noexcept
2407  {
2408  DENSITY_ASSUME(m_control != nullptr);
2409  return m_value.m_pair.address();
2410  }
2411 
2412  const_iterator & operator++() noexcept
2413  {
2414  DENSITY_ASSUME(m_queue != nullptr);
2415  m_control = m_queue->next_valid(m_control);
2416  if (m_control != nullptr)
2417  {
2418  RUNTIME_TYPE & type = *type_after_control(m_control);
2419  new (&m_value.m_pair) value_type(type, get_element(m_control));
2420  }
2421  return *this;
2422  }
2423 
2424  const_iterator operator++(int) noexcept
2425  {
2426  DENSITY_ASSUME(m_queue != nullptr);
2427  auto const prev_state = *this;
2428  ++*this;
2429  return prev_state;
2430  }
2431 
2432  bool operator==(const const_iterator & i_other) const noexcept
2433  {
2434  return m_control == i_other.m_control;
2435  }
2436 
2437  bool operator!=(const const_iterator & i_other) const noexcept
2438  {
2439  return m_control != i_other.m_control;
2440  }
2441 
2442  private:
2443  ControlBlock * m_control = nullptr;
2444  const heter_queue * m_queue = nullptr;
2445  union Value {
2446  value_type m_pair;
2447  Value() noexcept {}
2448  ~Value() {}
2449  } m_value;
2450  };
2451 
2452  // iterator
2453 
2454  class iterator
2455  {
2456  public:
2457  using iterator_category = std::input_iterator_tag;
2458  using runtime_type = RUNTIME_TYPE;
2460  using pointer = value_type *;
2461  using const_pointer = const value_type *;
2462  using reference = value_type &;
2463  using const_reference = const value_type &;
2464  using size_type = std::size_t;
2465  using difference_type = std::ptrdiff_t;
2466 
2467  iterator() noexcept = default;
2468  iterator(const iterator & i_source) noexcept
2469  : m_control(i_source.m_control), m_queue(i_source.m_queue)
2470  {
2471  new (&m_value.m_pair) value_type(m_value.m_pair);
2472  }
2473 
2474  iterator & operator=(const iterator & i_source) noexcept
2475  {
2476  m_control = i_source.m_control;
2477  m_queue = i_source.m_queue;
2478  new (&m_value.m_pair) value_type(m_value.m_pair);
2479  return *this;
2480  }
2481 
2482  iterator(const heter_queue * i_queue, ControlBlock * i_control) noexcept
2483  : m_control(i_control), m_queue(i_queue)
2484  {
2485  if (m_control != nullptr)
2486  {
2487  const RUNTIME_TYPE & type = *type_after_control(m_control);
2488  new (&m_value.m_pair) value_type(type, get_element(m_control));
2489  }
2490  }
2491 
2492  const value_type & operator*() const noexcept
2493  {
2494  DENSITY_ASSUME(m_control != nullptr);
2495  return m_value.m_pair;
2496  }
2497 
2498  const value_type * operator->() const noexcept
2499  {
2500  DENSITY_ASSUME(m_control != nullptr);
2501  return &m_value.m_pair;
2502  }
2503 
2504  const RUNTIME_TYPE & complete_type() const noexcept
2505  {
2506  DENSITY_ASSUME(m_control != nullptr);
2507  return m_value.m_pair.type();
2508  }
2509 
2510  void * element_ptr() const noexcept
2511  {
2512  DENSITY_ASSUME(m_control != nullptr);
2513  return m_value.m_pair.address();
2514  }
2515 
2516  iterator & operator++() noexcept
2517  {
2518  DENSITY_ASSUME(m_queue != nullptr);
2519  m_control = m_queue->next_valid(m_control);
2520  if (m_control != nullptr)
2521  {
2522  RUNTIME_TYPE & type = *type_after_control(m_control);
2523  new (&m_value.m_pair) value_type(type, get_element(m_control));
2524  }
2525  return *this;
2526  }
2527 
2528  iterator operator++(int) noexcept
2529  {
2530  DENSITY_ASSUME(m_queue != nullptr);
2531  auto const prev_state = *this;
2532  ++*this;
2533  return prev_state;
2534  }
2535 
2536  bool operator==(const iterator & i_other) const noexcept
2537  {
2538  return m_control == i_other.m_control;
2539  }
2540 
2541  bool operator!=(const iterator & i_other) const noexcept
2542  {
2543  return m_control != i_other.m_control;
2544  }
2545 
2546  private:
2547  ControlBlock * m_control = nullptr;
2548  const heter_queue * m_queue = nullptr;
2549  union Value {
2550  value_type m_pair;
2551  Value() noexcept {}
2552  ~Value() {}
2553  } m_value;
2554  };
2555 
2556  iterator begin() noexcept { return iterator(this, first_valid(m_head)); }
2557  iterator end() noexcept { return iterator(); }
2558 
2559  const_iterator begin() const noexcept { return const_iterator(this, first_valid(m_head)); }
2560  const_iterator end() const noexcept { return const_iterator(); }
2561 
2562  const_iterator cbegin() const noexcept { return const_iterator(this, first_valid(m_head)); }
2563  const_iterator cend() const noexcept { return const_iterator(); }
2564 
2580  bool operator==(const heter_queue & i_source) const
2581  {
2582  const auto end_1 = cend();
2583  const auto end_2 = i_source.cend();
2584  auto it_2 = i_source.cbegin();
2585  for (auto it_1 = cbegin(); it_1 != end_1; ++it_1, ++it_2)
2586  {
2587  if (
2588  it_2 == end_2 || it_1.complete_type() != it_2.complete_type() ||
2589  !it_1.complete_type().are_equal(it_1.element_ptr(), it_2.element_ptr()))
2590  {
2591  return false;
2592  }
2593  }
2594  return it_2 == end_2;
2595  }
2596 
2612  bool operator!=(const heter_queue & i_source) const { return !operator==(i_source); }
2613 
2614  private:
2615  ControlBlock * first_valid(ControlBlock * i_from) const
2616  {
2617  for (auto curr = i_from; curr != m_tail;)
2618  {
2619  if ((curr->m_next & (detail::Queue_Busy | detail::Queue_Dead)) == 0)
2620  {
2621  return curr;
2622  }
2623  curr = reinterpret_cast<ControlBlock *>(curr->m_next & ~detail::Queue_AllFlags);
2624  }
2625  return nullptr;
2626  }
2627 
2628  ControlBlock * next_valid(ControlBlock * i_from) const
2629  {
2630  DENSITY_ASSUME(i_from != m_tail);
2631  for (auto curr =
2632  reinterpret_cast<ControlBlock *>(i_from->m_next & ~static_cast<uintptr_t>(3));
2633  curr != m_tail;)
2634  {
2635  if ((curr->m_next & (detail::Queue_Busy | detail::Queue_Dead)) == 0)
2636  {
2637  return curr;
2638  }
2639  curr = reinterpret_cast<ControlBlock *>(curr->m_next & ~detail::Queue_AllFlags);
2640  }
2641  return nullptr;
2642  }
2643 
2644  static RUNTIME_TYPE * type_after_control(ControlBlock * i_control) noexcept
2645  {
2646  return reinterpret_cast<RUNTIME_TYPE *>(address_add(i_control, s_sizeof_ControlBlock));
2647  }
2648 
2649  static void * get_unaligned_element(ControlBlock * i_control) noexcept
2650  {
2651  auto result = address_add(i_control, s_sizeof_ControlBlock + s_sizeof_RuntimeType);
2652  if (i_control->m_next & detail::Queue_External)
2653  {
2654  result = static_cast<ExternalBlock *>(result)->m_element;
2655  }
2656  return result;
2657  }
2658 
2659  static void * get_element(detail::QueueControl * i_control) noexcept
2660  {
2661  auto result = address_add(i_control, s_sizeof_ControlBlock + s_sizeof_RuntimeType);
2662  if (i_control->m_next & detail::Queue_External)
2663  {
2664  result = static_cast<ExternalBlock *>(result)->m_element;
2665  }
2666  else
2667  {
2668  result = address_upper_align(result, type_after_control(i_control)->alignment());
2669  }
2670  return result;
2671  }
2672 
2674  static bool same_page(const void * i_first, const void * i_second) noexcept
2675  {
2676  auto const page_mask = ALLOCATOR_TYPE::page_alignment - 1;
2677  return ((reinterpret_cast<uintptr_t>(i_first) ^ reinterpret_cast<uintptr_t>(i_second)) &
2678  ~page_mask) == 0;
2679  }
2680 
2681  struct ExternalBlock
2682  {
2683  void * m_element;
2684  size_t m_size, m_alignment;
2685  };
2686 
2687  static void * get_end_of_page(void * i_address) noexcept
2688  {
2689  return address_add(
2690  address_lower_align(i_address, allocator_type::page_alignment),
2691  allocator_type::page_size - s_sizeof_ControlBlock);
2692  }
2693 
2694 
2697  template <uintptr_t CONTROL_BITS, bool INCLUDE_TYPE>
2698  Allocation inplace_allocate(size_t i_size, size_t i_alignment)
2699  {
2700  DENSITY_ASSERT_INTERNAL(is_power_of_2(i_alignment) && (i_size % i_alignment) == 0);
2701 
2702  if (i_alignment < min_alignment)
2703  {
2704  i_alignment = min_alignment;
2705  i_size = uint_upper_align(i_size, min_alignment);
2706  }
2707 
2708  for (;;)
2709  {
2711  address_is_aligned(m_tail, min_alignment) ||
2712  m_tail == reinterpret_cast<ControlBlock *>(s_invalid_control_block));
2713 
2714  // allocate space for the control block and the runtime type
2715  auto const control_block = m_tail;
2716  void * new_tail = address_add(
2717  control_block,
2718  INCLUDE_TYPE ? (s_sizeof_ControlBlock + s_sizeof_RuntimeType)
2719  : s_sizeof_ControlBlock);
2720 
2721  // allocate space for the element
2722  new_tail = address_upper_align(new_tail, i_alignment);
2723  void * const user_storage = new_tail;
2724  new_tail = address_add(new_tail, i_size);
2725 
2726  // check if a page overflow would occur
2727  void * end_of_page = get_end_of_page(control_block);
2728  if (DENSITY_LIKELY(new_tail <= end_of_page))
2729  {
2730  DENSITY_ASSUME(control_block != nullptr);
2731  new (control_block) ControlBlock();
2732 
2733  control_block->m_next = reinterpret_cast<uintptr_t>(new_tail) + CONTROL_BITS;
2734  m_tail = static_cast<ControlBlock *>(new_tail);
2735  return Allocation{control_block, user_storage};
2736  }
2737  else if (
2738  i_size + (i_alignment - min_alignment) <=
2739  s_max_size_inpage) // if this allocation may fit in a page
2740  {
2741  // allocate a new page and redo
2742  allocate_new_page();
2743  }
2744  else
2745  {
2746  // this allocation would never fit in a page, allocate an external block
2747  return external_allocate<CONTROL_BITS>(i_size, i_alignment);
2748  }
2749  }
2750  }
2751 
2753  template <uintptr_t CONTROL_BITS, bool INCLUDE_TYPE, size_t SIZE, size_t ALIGNMENT>
2754  Allocation inplace_allocate()
2755  {
2756  static_assert(is_power_of_2(ALIGNMENT) && (SIZE % ALIGNMENT) == 0, "");
2757 
2759  address_is_aligned(m_tail, min_alignment) ||
2760  m_tail == reinterpret_cast<ControlBlock *>(s_invalid_control_block));
2761 
2762  constexpr size_t alignment = detail::size_max(ALIGNMENT, min_alignment);
2763  constexpr size_t size = uint_upper_align(SIZE, alignment);
2764  constexpr bool can_fit_in_a_page =
2765  size + (alignment - min_alignment) <= s_max_size_inpage;
2766  constexpr bool over_aligned = alignment > min_alignment;
2767 
2768  for (;;)
2769  {
2770  // allocate space for the control block and the runtime type
2771  auto const control_block = m_tail;
2772  void * new_tail = address_add(
2773  control_block,
2774  INCLUDE_TYPE ? (s_sizeof_ControlBlock + s_sizeof_RuntimeType)
2775  : s_sizeof_ControlBlock);
2776 
2777  // allocate space for the element
2778  if (over_aligned)
2779  {
2780  new_tail = address_upper_align(new_tail, alignment);
2781  }
2783  address_is_aligned(new_tail, min_alignment) ||
2784  m_tail == reinterpret_cast<ControlBlock *>(s_invalid_control_block));
2785  void * new_element = new_tail;
2786  new_tail = address_add(new_tail, size);
2787 
2788  // check if a page overflow would occur
2789  void * end_of_page = get_end_of_page(control_block);
2790  if (DENSITY_LIKELY(new_tail <= end_of_page))
2791  {
2792  DENSITY_ASSUME(control_block != nullptr);
2793  new (control_block) ControlBlock();
2794 
2795  control_block->m_next = reinterpret_cast<uintptr_t>(new_tail) + CONTROL_BITS;
2796  m_tail = static_cast<ControlBlock *>(new_tail);
2797  return Allocation{control_block, new_element};
2798  }
2799  else if (can_fit_in_a_page)
2800  {
2801  // allocate a new page and redo
2802  allocate_new_page();
2803  }
2804  else
2805  {
2806  // this allocation would never fit in a page, allocate an external block
2807  return external_allocate<CONTROL_BITS>(size, alignment);
2808  }
2809  }
2810  }
2811 
2812  template <uintptr_t CONTROL_BITS>
2813  Allocation external_allocate(size_t i_size, size_t i_alignment)
2814  {
2815  auto const external_block = ALLOCATOR_TYPE::allocate(i_size, i_alignment);
2816  try
2817  {
2818  /* external blocks always allocate space for the type, because it would be complicated
2819  for the consumers to handle both cases*/
2820  auto const inplace_put = inplace_allocate<CONTROL_BITS, true>(
2821  sizeof(ExternalBlock), alignof(ExternalBlock));
2822 
2823  new (inplace_put.m_user_storage) ExternalBlock{external_block, i_size, i_alignment};
2824 
2825  DENSITY_ASSERT((inplace_put.m_control_block->m_next & detail::Queue_External) == 0);
2826  inplace_put.m_control_block->m_next |= detail::Queue_External;
2827  return Allocation{inplace_put.m_control_block, external_block};
2828  }
2829  catch (...)
2830  {
2831  /* if inplace_allocate fails, that means that we were able to allocate the external block,
2832  but we were not able to put the struct ExternalBlock in the page (because a new page was
2833  necessary, but we could not allocate it). */
2834  ALLOCATOR_TYPE::deallocate(external_block, i_size, i_alignment);
2835  throw;
2836  }
2837  }
2838 
2839  DENSITY_NO_INLINE void allocate_new_page()
2840  {
2841  // page overflow
2842  if (DENSITY_LIKELY(m_tail != reinterpret_cast<ControlBlock *>(s_invalid_control_block)))
2843  {
2844  auto const control_block = m_tail;
2845 
2846  DENSITY_ASSUME(control_block != nullptr);
2847  new (control_block) ControlBlock();
2848 
2849  auto new_page = allocator_type::allocate_page();
2850  control_block->m_next = reinterpret_cast<uintptr_t>(new_page) + detail::Queue_Dead;
2851  m_tail = static_cast<ControlBlock *>(new_page);
2852  }
2853  else
2854  {
2855  // this happens only on a virgin queue
2856  m_tail = m_head = static_cast<ControlBlock *>(allocator_type::allocate_page());
2857  }
2858  }
2859 
2860  DENSITY_NO_INLINE static void cancel_put_impl(ControlBlock * i_control_block)
2861  {
2862  const auto type_ptr = type_after_control(i_control_block);
2863  type_ptr->destroy(get_element(i_control_block));
2864 
2865  type_ptr->RUNTIME_TYPE::~RUNTIME_TYPE();
2866 
2868  (i_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) == 0);
2869  i_control_block->m_next += detail::Queue_Dead;
2870  }
2871 
2872  static void commit_reentrant_put_impl(ControlBlock * i_control_block)
2873  {
2875  (i_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2876  detail::Queue_Busy);
2877  i_control_block->m_next -= detail::Queue_Busy;
2878  }
2879 
2880  DENSITY_NO_INLINE static void cancel_reentrant_put_impl(ControlBlock * i_control_block)
2881  {
2882  const auto type_ptr = type_after_control(i_control_block);
2883  type_ptr->destroy(get_element(i_control_block));
2884 
2885  type_ptr->RUNTIME_TYPE::~RUNTIME_TYPE();
2886 
2888  (i_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2889  detail::Queue_Busy);
2890  i_control_block->m_next += (detail::Queue_Dead - detail::Queue_Busy);
2891  }
2892 
2893  ControlBlock * start_consume_impl() noexcept
2894  {
2895  auto curr = m_head;
2896  auto const tail = m_tail;
2897  while (curr != tail)
2898  {
2899  if ((curr->m_next & (detail::Queue_Busy | detail::Queue_Dead)) == 0)
2900  {
2901  curr->m_next += detail::Queue_Busy;
2902  return curr;
2903  }
2904 
2905  curr = reinterpret_cast<ControlBlock *>(curr->m_next & ~detail::Queue_AllFlags);
2906  }
2907 
2908  return nullptr;
2909  }
2910 
2911  void commit_consume_impl(ControlBlock * i_control_block) noexcept
2912  {
2914  (i_control_block->m_next & (detail::Queue_Busy | detail::Queue_Dead)) ==
2915  detail::Queue_Busy);
2916  i_control_block->m_next += (detail::Queue_Dead - detail::Queue_Busy);
2917 
2918  clean_dead_elements();
2919  }
2920 
2921  void clean_dead_elements() noexcept
2922  {
2923  auto curr = m_head;
2924  while (curr != m_tail)
2925  {
2926  // break if the current block is busy or is not dead
2927  if (
2928  (curr->m_next & (detail::Queue_Busy | detail::Queue_Dead)) != detail::Queue_Dead)
2929  {
2930  break;
2931  }
2932 
2933  auto next =
2934  reinterpret_cast<ControlBlock *>(curr->m_next & ~detail::Queue_AllFlags);
2935  if (curr->m_next & detail::Queue_External)
2936  {
2937  auto result = address_add(curr, s_sizeof_ControlBlock + s_sizeof_RuntimeType);
2938  const auto & block = *static_cast<ExternalBlock *>(result);
2939  ALLOCATOR_TYPE::deallocate(block.m_element, block.m_size, block.m_alignment);
2940  }
2941 
2942  if (!same_page(next, curr))
2943  {
2944  allocator_type::deallocate_page(curr);
2945  }
2946 
2947  curr = next;
2948  }
2949 
2951  curr == m_tail ||
2952  (curr->m_next & (detail::Queue_Busy | detail::Queue_Dead)) != detail::Queue_Dead);
2953  m_head = curr;
2954  }
2955 
2956  void cancel_consume_impl(ControlBlock * i_control_block) noexcept
2957  {
2959  (i_control_block->m_next & (detail::Queue_AllFlags - detail::Queue_External)) ==
2960  detail::Queue_Busy);
2961  i_control_block->m_next -= detail::Queue_Busy;
2962  }
2963 
2964  void destroy() noexcept
2965  {
2966  clear();
2967  DENSITY_ASSERT_INTERNAL(m_tail == m_head);
2968  if (m_head != reinterpret_cast<ControlBlock *>(s_invalid_control_block))
2969  {
2970  allocator_type::deallocate_page(m_head);
2971  }
2972  }
2973  };
2974 
2975 } // namespace density
~consume_operation()
Definition: heter_queue.h:891
void * address_add(void *i_address, size_t i_offset) noexcept
Definition: density_common.h:142
void commit_nodestroy() noexcept
Definition: heter_queue.h:1956
reentrant_consume_operation(reentrant_consume_operation &&i_source) noexcept
Definition: heter_queue.h:1852
put_transaction start_dyn_push(const RUNTIME_TYPE &i_type)
Definition: heter_queue.h:1288
bool empty() const noexcept
Definition: heter_queue.h:458
bool address_is_aligned(const void *i_address, size_t i_alignment) noexcept
Definition: density_common.h:118
const RUNTIME_TYPE & complete_type() const noexcept
Definition: heter_queue.h:1994
reentrant_put_transaction start_reentrant_dyn_push(const RUNTIME_TYPE &i_type)
Definition: heter_queue.h:2175
heter_queue(const heter_queue &i_source)
Definition: heter_queue.h:366
reentrant_put_transaction start_reentrant_dyn_push_copy(const RUNTIME_TYPE &i_type, const void *i_source)
Definition: heter_queue.h:2211
allocator_type get_allocator() noexcept(std::is_nothrow_copy_constructible< allocator_type >::value)
Definition: heter_queue.h:417
void * unaligned_element_ptr() const noexcept
Definition: heter_queue.h:1017
void commit() noexcept
Definition: heter_queue.h:704
put_transaction< ELEMENT_TYPE > start_emplace(CONSTRUCTION_PARAMS &&...i_construction_params)
Definition: heter_queue.h:1237
heter_queue(heter_queue &&i_source) noexcept
Definition: heter_queue.h:346
Definition: conc_function_queue.h:11
void commit_nodestroy() noexcept
Definition: heter_queue.h:966
void emplace(CONSTRUCTION_PARAMS &&...i_construction_params)
Definition: heter_queue.h:1112
friend void swap(consume_operation &i_first, consume_operation &i_second) noexcept
Definition: heter_queue.h:902
heter_queue * queue() const noexcept
Definition: heter_queue.h:1731
void cancel() noexcept
Definition: heter_queue.h:720
constexpr heter_queue(const ALLOCATOR_TYPE &i_source_allocator) noexcept
Definition: heter_queue.h:312
void commit() noexcept
Definition: heter_queue.h:1693
ELEMENT_COMPLETE_TYPE & element() const noexcept
Definition: heter_queue.h:784
void cancel() noexcept
Definition: heter_queue.h:1981
bool operator==(const heter_queue &i_source) const
Definition: heter_queue.h:2580
friend void swap(heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &i_first, heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &i_second) noexcept
Definition: heter_queue.h:435
Definition: runtime_type.h:1061
Definition: heter_queue.h:200
void dyn_push_move(const RUNTIME_TYPE &i_type, void *i_source)
Definition: heter_queue.h:1177
const RUNTIME_TYPE & complete_type() const noexcept
Definition: heter_queue.h:1004
void * raw_allocate(size_t i_size, size_t i_alignment)
Definition: heter_queue.h:1600
reentrant_consume_operation() noexcept
Definition: heter_queue.h:1837
put_transaction() noexcept
Definition: heter_queue.h:524
void reentrant_dyn_push(const RUNTIME_TYPE &i_type)
Definition: heter_queue.h:2092
auto raw_allocate_copy(const INPUT_RANGE &i_source_range) -> decltype(std::declval< reentrant_put_transaction >().raw_allocate_copy( std::begin(i_source_range), std::end(i_source_range)))
Definition: heter_queue.h:1678
void push(ELEMENT_TYPE &&i_source)
Definition: heter_queue.h:1089
heter_queue * queue() const noexcept
Definition: heter_queue.h:922
bool try_start_reentrant_consume(reentrant_consume_operation &i_consume) noexcept
Definition: heter_queue.h:2342
COMPLETE_ELEMENT_TYPE & element() const noexcept
Definition: heter_queue.h:2033
auto raw_allocate_copy(const INPUT_RANGE &i_source_range) -> decltype(std::declval< put_transaction >().raw_allocate_copy( std::begin(i_source_range), std::end(i_source_range)))
Definition: heter_queue.h:689
void commit() noexcept
Definition: heter_queue.h:935
put_transaction & operator=(put_transaction< OTHERTYPE > &&i_source) noexcept
Definition: heter_queue.h:564
#define DENSITY_ASSERT(...)
Definition: density_config.h:19
const RUNTIME_TYPE & complete_type() const noexcept
Definition: heter_queue.h:795
reentrant_put_transaction(reentrant_put_transaction< OTHERTYPE > &&i_source) noexcept
Definition: heter_queue.h:1535
#define DENSITY_NO_INLINE
Definition: density_config.h:86
Definition: heter_queue.h:843
bool empty() const noexcept
Definition: heter_queue.h:1721
reentrant_consume_operation try_start_reentrant_consume() noexcept
Definition: heter_queue.h:2322
Definition: heter_queue.h:514
put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_push(ELEMENT_TYPE &&i_source)
Definition: heter_queue.h:1208
void reentrant_dyn_push_move(const RUNTIME_TYPE &i_type, void *i_source)
Definition: heter_queue.h:2112
void * element_ptr() const noexcept
Definition: heter_queue.h:757
std::iterator_traits< INPUT_ITERATOR >::value_type * raw_allocate_copy(INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end)
Definition: heter_queue.h:646
allocator_type & get_allocator_ref() noexcept
Definition: heter_queue.h:425
ELEMENT_COMPLETE_TYPE & element() const noexcept
Definition: heter_queue.h:1771
~reentrant_put_transaction()
Definition: heter_queue.h:1791
consume_operation() noexcept
Definition: heter_queue.h:849
void pop() noexcept
Definition: heter_queue.h:1428
heter_queue * queue() const noexcept
Definition: heter_queue.h:740
void cancel() noexcept
Definition: heter_queue.h:1711
heter_queue & operator=(const heter_queue &i_source)
Definition: heter_queue.h:406
void clear() noexcept
Definition: heter_queue.h:480
bool operator!=(const heter_queue &i_source) const
Definition: heter_queue.h:2612
constexpr bool is_power_of_2(size_t i_number) noexcept
Definition: density_common.h:109
constexpr heter_queue(ALLOCATOR_TYPE &&i_source_allocator) noexcept
Definition: heter_queue.h:329
friend void swap(reentrant_consume_operation &i_first, reentrant_consume_operation &i_second) noexcept
Definition: heter_queue.h:1891
heter_queue & operator=(heter_queue &&i_source) noexcept
Definition: heter_queue.h:389
bool empty() const noexcept
Definition: heter_queue.h:912
void * raw_allocate(size_t i_size, size_t i_alignment)
Definition: heter_queue.h:612
void * address_upper_align(void *i_address, size_t i_alignment) noexcept
Definition: density_common.h:264
void reentrant_pop() noexcept
Definition: heter_queue.h:2290
bool try_reentrant_pop() noexcept
Definition: heter_queue.h:2303
void reentrant_dyn_push_copy(const RUNTIME_TYPE &i_type, const void *i_source)
Definition: heter_queue.h:2102
~heter_queue()
Definition: heter_queue.h:450
const RUNTIME_TYPE & complete_type() const noexcept
Definition: heter_queue.h:1782
#define DENSITY_ASSERT_INTERNAL(...)
Definition: density_config.h:28
reentrant_consume_operation & operator=(reentrant_consume_operation &&i_source) noexcept
Definition: heter_queue.h:1862
void * address_lower_align(void *i_address, size_t i_alignment) noexcept
Definition: density_common.h:198
reentrant_put_transaction start_reentrant_dyn_push_move(const RUNTIME_TYPE &i_type, void *i_source)
Definition: heter_queue.h:2246
consume_operation try_start_consume() noexcept
Definition: heter_queue.h:1456
COMPLETE_ELEMENT_TYPE & element() const noexcept
Definition: heter_queue.h:1043
put_transaction start_dyn_push_copy(const RUNTIME_TYPE &i_type, const void *i_source)
Definition: heter_queue.h:1337
friend void swap(put_transaction &i_first, put_transaction &i_second) noexcept
Definition: heter_queue.h:583
consume_operation(consume_operation &&i_source) noexcept
Definition: heter_queue.h:864
put_transaction(put_transaction< OTHERTYPE > &&i_source) noexcept
Definition: heter_queue.h:548
bool try_pop() noexcept
Definition: heter_queue.h:1440
~put_transaction()
Definition: heter_queue.h:804
friend void swap(reentrant_put_transaction &i_first, reentrant_put_transaction &i_second) noexcept
Definition: heter_queue.h:1571
constexpr UINT uint_upper_align(UINT i_uint, size_t i_alignment) noexcept
Definition: density_common.h:297
const allocator_type & get_allocator_ref() const noexcept
Definition: heter_queue.h:430
consume_operation & operator=(consume_operation &&i_source) noexcept
Definition: heter_queue.h:873
void commit() noexcept
Definition: heter_queue.h:1925
void reentrant_push(ELEMENT_TYPE &&i_source)
Definition: heter_queue.h:2068
reentrant_put_transaction() noexcept
Definition: heter_queue.h:1511
void * element_ptr() const noexcept
Definition: heter_queue.h:1744
void dyn_push_copy(const RUNTIME_TYPE &i_type, const void *i_source)
Definition: heter_queue.h:1154
void dyn_push(const RUNTIME_TYPE &i_type)
Definition: heter_queue.h:1134
#define DENSITY_LIKELY(bool_expr)
Definition: density_config.h:76
Definition: dynamic_reference.h:107
~reentrant_consume_operation()
Definition: heter_queue.h:1880
void * element_ptr() const noexcept
Definition: heter_queue.h:1030
constexpr heter_queue() noexcept
Definition: heter_queue.h:295
bool empty() const noexcept
Definition: heter_queue.h:730
bool try_start_consume(consume_operation &i_consume) noexcept
Definition: heter_queue.h:1474
void reentrant_emplace(CONSTRUCTION_PARAMS &&...i_construction_params)
Definition: heter_queue.h:2080
reentrant_put_transaction< ELEMENT_TYPE > start_reentrant_emplace(CONSTRUCTION_PARAMS &&...i_construction_params)
Definition: heter_queue.h:2137
heter_queue * queue() const noexcept
Definition: heter_queue.h:1912
reentrant_put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_reentrant_push(ELEMENT_TYPE &&i_source)
Definition: heter_queue.h:2124
void * unaligned_element_ptr() const noexcept
Definition: heter_queue.h:2007
#define DENSITY_ASSUME(bool_expr,...)
Definition: density_config.h:46
void * element_ptr() const noexcept
Definition: heter_queue.h:2020
reentrant_put_transaction & operator=(reentrant_put_transaction< OTHERTYPE > &&i_source) noexcept
Definition: heter_queue.h:1552
void cancel() noexcept
Definition: heter_queue.h:991
std::iterator_traits< INPUT_ITERATOR >::value_type * raw_allocate_copy(INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end)
Definition: heter_queue.h:1635
bool empty() const noexcept
Definition: heter_queue.h:1902
put_transaction start_dyn_push_move(const RUNTIME_TYPE &i_type, void *i_source)
Definition: heter_queue.h:1385