xref: /oneTBB/src/tbbmalloc/large_objects.cpp (revision 51c0b2f7)
1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2005-2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev #include "tbbmalloc_internal.h"
18*51c0b2f7Stbbdev #include "../tbb/environment.h"
19*51c0b2f7Stbbdev 
20*51c0b2f7Stbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
21*51c0b2f7Stbbdev     // Suppress warning: unary minus operator applied to unsigned type, result still unsigned
22*51c0b2f7Stbbdev     // TBB_REVAMP_TODO: review this warning
23*51c0b2f7Stbbdev     #pragma warning(push)
24*51c0b2f7Stbbdev     #pragma warning(disable:4146)
25*51c0b2f7Stbbdev #endif
26*51c0b2f7Stbbdev 
27*51c0b2f7Stbbdev /******************************* Allocation of large objects *********************************************/
28*51c0b2f7Stbbdev 
29*51c0b2f7Stbbdev namespace rml {
30*51c0b2f7Stbbdev namespace internal {
31*51c0b2f7Stbbdev 
32*51c0b2f7Stbbdev /* ---------------------------- Large Object cache init section ---------------------------------------- */
33*51c0b2f7Stbbdev 
34*51c0b2f7Stbbdev void LargeObjectCache::init(ExtMemoryPool *memPool)
35*51c0b2f7Stbbdev {
36*51c0b2f7Stbbdev     extMemPool = memPool;
37*51c0b2f7Stbbdev     // scalable_allocation_mode can be called before allocator initialization, respect this manual request
38*51c0b2f7Stbbdev     if (hugeSizeThreshold == 0) {
39*51c0b2f7Stbbdev         // Huge size threshold initialization if environment variable was set
40*51c0b2f7Stbbdev         long requestedThreshold = tbb::detail::r1::GetIntegralEnvironmentVariable("TBB_MALLOC_SET_HUGE_SIZE_THRESHOLD");
41*51c0b2f7Stbbdev         // Read valid env or initialize by default with max possible values
42*51c0b2f7Stbbdev         if (requestedThreshold != -1) {
43*51c0b2f7Stbbdev             setHugeSizeThreshold(requestedThreshold);
44*51c0b2f7Stbbdev         } else {
45*51c0b2f7Stbbdev             setHugeSizeThreshold(maxHugeSize);
46*51c0b2f7Stbbdev         }
47*51c0b2f7Stbbdev     }
48*51c0b2f7Stbbdev }
49*51c0b2f7Stbbdev 
50*51c0b2f7Stbbdev /* ----------------------------- Huge size threshold settings ----------------------------------------- */
51*51c0b2f7Stbbdev 
52*51c0b2f7Stbbdev void LargeObjectCache::setHugeSizeThreshold(size_t value)
53*51c0b2f7Stbbdev {
54*51c0b2f7Stbbdev     // Valid in the huge cache range: [MaxLargeSize, MaxHugeSize].
55*51c0b2f7Stbbdev     if (value <= maxHugeSize) {
56*51c0b2f7Stbbdev         hugeSizeThreshold = value >= maxLargeSize ? alignToBin(value) : maxLargeSize;
57*51c0b2f7Stbbdev 
58*51c0b2f7Stbbdev         // Calculate local indexes for the global threshold size (for fast search inside a regular cleanup)
59*51c0b2f7Stbbdev         largeCache.hugeSizeThresholdIdx = LargeCacheType::numBins;
60*51c0b2f7Stbbdev         hugeCache.hugeSizeThresholdIdx = HugeCacheType::sizeToIdx(hugeSizeThreshold);
61*51c0b2f7Stbbdev     }
62*51c0b2f7Stbbdev }
63*51c0b2f7Stbbdev 
64*51c0b2f7Stbbdev bool LargeObjectCache::sizeInCacheRange(size_t size)
65*51c0b2f7Stbbdev {
66*51c0b2f7Stbbdev     return size <= maxHugeSize && (size <= defaultMaxHugeSize || size >= hugeSizeThreshold);
67*51c0b2f7Stbbdev }
68*51c0b2f7Stbbdev 
69*51c0b2f7Stbbdev /* ----------------------------------------------------------------------------------------------------- */
70*51c0b2f7Stbbdev 
71*51c0b2f7Stbbdev /* The functor called by the aggregator for the operation list */
72*51c0b2f7Stbbdev template<typename Props>
73*51c0b2f7Stbbdev class CacheBinFunctor {
74*51c0b2f7Stbbdev     typename LargeObjectCacheImpl<Props>::CacheBin *const bin;
75*51c0b2f7Stbbdev     ExtMemoryPool *const extMemPool;
76*51c0b2f7Stbbdev     typename LargeObjectCacheImpl<Props>::BinBitMask *const bitMask;
77*51c0b2f7Stbbdev     const int idx;
78*51c0b2f7Stbbdev 
79*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease;
80*51c0b2f7Stbbdev     bool needCleanup;
81*51c0b2f7Stbbdev     uintptr_t currTime;
82*51c0b2f7Stbbdev 
83*51c0b2f7Stbbdev     /* Do preprocessing under the operation list. */
84*51c0b2f7Stbbdev     /* All the OP_PUT_LIST operations are merged in the one operation.
85*51c0b2f7Stbbdev        All OP_GET operations are merged with the OP_PUT_LIST operations but
86*51c0b2f7Stbbdev        it demands the update of the moving average value in the bin.
87*51c0b2f7Stbbdev        Only the last OP_CLEAN_TO_THRESHOLD operation has sense.
88*51c0b2f7Stbbdev        The OP_CLEAN_ALL operation also should be performed only once.
89*51c0b2f7Stbbdev        Moreover it cancels the OP_CLEAN_TO_THRESHOLD operation. */
90*51c0b2f7Stbbdev     class OperationPreprocessor {
91*51c0b2f7Stbbdev         // TODO: remove the dependency on CacheBin.
92*51c0b2f7Stbbdev         typename LargeObjectCacheImpl<Props>::CacheBin *const  bin;
93*51c0b2f7Stbbdev 
94*51c0b2f7Stbbdev         /* Contains the relative time in the operation list.
95*51c0b2f7Stbbdev            It counts in the reverse order since the aggregator also
96*51c0b2f7Stbbdev            provides operations in the reverse order. */
97*51c0b2f7Stbbdev         uintptr_t lclTime;
98*51c0b2f7Stbbdev 
99*51c0b2f7Stbbdev         /* opGet contains only OP_GET operations which cannot be merge with OP_PUT operations
100*51c0b2f7Stbbdev            opClean contains all OP_CLEAN_TO_THRESHOLD and OP_CLEAN_ALL operations. */
101*51c0b2f7Stbbdev         CacheBinOperation *opGet, *opClean;
102*51c0b2f7Stbbdev         /* The time of the last OP_CLEAN_TO_THRESHOLD operations */
103*51c0b2f7Stbbdev         uintptr_t cleanTime;
104*51c0b2f7Stbbdev 
105*51c0b2f7Stbbdev         /* lastGetOpTime - the time of the last OP_GET operation.
106*51c0b2f7Stbbdev            lastGet - the same meaning as CacheBin::lastGet */
107*51c0b2f7Stbbdev         uintptr_t lastGetOpTime, lastGet;
108*51c0b2f7Stbbdev 
109*51c0b2f7Stbbdev         /* The total sum of all usedSize changes requested with CBOP_UPDATE_USED_SIZE operations. */
110*51c0b2f7Stbbdev         size_t updateUsedSize;
111*51c0b2f7Stbbdev 
112*51c0b2f7Stbbdev         /* The list of blocks for the OP_PUT_LIST operation. */
113*51c0b2f7Stbbdev         LargeMemoryBlock *head, *tail;
114*51c0b2f7Stbbdev         int putListNum;
115*51c0b2f7Stbbdev 
116*51c0b2f7Stbbdev         /* if the OP_CLEAN_ALL is requested. */
117*51c0b2f7Stbbdev         bool isCleanAll;
118*51c0b2f7Stbbdev 
119*51c0b2f7Stbbdev         inline void commitOperation(CacheBinOperation *op) const;
120*51c0b2f7Stbbdev         inline void addOpToOpList(CacheBinOperation *op, CacheBinOperation **opList) const;
121*51c0b2f7Stbbdev         bool getFromPutList(CacheBinOperation* opGet, uintptr_t currTime);
122*51c0b2f7Stbbdev         void addToPutList( LargeMemoryBlock *head, LargeMemoryBlock *tail, int num );
123*51c0b2f7Stbbdev 
124*51c0b2f7Stbbdev     public:
125*51c0b2f7Stbbdev         OperationPreprocessor(typename LargeObjectCacheImpl<Props>::CacheBin *bin) :
126*51c0b2f7Stbbdev             bin(bin), lclTime(0), opGet(NULL), opClean(NULL), cleanTime(0),
127*51c0b2f7Stbbdev             lastGetOpTime(0), updateUsedSize(0), head(NULL), isCleanAll(false)  {}
128*51c0b2f7Stbbdev         void operator()(CacheBinOperation* opList);
129*51c0b2f7Stbbdev         uintptr_t getTimeRange() const { return -lclTime; }
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev         friend class CacheBinFunctor;
132*51c0b2f7Stbbdev     };
133*51c0b2f7Stbbdev 
134*51c0b2f7Stbbdev public:
135*51c0b2f7Stbbdev     CacheBinFunctor(typename LargeObjectCacheImpl<Props>::CacheBin *bin, ExtMemoryPool *extMemPool,
136*51c0b2f7Stbbdev                     typename LargeObjectCacheImpl<Props>::BinBitMask *bitMask, int idx) :
137*51c0b2f7Stbbdev         bin(bin), extMemPool(extMemPool), bitMask(bitMask), idx(idx), toRelease(NULL), needCleanup(false) {}
138*51c0b2f7Stbbdev     void operator()(CacheBinOperation* opList);
139*51c0b2f7Stbbdev 
140*51c0b2f7Stbbdev     bool isCleanupNeeded() const { return needCleanup; }
141*51c0b2f7Stbbdev     LargeMemoryBlock *getToRelease() const { return toRelease; }
142*51c0b2f7Stbbdev     uintptr_t getCurrTime() const { return currTime; }
143*51c0b2f7Stbbdev };
144*51c0b2f7Stbbdev 
145*51c0b2f7Stbbdev /* ---------------- Cache Bin Aggregator Operation Helpers ---------------- */
146*51c0b2f7Stbbdev 
147*51c0b2f7Stbbdev // The list of structures which describe the operation data
148*51c0b2f7Stbbdev struct OpGet {
149*51c0b2f7Stbbdev     static const CacheBinOperationType type = CBOP_GET;
150*51c0b2f7Stbbdev     LargeMemoryBlock **res;
151*51c0b2f7Stbbdev     size_t size;
152*51c0b2f7Stbbdev     uintptr_t currTime;
153*51c0b2f7Stbbdev };
154*51c0b2f7Stbbdev 
155*51c0b2f7Stbbdev struct OpPutList {
156*51c0b2f7Stbbdev     static const CacheBinOperationType type = CBOP_PUT_LIST;
157*51c0b2f7Stbbdev     LargeMemoryBlock *head;
158*51c0b2f7Stbbdev };
159*51c0b2f7Stbbdev 
160*51c0b2f7Stbbdev struct OpCleanToThreshold {
161*51c0b2f7Stbbdev     static const CacheBinOperationType type = CBOP_CLEAN_TO_THRESHOLD;
162*51c0b2f7Stbbdev     LargeMemoryBlock **res;
163*51c0b2f7Stbbdev     uintptr_t currTime;
164*51c0b2f7Stbbdev };
165*51c0b2f7Stbbdev 
166*51c0b2f7Stbbdev struct OpCleanAll {
167*51c0b2f7Stbbdev     static const CacheBinOperationType type = CBOP_CLEAN_ALL;
168*51c0b2f7Stbbdev     LargeMemoryBlock **res;
169*51c0b2f7Stbbdev };
170*51c0b2f7Stbbdev 
171*51c0b2f7Stbbdev struct OpUpdateUsedSize {
172*51c0b2f7Stbbdev     static const CacheBinOperationType type = CBOP_UPDATE_USED_SIZE;
173*51c0b2f7Stbbdev     size_t size;
174*51c0b2f7Stbbdev };
175*51c0b2f7Stbbdev 
176*51c0b2f7Stbbdev union CacheBinOperationData {
177*51c0b2f7Stbbdev private:
178*51c0b2f7Stbbdev     OpGet opGet;
179*51c0b2f7Stbbdev     OpPutList opPutList;
180*51c0b2f7Stbbdev     OpCleanToThreshold opCleanToThreshold;
181*51c0b2f7Stbbdev     OpCleanAll opCleanAll;
182*51c0b2f7Stbbdev     OpUpdateUsedSize opUpdateUsedSize;
183*51c0b2f7Stbbdev };
184*51c0b2f7Stbbdev 
185*51c0b2f7Stbbdev // Forward declarations
186*51c0b2f7Stbbdev template <typename OpTypeData> OpTypeData& opCast(CacheBinOperation &op);
187*51c0b2f7Stbbdev 
188*51c0b2f7Stbbdev // Describes the aggregator operation
189*51c0b2f7Stbbdev struct CacheBinOperation : public MallocAggregatedOperation<CacheBinOperation>::type {
190*51c0b2f7Stbbdev     CacheBinOperationType type;
191*51c0b2f7Stbbdev 
192*51c0b2f7Stbbdev     template <typename OpTypeData>
193*51c0b2f7Stbbdev     CacheBinOperation(OpTypeData &d, CacheBinOperationStatus st = CBST_WAIT) {
194*51c0b2f7Stbbdev         opCast<OpTypeData>(*this) = d;
195*51c0b2f7Stbbdev         type = OpTypeData::type;
196*51c0b2f7Stbbdev         MallocAggregatedOperation<CacheBinOperation>::type::status = st;
197*51c0b2f7Stbbdev     }
198*51c0b2f7Stbbdev private:
199*51c0b2f7Stbbdev     CacheBinOperationData data;
200*51c0b2f7Stbbdev 
201*51c0b2f7Stbbdev     template <typename OpTypeData>
202*51c0b2f7Stbbdev     friend OpTypeData& opCast(CacheBinOperation &op);
203*51c0b2f7Stbbdev };
204*51c0b2f7Stbbdev 
205*51c0b2f7Stbbdev // The opCast function can be the member of CacheBinOperation but it will have
206*51c0b2f7Stbbdev // small stylistic ambiguity: it will look like a getter (with a cast) for the
207*51c0b2f7Stbbdev // CacheBinOperation::data data member but it should return a reference to
208*51c0b2f7Stbbdev // simplify the code from a lot of getter/setter calls. So the global cast in
209*51c0b2f7Stbbdev // the style of static_cast (or reinterpret_cast) seems to be more readable and
210*51c0b2f7Stbbdev // have more explicit semantic.
211*51c0b2f7Stbbdev template <typename OpTypeData>
212*51c0b2f7Stbbdev OpTypeData& opCast(CacheBinOperation &op) {
213*51c0b2f7Stbbdev     return *reinterpret_cast<OpTypeData*>(&op.data);
214*51c0b2f7Stbbdev }
215*51c0b2f7Stbbdev 
216*51c0b2f7Stbbdev /* ------------------------------------------------------------------------ */
217*51c0b2f7Stbbdev 
218*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT
219*51c0b2f7Stbbdev //intptr_t mallocCalls, cacheHits;
220*51c0b2f7Stbbdev std::atomic<intptr_t> mallocCalls, cacheHits;
221*51c0b2f7Stbbdev //intptr_t memAllocKB, memHitKB;
222*51c0b2f7Stbbdev std::atomic<intptr_t> memAllocKB, memHitKB;
223*51c0b2f7Stbbdev #endif
224*51c0b2f7Stbbdev 
225*51c0b2f7Stbbdev inline bool lessThanWithOverflow(intptr_t a, intptr_t b)
226*51c0b2f7Stbbdev {
227*51c0b2f7Stbbdev     return (a < b && (b - a < UINTPTR_MAX/2)) ||
228*51c0b2f7Stbbdev            (a > b && (a - b > UINTPTR_MAX/2));
229*51c0b2f7Stbbdev }
230*51c0b2f7Stbbdev 
231*51c0b2f7Stbbdev /* ----------------------------------- Operation processing methods ------------------------------------ */
232*51c0b2f7Stbbdev 
233*51c0b2f7Stbbdev template<typename Props> void CacheBinFunctor<Props>::
234*51c0b2f7Stbbdev     OperationPreprocessor::commitOperation(CacheBinOperation *op) const
235*51c0b2f7Stbbdev {
236*51c0b2f7Stbbdev     // FencedStore( (intptr_t&)(op->status), CBST_DONE );
237*51c0b2f7Stbbdev     op->status.store(CBST_DONE, std::memory_order_release);
238*51c0b2f7Stbbdev }
239*51c0b2f7Stbbdev 
240*51c0b2f7Stbbdev template<typename Props> void CacheBinFunctor<Props>::
241*51c0b2f7Stbbdev     OperationPreprocessor::addOpToOpList(CacheBinOperation *op, CacheBinOperation **opList) const
242*51c0b2f7Stbbdev {
243*51c0b2f7Stbbdev     op->next = *opList;
244*51c0b2f7Stbbdev     *opList = op;
245*51c0b2f7Stbbdev }
246*51c0b2f7Stbbdev 
247*51c0b2f7Stbbdev template<typename Props> bool CacheBinFunctor<Props>::
248*51c0b2f7Stbbdev     OperationPreprocessor::getFromPutList(CacheBinOperation *opGet, uintptr_t currTime)
249*51c0b2f7Stbbdev {
250*51c0b2f7Stbbdev     if ( head ) {
251*51c0b2f7Stbbdev         uintptr_t age = head->age;
252*51c0b2f7Stbbdev         LargeMemoryBlock *next = head->next;
253*51c0b2f7Stbbdev         *opCast<OpGet>(*opGet).res = head;
254*51c0b2f7Stbbdev         commitOperation( opGet );
255*51c0b2f7Stbbdev         head = next;
256*51c0b2f7Stbbdev         putListNum--;
257*51c0b2f7Stbbdev         MALLOC_ASSERT( putListNum>=0, ASSERT_TEXT );
258*51c0b2f7Stbbdev 
259*51c0b2f7Stbbdev         // use moving average with current hit interval
260*51c0b2f7Stbbdev         bin->updateMeanHitRange( currTime - age );
261*51c0b2f7Stbbdev         return true;
262*51c0b2f7Stbbdev     }
263*51c0b2f7Stbbdev     return false;
264*51c0b2f7Stbbdev }
265*51c0b2f7Stbbdev 
266*51c0b2f7Stbbdev template<typename Props> void CacheBinFunctor<Props>::
267*51c0b2f7Stbbdev     OperationPreprocessor::addToPutList(LargeMemoryBlock *h, LargeMemoryBlock *t, int num)
268*51c0b2f7Stbbdev {
269*51c0b2f7Stbbdev     if ( head ) {
270*51c0b2f7Stbbdev         MALLOC_ASSERT( tail, ASSERT_TEXT );
271*51c0b2f7Stbbdev         tail->next = h;
272*51c0b2f7Stbbdev         h->prev = tail;
273*51c0b2f7Stbbdev         tail = t;
274*51c0b2f7Stbbdev         putListNum += num;
275*51c0b2f7Stbbdev     } else {
276*51c0b2f7Stbbdev         head = h;
277*51c0b2f7Stbbdev         tail = t;
278*51c0b2f7Stbbdev         putListNum = num;
279*51c0b2f7Stbbdev     }
280*51c0b2f7Stbbdev }
281*51c0b2f7Stbbdev 
282*51c0b2f7Stbbdev template<typename Props> void CacheBinFunctor<Props>::
283*51c0b2f7Stbbdev     OperationPreprocessor::operator()(CacheBinOperation* opList)
284*51c0b2f7Stbbdev {
285*51c0b2f7Stbbdev     for ( CacheBinOperation *op = opList, *opNext; op; op = opNext ) {
286*51c0b2f7Stbbdev         opNext = op->next;
287*51c0b2f7Stbbdev         switch ( op->type ) {
288*51c0b2f7Stbbdev         case CBOP_GET:
289*51c0b2f7Stbbdev             {
290*51c0b2f7Stbbdev                 lclTime--;
291*51c0b2f7Stbbdev                 if ( !lastGetOpTime ) {
292*51c0b2f7Stbbdev                     lastGetOpTime = lclTime;
293*51c0b2f7Stbbdev                     lastGet = 0;
294*51c0b2f7Stbbdev                 } else if ( !lastGet ) lastGet = lclTime;
295*51c0b2f7Stbbdev 
296*51c0b2f7Stbbdev                 if ( !getFromPutList(op,lclTime) ) {
297*51c0b2f7Stbbdev                     opCast<OpGet>(*op).currTime = lclTime;
298*51c0b2f7Stbbdev                     addOpToOpList( op, &opGet );
299*51c0b2f7Stbbdev                 }
300*51c0b2f7Stbbdev             }
301*51c0b2f7Stbbdev             break;
302*51c0b2f7Stbbdev 
303*51c0b2f7Stbbdev         case CBOP_PUT_LIST:
304*51c0b2f7Stbbdev             {
305*51c0b2f7Stbbdev                 LargeMemoryBlock *head = opCast<OpPutList>(*op).head;
306*51c0b2f7Stbbdev                 LargeMemoryBlock *curr = head, *prev = NULL;
307*51c0b2f7Stbbdev 
308*51c0b2f7Stbbdev                 int num = 0;
309*51c0b2f7Stbbdev                 do {
310*51c0b2f7Stbbdev                     // we do not kept prev pointers during assigning blocks to bins, set them now
311*51c0b2f7Stbbdev                     curr->prev = prev;
312*51c0b2f7Stbbdev 
313*51c0b2f7Stbbdev                     // Save the local times to the memory blocks. Local times are necessary
314*51c0b2f7Stbbdev                     // for the getFromPutList function which updates the hit range value in
315*51c0b2f7Stbbdev                     // CacheBin when OP_GET and OP_PUT_LIST operations are merged successfully.
316*51c0b2f7Stbbdev                     // The age will be updated to the correct global time after preprocessing
317*51c0b2f7Stbbdev                     // when global cache time is updated.
318*51c0b2f7Stbbdev                     curr->age = --lclTime;
319*51c0b2f7Stbbdev 
320*51c0b2f7Stbbdev                     prev = curr;
321*51c0b2f7Stbbdev                     num += 1;
322*51c0b2f7Stbbdev 
323*51c0b2f7Stbbdev                     STAT_increment(getThreadId(), ThreadCommonCounters, cacheLargeObj);
324*51c0b2f7Stbbdev                 } while ((curr = curr->next) != NULL);
325*51c0b2f7Stbbdev 
326*51c0b2f7Stbbdev                 LargeMemoryBlock *tail = prev;
327*51c0b2f7Stbbdev                 addToPutList(head, tail, num);
328*51c0b2f7Stbbdev 
329*51c0b2f7Stbbdev                 while ( opGet ) {
330*51c0b2f7Stbbdev                     CacheBinOperation *next = opGet->next;
331*51c0b2f7Stbbdev                     if ( !getFromPutList(opGet, opCast<OpGet>(*opGet).currTime) )
332*51c0b2f7Stbbdev                         break;
333*51c0b2f7Stbbdev                     opGet = next;
334*51c0b2f7Stbbdev                 }
335*51c0b2f7Stbbdev             }
336*51c0b2f7Stbbdev             break;
337*51c0b2f7Stbbdev 
338*51c0b2f7Stbbdev         case CBOP_UPDATE_USED_SIZE:
339*51c0b2f7Stbbdev             updateUsedSize += opCast<OpUpdateUsedSize>(*op).size;
340*51c0b2f7Stbbdev             commitOperation( op );
341*51c0b2f7Stbbdev             break;
342*51c0b2f7Stbbdev 
343*51c0b2f7Stbbdev         case CBOP_CLEAN_ALL:
344*51c0b2f7Stbbdev             isCleanAll = true;
345*51c0b2f7Stbbdev             addOpToOpList( op, &opClean );
346*51c0b2f7Stbbdev             break;
347*51c0b2f7Stbbdev 
348*51c0b2f7Stbbdev         case CBOP_CLEAN_TO_THRESHOLD:
349*51c0b2f7Stbbdev             {
350*51c0b2f7Stbbdev                 uintptr_t currTime = opCast<OpCleanToThreshold>(*op).currTime;
351*51c0b2f7Stbbdev                 // We don't worry about currTime overflow since it is a rare
352*51c0b2f7Stbbdev                 // occurrence and doesn't affect correctness
353*51c0b2f7Stbbdev                 cleanTime = cleanTime < currTime ? currTime : cleanTime;
354*51c0b2f7Stbbdev                 addOpToOpList( op, &opClean );
355*51c0b2f7Stbbdev             }
356*51c0b2f7Stbbdev             break;
357*51c0b2f7Stbbdev 
358*51c0b2f7Stbbdev         default:
359*51c0b2f7Stbbdev             MALLOC_ASSERT( false, "Unknown operation." );
360*51c0b2f7Stbbdev         }
361*51c0b2f7Stbbdev     }
362*51c0b2f7Stbbdev     MALLOC_ASSERT( !( opGet && head ), "Not all put/get pairs are processed!" );
363*51c0b2f7Stbbdev }
364*51c0b2f7Stbbdev 
365*51c0b2f7Stbbdev template<typename Props> void CacheBinFunctor<Props>::operator()(CacheBinOperation* opList)
366*51c0b2f7Stbbdev {
367*51c0b2f7Stbbdev     MALLOC_ASSERT( opList, "Empty operation list is passed into operation handler." );
368*51c0b2f7Stbbdev 
369*51c0b2f7Stbbdev     OperationPreprocessor prep(bin);
370*51c0b2f7Stbbdev     prep(opList);
371*51c0b2f7Stbbdev 
372*51c0b2f7Stbbdev     if ( uintptr_t timeRange = prep.getTimeRange() ) {
373*51c0b2f7Stbbdev         uintptr_t startTime = extMemPool->loc.getCurrTimeRange(timeRange);
374*51c0b2f7Stbbdev         // endTime is used as the current (base) time since the local time is negative.
375*51c0b2f7Stbbdev         uintptr_t endTime = startTime + timeRange;
376*51c0b2f7Stbbdev 
377*51c0b2f7Stbbdev         if ( prep.lastGetOpTime && prep.lastGet ) bin->setLastGet(prep.lastGet+endTime);
378*51c0b2f7Stbbdev 
379*51c0b2f7Stbbdev         if ( CacheBinOperation *opGet = prep.opGet ) {
380*51c0b2f7Stbbdev             bool isEmpty = false;
381*51c0b2f7Stbbdev             do {
382*51c0b2f7Stbbdev #if __TBB_MALLOC_WHITEBOX_TEST
383*51c0b2f7Stbbdev                 tbbmalloc_whitebox::locGetProcessed++;
384*51c0b2f7Stbbdev #endif
385*51c0b2f7Stbbdev                 const OpGet &opGetData = opCast<OpGet>(*opGet);
386*51c0b2f7Stbbdev                 if ( !isEmpty ) {
387*51c0b2f7Stbbdev                     if ( LargeMemoryBlock *res = bin->get() ) {
388*51c0b2f7Stbbdev                         uintptr_t getTime = opGetData.currTime + endTime;
389*51c0b2f7Stbbdev                         // use moving average with current hit interval
390*51c0b2f7Stbbdev                         bin->updateMeanHitRange( getTime - res->age);
391*51c0b2f7Stbbdev                         bin->updateCachedSize( -opGetData.size );
392*51c0b2f7Stbbdev                         *opGetData.res = res;
393*51c0b2f7Stbbdev                     } else {
394*51c0b2f7Stbbdev                         isEmpty = true;
395*51c0b2f7Stbbdev                         uintptr_t lastGetOpTime = prep.lastGetOpTime+endTime;
396*51c0b2f7Stbbdev                         bin->forgetOutdatedState(lastGetOpTime);
397*51c0b2f7Stbbdev                         bin->updateAgeThreshold(lastGetOpTime);
398*51c0b2f7Stbbdev                     }
399*51c0b2f7Stbbdev                 }
400*51c0b2f7Stbbdev 
401*51c0b2f7Stbbdev                 CacheBinOperation *opNext = opGet->next;
402*51c0b2f7Stbbdev                 bin->updateUsedSize( opGetData.size, bitMask, idx );
403*51c0b2f7Stbbdev                 prep.commitOperation( opGet );
404*51c0b2f7Stbbdev                 opGet = opNext;
405*51c0b2f7Stbbdev             } while ( opGet );
406*51c0b2f7Stbbdev             if ( prep.lastGetOpTime )
407*51c0b2f7Stbbdev                 bin->setLastGet( prep.lastGetOpTime + endTime );
408*51c0b2f7Stbbdev         } else if ( LargeMemoryBlock *curr = prep.head ) {
409*51c0b2f7Stbbdev             curr->prev = NULL;
410*51c0b2f7Stbbdev             while ( curr ) {
411*51c0b2f7Stbbdev                 // Update local times to global times
412*51c0b2f7Stbbdev                 curr->age += endTime;
413*51c0b2f7Stbbdev                 curr=curr->next;
414*51c0b2f7Stbbdev             }
415*51c0b2f7Stbbdev #if __TBB_MALLOC_WHITEBOX_TEST
416*51c0b2f7Stbbdev             tbbmalloc_whitebox::locPutProcessed+=prep.putListNum;
417*51c0b2f7Stbbdev #endif
418*51c0b2f7Stbbdev             toRelease = bin->putList(prep.head, prep.tail, bitMask, idx, prep.putListNum, extMemPool->loc.hugeSizeThreshold);
419*51c0b2f7Stbbdev         }
420*51c0b2f7Stbbdev         needCleanup = extMemPool->loc.isCleanupNeededOnRange(timeRange, startTime);
421*51c0b2f7Stbbdev         currTime = endTime - 1;
422*51c0b2f7Stbbdev     }
423*51c0b2f7Stbbdev 
424*51c0b2f7Stbbdev     if ( CacheBinOperation *opClean = prep.opClean ) {
425*51c0b2f7Stbbdev         if ( prep.isCleanAll )
426*51c0b2f7Stbbdev             *opCast<OpCleanAll>(*opClean).res = bin->cleanAll(bitMask, idx);
427*51c0b2f7Stbbdev         else
428*51c0b2f7Stbbdev             *opCast<OpCleanToThreshold>(*opClean).res = bin->cleanToThreshold(prep.cleanTime, bitMask, idx);
429*51c0b2f7Stbbdev 
430*51c0b2f7Stbbdev         CacheBinOperation *opNext = opClean->next;
431*51c0b2f7Stbbdev         prep.commitOperation( opClean );
432*51c0b2f7Stbbdev 
433*51c0b2f7Stbbdev         while ((opClean = opNext) != NULL) {
434*51c0b2f7Stbbdev             opNext = opClean->next;
435*51c0b2f7Stbbdev             prep.commitOperation(opClean);
436*51c0b2f7Stbbdev         }
437*51c0b2f7Stbbdev     }
438*51c0b2f7Stbbdev 
439*51c0b2f7Stbbdev     if ( size_t size = prep.updateUsedSize )
440*51c0b2f7Stbbdev         bin->updateUsedSize(size, bitMask, idx);
441*51c0b2f7Stbbdev }
442*51c0b2f7Stbbdev /* ----------------------------------------------------------------------------------------------------- */
443*51c0b2f7Stbbdev /* --------------------------- Methods for creating and executing operations --------------------------- */
444*51c0b2f7Stbbdev template<typename Props> void LargeObjectCacheImpl<Props>::
445*51c0b2f7Stbbdev     CacheBin::ExecuteOperation(CacheBinOperation *op, ExtMemoryPool *extMemPool, BinBitMask *bitMask, int idx, bool longLifeTime)
446*51c0b2f7Stbbdev {
447*51c0b2f7Stbbdev     CacheBinFunctor<Props> func( this, extMemPool, bitMask, idx );
448*51c0b2f7Stbbdev     aggregator.execute( op, func, longLifeTime );
449*51c0b2f7Stbbdev 
450*51c0b2f7Stbbdev     if ( LargeMemoryBlock *toRelease = func.getToRelease()) {
451*51c0b2f7Stbbdev         extMemPool->backend.returnLargeObject(toRelease);
452*51c0b2f7Stbbdev     }
453*51c0b2f7Stbbdev 
454*51c0b2f7Stbbdev     if ( func.isCleanupNeeded() ) {
455*51c0b2f7Stbbdev         extMemPool->loc.doCleanup( func.getCurrTime(), /*doThreshDecr=*/false);
456*51c0b2f7Stbbdev     }
457*51c0b2f7Stbbdev }
458*51c0b2f7Stbbdev 
459*51c0b2f7Stbbdev template<typename Props> LargeMemoryBlock *LargeObjectCacheImpl<Props>::
460*51c0b2f7Stbbdev     CacheBin::get(ExtMemoryPool *extMemPool, size_t size, BinBitMask *bitMask, int idx)
461*51c0b2f7Stbbdev {
462*51c0b2f7Stbbdev     LargeMemoryBlock *lmb=NULL;
463*51c0b2f7Stbbdev     OpGet data = {&lmb, size};
464*51c0b2f7Stbbdev     CacheBinOperation op(data);
465*51c0b2f7Stbbdev     ExecuteOperation( &op, extMemPool, bitMask, idx );
466*51c0b2f7Stbbdev     return lmb;
467*51c0b2f7Stbbdev }
468*51c0b2f7Stbbdev 
469*51c0b2f7Stbbdev template<typename Props> void LargeObjectCacheImpl<Props>::
470*51c0b2f7Stbbdev     CacheBin::putList(ExtMemoryPool *extMemPool, LargeMemoryBlock *head, BinBitMask *bitMask, int idx)
471*51c0b2f7Stbbdev {
472*51c0b2f7Stbbdev     MALLOC_ASSERT(sizeof(LargeMemoryBlock)+sizeof(CacheBinOperation)<=head->unalignedSize, "CacheBinOperation is too large to be placed in LargeMemoryBlock!");
473*51c0b2f7Stbbdev 
474*51c0b2f7Stbbdev     OpPutList data = {head};
475*51c0b2f7Stbbdev     CacheBinOperation *op = new (head+1) CacheBinOperation(data, CBST_NOWAIT);
476*51c0b2f7Stbbdev     ExecuteOperation( op, extMemPool, bitMask, idx, false );
477*51c0b2f7Stbbdev }
478*51c0b2f7Stbbdev 
479*51c0b2f7Stbbdev template<typename Props> bool LargeObjectCacheImpl<Props>::
480*51c0b2f7Stbbdev     CacheBin::cleanToThreshold(ExtMemoryPool *extMemPool, BinBitMask *bitMask, uintptr_t currTime, int idx)
481*51c0b2f7Stbbdev {
482*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease = NULL;
483*51c0b2f7Stbbdev 
484*51c0b2f7Stbbdev     /* oldest may be more recent then age, that's why cast to signed type
485*51c0b2f7Stbbdev        was used. age overflow is also processed correctly. */
486*51c0b2f7Stbbdev     if (last && (intptr_t)(currTime - oldest) > ageThreshold) {
487*51c0b2f7Stbbdev         OpCleanToThreshold data = {&toRelease, currTime};
488*51c0b2f7Stbbdev         CacheBinOperation op(data);
489*51c0b2f7Stbbdev         ExecuteOperation( &op, extMemPool, bitMask, idx );
490*51c0b2f7Stbbdev     }
491*51c0b2f7Stbbdev     bool released = toRelease;
492*51c0b2f7Stbbdev 
493*51c0b2f7Stbbdev     Backend *backend = &extMemPool->backend;
494*51c0b2f7Stbbdev     while ( toRelease ) {
495*51c0b2f7Stbbdev         LargeMemoryBlock *helper = toRelease->next;
496*51c0b2f7Stbbdev         backend->returnLargeObject(toRelease);
497*51c0b2f7Stbbdev         toRelease = helper;
498*51c0b2f7Stbbdev     }
499*51c0b2f7Stbbdev     return released;
500*51c0b2f7Stbbdev }
501*51c0b2f7Stbbdev 
502*51c0b2f7Stbbdev template<typename Props> bool LargeObjectCacheImpl<Props>::
503*51c0b2f7Stbbdev     CacheBin::releaseAllToBackend(ExtMemoryPool *extMemPool, BinBitMask *bitMask, int idx)
504*51c0b2f7Stbbdev {
505*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease = NULL;
506*51c0b2f7Stbbdev 
507*51c0b2f7Stbbdev     if (last) {
508*51c0b2f7Stbbdev         OpCleanAll data = {&toRelease};
509*51c0b2f7Stbbdev         CacheBinOperation op(data);
510*51c0b2f7Stbbdev         ExecuteOperation(&op, extMemPool, bitMask, idx);
511*51c0b2f7Stbbdev     }
512*51c0b2f7Stbbdev     bool released = toRelease;
513*51c0b2f7Stbbdev 
514*51c0b2f7Stbbdev     Backend *backend = &extMemPool->backend;
515*51c0b2f7Stbbdev     while ( toRelease ) {
516*51c0b2f7Stbbdev         LargeMemoryBlock *helper = toRelease->next;
517*51c0b2f7Stbbdev         MALLOC_ASSERT(!helper || lessThanWithOverflow(helper->age, toRelease->age),
518*51c0b2f7Stbbdev                       ASSERT_TEXT);
519*51c0b2f7Stbbdev         backend->returnLargeObject(toRelease);
520*51c0b2f7Stbbdev         toRelease = helper;
521*51c0b2f7Stbbdev     }
522*51c0b2f7Stbbdev     return released;
523*51c0b2f7Stbbdev }
524*51c0b2f7Stbbdev 
525*51c0b2f7Stbbdev template<typename Props> void LargeObjectCacheImpl<Props>::
526*51c0b2f7Stbbdev     CacheBin::updateUsedSize(ExtMemoryPool *extMemPool, size_t size, BinBitMask *bitMask, int idx)
527*51c0b2f7Stbbdev {
528*51c0b2f7Stbbdev     OpUpdateUsedSize data = {size};
529*51c0b2f7Stbbdev     CacheBinOperation op(data);
530*51c0b2f7Stbbdev     ExecuteOperation( &op, extMemPool, bitMask, idx );
531*51c0b2f7Stbbdev }
532*51c0b2f7Stbbdev 
533*51c0b2f7Stbbdev /* ------------------------------ Unsafe methods used with the aggregator ------------------------------ */
534*51c0b2f7Stbbdev 
535*51c0b2f7Stbbdev template<typename Props> LargeMemoryBlock *LargeObjectCacheImpl<Props>::
536*51c0b2f7Stbbdev     CacheBin::putList(LargeMemoryBlock *head, LargeMemoryBlock *tail, BinBitMask *bitMask, int idx, int num, size_t hugeSizeThreshold)
537*51c0b2f7Stbbdev {
538*51c0b2f7Stbbdev     size_t size = head->unalignedSize;
539*51c0b2f7Stbbdev     usedSize -= num*size;
540*51c0b2f7Stbbdev     MALLOC_ASSERT( !last || (last->age != 0 && last->age != -1U), ASSERT_TEXT );
541*51c0b2f7Stbbdev     MALLOC_ASSERT( (tail==head && num==1) || (tail!=head && num>1), ASSERT_TEXT );
542*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease = NULL;
543*51c0b2f7Stbbdev     if (size < hugeSizeThreshold && !lastCleanedAge) {
544*51c0b2f7Stbbdev         // 1st object of such size was released.
545*51c0b2f7Stbbdev         // Not cache it, and remember when this occurs
546*51c0b2f7Stbbdev         // to take into account during cache miss.
547*51c0b2f7Stbbdev         lastCleanedAge = tail->age;
548*51c0b2f7Stbbdev         toRelease = tail;
549*51c0b2f7Stbbdev         tail = tail->prev;
550*51c0b2f7Stbbdev         if (tail)
551*51c0b2f7Stbbdev             tail->next = NULL;
552*51c0b2f7Stbbdev         else
553*51c0b2f7Stbbdev             head = NULL;
554*51c0b2f7Stbbdev         num--;
555*51c0b2f7Stbbdev     }
556*51c0b2f7Stbbdev     if (num) {
557*51c0b2f7Stbbdev         // add [head;tail] list to cache
558*51c0b2f7Stbbdev         MALLOC_ASSERT( tail, ASSERT_TEXT );
559*51c0b2f7Stbbdev         tail->next = first;
560*51c0b2f7Stbbdev         if (first)
561*51c0b2f7Stbbdev             first->prev = tail;
562*51c0b2f7Stbbdev         first = head;
563*51c0b2f7Stbbdev         if (!last) {
564*51c0b2f7Stbbdev             MALLOC_ASSERT(0 == oldest, ASSERT_TEXT);
565*51c0b2f7Stbbdev             oldest = tail->age;
566*51c0b2f7Stbbdev             last = tail;
567*51c0b2f7Stbbdev         }
568*51c0b2f7Stbbdev 
569*51c0b2f7Stbbdev         cachedSize += num*size;
570*51c0b2f7Stbbdev     }
571*51c0b2f7Stbbdev 
572*51c0b2f7Stbbdev     // No used object, and nothing in the bin, mark the bin as empty
573*51c0b2f7Stbbdev     if (!usedSize && !first)
574*51c0b2f7Stbbdev         bitMask->set(idx, false);
575*51c0b2f7Stbbdev 
576*51c0b2f7Stbbdev     return toRelease;
577*51c0b2f7Stbbdev }
578*51c0b2f7Stbbdev 
579*51c0b2f7Stbbdev template<typename Props> LargeMemoryBlock *LargeObjectCacheImpl<Props>::
580*51c0b2f7Stbbdev     CacheBin::get()
581*51c0b2f7Stbbdev {
582*51c0b2f7Stbbdev     LargeMemoryBlock *result=first;
583*51c0b2f7Stbbdev     if (result) {
584*51c0b2f7Stbbdev         first = result->next;
585*51c0b2f7Stbbdev         if (first)
586*51c0b2f7Stbbdev             first->prev = NULL;
587*51c0b2f7Stbbdev         else {
588*51c0b2f7Stbbdev             last = NULL;
589*51c0b2f7Stbbdev             oldest = 0;
590*51c0b2f7Stbbdev         }
591*51c0b2f7Stbbdev     }
592*51c0b2f7Stbbdev 
593*51c0b2f7Stbbdev     return result;
594*51c0b2f7Stbbdev }
595*51c0b2f7Stbbdev 
596*51c0b2f7Stbbdev template<typename Props> void LargeObjectCacheImpl<Props>::
597*51c0b2f7Stbbdev     CacheBin::forgetOutdatedState(uintptr_t currTime)
598*51c0b2f7Stbbdev {
599*51c0b2f7Stbbdev     // If the time since the last get is LongWaitFactor times more than ageThreshold
600*51c0b2f7Stbbdev     // for the bin, treat the bin as rarely-used and forget everything we know
601*51c0b2f7Stbbdev     // about it.
602*51c0b2f7Stbbdev     // If LongWaitFactor is too small, we forget too early and
603*51c0b2f7Stbbdev     // so prevents good caching, while if too high, caching blocks
604*51c0b2f7Stbbdev     // with unrelated usage pattern occurs.
605*51c0b2f7Stbbdev     const uintptr_t sinceLastGet = currTime - lastGet;
606*51c0b2f7Stbbdev     bool doCleanup = false;
607*51c0b2f7Stbbdev 
608*51c0b2f7Stbbdev     if (ageThreshold)
609*51c0b2f7Stbbdev         doCleanup = sinceLastGet > Props::LongWaitFactor * ageThreshold;
610*51c0b2f7Stbbdev     else if (lastCleanedAge)
611*51c0b2f7Stbbdev         doCleanup = sinceLastGet > Props::LongWaitFactor * (lastCleanedAge - lastGet);
612*51c0b2f7Stbbdev 
613*51c0b2f7Stbbdev     if (doCleanup) {
614*51c0b2f7Stbbdev         lastCleanedAge = 0;
615*51c0b2f7Stbbdev         ageThreshold = 0;
616*51c0b2f7Stbbdev     }
617*51c0b2f7Stbbdev 
618*51c0b2f7Stbbdev }
619*51c0b2f7Stbbdev 
620*51c0b2f7Stbbdev template<typename Props> LargeMemoryBlock *LargeObjectCacheImpl<Props>::
621*51c0b2f7Stbbdev     CacheBin::cleanToThreshold(uintptr_t currTime, BinBitMask *bitMask, int idx)
622*51c0b2f7Stbbdev {
623*51c0b2f7Stbbdev     /* oldest may be more recent then age, that's why cast to signed type
624*51c0b2f7Stbbdev     was used. age overflow is also processed correctly. */
625*51c0b2f7Stbbdev     if ( !last || (intptr_t)(currTime - last->age) < ageThreshold ) return NULL;
626*51c0b2f7Stbbdev 
627*51c0b2f7Stbbdev #if MALLOC_DEBUG
628*51c0b2f7Stbbdev     uintptr_t nextAge = 0;
629*51c0b2f7Stbbdev #endif
630*51c0b2f7Stbbdev     do {
631*51c0b2f7Stbbdev #if MALLOC_DEBUG
632*51c0b2f7Stbbdev         // check that list ordered
633*51c0b2f7Stbbdev         MALLOC_ASSERT(!nextAge || lessThanWithOverflow(nextAge, last->age),
634*51c0b2f7Stbbdev             ASSERT_TEXT);
635*51c0b2f7Stbbdev         nextAge = last->age;
636*51c0b2f7Stbbdev #endif
637*51c0b2f7Stbbdev         cachedSize -= last->unalignedSize;
638*51c0b2f7Stbbdev         last = last->prev;
639*51c0b2f7Stbbdev     } while (last && (intptr_t)(currTime - last->age) > ageThreshold);
640*51c0b2f7Stbbdev 
641*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease = NULL;
642*51c0b2f7Stbbdev     if (last) {
643*51c0b2f7Stbbdev         toRelease = last->next;
644*51c0b2f7Stbbdev         oldest = last->age;
645*51c0b2f7Stbbdev         last->next = NULL;
646*51c0b2f7Stbbdev     } else {
647*51c0b2f7Stbbdev         toRelease = first;
648*51c0b2f7Stbbdev         first = NULL;
649*51c0b2f7Stbbdev         oldest = 0;
650*51c0b2f7Stbbdev         if (!usedSize)
651*51c0b2f7Stbbdev             bitMask->set(idx, false);
652*51c0b2f7Stbbdev     }
653*51c0b2f7Stbbdev     MALLOC_ASSERT( toRelease, ASSERT_TEXT );
654*51c0b2f7Stbbdev     lastCleanedAge = toRelease->age;
655*51c0b2f7Stbbdev 
656*51c0b2f7Stbbdev     return toRelease;
657*51c0b2f7Stbbdev }
658*51c0b2f7Stbbdev 
659*51c0b2f7Stbbdev template<typename Props> LargeMemoryBlock *LargeObjectCacheImpl<Props>::
660*51c0b2f7Stbbdev     CacheBin::cleanAll(BinBitMask *bitMask, int idx)
661*51c0b2f7Stbbdev {
662*51c0b2f7Stbbdev     if (!last) return NULL;
663*51c0b2f7Stbbdev 
664*51c0b2f7Stbbdev     LargeMemoryBlock *toRelease = first;
665*51c0b2f7Stbbdev     last = NULL;
666*51c0b2f7Stbbdev     first = NULL;
667*51c0b2f7Stbbdev     oldest = 0;
668*51c0b2f7Stbbdev     cachedSize = 0;
669*51c0b2f7Stbbdev     if (!usedSize)
670*51c0b2f7Stbbdev         bitMask->set(idx, false);
671*51c0b2f7Stbbdev 
672*51c0b2f7Stbbdev     return toRelease;
673*51c0b2f7Stbbdev }
674*51c0b2f7Stbbdev 
675*51c0b2f7Stbbdev /* ----------------------------------------------------------------------------------------------------- */
676*51c0b2f7Stbbdev 
677*51c0b2f7Stbbdev template<typename Props> size_t LargeObjectCacheImpl<Props>::
678*51c0b2f7Stbbdev     CacheBin::reportStat(int num, FILE *f)
679*51c0b2f7Stbbdev {
680*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT
681*51c0b2f7Stbbdev     if (first)
682*51c0b2f7Stbbdev         printf("%d(%lu): total %lu KB thr %ld lastCln %lu oldest %lu\n",
683*51c0b2f7Stbbdev                num, num*Props::CacheStep+Props::MinSize,
684*51c0b2f7Stbbdev                cachedSize/1024, ageThreshold, lastCleanedAge, oldest);
685*51c0b2f7Stbbdev #else
686*51c0b2f7Stbbdev     suppress_unused_warning(num);
687*51c0b2f7Stbbdev     suppress_unused_warning(f);
688*51c0b2f7Stbbdev #endif
689*51c0b2f7Stbbdev     return cachedSize;
690*51c0b2f7Stbbdev }
691*51c0b2f7Stbbdev 
692*51c0b2f7Stbbdev // Release objects from cache blocks that are older than ageThreshold
693*51c0b2f7Stbbdev template<typename Props>
694*51c0b2f7Stbbdev bool LargeObjectCacheImpl<Props>::regularCleanup(ExtMemoryPool *extMemPool, uintptr_t currTime, bool doThreshDecr)
695*51c0b2f7Stbbdev {
696*51c0b2f7Stbbdev     bool released = false;
697*51c0b2f7Stbbdev     BinsSummary binsSummary;
698*51c0b2f7Stbbdev 
699*51c0b2f7Stbbdev     // Threshold settings is below this cache or starts from zero index
700*51c0b2f7Stbbdev     if (hugeSizeThresholdIdx == 0) return false;
701*51c0b2f7Stbbdev 
702*51c0b2f7Stbbdev     // Starting searching for bin that is less than huge size threshold (can be cleaned-up)
703*51c0b2f7Stbbdev     int startSearchIdx = hugeSizeThresholdIdx - 1;
704*51c0b2f7Stbbdev 
705*51c0b2f7Stbbdev     for (int i = bitMask.getMaxTrue(startSearchIdx); i >= 0; i = bitMask.getMaxTrue(i-1)) {
706*51c0b2f7Stbbdev         bin[i].updateBinsSummary(&binsSummary);
707*51c0b2f7Stbbdev         if (!doThreshDecr && tooLargeLOC.load(std::memory_order_relaxed) > 2 && binsSummary.isLOCTooLarge()) {
708*51c0b2f7Stbbdev             // if LOC is too large for quite long time, decrease the threshold
709*51c0b2f7Stbbdev             // based on bin hit statistics.
710*51c0b2f7Stbbdev             // For this, redo cleanup from the beginning.
711*51c0b2f7Stbbdev             // Note: on this iteration total usedSz can be not too large
712*51c0b2f7Stbbdev             // in comparison to total cachedSz, as we calculated it only
713*51c0b2f7Stbbdev             // partially. We are ok with it.
714*51c0b2f7Stbbdev             i = bitMask.getMaxTrue(startSearchIdx)+1;
715*51c0b2f7Stbbdev             doThreshDecr = true;
716*51c0b2f7Stbbdev             binsSummary.reset();
717*51c0b2f7Stbbdev             continue;
718*51c0b2f7Stbbdev         }
719*51c0b2f7Stbbdev         if (doThreshDecr)
720*51c0b2f7Stbbdev             bin[i].decreaseThreshold();
721*51c0b2f7Stbbdev 
722*51c0b2f7Stbbdev         if (bin[i].cleanToThreshold(extMemPool, &bitMask, currTime, i)) {
723*51c0b2f7Stbbdev             released = true;
724*51c0b2f7Stbbdev         }
725*51c0b2f7Stbbdev     }
726*51c0b2f7Stbbdev     // We want to find if LOC was too large for some time continuously,
727*51c0b2f7Stbbdev     // so OK with races between incrementing and zeroing, but incrementing
728*51c0b2f7Stbbdev     // must be atomic.
729*51c0b2f7Stbbdev     if (binsSummary.isLOCTooLarge()) {
730*51c0b2f7Stbbdev         tooLargeLOC++;
731*51c0b2f7Stbbdev     } else {
732*51c0b2f7Stbbdev         tooLargeLOC.store(0, std::memory_order_relaxed);
733*51c0b2f7Stbbdev     }
734*51c0b2f7Stbbdev     return released;
735*51c0b2f7Stbbdev }
736*51c0b2f7Stbbdev 
737*51c0b2f7Stbbdev template<typename Props>
738*51c0b2f7Stbbdev bool LargeObjectCacheImpl<Props>::cleanAll(ExtMemoryPool *extMemPool)
739*51c0b2f7Stbbdev {
740*51c0b2f7Stbbdev     bool released = false;
741*51c0b2f7Stbbdev     for (int i = numBins-1; i >= 0; i--) {
742*51c0b2f7Stbbdev         released |= bin[i].releaseAllToBackend(extMemPool, &bitMask, i);
743*51c0b2f7Stbbdev     }
744*51c0b2f7Stbbdev     return released;
745*51c0b2f7Stbbdev }
746*51c0b2f7Stbbdev 
747*51c0b2f7Stbbdev template<typename Props>
748*51c0b2f7Stbbdev void LargeObjectCacheImpl<Props>::reset() {
749*51c0b2f7Stbbdev     tooLargeLOC.store(0, std::memory_order_relaxed);
750*51c0b2f7Stbbdev     for (int i = numBins-1; i >= 0; i--)
751*51c0b2f7Stbbdev         bin[i].init();
752*51c0b2f7Stbbdev     bitMask.reset();
753*51c0b2f7Stbbdev }
754*51c0b2f7Stbbdev 
755*51c0b2f7Stbbdev #if __TBB_MALLOC_WHITEBOX_TEST
756*51c0b2f7Stbbdev template<typename Props>
757*51c0b2f7Stbbdev size_t LargeObjectCacheImpl<Props>::getLOCSize() const
758*51c0b2f7Stbbdev {
759*51c0b2f7Stbbdev     size_t size = 0;
760*51c0b2f7Stbbdev     for (int i = numBins-1; i >= 0; i--)
761*51c0b2f7Stbbdev         size += bin[i].getSize();
762*51c0b2f7Stbbdev     return size;
763*51c0b2f7Stbbdev }
764*51c0b2f7Stbbdev 
765*51c0b2f7Stbbdev size_t LargeObjectCache::getLOCSize() const
766*51c0b2f7Stbbdev {
767*51c0b2f7Stbbdev     return largeCache.getLOCSize() + hugeCache.getLOCSize();
768*51c0b2f7Stbbdev }
769*51c0b2f7Stbbdev 
770*51c0b2f7Stbbdev template<typename Props>
771*51c0b2f7Stbbdev size_t LargeObjectCacheImpl<Props>::getUsedSize() const
772*51c0b2f7Stbbdev {
773*51c0b2f7Stbbdev     size_t size = 0;
774*51c0b2f7Stbbdev     for (int i = numBins-1; i >= 0; i--)
775*51c0b2f7Stbbdev         size += bin[i].getUsedSize();
776*51c0b2f7Stbbdev     return size;
777*51c0b2f7Stbbdev }
778*51c0b2f7Stbbdev 
779*51c0b2f7Stbbdev size_t LargeObjectCache::getUsedSize() const
780*51c0b2f7Stbbdev {
781*51c0b2f7Stbbdev     return largeCache.getUsedSize() + hugeCache.getUsedSize();
782*51c0b2f7Stbbdev }
783*51c0b2f7Stbbdev #endif // __TBB_MALLOC_WHITEBOX_TEST
784*51c0b2f7Stbbdev 
785*51c0b2f7Stbbdev inline bool LargeObjectCache::isCleanupNeededOnRange(uintptr_t range, uintptr_t currTime)
786*51c0b2f7Stbbdev {
787*51c0b2f7Stbbdev     return range >= cacheCleanupFreq
788*51c0b2f7Stbbdev         || currTime+range < currTime-1 // overflow, 0 is power of 2, do cleanup
789*51c0b2f7Stbbdev         // (prev;prev+range] contains n*cacheCleanupFreq
790*51c0b2f7Stbbdev         || alignUp(currTime, cacheCleanupFreq)<currTime+range;
791*51c0b2f7Stbbdev }
792*51c0b2f7Stbbdev 
793*51c0b2f7Stbbdev bool LargeObjectCache::doCleanup(uintptr_t currTime, bool doThreshDecr)
794*51c0b2f7Stbbdev {
795*51c0b2f7Stbbdev     if (!doThreshDecr)
796*51c0b2f7Stbbdev         extMemPool->allLocalCaches.markUnused();
797*51c0b2f7Stbbdev     return largeCache.regularCleanup(extMemPool, currTime, doThreshDecr)
798*51c0b2f7Stbbdev         | hugeCache.regularCleanup(extMemPool, currTime, doThreshDecr);
799*51c0b2f7Stbbdev }
800*51c0b2f7Stbbdev 
801*51c0b2f7Stbbdev bool LargeObjectCache::decreasingCleanup()
802*51c0b2f7Stbbdev {
803*51c0b2f7Stbbdev     return doCleanup(cacheCurrTime.load(std::memory_order_acquire), /*doThreshDecr=*/true);
804*51c0b2f7Stbbdev }
805*51c0b2f7Stbbdev 
806*51c0b2f7Stbbdev bool LargeObjectCache::regularCleanup()
807*51c0b2f7Stbbdev {
808*51c0b2f7Stbbdev     return doCleanup(cacheCurrTime.load(std::memory_order_acquire), /*doThreshDecr=*/false);
809*51c0b2f7Stbbdev }
810*51c0b2f7Stbbdev 
811*51c0b2f7Stbbdev bool LargeObjectCache::cleanAll()
812*51c0b2f7Stbbdev {
813*51c0b2f7Stbbdev     return largeCache.cleanAll(extMemPool) | hugeCache.cleanAll(extMemPool);
814*51c0b2f7Stbbdev }
815*51c0b2f7Stbbdev 
816*51c0b2f7Stbbdev void LargeObjectCache::reset()
817*51c0b2f7Stbbdev {
818*51c0b2f7Stbbdev     largeCache.reset();
819*51c0b2f7Stbbdev     hugeCache.reset();
820*51c0b2f7Stbbdev }
821*51c0b2f7Stbbdev 
822*51c0b2f7Stbbdev template<typename Props>
823*51c0b2f7Stbbdev LargeMemoryBlock *LargeObjectCacheImpl<Props>::get(ExtMemoryPool *extMemoryPool, size_t size)
824*51c0b2f7Stbbdev {
825*51c0b2f7Stbbdev     int idx = Props::sizeToIdx(size);
826*51c0b2f7Stbbdev 
827*51c0b2f7Stbbdev     LargeMemoryBlock *lmb = bin[idx].get(extMemoryPool, size, &bitMask, idx);
828*51c0b2f7Stbbdev 
829*51c0b2f7Stbbdev     if (lmb) {
830*51c0b2f7Stbbdev         MALLOC_ITT_SYNC_ACQUIRED(bin+idx);
831*51c0b2f7Stbbdev         STAT_increment(getThreadId(), ThreadCommonCounters, allocCachedLargeObj);
832*51c0b2f7Stbbdev     }
833*51c0b2f7Stbbdev     return lmb;
834*51c0b2f7Stbbdev }
835*51c0b2f7Stbbdev 
836*51c0b2f7Stbbdev template<typename Props>
837*51c0b2f7Stbbdev void LargeObjectCacheImpl<Props>::updateCacheState(ExtMemoryPool *extMemPool, DecreaseOrIncrease op, size_t size)
838*51c0b2f7Stbbdev {
839*51c0b2f7Stbbdev     int idx = Props::sizeToIdx(size);
840*51c0b2f7Stbbdev     MALLOC_ASSERT(idx<numBins, ASSERT_TEXT);
841*51c0b2f7Stbbdev     bin[idx].updateUsedSize(extMemPool, op==decrease? -size : size, &bitMask, idx);
842*51c0b2f7Stbbdev }
843*51c0b2f7Stbbdev 
844*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT
845*51c0b2f7Stbbdev template<typename Props>
846*51c0b2f7Stbbdev void LargeObjectCacheImpl<Props>::reportStat(FILE *f)
847*51c0b2f7Stbbdev {
848*51c0b2f7Stbbdev     size_t cachedSize = 0;
849*51c0b2f7Stbbdev     for (int i=0; i<numBins; i++)
850*51c0b2f7Stbbdev         cachedSize += bin[i].reportStat(i, f);
851*51c0b2f7Stbbdev     fprintf(f, "total LOC size %lu MB\n", cachedSize/1024/1024);
852*51c0b2f7Stbbdev }
853*51c0b2f7Stbbdev 
854*51c0b2f7Stbbdev void LargeObjectCache::reportStat(FILE *f)
855*51c0b2f7Stbbdev {
856*51c0b2f7Stbbdev     largeCache.reportStat(f);
857*51c0b2f7Stbbdev     hugeCache.reportStat(f);
858*51c0b2f7Stbbdev     fprintf(f, "cache time %lu\n", cacheCurrTime.load(std::memory_order_relaxed));
859*51c0b2f7Stbbdev }
860*51c0b2f7Stbbdev #endif
861*51c0b2f7Stbbdev 
862*51c0b2f7Stbbdev template<typename Props>
863*51c0b2f7Stbbdev void LargeObjectCacheImpl<Props>::putList(ExtMemoryPool *extMemPool, LargeMemoryBlock *toCache)
864*51c0b2f7Stbbdev {
865*51c0b2f7Stbbdev     int toBinIdx = Props::sizeToIdx(toCache->unalignedSize);
866*51c0b2f7Stbbdev 
867*51c0b2f7Stbbdev     MALLOC_ITT_SYNC_RELEASING(bin+toBinIdx);
868*51c0b2f7Stbbdev     bin[toBinIdx].putList(extMemPool, toCache, &bitMask, toBinIdx);
869*51c0b2f7Stbbdev }
870*51c0b2f7Stbbdev 
871*51c0b2f7Stbbdev void LargeObjectCache::updateCacheState(DecreaseOrIncrease op, size_t size)
872*51c0b2f7Stbbdev {
873*51c0b2f7Stbbdev     if (size < maxLargeSize)
874*51c0b2f7Stbbdev         largeCache.updateCacheState(extMemPool, op, size);
875*51c0b2f7Stbbdev     else if (size < maxHugeSize)
876*51c0b2f7Stbbdev         hugeCache.updateCacheState(extMemPool, op, size);
877*51c0b2f7Stbbdev }
878*51c0b2f7Stbbdev 
879*51c0b2f7Stbbdev uintptr_t LargeObjectCache::getCurrTime()
880*51c0b2f7Stbbdev {
881*51c0b2f7Stbbdev     return ++cacheCurrTime;
882*51c0b2f7Stbbdev }
883*51c0b2f7Stbbdev 
884*51c0b2f7Stbbdev uintptr_t LargeObjectCache::getCurrTimeRange(uintptr_t range)
885*51c0b2f7Stbbdev {
886*51c0b2f7Stbbdev     return (cacheCurrTime.fetch_add(range) + 1);
887*51c0b2f7Stbbdev }
888*51c0b2f7Stbbdev 
889*51c0b2f7Stbbdev void LargeObjectCache::registerRealloc(size_t oldSize, size_t newSize)
890*51c0b2f7Stbbdev {
891*51c0b2f7Stbbdev     updateCacheState(decrease, oldSize);
892*51c0b2f7Stbbdev     updateCacheState(increase, alignToBin(newSize));
893*51c0b2f7Stbbdev }
894*51c0b2f7Stbbdev 
895*51c0b2f7Stbbdev size_t LargeObjectCache::alignToBin(size_t size) {
896*51c0b2f7Stbbdev     return size < maxLargeSize ? LargeCacheType::alignToBin(size) : HugeCacheType::alignToBin(size);
897*51c0b2f7Stbbdev }
898*51c0b2f7Stbbdev 
899*51c0b2f7Stbbdev // Used for internal purpose
900*51c0b2f7Stbbdev int LargeObjectCache::sizeToIdx(size_t size)
901*51c0b2f7Stbbdev {
902*51c0b2f7Stbbdev     MALLOC_ASSERT(size <= maxHugeSize, ASSERT_TEXT);
903*51c0b2f7Stbbdev     return size < maxLargeSize ?
904*51c0b2f7Stbbdev         LargeCacheType::sizeToIdx(size) :
905*51c0b2f7Stbbdev         LargeCacheType::numBins + HugeCacheType::sizeToIdx(size);
906*51c0b2f7Stbbdev }
907*51c0b2f7Stbbdev 
908*51c0b2f7Stbbdev void LargeObjectCache::putList(LargeMemoryBlock *list)
909*51c0b2f7Stbbdev {
910*51c0b2f7Stbbdev     LargeMemoryBlock *toProcess, *n;
911*51c0b2f7Stbbdev 
912*51c0b2f7Stbbdev     for (LargeMemoryBlock *curr = list; curr; curr = toProcess) {
913*51c0b2f7Stbbdev         LargeMemoryBlock *tail = curr;
914*51c0b2f7Stbbdev         toProcess = curr->next;
915*51c0b2f7Stbbdev         if (!sizeInCacheRange(curr->unalignedSize)) {
916*51c0b2f7Stbbdev             extMemPool->backend.returnLargeObject(curr);
917*51c0b2f7Stbbdev             continue;
918*51c0b2f7Stbbdev         }
919*51c0b2f7Stbbdev         int currIdx = sizeToIdx(curr->unalignedSize);
920*51c0b2f7Stbbdev 
921*51c0b2f7Stbbdev         // Find all blocks fitting to same bin. Not use more efficient sorting
922*51c0b2f7Stbbdev         // algorithm because list is short (commonly,
923*51c0b2f7Stbbdev         // LocalLOC's HIGH_MARK-LOW_MARK, i.e. 24 items).
924*51c0b2f7Stbbdev         for (LargeMemoryBlock *b = toProcess; b; b = n) {
925*51c0b2f7Stbbdev             n = b->next;
926*51c0b2f7Stbbdev             if (sizeToIdx(b->unalignedSize) == currIdx) {
927*51c0b2f7Stbbdev                 tail->next = b;
928*51c0b2f7Stbbdev                 tail = b;
929*51c0b2f7Stbbdev                 if (toProcess == b)
930*51c0b2f7Stbbdev                     toProcess = toProcess->next;
931*51c0b2f7Stbbdev                 else {
932*51c0b2f7Stbbdev                     b->prev->next = b->next;
933*51c0b2f7Stbbdev                     if (b->next)
934*51c0b2f7Stbbdev                         b->next->prev = b->prev;
935*51c0b2f7Stbbdev                 }
936*51c0b2f7Stbbdev             }
937*51c0b2f7Stbbdev         }
938*51c0b2f7Stbbdev         tail->next = NULL;
939*51c0b2f7Stbbdev         if (curr->unalignedSize < maxLargeSize)
940*51c0b2f7Stbbdev             largeCache.putList(extMemPool, curr);
941*51c0b2f7Stbbdev         else
942*51c0b2f7Stbbdev             hugeCache.putList(extMemPool, curr);
943*51c0b2f7Stbbdev     }
944*51c0b2f7Stbbdev }
945*51c0b2f7Stbbdev 
946*51c0b2f7Stbbdev void LargeObjectCache::put(LargeMemoryBlock *largeBlock)
947*51c0b2f7Stbbdev {
948*51c0b2f7Stbbdev     size_t blockSize = largeBlock->unalignedSize;
949*51c0b2f7Stbbdev     if (sizeInCacheRange(blockSize)) {
950*51c0b2f7Stbbdev         largeBlock->next = NULL;
951*51c0b2f7Stbbdev         if (blockSize < maxLargeSize)
952*51c0b2f7Stbbdev             largeCache.putList(extMemPool, largeBlock);
953*51c0b2f7Stbbdev         else
954*51c0b2f7Stbbdev             hugeCache.putList(extMemPool, largeBlock);
955*51c0b2f7Stbbdev     } else {
956*51c0b2f7Stbbdev         extMemPool->backend.returnLargeObject(largeBlock);
957*51c0b2f7Stbbdev     }
958*51c0b2f7Stbbdev }
959*51c0b2f7Stbbdev 
960*51c0b2f7Stbbdev LargeMemoryBlock *LargeObjectCache::get(size_t size)
961*51c0b2f7Stbbdev {
962*51c0b2f7Stbbdev     MALLOC_ASSERT( size >= minLargeSize, ASSERT_TEXT );
963*51c0b2f7Stbbdev     if (sizeInCacheRange(size)) {
964*51c0b2f7Stbbdev         return size < maxLargeSize ?
965*51c0b2f7Stbbdev             largeCache.get(extMemPool, size) : hugeCache.get(extMemPool, size);
966*51c0b2f7Stbbdev     }
967*51c0b2f7Stbbdev     return NULL;
968*51c0b2f7Stbbdev }
969*51c0b2f7Stbbdev 
970*51c0b2f7Stbbdev LargeMemoryBlock *ExtMemoryPool::mallocLargeObject(MemoryPool *pool, size_t allocationSize)
971*51c0b2f7Stbbdev {
972*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT
973*51c0b2f7Stbbdev     mallocCalls++;
974*51c0b2f7Stbbdev     memAllocKB.fetch_add(allocationSize/1024);
975*51c0b2f7Stbbdev #endif
976*51c0b2f7Stbbdev     LargeMemoryBlock* lmb = loc.get(allocationSize);
977*51c0b2f7Stbbdev     if (!lmb) {
978*51c0b2f7Stbbdev         BackRefIdx backRefIdx = BackRefIdx::newBackRef(/*largeObj=*/true);
979*51c0b2f7Stbbdev         if (backRefIdx.isInvalid())
980*51c0b2f7Stbbdev             return NULL;
981*51c0b2f7Stbbdev 
982*51c0b2f7Stbbdev         // unalignedSize is set in getLargeBlock
983*51c0b2f7Stbbdev         lmb = backend.getLargeBlock(allocationSize);
984*51c0b2f7Stbbdev         if (!lmb) {
985*51c0b2f7Stbbdev             removeBackRef(backRefIdx);
986*51c0b2f7Stbbdev             loc.updateCacheState(decrease, allocationSize);
987*51c0b2f7Stbbdev             return NULL;
988*51c0b2f7Stbbdev         }
989*51c0b2f7Stbbdev         lmb->backRefIdx = backRefIdx;
990*51c0b2f7Stbbdev         lmb->pool = pool;
991*51c0b2f7Stbbdev         STAT_increment(getThreadId(), ThreadCommonCounters, allocNewLargeObj);
992*51c0b2f7Stbbdev     } else {
993*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT
994*51c0b2f7Stbbdev         cacheHits++;
995*51c0b2f7Stbbdev         memHitKB.fetch_add(allocationSize/1024);
996*51c0b2f7Stbbdev #endif
997*51c0b2f7Stbbdev     }
998*51c0b2f7Stbbdev     return lmb;
999*51c0b2f7Stbbdev }
1000*51c0b2f7Stbbdev 
1001*51c0b2f7Stbbdev void ExtMemoryPool::freeLargeObject(LargeMemoryBlock *mBlock)
1002*51c0b2f7Stbbdev {
1003*51c0b2f7Stbbdev     loc.put(mBlock);
1004*51c0b2f7Stbbdev }
1005*51c0b2f7Stbbdev 
1006*51c0b2f7Stbbdev void ExtMemoryPool::freeLargeObjectList(LargeMemoryBlock *head)
1007*51c0b2f7Stbbdev {
1008*51c0b2f7Stbbdev     loc.putList(head);
1009*51c0b2f7Stbbdev }
1010*51c0b2f7Stbbdev 
1011*51c0b2f7Stbbdev bool ExtMemoryPool::softCachesCleanup()
1012*51c0b2f7Stbbdev {
1013*51c0b2f7Stbbdev     return loc.regularCleanup();
1014*51c0b2f7Stbbdev }
1015*51c0b2f7Stbbdev 
1016*51c0b2f7Stbbdev bool ExtMemoryPool::hardCachesCleanup()
1017*51c0b2f7Stbbdev {
1018*51c0b2f7Stbbdev     // thread-local caches must be cleaned before LOC,
1019*51c0b2f7Stbbdev     // because object from thread-local cache can be released to LOC
1020*51c0b2f7Stbbdev     bool ret = releaseAllLocalCaches();
1021*51c0b2f7Stbbdev     ret |= orphanedBlocks.cleanup(&backend);
1022*51c0b2f7Stbbdev     ret |= loc.cleanAll();
1023*51c0b2f7Stbbdev     ret |= backend.clean();
1024*51c0b2f7Stbbdev     return ret;
1025*51c0b2f7Stbbdev }
1026*51c0b2f7Stbbdev 
1027*51c0b2f7Stbbdev #if BACKEND_HAS_MREMAP
1028*51c0b2f7Stbbdev void *ExtMemoryPool::remap(void *ptr, size_t oldSize, size_t newSize, size_t alignment)
1029*51c0b2f7Stbbdev {
1030*51c0b2f7Stbbdev     const size_t oldUnalignedSize = ((LargeObjectHdr*)ptr - 1)->memoryBlock->unalignedSize;
1031*51c0b2f7Stbbdev     void *o = backend.remap(ptr, oldSize, newSize, alignment);
1032*51c0b2f7Stbbdev     if (o) {
1033*51c0b2f7Stbbdev         LargeMemoryBlock *lmb = ((LargeObjectHdr*)o - 1)->memoryBlock;
1034*51c0b2f7Stbbdev         loc.registerRealloc(oldUnalignedSize, lmb->unalignedSize);
1035*51c0b2f7Stbbdev     }
1036*51c0b2f7Stbbdev     return o;
1037*51c0b2f7Stbbdev }
1038*51c0b2f7Stbbdev #endif /* BACKEND_HAS_MREMAP */
1039*51c0b2f7Stbbdev 
1040*51c0b2f7Stbbdev /*********** End allocation of large objects **********/
1041*51c0b2f7Stbbdev 
1042*51c0b2f7Stbbdev } // namespace internal
1043*51c0b2f7Stbbdev } // namespace rml
1044*51c0b2f7Stbbdev 
1045*51c0b2f7Stbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
1046*51c0b2f7Stbbdev     #pragma warning(pop)
1047*51c0b2f7Stbbdev #endif
1048*51c0b2f7Stbbdev 
1049