// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SubAllocator.h // // Class definition for the ConcRT sub allocator. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #pragma once namespace Concurrency { namespace details { /// /// Each allocation via the sub allocator has an AllocationEntry header. All we need the allocation entry /// for, is to tell us the id of the bucket, which indicates the size of the allocation. However, the size /// of the header is pointer size - since we want to align the user's allocation. /// union AllocationEntry { // The index to the bucket in the suballocator, that this entry belongs to. ULONG_PTR m_bucketIndex; // Pointer to the next allocation in the bucket. This is used to chain allocations in the bucket, and we // do not require a lock since only one thread is guaranteed to be touching the suballocator at a time. AllocationEntry* m_pNext; }; // A bucket that stores a particular size memory block. A SubAllocator has several allocator buckets. class AllocatorBucket { public: /// /// Constructs an allocator bucket. /// AllocatorBucket(); /// /// Destroys an allocator bucket. /// ~AllocatorBucket(); /// /// Returns an allocation from the bucket if it is non-empty, and NULL if it is empty. /// AllocationEntry* Alloc(); /// /// Adds the block to the bucket and returns true if the maximum depth is not reached. /// If the bucket is 'full', it returns false, and the caller is responsible for freeing /// the block to the CRT heap. /// bool Free(AllocationEntry* pAllocation); private: // The current depth of the bucket. int m_depth; // The head of the free block list. AllocationEntry* m_pHead; // The maximum number of allocations the sub allocator will cache per bucket. static const int s_maxBucketDepth = 32; }; #pragma warning(push) #pragma warning(disable: 4324) // structure was padded due to alignment specifier class SubAllocator { public: /// /// Constructs a suballocator. /// SubAllocator() : m_fExternalAllocator(false) { } /// /// Allocates a block of memory of the size specified. /// /// /// Number of bytes to allocate. /// /// /// A pointer to newly allocated memory. /// void* Alloc(size_t numBytes); /// /// Frees a block of memory previously allocated by the Alloc API. /// /// /// A pointer to an allocation previously allocated by Alloc. /// void Free(void* pAllocation); /// /// A static allocation API that allocates directly from the CRT heap, and encodes the bucket id in the allocation, /// based on the size of the block. This is used by callers that are unable to get access to a suballocator at /// the time they are allocating memory. /// static void* StaticAlloc(size_t numBytes); /// /// A static free API that frees directly to the CRT heap. This is used by callers that are unable to get access /// to a suballocator at the time they are freeing memory. /// static void StaticFree(void* pAllocation) { delete [] (char*) ((AllocationEntry*)pAllocation - 1); } /// /// Returns an index into the array of allocator buckets for this sub allocator. The allocation size of the /// bucket is guaranteed to satisfy numBytes. /// /// /// The size of the allocation. This is what the user requested plus space for the ConcRT allocator header. /// /// /// An index into the array of allocator buckets such that.s_bucketSizes[returnedBucketIndex] >= numBytes /// static int GetBucketIndex(size_t numBytes); /// /// Every time an allocator is reused, this flag is set to denote whether it is one out of the 'fixed pool' - the set /// of allocators that are used for external contexts. /// void SetExternalAllocatorFlag(bool flag) { m_fExternalAllocator = flag; } /// /// Returns true, if this allocator is assigned to, or was last assigned to an external context. /// bool IsExternalAllocator() { return m_fExternalAllocator; } private: // private methods #if defined(_DEBUG) /// /// Initialize a block allocated from the freelist. Perform debug validation on the block to /// detect user errors. /// bool InitAndCheckBlockOnAlloc(AllocationEntry *pAllocationEntry, size_t numBytes); /// /// Initialize a block that is added to the freelist. Perform debug validation on the block to /// detect user errors. /// bool InitAndCheckBlockOnFree(AllocationEntry *pAllocationEntry, size_t numBytes); /// /// Helper routine that checks where the given block is filled with /// the given pattern. /// bool CheckBytes(unsigned char * pBlock, unsigned char bCheck, size_t numBytes); #endif // private member variables friend class SchedulerBase; template friend class LockFreeStack; // Entry for freelist of allocators SLIST_ENTRY m_slNext{}; // The total number of buckets. static const int s_numBuckets = 96; // The array of buckets that holds memory for allocation. AllocatorBucket m_buckets[s_numBuckets]; // This flag is set to true, when it this suballocator is handed to a caller that invoked GetSubAllocator with an argument // of 'true'. bool m_fExternalAllocator; // An array that holds the bucket sizes. static const int s_bucketSizes[s_numBuckets]; // A number such that 2 ^ GranularityShift = the minimum granularity of the allocation buckets. static const int GranularityShift; // The allocation size supported by the largest bucket. static const int MaxAllocationSize; }; #pragma warning(pop) } // namespace details } // namespace Concurrency