1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "common_components/heap/allocator/region_manager.h"
17 #include "common_components/heap/collector/marking_collector.h"
18 #include "common_components/heap/heap.cpp"
19 #include "common_components/common_runtime/base_runtime_param.h"
20 #include "common_components/heap/heap_manager.h"
21 #include "common_components/tests/test_helper.h"
22 #include <cstdint>
23
24 using namespace common;
25
26 namespace common::test {
27 const size_t SIZE_SIXTEEN = 16;
28 const size_t SIZE_MAX_TEST = 1024;
29 const size_t SIZE_HALF_MAX_TEST = 512;
30 const uint32_t NUM_TEN = 10;
31 const uint32_t NUM_TWO = 2;
32 const uint32_t NUM_THREE = 3;
33 const uint32_t NUM_FIVE = 5;
34
35 class RegionManagerTest : public common::test::BaseTestWithScope {
36 protected:
37 void* regionMemory_;
38 size_t totalUnits_ = SIZE_MAX_TEST;
39 size_t heapSize_;
40 Mutator* mutator_ = nullptr;
41
SetUpTestCase()42 static void SetUpTestCase()
43 {
44 BaseRuntime::GetInstance()->Init();
45 }
46
TearDownTestCase()47 static void TearDownTestCase()
48 {
49 BaseRuntime::GetInstance()->Fini();
50 }
51
SetUp()52 void SetUp() override
53 {
54 heapSize_ = totalUnits_ * RegionDesc::UNIT_SIZE;
55 size_t allocSize = heapSize_ + totalUnits_ * sizeof(RegionDesc);
56 regionMemory_ = malloc(allocSize);
57 ASSERT_NE(regionMemory_, nullptr);
58 uintptr_t unitInfoStart = reinterpret_cast<uintptr_t>(regionMemory_);
59 size_t metadataSize = totalUnits_ * sizeof(RegionDesc);
60 uintptr_t heapStartAddress = unitInfoStart + metadataSize;
61 RegionDesc::Initialize(totalUnits_, unitInfoStart, heapStartAddress);
62 mutator_ = Mutator::NewMutator();
63 ASSERT_NE(mutator_, nullptr);
64 mutator_->InitTid();
65 ThreadLocal::GetThreadLocalData()->mutator = mutator_;
66 }
67
TearDown()68 void TearDown() override
69 {
70 if (mutator_) {
71 delete mutator_;
72 mutator_ = nullptr;
73 }
74 if (regionMemory_) {
75 free(regionMemory_);
76 regionMemory_ = nullptr;
77 }
78 }
79 };
80
HWTEST_F_L0(RegionManagerTest,VisitLiveObjectsUntilFalse_LiveByteCountZero)81 HWTEST_F_L0(RegionManagerTest, VisitLiveObjectsUntilFalse_LiveByteCountZero)
82 {
83 size_t unitIdx = 0;
84 size_t nUnit = 1;
85 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::SMALL_SIZED_UNITS);
86 region->AddLiveByteCount(0);
87
88 bool callbackCalled = false;
89 bool result = region->VisitLiveObjectsUntilFalse([&callbackCalled](BaseObject* obj) {
90 callbackCalled = true;
91 return true;
92 });
93 EXPECT_TRUE(result);
94 EXPECT_FALSE(callbackCalled);
95 }
96
HWTEST_F_L0(RegionManagerTest,VisitLiveObjectsUntilFalse_IsSmallRegion)97 HWTEST_F_L0(RegionManagerTest, VisitLiveObjectsUntilFalse_IsSmallRegion)
98 {
99 size_t unitIdx = 0;
100 size_t nUnit = 1;
101 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::SMALL_SIZED_UNITS);
102 ASSERT_NE(region, nullptr);
103 region->AddLiveByteCount(SIZE_SIXTEEN);
104
105 bool callbackCalled = false;
106 bool result = region->VisitLiveObjectsUntilFalse(
107 [&callbackCalled](BaseObject* obj) {
108 callbackCalled = true;
109 return true;
110 });
111 EXPECT_FALSE(callbackCalled);
112 EXPECT_TRUE(result);
113 }
114
HWTEST_F_L0(RegionManagerTest,VisitLiveObjectsUntilFalse_IsLargeRegion)115 HWTEST_F_L0(RegionManagerTest, VisitLiveObjectsUntilFalse_IsLargeRegion)
116 {
117 size_t unitIdx = 0;
118 size_t nUnit = 4;
119 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
120 ASSERT_NE(region, nullptr);
121 region->AddLiveByteCount(SIZE_SIXTEEN);
122 bool callbackCalled = false;
123
124 bool result = region->VisitLiveObjectsUntilFalse(
125 [&callbackCalled](BaseObject* obj) {
126 callbackCalled = true;
127 return true;
128 });
129 EXPECT_TRUE(callbackCalled);
130 EXPECT_TRUE(result);
131 }
132
HWTEST_F_L0(RegionManagerTest,VisitLiveObjectsUntilFalse)133 HWTEST_F_L0(RegionManagerTest, VisitLiveObjectsUntilFalse)
134 {
135 size_t unitIdx = 0;
136 size_t nUnit = 1;
137 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::SMALL_SIZED_UNITS);
138 ASSERT_NE(region, nullptr);
139 region->AddLiveByteCount(SIZE_SIXTEEN);
140 bool callbackCalled = false;
141
142 bool result = region->VisitLiveObjectsUntilFalse(
143 [&callbackCalled](BaseObject* obj) {
144 callbackCalled = true;
145 return true;
146 });
147 EXPECT_FALSE(callbackCalled);
148 EXPECT_TRUE(result);
149 }
HWTEST_F_L0(RegionManagerTest,VisitAllObjectsBeforeFix1)150 HWTEST_F_L0(RegionManagerTest, VisitAllObjectsBeforeFix1)
151 {
152 size_t unitIdx = 0;
153 size_t nUnit = 4;
154 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
155 ASSERT_NE(region, nullptr);
156
157 uintptr_t start = region->GetRegionStart();
158 region->SetRegionAllocPtr(start + SIZE_SIXTEEN);
159 bool callbackCalled = false;
160 region->VisitAllObjectsBeforeCopy([&](BaseObject* obj) {
161 callbackCalled = true;
162 EXPECT_EQ(obj, reinterpret_cast<BaseObject*>(region->GetRegionStart()));
163 });
164 EXPECT_TRUE(callbackCalled);
165 }
166
HWTEST_F_L0(RegionManagerTest,VisitAllObjectsBeforeFix2)167 HWTEST_F_L0(RegionManagerTest, VisitAllObjectsBeforeFix2)
168 {
169 size_t unitIdx = 0;
170 size_t nUnit = 1;
171 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::SMALL_SIZED_UNITS);
172 RegionDesc::InitFreeRegion(unitIdx, nUnit);
173 ASSERT_NE(region, nullptr);
174
175 uintptr_t start = region->GetRegionStart();
176 region->SetRegionAllocPtr(start + SIZE_SIXTEEN);
177 bool callbackCalled = false;
178 region->VisitAllObjectsBeforeCopy([&](BaseObject* obj) {
179 callbackCalled = true;
180 EXPECT_EQ(obj, reinterpret_cast<BaseObject*>(region->GetRegionStart()));
181 });
182 EXPECT_FALSE(callbackCalled);
183 }
184
HWTEST_F_L0(RegionManagerTest,VisitAllObjectsBeforeFix3)185 HWTEST_F_L0(RegionManagerTest, VisitAllObjectsBeforeFix3)
186 {
187 size_t unitIdx = 0;
188 size_t nUnit = 4;
189
190 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
191 ASSERT_NE(region, nullptr);
192
193 bool callbackCalled = false;
194 region->VisitAllObjectsBeforeCopy([&](BaseObject* obj) {
195 callbackCalled = true;
196 EXPECT_EQ(obj, reinterpret_cast<BaseObject*>(region->GetRegionStart()));
197 });
198 EXPECT_FALSE(callbackCalled);
199 }
200
HWTEST_F_L0(RegionManagerTest,VisitAllObjectsBeforeFix4)201 HWTEST_F_L0(RegionManagerTest, VisitAllObjectsBeforeFix4)
202 {
203 size_t unitIdx = 0;
204 size_t nUnit = 1;
205
206 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::SMALL_SIZED_UNITS);
207 ASSERT_NE(region, nullptr);
208
209 bool callbackCalled = false;
210 region->VisitAllObjectsBeforeCopy([&](BaseObject* obj) {
211 callbackCalled = true;
212 EXPECT_EQ(obj, reinterpret_cast<BaseObject*>(region->GetRegionStart()));
213 });
214 EXPECT_FALSE(callbackCalled);
215 }
216
HWTEST_F_L0(RegionManagerTest,PrependRegionLockedTest)217 HWTEST_F_L0(RegionManagerTest, PrependRegionLockedTest)
218 {
219 RegionList list("list");
220 list.PrependRegionLocked(nullptr, RegionDesc::RegionType::FREE_REGION);
221 EXPECT_EQ(list.GetHeadRegion(), nullptr);
222 }
223
HWTEST_F_L0(RegionManagerTest,ReleaseGarbageRegions)224 HWTEST_F_L0(RegionManagerTest, ReleaseGarbageRegions)
225 {
226 size_t targetCachedSize = 0;
227 RegionManager manager;
228 FreeRegionManager freeRegionManager(manager);
229 freeRegionManager.Initialize(NUM_TEN);
230 freeRegionManager.AddGarbageUnits(0, 1);
231 freeRegionManager.AddGarbageUnits(NUM_TWO, NUM_TWO);
232 freeRegionManager.AddGarbageUnits(NUM_FIVE, NUM_THREE);
233 size_t released = freeRegionManager.ReleaseGarbageRegions(targetCachedSize);
234 EXPECT_GT(released, 0);
235 }
236
HWTEST_F_L0(RegionManagerTest,ReclaimRegion1)237 HWTEST_F_L0(RegionManagerTest, ReclaimRegion1)
238 {
239 size_t huge_page = (2048 * KB) / getpagesize();
240 size_t nUnit = 1;
241 size_t unitIdx = 0;
242 RegionManager manager;
243 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
244 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit,
245 RegionDesc::UnitRole::SMALL_SIZED_UNITS);
246 ASSERT_NE(region, nullptr);
247 EXPECT_FALSE(region->IsLargeRegion());
248 manager.ReclaimRegion(region);
249 EXPECT_GT(manager.GetDirtyUnitCount(), 0);
250 }
251
HWTEST_F_L0(RegionManagerTest,ReclaimRegion2)252 HWTEST_F_L0(RegionManagerTest, ReclaimRegion2)
253 {
254 size_t huge_page = (2048 * KB) / getpagesize();
255 size_t nUnit = huge_page;
256 size_t unitIdx = 0;
257 RegionManager manager;
258 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
259 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit,
260 RegionDesc::UnitRole::LARGE_SIZED_UNITS);
261 ASSERT_NE(region, nullptr);
262 manager.ReclaimRegion(region);
263 EXPECT_GT(manager.GetDirtyUnitCount(), 0);
264 }
265
HWTEST_F_L0(RegionManagerTest,ReleaseRegion)266 HWTEST_F_L0(RegionManagerTest, ReleaseRegion)
267 {
268 size_t huge_page = (2048 * KB) / getpagesize();
269 size_t nUnit = 1;
270 size_t unitIdx = 0;
271 RegionManager manager;
272 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
273 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit,
274 RegionDesc::UnitRole::LARGE_SIZED_UNITS);
275 ASSERT_NE(region, nullptr);
276 auto ret = manager.ReleaseRegion(region);
277 EXPECT_EQ(ret, region->GetRegionSize());
278 }
279
HWTEST_F_L0(RegionManagerTest,TakeRegion1)280 HWTEST_F_L0(RegionManagerTest, TakeRegion1)
281 {
282 ASSERT_NE(mutator_, nullptr);
283 mutator_->SetMutatorPhase(GCPhase::GC_PHASE_ENUM);
284 RegionManager manager;
285 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
286 size_t nUnit = 4;
287 RegionDesc* garbageRegion = RegionDesc::InitRegion(SIZE_HALF_MAX_TEST, nUnit,
288 RegionDesc::UnitRole::LARGE_SIZED_UNITS);
289 auto size = manager.CollectRegion(garbageRegion);
290 RegionDesc* region = manager.TakeRegion(1, RegionDesc::UnitRole::SMALL_SIZED_UNITS, false, false);
291 EXPECT_GT(manager.GetDirtyUnitCount(), 0);
292 }
293
HWTEST_F_L0(RegionManagerTest,TakeRegion2)294 HWTEST_F_L0(RegionManagerTest, TakeRegion2)
295 {
296 ASSERT_NE(mutator_, nullptr);
297 mutator_->SetMutatorPhase(GCPhase::GC_PHASE_ENUM);
298 RegionManager manager;
299 size_t nUnit = 1;
300 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
301 RegionDesc* garbageRegion = RegionDesc::InitRegion(SIZE_HALF_MAX_TEST, nUnit,
302 RegionDesc::UnitRole::LARGE_SIZED_UNITS);
303 auto size = manager.CollectRegion(garbageRegion);
304 RegionDesc* region = manager.TakeRegion(16, RegionDesc::UnitRole::LARGE_SIZED_UNITS, true, false);
305 EXPECT_NE(region, nullptr);
306 }
307
HWTEST_F_L0(RegionManagerTest,AllocPinnedFromFreeList)308 HWTEST_F_L0(RegionManagerTest, AllocPinnedFromFreeList)
309 {
310 ASSERT_NE(mutator_, nullptr);
311 mutator_->SetMutatorPhase(GCPhase::GC_PHASE_FIX);
312 RegionManager manager;
313 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
314 EXPECT_EQ(manager.AllocPinnedFromFreeList(0), 0);
315 }
316
HWTEST_F_L0(RegionManagerTest,AllocReadOnly1)317 HWTEST_F_L0(RegionManagerTest, AllocReadOnly1)
318 {
319 auto* mutator = common::Mutator::GetMutator();
320 mutator->SetMutatorPhase(GCPhase::GC_PHASE_ENUM);
321 RegionManager manager;
322 manager.ClearAllGCInfo();
323 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
324 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
325 EXPECT_EQ(ret, 0);
326 }
327
HWTEST_F_L0(RegionManagerTest,AllocReadOnly2)328 HWTEST_F_L0(RegionManagerTest, AllocReadOnly2)
329 {
330 auto* mutator = common::Mutator::GetMutator();
331 mutator->SetMutatorPhase(GCPhase::GC_PHASE_MARK);
332 RegionManager manager;
333 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
334 manager.ClearAllGCInfo();
335 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
336 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
337 EXPECT_NE(ret, 0);
338 }
339
HWTEST_F_L0(RegionManagerTest,AllocReadOnly3)340 HWTEST_F_L0(RegionManagerTest, AllocReadOnly3)
341 {
342 auto* mutator = common::Mutator::GetMutator();
343 mutator->SetMutatorPhase(GCPhase::GC_PHASE_POST_MARK);
344 RegionManager manager;
345 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
346 manager.ClearAllGCInfo();
347 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
348 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
349 EXPECT_NE(ret, 0);
350 }
351
HWTEST_F_L0(RegionManagerTest,AllocReadOnly4)352 HWTEST_F_L0(RegionManagerTest, AllocReadOnly4)
353 {
354 auto* mutator = common::Mutator::GetMutator();
355 mutator->SetMutatorPhase(GCPhase::GC_PHASE_PRECOPY);
356 RegionManager manager;
357 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
358 manager.ClearAllGCInfo();
359 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
360 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
361 EXPECT_NE(ret, 0);
362 }
363
HWTEST_F_L0(RegionManagerTest,AllocReadOnly5)364 HWTEST_F_L0(RegionManagerTest, AllocReadOnly5)
365 {
366 auto* mutator = common::Mutator::GetMutator();
367 mutator->SetMutatorPhase(GCPhase::GC_PHASE_COPY);
368 RegionManager manager;
369 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
370 manager.ClearAllGCInfo();
371 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
372 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
373 EXPECT_NE(ret, 0);
374 }
375
HWTEST_F_L0(RegionManagerTest,AllocReadOnly6)376 HWTEST_F_L0(RegionManagerTest, AllocReadOnly6)
377 {
378 auto* mutator = common::Mutator::GetMutator();
379 mutator->SetMutatorPhase(GCPhase::GC_PHASE_FIX);
380 RegionManager manager;
381 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
382 manager.ClearAllGCInfo();
383 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
384 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
385 EXPECT_NE(ret, 0);
386 }
387
HWTEST_F_L0(RegionManagerTest,AllocReadOnly7)388 HWTEST_F_L0(RegionManagerTest, AllocReadOnly7)
389 {
390 auto* mutator = common::Mutator::GetMutator();
391 mutator->SetMutatorPhase(GCPhase::GC_PHASE_UNDEF);
392 RegionManager manager;
393 manager.Initialize(SIZE_MAX_TEST, reinterpret_cast<uintptr_t>(regionMemory_));
394 manager.ClearAllGCInfo();
395 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
396 uintptr_t ret = manager.AllocReadOnly(sizeof(RegionDesc), false);
397 EXPECT_NE(ret, 0);
398 }
399
HWTEST_F_L0(RegionManagerTest,VisitRememberSetTest)400 HWTEST_F_L0(RegionManagerTest, VisitRememberSetTest)
401 {
402 size_t totalUnits = 1024;
403 size_t heapSize = totalUnits * RegionDesc::UNIT_SIZE;
404
405 void* regionMemory = malloc(heapSize + 4096);
406 ASSERT_NE(regionMemory, nullptr);
407
408 uintptr_t heapStartAddress = reinterpret_cast<uintptr_t>(regionMemory);
409 uintptr_t regionInfoAddr = heapStartAddress + 4096;
410
411 RegionManager manager;
412 manager.Initialize(totalUnits, regionInfoAddr);
413
414 size_t unitIdx = 0;
415 size_t nUnit = 4;
416 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
417 ASSERT_NE(region, nullptr);
418
419 manager.MarkRememberSet([&](BaseObject* obj) {});
420
421 int callbackCount = 0;
422 region->VisitRememberSet([&](BaseObject* obj) {
423 callbackCount++;
424 });
425
426 EXPECT_GE(callbackCount, 0);
427 free(regionMemory);
428 }
429
HWTEST_F_L0(RegionManagerTest,VisitRememberSetBeforeCopyTest)430 HWTEST_F_L0(RegionManagerTest, VisitRememberSetBeforeCopyTest)
431 {
432 size_t totalUnits = 1024;
433 size_t heapSize = totalUnits * RegionDesc::UNIT_SIZE;
434
435 void* regionMemory = malloc(heapSize + 4096);
436 ASSERT_NE(regionMemory, nullptr);
437
438 uintptr_t heapStartAddress = reinterpret_cast<uintptr_t>(regionMemory);
439 uintptr_t regionInfoAddr = heapStartAddress + 4096;
440
441 RegionManager manager;
442 manager.Initialize(totalUnits, regionInfoAddr);
443
444 size_t unitIdx = 0;
445 size_t nUnit = 4;
446 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
447 ASSERT_NE(region, nullptr);
448
449 manager.MarkRememberSet([&](BaseObject* obj) {});
450
451 int callbackCount = 0;
452 region->VisitRememberSetBeforeCopy([&](BaseObject* obj) {
453 callbackCount++;
454 });
455
456 EXPECT_GE(callbackCount, 0);
457 free(regionMemory);
458 }
459
HWTEST_F_L0(RegionManagerTest,VisitRememberSetBeforeMarkingTest)460 HWTEST_F_L0(RegionManagerTest, VisitRememberSetBeforeMarkingTest)
461 {
462 size_t totalUnits = 1024;
463 size_t heapSize = totalUnits * RegionDesc::UNIT_SIZE;
464
465 void* regionMemory = malloc(heapSize + 4096);
466 ASSERT_NE(regionMemory, nullptr);
467
468 uintptr_t heapStartAddress = reinterpret_cast<uintptr_t>(regionMemory);
469 uintptr_t regionInfoAddr = heapStartAddress + 4096;
470
471 RegionManager manager;
472 manager.Initialize(totalUnits, regionInfoAddr);
473
474 size_t unitIdx = 0;
475 size_t nUnit = 4;
476 RegionDesc* region = RegionDesc::InitRegion(unitIdx, nUnit, RegionDesc::UnitRole::LARGE_SIZED_UNITS);
477 ASSERT_NE(region, nullptr);
478
479 manager.MarkRememberSet([&](BaseObject* obj) {});
480
481 int callbackCount = 0;
482 region->VisitRememberSetBeforeMarking([&](BaseObject* obj) {
483 callbackCount++;
484 });
485
486 EXPECT_GE(callbackCount, 0);
487 free(regionMemory);
488 }
489 }
490