dispenso 1.4.1
A library for task parallelism
Loading...
Searching...
No Matches
resource_pool.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
15#pragma once
16
17#include <dispenso/platform.h>
19#include <moodycamel/blockingconcurrentqueue.h>
20
21namespace dispenso {
22
23template <typename T>
24class ResourcePool;
25
30template <typename T>
31class Resource {
32 public:
33 Resource(Resource&& other) : resource_(other.resource_), pool_(other.pool_) {
34 other.resource_ = nullptr;
35 }
36
37 Resource& operator=(Resource&& other) {
38 if (&other != this) {
39 recycle();
40 resource_ = other.resource_;
41 pool_ = other.pool_;
42 other.resource_ = nullptr;
43 }
44 return *this;
45 }
46
52 T& get() {
53 return *resource_;
54 }
55
56 ~Resource() {
57 recycle();
58 }
59
60 private:
61 Resource(T* res, ResourcePool<T>* pool) : resource_(res), pool_(pool) {}
62
63 void recycle();
64
65 T* resource_;
66 ResourcePool<T>* pool_;
67
68 friend class ResourcePool<T>;
69};
70
75template <typename T>
77 public:
85 template <typename F>
86 ResourcePool(size_t size, const F& init)
87 : pool_(size),
88 backingResources_(
89 reinterpret_cast<char*>(
90 detail::alignedMalloc(size * detail::alignToCacheLine(sizeof(T))))),
91 size_(size) {
92 char* buf = backingResources_;
93
94 // There are three reasons we create our own buffer and use placement new:
95 // 1. We want to be able to handle non-movable non-copyable objects
96 // * Note that we could do this with std::deque
97 // 2. We want to minimize memory allocations, since that can be a common point of contention in
98 // multithreaded programs.
99 // 3. We can easily ensure that the objects are cache aligned to help avoid false sharing.
100
101 for (size_t i = 0; i < size; ++i) {
102 pool_.enqueue(new (buf) T(init()));
103 buf += detail::alignToCacheLine(sizeof(T));
104 }
105 }
106
113 T* t;
114 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
115 pool_.wait_dequeue(t);
116 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_END();
117 return Resource<T>(t, this);
118 }
119
125 assert(pool_.size_approx() == size_);
126 for (size_t i = 0; i < size_; ++i) {
127 T* t;
128 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
129 pool_.wait_dequeue(t);
130 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_END();
131 t->~T();
132 }
133 detail::alignedFree(backingResources_);
134 }
135
136 private:
137 void recycle(T* t) {
138 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
139 pool_.enqueue(t);
140 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_END();
141 }
142
143 moodycamel::BlockingConcurrentQueue<T*> pool_;
144 char* backingResources_;
145 size_t size_;
146
147 friend class Resource<T>;
148};
149
150template <typename T>
151void Resource<T>::recycle() {
152 if (resource_) {
153 pool_->recycle(resource_);
154 }
155}
156
157} // namespace dispenso
Resource< T > acquire()
ResourcePool(size_t size, const F &init)