Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / system / tests / TestSystemObject.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2016-2017 Nest Labs, Inc.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18
19 /**
20  *    @file
21  *      This is a unit test suite for <tt>chip::System::Object</tt>, *
22  *      the part of the CHIP System Layer that implements objects and
23  *      their static allocation pools.
24  *
25  */
26
27 #ifndef __STDC_LIMIT_MACROS
28 #define __STDC_LIMIT_MACROS
29 #endif
30
31 // Install a sleep in the high water mark function, to force
32 // collisions between the threads that call it.
33
34 // clang-format off
35 #define SYSTEM_OBJECT_HWM_TEST_HOOK() do { usleep(1000); } while(0)
36 // clang-format on
37
38 #include <system/SystemLayer.h>
39
40 #include <support/CodeUtils.h>
41 #include <support/ErrorStr.h>
42 #include <support/UnitTestRegistration.h>
43
44 #include <nlunit-test.h>
45
46 #if CHIP_SYSTEM_CONFIG_USE_LWIP
47 #include <lwip/init.h>
48 #include <lwip/tcpip.h>
49 #include <lwip/sys.h>
50 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
51
52 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
53 #include <pthread.h>
54 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
55
56 #include <stdint.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <errno.h>
60
61 // Test context
62 using namespace chip::System;
63
64 namespace chip {
65 namespace System {
66
67 static int Initialize(void * aContext);
68 static int Finalize(void * aContext);
69
70 class TestObject : public Object
71 {
72 public:
73     Error Init();
74
75     static void CheckRetention(nlTestSuite * inSuite, void * aContext);
76     static void CheckConcurrency(nlTestSuite * inSuite, void * aContext);
77     static void CheckHighWatermark(nlTestSuite * inSuite, void * aContext);
78     static void CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext);
79
80 private:
81     enum
82     {
83         kPoolSize = 122 // a multiple of kNumThreads, less than CHIP_SYS_STATS_COUNT_MAX
84     };
85     static ObjectPool<TestObject, kPoolSize> sPool;
86
87 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
88     unsigned int mDelay;
89
90     static constexpr int kNumThreads         = 16;
91     static constexpr int kLoopIterations     = 100000;
92     static constexpr int kMaxDelayIterations = 3;
93
94     void Delay(volatile unsigned int & aAccumulator);
95     static void * CheckConcurrencyThread(void * aContext);
96     static void * CheckHighWatermarkThread(void * aContext);
97     static void MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) );
98 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
99
100     // Not defined
101     TestObject(const TestObject &) = delete;
102     TestObject & operator=(const TestObject &) = delete;
103 };
104
105 ObjectPool<TestObject, TestObject::kPoolSize> TestObject::sPool;
106
107 Error TestObject::Init()
108 {
109 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
110     this->mDelay = kMaxDelayIterations > 0 ? 1 : 0;
111     if (kMaxDelayIterations > 1)
112     {
113         this->mDelay += static_cast<unsigned int>(rand() % kMaxDelayIterations);
114     }
115 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
116
117     return CHIP_SYSTEM_NO_ERROR;
118 }
119
120 namespace {
121 struct TestContext
122 {
123     nlTestSuite * mTestSuite;
124     void * mLayerContext;
125     volatile unsigned int mAccumulator;
126 };
127
128 struct TestContext sContext;
129 } // namespace
130
131 // Test Object retention
132
133 void TestObject::CheckRetention(nlTestSuite * inSuite, void * aContext)
134 {
135     TestContext & lContext = *static_cast<TestContext *>(aContext);
136     Layer lLayer;
137     unsigned int i, j;
138
139     lLayer.Init(lContext.mLayerContext);
140     memset(&sPool, 0, sizeof(sPool));
141
142     for (i = 0; i < kPoolSize; ++i)
143     {
144         TestObject * lCreated = sPool.TryCreate(lLayer);
145
146         NL_TEST_ASSERT(lContext.mTestSuite, lCreated != nullptr);
147         if (lCreated == nullptr)
148             continue;
149         NL_TEST_ASSERT(lContext.mTestSuite, lCreated->IsRetained(lLayer));
150         NL_TEST_ASSERT(lContext.mTestSuite, &(lCreated->SystemLayer()) == &lLayer);
151
152         lCreated->Init();
153
154         for (j = 0; j < kPoolSize; ++j)
155         {
156             TestObject * lGotten = sPool.Get(lLayer, j);
157
158             if (j > i)
159             {
160                 NL_TEST_ASSERT(lContext.mTestSuite, lGotten == nullptr);
161             }
162             else
163             {
164                 NL_TEST_ASSERT(lContext.mTestSuite, lGotten != nullptr);
165                 lGotten->Retain();
166             }
167         }
168     }
169
170     for (i = 0; i < kPoolSize; ++i)
171     {
172         TestObject * lGotten = sPool.Get(lLayer, i);
173
174         NL_TEST_ASSERT(lContext.mTestSuite, lGotten != nullptr);
175
176         for (j = kPoolSize; j > i; --j)
177         {
178             NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer));
179             lGotten->Release();
180         }
181
182         NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer));
183         lGotten->Release();
184         NL_TEST_ASSERT(lContext.mTestSuite, !lGotten->IsRetained(lLayer));
185     }
186
187     for (i = 0; i < kPoolSize; ++i)
188     {
189         TestObject * lGotten = sPool.Get(lLayer, i);
190
191         NL_TEST_ASSERT(lContext.mTestSuite, lGotten == nullptr);
192     }
193
194     lLayer.Shutdown();
195 }
196
197 // Test Object concurrency
198
199 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
200 void TestObject::Delay(volatile unsigned int & aAccumulator)
201 {
202     unsigned int lSum = 0;
203
204     if (kMaxDelayIterations > 0)
205     {
206         for (unsigned int z = 0; z < this->mDelay; ++z)
207         {
208             lSum += static_cast<unsigned int>(rand());
209         }
210
211         lSum = lSum / this->mDelay;
212     }
213
214     aAccumulator = lSum;
215 }
216
217 void * TestObject::CheckConcurrencyThread(void * aContext)
218 {
219     const unsigned int kNumObjects = kPoolSize / kNumThreads;
220     TestObject * lObject           = nullptr;
221     TestContext & lContext         = *static_cast<TestContext *>(aContext);
222     Layer lLayer;
223     unsigned int i;
224
225     lLayer.Init(lContext.mLayerContext);
226
227     // Take this thread's share of objects
228
229     for (i = 0; i < kNumObjects; ++i)
230     {
231         while (lObject == nullptr)
232         {
233             lObject = sPool.TryCreate(lLayer);
234         }
235
236         NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
237         NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
238
239         lObject->Init();
240         lObject->Delay(lContext.mAccumulator);
241     }
242
243     // Free the last object of the pool, if it belongs to
244     // this thread.
245
246     lObject = sPool.Get(lLayer, kPoolSize - 1);
247
248     if (lObject != nullptr)
249     {
250         lObject->Release();
251         NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
252     }
253
254     // For each iteration, take one more object, and free one starting from the end
255     // of the pool
256
257     for (i = 0; i < kLoopIterations; ++i)
258     {
259         unsigned int j;
260
261         lObject = nullptr;
262         while (lObject == nullptr)
263         {
264             lObject = sPool.TryCreate(lLayer);
265         }
266
267         NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
268         NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
269
270         lObject->Init();
271         lObject->Delay(lContext.mAccumulator);
272
273         j       = kPoolSize;
274         lObject = nullptr;
275         while (j-- > 0)
276         {
277             lObject = sPool.Get(lLayer, j);
278
279             if (lObject == nullptr)
280                 continue;
281
282             lObject->Release();
283             NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
284             break;
285         }
286
287         NL_TEST_ASSERT(lContext.mTestSuite, lObject != nullptr);
288     }
289
290     // Cleanup
291
292     for (i = 0; i < kPoolSize; ++i)
293     {
294         lObject = sPool.Get(lLayer, i);
295
296         if (lObject == nullptr)
297             continue;
298
299         lObject->Release();
300         NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
301     }
302
303     lLayer.Shutdown();
304
305     return aContext;
306 }
307
308 void * TestObject::CheckHighWatermarkThread(void * aContext)
309 {
310     TestContext & lContext = *static_cast<TestContext *>(aContext);
311     int i;
312     chip::System::Stats::count_t lNumInUse;
313     chip::System::Stats::count_t lHighWatermark;
314
315     i = (rand() % CHIP_SYS_STATS_COUNT_MAX);
316
317     sPool.UpdateHighWatermark(static_cast<unsigned int>(i));
318
319     sPool.GetStatistics(lNumInUse, lHighWatermark);
320
321     NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark >= i);
322     if (lHighWatermark < i)
323     {
324         printf("hwm: %d, i: %d\n", lHighWatermark, i);
325     }
326
327     return aContext;
328 }
329
330 void TestObject::MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) )
331 {
332     TestContext & lContext = *static_cast<TestContext *>(aContext);
333     pthread_t lThread[kNumThreads];
334
335     memset(&sPool, 0, sizeof(sPool));
336
337     for (unsigned int i = 0; i < kNumThreads; ++i)
338     {
339         int lError = pthread_create(&lThread[i], nullptr, aStartRoutine, &lContext);
340
341         NL_TEST_ASSERT(lContext.mTestSuite, lError == 0);
342     }
343
344     for (pthread_t thread : lThread)
345     {
346         int lError = pthread_join(thread, nullptr);
347
348         NL_TEST_ASSERT(lContext.mTestSuite, lError == 0);
349     }
350 }
351 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
352
353 void TestObject::CheckConcurrency(nlTestSuite * inSuite, void * aContext)
354 {
355 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
356     MultithreadedTest(inSuite, aContext, CheckConcurrencyThread);
357 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
358 }
359
360 void TestObject::CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext)
361 {
362 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
363     for (unsigned int i = 0; i < 1000; i++)
364     {
365         MultithreadedTest(inSuite, aContext, CheckHighWatermarkThread);
366     }
367 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
368 }
369
370 void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext)
371 {
372     memset(&sPool, 0, sizeof(sPool));
373
374     const int kNumObjects  = kPoolSize;
375     TestObject * lObject   = nullptr;
376     TestContext & lContext = *static_cast<TestContext *>(aContext);
377     Layer lLayer;
378     chip::System::Stats::count_t lNumInUse;
379     chip::System::Stats::count_t lHighWatermark;
380
381     lLayer.Init(lContext.mLayerContext);
382
383     // Take all objects one at a time and check the watermark
384     // increases monotonically
385
386     for (int i = 0; i < kNumObjects; ++i)
387     {
388         lObject = sPool.TryCreate(lLayer);
389
390         NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
391         NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
392
393         sPool.GetStatistics(lNumInUse, lHighWatermark);
394         NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1));
395         NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == lNumInUse);
396
397         lObject->Init();
398     }
399
400     // Fail an allocation and check that both stats don't change
401
402     lObject = sPool.TryCreate(lLayer);
403     NL_TEST_ASSERT(lContext.mTestSuite, lObject == nullptr);
404
405     sPool.GetStatistics(lNumInUse, lHighWatermark);
406     NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == kNumObjects);
407     NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
408
409     // Free all objects one at a time and check that the watermark does not
410     // change.
411
412     for (int i = 0; i < kNumObjects; ++i)
413     {
414         lObject = sPool.Get(lLayer, static_cast<size_t>(i));
415
416         NL_TEST_ASSERT(lContext.mTestSuite, lObject != nullptr);
417
418         lObject->Release();
419         NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
420
421         sPool.GetStatistics(lNumInUse, lHighWatermark);
422         NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (kNumObjects - i - 1));
423         NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
424     }
425
426     // Take all objects one at a time  again and check the watermark
427     // does not move
428
429     for (int i = 0; i < kNumObjects; ++i)
430     {
431         lObject = sPool.TryCreate(lLayer);
432
433         NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
434         NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
435
436         sPool.GetStatistics(lNumInUse, lHighWatermark);
437         NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1));
438         NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
439
440         lObject->Init();
441     }
442
443     // Cleanup
444
445     for (size_t i = 0; i < kPoolSize; ++i)
446     {
447         lObject = sPool.Get(lLayer, i);
448
449         if (lObject == nullptr)
450             continue;
451
452         lObject->Release();
453         NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
454     }
455
456     lLayer.Shutdown();
457 }
458
459 // Test Suite
460
461 /**
462  *   Test Suite. It lists all the test functions.
463  */
464 // clang-format off
465 static const nlTest sTests[] =
466 {
467     NL_TEST_DEF("Retention",                TestObject::CheckRetention),
468     NL_TEST_DEF("Concurrency",              TestObject::CheckConcurrency),
469     NL_TEST_DEF("HighWatermark",            TestObject::CheckHighWatermark),
470     NL_TEST_DEF("HighWatermarkConcurrency", TestObject::CheckHighWatermarkConcurrency),
471         NL_TEST_SENTINEL()
472 };
473
474 static nlTestSuite sTestSuite =
475 {
476     "chip-system-object",
477     &sTests[0],
478     Initialize,
479     Finalize
480 };
481 // clang-format on
482
483 /**
484  *  Initialize the test suite.
485  */
486 static int Initialize(void * aContext)
487 {
488     TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
489     void * lLayerContext   = nullptr;
490
491 #if CHIP_SYSTEM_CONFIG_USE_LWIP && LWIP_VERSION_MAJOR <= 2 && LWIP_VERSION_MINOR < 1
492     static sys_mbox_t * sLwIPEventQueue = NULL;
493
494     if (sLwIPEventQueue == NULL)
495     {
496         sys_mbox_new(sLwIPEventQueue, 100);
497     }
498
499     lLayerContext = &sLwIPEventQueue;
500 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
501
502     lContext.mTestSuite    = &sTestSuite;
503     lContext.mLayerContext = lLayerContext;
504     lContext.mAccumulator  = 0;
505
506     return SUCCESS;
507 }
508
509 /**
510  *  Finalize the test suite.
511  */
512 static int Finalize(void * aContext)
513 {
514     TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
515
516     lContext.mTestSuite = nullptr;
517
518     return SUCCESS;
519 }
520
521 } // namespace System
522 } // namespace chip
523
524 int TestSystemObject(void)
525 {
526     // Initialize standard pseudo-random number generator
527     srand(0);
528
529     // Run test suit againt one lContext.
530     nlTestRunner(&sTestSuite, &chip::System::sContext);
531
532     return nlTestRunnerStats(&sTestSuite);
533 }
534
535 CHIP_REGISTER_TEST_SUITE(TestSystemObject)