1 //===- ArrayList.h ----------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H
10 #define LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H
12 #include "llvm/Support/PerThreadBumpPtrAllocator.h"
16 namespace dwarflinker_parallel
{
18 /// This class is a simple list of T structures. It keeps elements as
19 /// pre-allocated groups to save memory for each element's next pointer.
20 /// It allocates internal data using specified per-thread BumpPtrAllocator.
21 /// Method add() can be called asynchronously.
22 template <typename T
, size_t ItemsGroupSize
= 512> class ArrayList
{
24 /// Add specified \p Item to the list.
25 T
&add(const T
&Item
) {
28 // Allocate head group if it is not allocated yet.
30 if (allocateNewGroup(GroupsHead
))
31 LastGroup
= GroupsHead
.load();
38 CurItemsCount
= CurGroup
->ItemsCount
.fetch_add(1);
40 // Check whether current group is full.
41 if (CurItemsCount
< ItemsGroupSize
)
44 // Allocate next group if necessary.
46 allocateNewGroup(CurGroup
->Next
);
48 LastGroup
.compare_exchange_weak(CurGroup
, CurGroup
->Next
);
51 // Store item into the current group.
52 CurGroup
->Items
[CurItemsCount
] = Item
;
53 return CurGroup
->Items
[CurItemsCount
];
56 using ItemHandlerTy
= function_ref
<void(T
&)>;
58 /// Enumerate all items and apply specified \p Handler to each.
59 void forEach(ItemHandlerTy Handler
) {
60 for (ItemsGroup
*CurGroup
= GroupsHead
; CurGroup
;
61 CurGroup
= CurGroup
->Next
) {
62 for (T
&Item
: *CurGroup
)
67 /// Check whether list is empty.
68 bool empty() { return !GroupsHead
; }
76 void setAllocator(parallel::PerThreadBumpPtrAllocator
*Allocator
) {
77 this->Allocator
= Allocator
;
82 using ArrayTy
= std::array
<T
, ItemsGroupSize
>;
84 // Array of items kept by this group.
87 // Pointer to the next items group.
88 std::atomic
<ItemsGroup
*> Next
= nullptr;
90 // Number of items in this group.
91 // NOTE: ItemsCount could be inaccurate as it might be incremented by
92 // several threads. Use getItemsCount() method to get real number of items
94 std::atomic
<size_t> ItemsCount
= 0;
96 size_t getItemsCount() const {
97 return std::min(ItemsCount
.load(), ItemsGroupSize
);
100 typename
ArrayTy::iterator
begin() { return Items
.begin(); }
101 typename
ArrayTy::iterator
end() { return Items
.begin() + getItemsCount(); }
104 // Allocate new group. Put allocated group into the \p AtomicGroup if
105 // it is empty. If \p AtomicGroup is filled by another thread then
106 // put allocated group into the end of groups list.
107 // \returns true if allocated group is put into the \p AtomicGroup.
108 bool allocateNewGroup(std::atomic
<ItemsGroup
*> &AtomicGroup
) {
109 ItemsGroup
*CurGroup
= nullptr;
111 // Allocate new group.
112 ItemsGroup
*NewGroup
= Allocator
->Allocate
<ItemsGroup
>();
113 NewGroup
->ItemsCount
= 0;
114 NewGroup
->Next
= nullptr;
116 // Try to replace current group with allocated one.
117 if (AtomicGroup
.compare_exchange_weak(CurGroup
, NewGroup
))
120 // Put allocated group as last group.
122 ItemsGroup
*NextGroup
= CurGroup
->Next
;
125 if (CurGroup
->Next
.compare_exchange_weak(NextGroup
, NewGroup
))
129 CurGroup
= NextGroup
;
135 std::atomic
<ItemsGroup
*> GroupsHead
= nullptr;
136 std::atomic
<ItemsGroup
*> LastGroup
= nullptr;
137 parallel::PerThreadBumpPtrAllocator
*Allocator
= nullptr;
140 } // end of namespace dwarflinker_parallel
141 } // end namespace llvm
143 #endif // LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H