1*1db9f3b2SDimitry Andric //===- ArrayList.h ----------------------------------------------*- C++ -*-===// 2*1db9f3b2SDimitry Andric // 3*1db9f3b2SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*1db9f3b2SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*1db9f3b2SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*1db9f3b2SDimitry Andric // 7*1db9f3b2SDimitry Andric //===----------------------------------------------------------------------===// 8*1db9f3b2SDimitry Andric 9*1db9f3b2SDimitry Andric #ifndef LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H 10*1db9f3b2SDimitry Andric #define LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H 11*1db9f3b2SDimitry Andric 12*1db9f3b2SDimitry Andric #include "llvm/Support/PerThreadBumpPtrAllocator.h" 13*1db9f3b2SDimitry Andric #include <atomic> 14*1db9f3b2SDimitry Andric 15*1db9f3b2SDimitry Andric namespace llvm { 16*1db9f3b2SDimitry Andric namespace dwarf_linker { 17*1db9f3b2SDimitry Andric namespace parallel { 18*1db9f3b2SDimitry Andric 19*1db9f3b2SDimitry Andric /// This class is a simple list of T structures. It keeps elements as 20*1db9f3b2SDimitry Andric /// pre-allocated groups to save memory for each element's next pointer. 21*1db9f3b2SDimitry Andric /// It allocates internal data using specified per-thread BumpPtrAllocator. 22*1db9f3b2SDimitry Andric /// Method add() can be called asynchronously. 23*1db9f3b2SDimitry Andric template <typename T, size_t ItemsGroupSize = 512> class ArrayList { 24*1db9f3b2SDimitry Andric public: ArrayList(llvm::parallel::PerThreadBumpPtrAllocator * Allocator)25*1db9f3b2SDimitry Andric ArrayList(llvm::parallel::PerThreadBumpPtrAllocator *Allocator) 26*1db9f3b2SDimitry Andric : Allocator(Allocator) {} 27*1db9f3b2SDimitry Andric 28*1db9f3b2SDimitry Andric /// Add specified \p Item to the list. add(const T & Item)29*1db9f3b2SDimitry Andric T &add(const T &Item) { 30*1db9f3b2SDimitry Andric assert(Allocator); 31*1db9f3b2SDimitry Andric 32*1db9f3b2SDimitry Andric // Allocate head group if it is not allocated yet. 33*1db9f3b2SDimitry Andric while (!LastGroup) { 34*1db9f3b2SDimitry Andric if (allocateNewGroup(GroupsHead)) 35*1db9f3b2SDimitry Andric LastGroup = GroupsHead.load(); 36*1db9f3b2SDimitry Andric } 37*1db9f3b2SDimitry Andric 38*1db9f3b2SDimitry Andric ItemsGroup *CurGroup; 39*1db9f3b2SDimitry Andric size_t CurItemsCount; 40*1db9f3b2SDimitry Andric do { 41*1db9f3b2SDimitry Andric CurGroup = LastGroup; 42*1db9f3b2SDimitry Andric CurItemsCount = CurGroup->ItemsCount.fetch_add(1); 43*1db9f3b2SDimitry Andric 44*1db9f3b2SDimitry Andric // Check whether current group is full. 45*1db9f3b2SDimitry Andric if (CurItemsCount < ItemsGroupSize) 46*1db9f3b2SDimitry Andric break; 47*1db9f3b2SDimitry Andric 48*1db9f3b2SDimitry Andric // Allocate next group if necessary. 49*1db9f3b2SDimitry Andric if (!CurGroup->Next) 50*1db9f3b2SDimitry Andric allocateNewGroup(CurGroup->Next); 51*1db9f3b2SDimitry Andric 52*1db9f3b2SDimitry Andric LastGroup.compare_exchange_weak(CurGroup, CurGroup->Next); 53*1db9f3b2SDimitry Andric } while (true); 54*1db9f3b2SDimitry Andric 55*1db9f3b2SDimitry Andric // Store item into the current group. 56*1db9f3b2SDimitry Andric CurGroup->Items[CurItemsCount] = Item; 57*1db9f3b2SDimitry Andric return CurGroup->Items[CurItemsCount]; 58*1db9f3b2SDimitry Andric } 59*1db9f3b2SDimitry Andric 60*1db9f3b2SDimitry Andric using ItemHandlerTy = function_ref<void(T &)>; 61*1db9f3b2SDimitry Andric 62*1db9f3b2SDimitry Andric /// Enumerate all items and apply specified \p Handler to each. forEach(ItemHandlerTy Handler)63*1db9f3b2SDimitry Andric void forEach(ItemHandlerTy Handler) { 64*1db9f3b2SDimitry Andric for (ItemsGroup *CurGroup = GroupsHead; CurGroup; 65*1db9f3b2SDimitry Andric CurGroup = CurGroup->Next) { 66*1db9f3b2SDimitry Andric for (T &Item : *CurGroup) 67*1db9f3b2SDimitry Andric Handler(Item); 68*1db9f3b2SDimitry Andric } 69*1db9f3b2SDimitry Andric } 70*1db9f3b2SDimitry Andric 71*1db9f3b2SDimitry Andric /// Check whether list is empty. empty()72*1db9f3b2SDimitry Andric bool empty() { return !GroupsHead; } 73*1db9f3b2SDimitry Andric 74*1db9f3b2SDimitry Andric /// Erase list. erase()75*1db9f3b2SDimitry Andric void erase() { 76*1db9f3b2SDimitry Andric GroupsHead = nullptr; 77*1db9f3b2SDimitry Andric LastGroup = nullptr; 78*1db9f3b2SDimitry Andric } 79*1db9f3b2SDimitry Andric sort(function_ref<bool (const T & LHS,const T & RHS)> Comparator)80*1db9f3b2SDimitry Andric void sort(function_ref<bool(const T &LHS, const T &RHS)> Comparator) { 81*1db9f3b2SDimitry Andric SmallVector<T> SortedItems; 82*1db9f3b2SDimitry Andric forEach([&](T &Item) { SortedItems.push_back(Item); }); 83*1db9f3b2SDimitry Andric 84*1db9f3b2SDimitry Andric if (SortedItems.size()) { 85*1db9f3b2SDimitry Andric std::sort(SortedItems.begin(), SortedItems.end(), Comparator); 86*1db9f3b2SDimitry Andric 87*1db9f3b2SDimitry Andric size_t SortedItemIdx = 0; 88*1db9f3b2SDimitry Andric forEach([&](T &Item) { Item = SortedItems[SortedItemIdx++]; }); 89*1db9f3b2SDimitry Andric assert(SortedItemIdx == SortedItems.size()); 90*1db9f3b2SDimitry Andric } 91*1db9f3b2SDimitry Andric } 92*1db9f3b2SDimitry Andric size()93*1db9f3b2SDimitry Andric size_t size() { 94*1db9f3b2SDimitry Andric size_t Result = 0; 95*1db9f3b2SDimitry Andric 96*1db9f3b2SDimitry Andric for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr; 97*1db9f3b2SDimitry Andric CurGroup = CurGroup->Next) 98*1db9f3b2SDimitry Andric Result += CurGroup->getItemsCount(); 99*1db9f3b2SDimitry Andric 100*1db9f3b2SDimitry Andric return Result; 101*1db9f3b2SDimitry Andric } 102*1db9f3b2SDimitry Andric 103*1db9f3b2SDimitry Andric protected: 104*1db9f3b2SDimitry Andric struct ItemsGroup { 105*1db9f3b2SDimitry Andric using ArrayTy = std::array<T, ItemsGroupSize>; 106*1db9f3b2SDimitry Andric 107*1db9f3b2SDimitry Andric // Array of items kept by this group. 108*1db9f3b2SDimitry Andric ArrayTy Items; 109*1db9f3b2SDimitry Andric 110*1db9f3b2SDimitry Andric // Pointer to the next items group. 111*1db9f3b2SDimitry Andric std::atomic<ItemsGroup *> Next = nullptr; 112*1db9f3b2SDimitry Andric 113*1db9f3b2SDimitry Andric // Number of items in this group. 114*1db9f3b2SDimitry Andric // NOTE: ItemsCount could be inaccurate as it might be incremented by 115*1db9f3b2SDimitry Andric // several threads. Use getItemsCount() method to get real number of items 116*1db9f3b2SDimitry Andric // inside ItemsGroup. 117*1db9f3b2SDimitry Andric std::atomic<size_t> ItemsCount = 0; 118*1db9f3b2SDimitry Andric getItemsCountItemsGroup119*1db9f3b2SDimitry Andric size_t getItemsCount() const { 120*1db9f3b2SDimitry Andric return std::min(ItemsCount.load(), ItemsGroupSize); 121*1db9f3b2SDimitry Andric } 122*1db9f3b2SDimitry Andric beginItemsGroup123*1db9f3b2SDimitry Andric typename ArrayTy::iterator begin() { return Items.begin(); } endItemsGroup124*1db9f3b2SDimitry Andric typename ArrayTy::iterator end() { return Items.begin() + getItemsCount(); } 125*1db9f3b2SDimitry Andric }; 126*1db9f3b2SDimitry Andric 127*1db9f3b2SDimitry Andric // Allocate new group. Put allocated group into the \p AtomicGroup if 128*1db9f3b2SDimitry Andric // it is empty. If \p AtomicGroup is filled by another thread then 129*1db9f3b2SDimitry Andric // put allocated group into the end of groups list. 130*1db9f3b2SDimitry Andric // \returns true if allocated group is put into the \p AtomicGroup. allocateNewGroup(std::atomic<ItemsGroup * > & AtomicGroup)131*1db9f3b2SDimitry Andric bool allocateNewGroup(std::atomic<ItemsGroup *> &AtomicGroup) { 132*1db9f3b2SDimitry Andric ItemsGroup *CurGroup = nullptr; 133*1db9f3b2SDimitry Andric 134*1db9f3b2SDimitry Andric // Allocate new group. 135*1db9f3b2SDimitry Andric ItemsGroup *NewGroup = Allocator->Allocate<ItemsGroup>(); 136*1db9f3b2SDimitry Andric NewGroup->ItemsCount = 0; 137*1db9f3b2SDimitry Andric NewGroup->Next = nullptr; 138*1db9f3b2SDimitry Andric 139*1db9f3b2SDimitry Andric // Try to replace current group with allocated one. 140*1db9f3b2SDimitry Andric if (AtomicGroup.compare_exchange_weak(CurGroup, NewGroup)) 141*1db9f3b2SDimitry Andric return true; 142*1db9f3b2SDimitry Andric 143*1db9f3b2SDimitry Andric // Put allocated group as last group. 144*1db9f3b2SDimitry Andric while (CurGroup) { 145*1db9f3b2SDimitry Andric ItemsGroup *NextGroup = CurGroup->Next; 146*1db9f3b2SDimitry Andric 147*1db9f3b2SDimitry Andric if (!NextGroup) { 148*1db9f3b2SDimitry Andric if (CurGroup->Next.compare_exchange_weak(NextGroup, NewGroup)) 149*1db9f3b2SDimitry Andric break; 150*1db9f3b2SDimitry Andric } 151*1db9f3b2SDimitry Andric 152*1db9f3b2SDimitry Andric CurGroup = NextGroup; 153*1db9f3b2SDimitry Andric } 154*1db9f3b2SDimitry Andric 155*1db9f3b2SDimitry Andric return false; 156*1db9f3b2SDimitry Andric } 157*1db9f3b2SDimitry Andric 158*1db9f3b2SDimitry Andric std::atomic<ItemsGroup *> GroupsHead = nullptr; 159*1db9f3b2SDimitry Andric std::atomic<ItemsGroup *> LastGroup = nullptr; 160*1db9f3b2SDimitry Andric llvm::parallel::PerThreadBumpPtrAllocator *Allocator = nullptr; 161*1db9f3b2SDimitry Andric }; 162*1db9f3b2SDimitry Andric 163*1db9f3b2SDimitry Andric } // end of namespace parallel 164*1db9f3b2SDimitry Andric } // end of namespace dwarf_linker 165*1db9f3b2SDimitry Andric } // end of namespace llvm 166*1db9f3b2SDimitry Andric 167*1db9f3b2SDimitry Andric #endif // LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H 168