xref: /llvm-project/llvm/lib/DWARFLinker/Parallel/ArrayList.h (revision fef144cebb378f550ef098d370316554d647f625)
12357e899Savl-llvm //===- ArrayList.h ----------------------------------------------*- C++ -*-===//
22357e899Savl-llvm //
32357e899Savl-llvm // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42357e899Savl-llvm // See https://llvm.org/LICENSE.txt for license information.
52357e899Savl-llvm // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62357e899Savl-llvm //
72357e899Savl-llvm //===----------------------------------------------------------------------===//
82357e899Savl-llvm 
92357e899Savl-llvm #ifndef LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
102357e899Savl-llvm #define LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
112357e899Savl-llvm 
122357e899Savl-llvm #include "llvm/Support/PerThreadBumpPtrAllocator.h"
132357e899Savl-llvm #include <atomic>
142357e899Savl-llvm 
152357e899Savl-llvm namespace llvm {
162357e899Savl-llvm namespace dwarf_linker {
172357e899Savl-llvm namespace parallel {
182357e899Savl-llvm 
192357e899Savl-llvm /// This class is a simple list of T structures. It keeps elements as
202357e899Savl-llvm /// pre-allocated groups to save memory for each element's next pointer.
212357e899Savl-llvm /// It allocates internal data using specified per-thread BumpPtrAllocator.
222357e899Savl-llvm /// Method add() can be called asynchronously.
232357e899Savl-llvm template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
242357e899Savl-llvm public:
ArrayList(llvm::parallel::PerThreadBumpPtrAllocator * Allocator)252357e899Savl-llvm   ArrayList(llvm::parallel::PerThreadBumpPtrAllocator *Allocator)
262357e899Savl-llvm       : Allocator(Allocator) {}
272357e899Savl-llvm 
282357e899Savl-llvm   /// Add specified \p Item to the list.
add(const T & Item)292357e899Savl-llvm   T &add(const T &Item) {
302357e899Savl-llvm     assert(Allocator);
312357e899Savl-llvm 
322357e899Savl-llvm     // Allocate head group if it is not allocated yet.
332357e899Savl-llvm     while (!LastGroup) {
342357e899Savl-llvm       if (allocateNewGroup(GroupsHead))
352357e899Savl-llvm         LastGroup = GroupsHead.load();
362357e899Savl-llvm     }
372357e899Savl-llvm 
382357e899Savl-llvm     ItemsGroup *CurGroup;
392357e899Savl-llvm     size_t CurItemsCount;
402357e899Savl-llvm     do {
412357e899Savl-llvm       CurGroup = LastGroup;
422357e899Savl-llvm       CurItemsCount = CurGroup->ItemsCount.fetch_add(1);
432357e899Savl-llvm 
442357e899Savl-llvm       // Check whether current group is full.
452357e899Savl-llvm       if (CurItemsCount < ItemsGroupSize)
462357e899Savl-llvm         break;
472357e899Savl-llvm 
482357e899Savl-llvm       // Allocate next group if necessary.
492357e899Savl-llvm       if (!CurGroup->Next)
502357e899Savl-llvm         allocateNewGroup(CurGroup->Next);
512357e899Savl-llvm 
522357e899Savl-llvm       LastGroup.compare_exchange_weak(CurGroup, CurGroup->Next);
532357e899Savl-llvm     } while (true);
542357e899Savl-llvm 
552357e899Savl-llvm     // Store item into the current group.
562357e899Savl-llvm     CurGroup->Items[CurItemsCount] = Item;
572357e899Savl-llvm     return CurGroup->Items[CurItemsCount];
582357e899Savl-llvm   }
592357e899Savl-llvm 
602357e899Savl-llvm   using ItemHandlerTy = function_ref<void(T &)>;
612357e899Savl-llvm 
622357e899Savl-llvm   /// Enumerate all items and apply specified \p Handler to each.
forEach(ItemHandlerTy Handler)632357e899Savl-llvm   void forEach(ItemHandlerTy Handler) {
642357e899Savl-llvm     for (ItemsGroup *CurGroup = GroupsHead; CurGroup;
652357e899Savl-llvm          CurGroup = CurGroup->Next) {
662357e899Savl-llvm       for (T &Item : *CurGroup)
672357e899Savl-llvm         Handler(Item);
682357e899Savl-llvm     }
692357e899Savl-llvm   }
702357e899Savl-llvm 
712357e899Savl-llvm   /// Check whether list is empty.
empty()722357e899Savl-llvm   bool empty() { return !GroupsHead; }
732357e899Savl-llvm 
742357e899Savl-llvm   /// Erase list.
erase()752357e899Savl-llvm   void erase() {
762357e899Savl-llvm     GroupsHead = nullptr;
772357e899Savl-llvm     LastGroup = nullptr;
782357e899Savl-llvm   }
792357e899Savl-llvm 
sort(function_ref<bool (const T & LHS,const T & RHS)> Comparator)802357e899Savl-llvm   void sort(function_ref<bool(const T &LHS, const T &RHS)> Comparator) {
812357e899Savl-llvm     SmallVector<T> SortedItems;
822357e899Savl-llvm     forEach([&](T &Item) { SortedItems.push_back(Item); });
832357e899Savl-llvm 
842357e899Savl-llvm     if (SortedItems.size()) {
85*fef144ceSKazu Hirata       std::sort(SortedItems.begin(), SortedItems.end(), Comparator);
862357e899Savl-llvm 
872357e899Savl-llvm       size_t SortedItemIdx = 0;
882357e899Savl-llvm       forEach([&](T &Item) { Item = SortedItems[SortedItemIdx++]; });
892357e899Savl-llvm       assert(SortedItemIdx == SortedItems.size());
902357e899Savl-llvm     }
912357e899Savl-llvm   }
922357e899Savl-llvm 
size()932357e899Savl-llvm   size_t size() {
942357e899Savl-llvm     size_t Result = 0;
952357e899Savl-llvm 
962357e899Savl-llvm     for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr;
972357e899Savl-llvm          CurGroup = CurGroup->Next)
982357e899Savl-llvm       Result += CurGroup->getItemsCount();
992357e899Savl-llvm 
1002357e899Savl-llvm     return Result;
1012357e899Savl-llvm   }
1022357e899Savl-llvm 
1032357e899Savl-llvm protected:
1042357e899Savl-llvm   struct ItemsGroup {
1052357e899Savl-llvm     using ArrayTy = std::array<T, ItemsGroupSize>;
1062357e899Savl-llvm 
1072357e899Savl-llvm     // Array of items kept by this group.
1082357e899Savl-llvm     ArrayTy Items;
1092357e899Savl-llvm 
1102357e899Savl-llvm     // Pointer to the next items group.
1112357e899Savl-llvm     std::atomic<ItemsGroup *> Next = nullptr;
1122357e899Savl-llvm 
1132357e899Savl-llvm     // Number of items in this group.
1142357e899Savl-llvm     // NOTE: ItemsCount could be inaccurate as it might be incremented by
1152357e899Savl-llvm     // several threads. Use getItemsCount() method to get real number of items
1162357e899Savl-llvm     // inside ItemsGroup.
1172357e899Savl-llvm     std::atomic<size_t> ItemsCount = 0;
1182357e899Savl-llvm 
getItemsCountItemsGroup1192357e899Savl-llvm     size_t getItemsCount() const {
1202357e899Savl-llvm       return std::min(ItemsCount.load(), ItemsGroupSize);
1212357e899Savl-llvm     }
1222357e899Savl-llvm 
beginItemsGroup1232357e899Savl-llvm     typename ArrayTy::iterator begin() { return Items.begin(); }
endItemsGroup1242357e899Savl-llvm     typename ArrayTy::iterator end() { return Items.begin() + getItemsCount(); }
1252357e899Savl-llvm   };
1262357e899Savl-llvm 
1272357e899Savl-llvm   // Allocate new group. Put allocated group into the \p AtomicGroup if
1282357e899Savl-llvm   // it is empty. If \p AtomicGroup is filled by another thread then
1292357e899Savl-llvm   // put allocated group into the end of groups list.
1302357e899Savl-llvm   // \returns true if allocated group is put into the \p AtomicGroup.
allocateNewGroup(std::atomic<ItemsGroup * > & AtomicGroup)1312357e899Savl-llvm   bool allocateNewGroup(std::atomic<ItemsGroup *> &AtomicGroup) {
1322357e899Savl-llvm     ItemsGroup *CurGroup = nullptr;
1332357e899Savl-llvm 
1342357e899Savl-llvm     // Allocate new group.
1352357e899Savl-llvm     ItemsGroup *NewGroup = Allocator->Allocate<ItemsGroup>();
1362357e899Savl-llvm     NewGroup->ItemsCount = 0;
1372357e899Savl-llvm     NewGroup->Next = nullptr;
1382357e899Savl-llvm 
1392357e899Savl-llvm     // Try to replace current group with allocated one.
1402357e899Savl-llvm     if (AtomicGroup.compare_exchange_weak(CurGroup, NewGroup))
1412357e899Savl-llvm       return true;
1422357e899Savl-llvm 
1432357e899Savl-llvm     // Put allocated group as last group.
1442357e899Savl-llvm     while (CurGroup) {
1452357e899Savl-llvm       ItemsGroup *NextGroup = CurGroup->Next;
1462357e899Savl-llvm 
1472357e899Savl-llvm       if (!NextGroup) {
1482357e899Savl-llvm         if (CurGroup->Next.compare_exchange_weak(NextGroup, NewGroup))
1492357e899Savl-llvm           break;
1502357e899Savl-llvm       }
1512357e899Savl-llvm 
1522357e899Savl-llvm       CurGroup = NextGroup;
1532357e899Savl-llvm     }
1542357e899Savl-llvm 
1552357e899Savl-llvm     return false;
1562357e899Savl-llvm   }
1572357e899Savl-llvm 
1582357e899Savl-llvm   std::atomic<ItemsGroup *> GroupsHead = nullptr;
1592357e899Savl-llvm   std::atomic<ItemsGroup *> LastGroup = nullptr;
1602357e899Savl-llvm   llvm::parallel::PerThreadBumpPtrAllocator *Allocator = nullptr;
1612357e899Savl-llvm };
1622357e899Savl-llvm 
1632357e899Savl-llvm } // end of namespace parallel
1642357e899Savl-llvm } // end of namespace dwarf_linker
1652357e899Savl-llvm } // end of namespace llvm
1662357e899Savl-llvm 
1672357e899Savl-llvm #endif // LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
168