stack_pool.inl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #include "stack_storage.h"
  2. #include "stack_utils.h"
  3. namespace NCoro::NStack {
  4. template<typename TGuard>
  5. TPool<TGuard>::TPool(size_t stackSize, const TPoolAllocatorSettings& settings, const TGuard& guard)
  6. : StackSize_(stackSize)
  7. , RssPagesToKeep_(IsSmallStack() ? settings.SmallStackRssPagesToKeep : settings.RssPagesToKeep)
  8. , Guard_(guard)
  9. , ChunkSize_(Guard_.GetPageAlignedSize() + StackSize_ * settings.StacksPerChunk)
  10. {
  11. Y_ASSERT(RssPagesToKeep_);
  12. if (!RssPagesToKeep_) {
  13. RssPagesToKeep_ = 1; // at least guard should be kept
  14. }
  15. const size_t stackSizeInPages = stackSize / PageSize;
  16. Y_ASSERT(stackSizeInPages >= RssPagesToKeep_);
  17. if (stackSizeInPages < RssPagesToKeep_) {
  18. RssPagesToKeep_ = stackSizeInPages; // keep all stack pages
  19. }
  20. Y_ASSERT(StackSize_ && !(StackSize_ & PageSizeMask)); // stack size is not zero and page aligned
  21. Y_ASSERT(Guard_.GetSize() < StackSize_); // stack has enough space to place guard
  22. Y_ASSERT(stackSizeInPages >= RssPagesToKeep_);
  23. Storage_ = MakeHolder<TStorage>(StackSize_, RssPagesToKeep_, settings.ReleaseRate);
  24. AllocNewMemoryChunk();
  25. }
  26. template<typename TGuard>
  27. TPool<TGuard>::TPool(TPool&& other) noexcept = default;
  28. template<typename TGuard>
  29. TPool<TGuard>::~TPool() {
  30. if (!Memory_.empty()) {
  31. Y_ASSERT(NextToAlloc_ && StackSize_);
  32. for (const auto& chunk : Memory_) {
  33. Y_ASSERT(chunk.Raw && chunk.Aligned);
  34. if (Guard_.ShouldRemoveProtectionBeforeFree()) {
  35. Guard_.RemoveProtection(chunk.Aligned, Guard_.GetPageAlignedSize()); // first page in chunk
  36. const char* endOfStacksMemory = chunk.Aligned + ChunkSize_;
  37. for (char* i = chunk.Aligned + Guard_.GetPageAlignedSize(); i < endOfStacksMemory; i += StackSize_) {
  38. Guard_.RemoveProtection(i, StackSize_);
  39. }
  40. }
  41. free(chunk.Raw);
  42. }
  43. }
  44. }
  45. template<typename TGuard>
  46. NDetails::TStack TPool<TGuard>::AllocStack(const char* name) {
  47. Y_ASSERT(!Memory_.empty());
  48. if (!Storage_->IsEmpty()) {
  49. return Storage_->GetStack(Guard_, name);
  50. } else {
  51. ++NumOfAllocated_;
  52. return AllocNewStack(name);
  53. }
  54. }
  55. template<typename TGuard>
  56. void TPool<TGuard>::FreeStack(NDetails::TStack& stack) {
  57. Y_ASSERT(Storage_->Size() < ((ChunkSize_ - Guard_.GetPageAlignedSize()) / StackSize_) * Memory_.size());
  58. Y_ASSERT(IsStackFromThisPool(stack));
  59. Storage_->ReturnStack(stack);
  60. }
  61. template<typename TGuard>
  62. size_t TPool<TGuard>::GetReleasedSize() const noexcept {
  63. return Storage_->GetReleasedSize();
  64. }
  65. template<typename TGuard>
  66. size_t TPool<TGuard>::GetFullSize() const noexcept {
  67. return Storage_->GetFullSize();
  68. }
  69. template<typename TGuard>
  70. void TPool<TGuard>::AllocNewMemoryChunk() {
  71. const size_t totalSizeInPages = ChunkSize_ / PageSize;
  72. TMemory memory;
  73. const auto res = GetAlignedMemory(totalSizeInPages, memory.Raw, memory.Aligned);
  74. Y_ABORT_UNLESS(res, "Failed to allocate memory for coro stack pool");
  75. NextToAlloc_ = memory.Aligned + Guard_.GetPageAlignedSize(); // skip first guard page
  76. Guard_.Protect(memory.Aligned, Guard_.GetPageAlignedSize(), false); // protect first guard page
  77. Memory_.push_back(std::move(memory));
  78. }
  79. template<typename TGuard>
  80. bool TPool<TGuard>::IsSmallStack() const noexcept {
  81. return StackSize_ / PageSize <= SmallStackMaxSizeInPages;
  82. }
  83. template<typename TGuard>
  84. bool TPool<TGuard>::IsStackFromThisPool(const NDetails::TStack& stack) const noexcept {
  85. for (const auto& chunk : Memory_) {
  86. const char* endOfStacksMemory = chunk.Aligned + ChunkSize_;
  87. if (chunk.Raw <= stack.GetRawMemory() && stack.GetRawMemory() < endOfStacksMemory) {
  88. return true;
  89. }
  90. }
  91. return false;
  92. }
  93. template<typename TGuard>
  94. NDetails::TStack TPool<TGuard>::AllocNewStack(const char* name) {
  95. if (NextToAlloc_ + StackSize_ > Memory_.rbegin()->Aligned + ChunkSize_) {
  96. AllocNewMemoryChunk(); // also sets NextToAlloc_ to first stack position in new allocated chunk of memory
  97. }
  98. Y_ASSERT(NextToAlloc_ + StackSize_ <= Memory_.rbegin()->Aligned + ChunkSize_);
  99. char* newStack = NextToAlloc_;
  100. NextToAlloc_ += StackSize_;
  101. Guard_.Protect(newStack, StackSize_, true);
  102. return NDetails::TStack{newStack, newStack, StackSize_, name};
  103. }
  104. }