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_DWARFLINKER_PARALLEL_ARRAYLIST_H
10 #define LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
12 #include "llvm/Support/PerThreadBumpPtrAllocator.h"
16 namespace dwarf_linker
{
19 /// This class is a simple list of T structures. It keeps elements as
20 /// pre-allocated groups to save memory for each element's next pointer.
21 /// It allocates internal data using specified per-thread BumpPtrAllocator.
22 /// Method add() can be called asynchronously.
23 template <typename T
, size_t ItemsGroupSize
= 512> class ArrayList
{
25 ArrayList(llvm::parallel::PerThreadBumpPtrAllocator
*Allocator
)
26 : Allocator(Allocator
) {}
28 /// Add specified \p Item to the list.
29 T
&add(const T
&Item
) {
32 // Allocate head group if it is not allocated yet.
34 if (allocateNewGroup(GroupsHead
))
35 LastGroup
= GroupsHead
.load();
42 CurItemsCount
= CurGroup
->ItemsCount
.fetch_add(1);
44 // Check whether current group is full.
45 if (CurItemsCount
< ItemsGroupSize
)
48 // Allocate next group if necessary.
50 allocateNewGroup(CurGroup
->Next
);
52 LastGroup
.compare_exchange_weak(CurGroup
, CurGroup
->Next
);
55 // Store item into the current group.
56 CurGroup
->Items
[CurItemsCount
] = Item
;
57 return CurGroup
->Items
[CurItemsCount
];
60 using ItemHandlerTy
= function_ref
<void(T
&)>;
62 /// Enumerate all items and apply specified \p Handler to each.
63 void forEach(ItemHandlerTy Handler
) {
64 for (ItemsGroup
*CurGroup
= GroupsHead
; CurGroup
;
65 CurGroup
= CurGroup
->Next
) {
66 for (T
&Item
: *CurGroup
)
71 /// Check whether list is empty.
72 bool empty() { return !GroupsHead
; }
80 void sort(function_ref
<bool(const T
&LHS
, const T
&RHS
)> Comparator
) {
81 SmallVector
<T
> SortedItems
;
82 forEach([&](T
&Item
) { SortedItems
.push_back(Item
); });
84 if (SortedItems
.size()) {
85 std::sort(SortedItems
.begin(), SortedItems
.end(), Comparator
);
87 size_t SortedItemIdx
= 0;
88 forEach([&](T
&Item
) { Item
= SortedItems
[SortedItemIdx
++]; });
89 assert(SortedItemIdx
== SortedItems
.size());
96 for (ItemsGroup
*CurGroup
= GroupsHead
; CurGroup
!= nullptr;
97 CurGroup
= CurGroup
->Next
)
98 Result
+= CurGroup
->getItemsCount();
105 using ArrayTy
= std::array
<T
, ItemsGroupSize
>;
107 // Array of items kept by this group.
110 // Pointer to the next items group.
111 std::atomic
<ItemsGroup
*> Next
= nullptr;
113 // Number of items in this group.
114 // NOTE: ItemsCount could be inaccurate as it might be incremented by
115 // several threads. Use getItemsCount() method to get real number of items
116 // inside ItemsGroup.
117 std::atomic
<size_t> ItemsCount
= 0;
119 size_t getItemsCount() const {
120 return std::min(ItemsCount
.load(), ItemsGroupSize
);
123 typename
ArrayTy::iterator
begin() { return Items
.begin(); }
124 typename
ArrayTy::iterator
end() { return Items
.begin() + getItemsCount(); }
127 // Allocate new group. Put allocated group into the \p AtomicGroup if
128 // it is empty. If \p AtomicGroup is filled by another thread then
129 // put allocated group into the end of groups list.
130 // \returns true if allocated group is put into the \p AtomicGroup.
131 bool allocateNewGroup(std::atomic
<ItemsGroup
*> &AtomicGroup
) {
132 ItemsGroup
*CurGroup
= nullptr;
134 // Allocate new group.
135 ItemsGroup
*NewGroup
= Allocator
->Allocate
<ItemsGroup
>();
136 NewGroup
->ItemsCount
= 0;
137 NewGroup
->Next
= nullptr;
139 // Try to replace current group with allocated one.
140 if (AtomicGroup
.compare_exchange_weak(CurGroup
, NewGroup
))
143 // Put allocated group as last group.
145 ItemsGroup
*NextGroup
= CurGroup
->Next
;
148 if (CurGroup
->Next
.compare_exchange_weak(NextGroup
, NewGroup
))
152 CurGroup
= NextGroup
;
158 std::atomic
<ItemsGroup
*> GroupsHead
= nullptr;
159 std::atomic
<ItemsGroup
*> LastGroup
= nullptr;
160 llvm::parallel::PerThreadBumpPtrAllocator
*Allocator
= nullptr;
163 } // end of namespace parallel
164 } // end of namespace dwarf_linker
165 } // end of namespace llvm
167 #endif // LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H