Replace glib threadpool usage with a 'dumb' thread implementation.
[platform/upstream/iotivity.git] / resource / csdk / connectivity / test / camutex_tests.cpp
1 //******************************************************************
2 //
3 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20 //
21 //
22 //*********************************************************************
23
24 // Defining _POSIX_C_SOURCE macro with 200809L (or greater) as value
25 // causes header files to expose definitions
26 // corresponding to the POSIX.1-2008 base
27 // specification (excluding the XSI extension).
28 // For POSIX.1-2008 base specification,
29 // Refer http://pubs.opengroup.org/stage7tc1/
30 //
31 // For this specific file, see use of usleep
32 #ifndef _POSIX_C_SOURCE
33 #define _POSIX_C_SOURCE 200809L
34 #endif // _POSIX_C_SOURCE
35
36 #include "gtest/gtest.h"
37
38 #include <camutex.h>
39 #include <cathreadpool.h>
40
41 #include <time.h>
42 #include <sys/time.h>
43 #include <unistd.h>
44
45 //#define DEBUG_VERBOSE 1
46
47 // The debug print lines are left in for now since the output can be
48 // helpful for developers trying to debug or extend the tests.
49 // However, by default they are #defined out so as not to get in
50 // the way of normal test runs.
51 #ifdef DEBUG_VERBOSE
52 #define DBG_printf(...) printf(__VA_ARGS__)
53 #else
54 #define DBG_printf(...)
55 #endif
56
57 static const uint64_t USECS_PER_SEC = 1000000;
58
59 static const uint64_t USECS_PER_MSEC = 1000;
60
61 static const int MINIMAL_LOOP_SLEEP = 20;
62 static const int MINIMAL_EXTRA_SLEEP = 25;
63
64 uint64_t getAbsTime()
65 {
66     uint64_t currentTime=0;
67 #if _POSIX_TIMERS > 0
68     struct timespec ts;
69     clock_gettime(CLOCK_MONOTONIC, &ts);
70     currentTime = ts.tv_sec * USECS_PER_SEC + ts.tv_nsec / 1000;
71 #else
72     struct timeval tv;
73     gettimeofday(&tv, NULL);
74     currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
75 #endif
76     return currentTime;
77 }
78
79 TEST(MutexTests, TC_01_CREATE)
80 {
81     ca_mutex mymutex = ca_mutex_new();
82
83     EXPECT_TRUE(mymutex != NULL);
84     if (mymutex != NULL)
85     {
86         ca_mutex_free(mymutex);
87     }
88 }
89
90 TEST(MutexTests, TC_02_TRY_LOCK)
91 {
92     ca_mutex mymutex = ca_mutex_new();
93
94     EXPECT_TRUE(mymutex != NULL);
95     if (mymutex != NULL)
96     {
97         EXPECT_TRUE(ca_mutex_trylock(mymutex)); // acquire it
98
99         ca_mutex_unlock(mymutex); // release it
100
101         ca_mutex_lock(mymutex); // acquire it
102
103         EXPECT_FALSE(ca_mutex_trylock(mymutex)); // he should be lock
104
105         EXPECT_FALSE(ca_mutex_trylock(NULL));
106
107         ca_mutex_unlock(mymutex); // release it
108         ca_mutex_free(mymutex);
109
110         EXPECT_FALSE(ca_mutex_trylock(NULL));
111     }
112 }
113
114 typedef struct _tagFunc1
115 {
116     ca_mutex mutex;
117     volatile bool thread_up;
118     volatile bool finished;
119 } _func1_struct;
120
121 void mutexFunc(void *context)
122 {
123     _func1_struct* pData = (_func1_struct*) context;
124
125     DBG_printf("Thread: trying to lock\n");
126
127     // setting the flag must be done before lock attempt, as the test
128     // thread starts off with the mutex locked
129     pData->thread_up = true;
130     ca_mutex_lock(pData->mutex);
131
132     DBG_printf("Thread: got lock\n");
133     usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
134     DBG_printf("Thread: releasing\n");
135
136     pData->finished = true; // assignment guarded by lock
137
138     ca_mutex_unlock(pData->mutex);
139 }
140
141 TEST(MutexTests, TC_03_THREAD_LOCKING)
142 {
143     ca_thread_pool_t mythreadpool;
144
145     EXPECT_EQ(CA_STATUS_OK, ca_thread_pool_init(3, &mythreadpool));
146
147     _func1_struct pData = {};
148
149     pData.mutex = ca_mutex_new();
150
151     EXPECT_TRUE(pData.mutex != NULL);
152     if (pData.mutex != NULL)
153     {
154         DBG_printf("test: Holding mutex in test\n");
155         ca_mutex_lock(pData.mutex);
156
157         DBG_printf("test: starting thread\n");
158         //start thread
159         EXPECT_EQ(CA_STATUS_OK,
160                   ca_thread_pool_add_task(mythreadpool, mutexFunc, &pData));
161
162         DBG_printf("test: waiting for thread to be up.\n");
163
164         while (!pData.thread_up)
165         {
166             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
167         }
168         // At this point the thread is running and close to trying to lock.
169         // For test purposes only, use of condition variables is being avoided,
170         // so a minor sleep is used.
171         usleep(MINIMAL_EXTRA_SLEEP * USECS_PER_MSEC);
172
173         DBG_printf("test: unlocking\n");
174
175         ca_mutex_unlock(pData.mutex);
176
177         DBG_printf("test: waiting for thread to release\n");
178         while (!pData.finished)
179         {
180             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
181         }
182
183         ca_mutex_lock(pData.mutex);
184
185         // Cleanup Everything
186
187         ca_mutex_unlock(pData.mutex);
188         ca_mutex_free(pData.mutex);
189     }
190
191     ca_thread_pool_free(mythreadpool);
192 }
193
194 TEST(ConditionTests, TC_01_CREATE)
195 {
196     ca_cond mycond = ca_cond_new();
197
198     EXPECT_TRUE(mycond != NULL);
199     if (mycond != NULL)
200     {
201         ca_cond_free(mycond);
202     }
203 }
204
205 // Normally we would use one pair of mutex/cond-var communicating to the
206 // worker threads and one pair back to the main thread. However since
207 // testing the ca_cond itself is the point, only one pair is used here.
208 typedef struct _tagFunc2
209 {
210     int id;
211     ca_mutex mutex;
212     ca_cond condition;
213     volatile bool thread_up;
214     volatile bool finished;
215 } _func2_struct;
216
217 void condFunc(void *context)
218 {
219     _func2_struct* pData = (_func2_struct*) context;
220
221     DBG_printf("Thread_%d: waiting on condition\n", pData->id);
222
223     ca_mutex_lock(pData->mutex);
224
225     pData->thread_up = true;
226
227     ca_cond_wait(pData->condition, pData->mutex);
228
229     pData->finished = true; // assignment guarded by lock
230
231     ca_mutex_unlock(pData->mutex);
232
233     DBG_printf("Thread_%d: completed.\n", pData->id);
234 }
235
236 TEST(ConditionTests, TC_02_SIGNAL)
237 {
238     const int MAX_WAIT_MS = 2000;
239     ca_thread_pool_t mythreadpool;
240
241     EXPECT_EQ(CA_STATUS_OK, ca_thread_pool_init(3, &mythreadpool));
242
243     ca_mutex sharedMutex = ca_mutex_new();
244     ca_cond sharedCond = ca_cond_new();
245
246     _func2_struct pData1 =
247     { 1, sharedMutex, sharedCond, false, false };
248     _func2_struct pData2 =
249     { 2, sharedMutex, sharedCond, false, false };
250
251     EXPECT_TRUE(pData1.mutex != NULL);
252     if (pData1.mutex != NULL)
253     {
254         DBG_printf("starting thread\n");
255         // start threads
256         EXPECT_EQ(CA_STATUS_OK,
257                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData1));
258         EXPECT_EQ(CA_STATUS_OK,
259                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData2));
260
261         //start thread
262         EXPECT_EQ(CA_STATUS_OK,
263                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData2));
264
265         DBG_printf("test    : sleeping\n");
266
267         while (!pData1.thread_up || !pData2.thread_up)
268         {
269             // For test purposes only, use of condition variables is being
270             // avoided, so a minor sleep is used.
271             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
272         }
273         // At this point the threads are running and both have locked. One
274         // has already started waiting on the condition and the other is at
275         // least close.
276
277         ca_mutex_lock(sharedMutex);
278         // once the lock is acquired it means both threads were waiting.
279         DBG_printf("test    : signaling first thread\n");
280         ca_cond_signal(sharedCond);
281         ca_mutex_unlock(sharedMutex);
282
283         // At this point either of the child threads might lock the mutex in
284         // their cond_wait call, or this test thread might lock it again if
285         // mutex_lock gets executed before the child threads can react to
286         // the signaling. Thus we wait on their flag variables
287         int waitCount = 1; // start with 1 for minumum targetWait value.
288         while (!pData1.finished && !pData2.finished)
289         {
290             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
291             waitCount++;
292         }
293
294         // As a rough hueristic wait twice as long for the second to possibly
295         // finish:
296         int targetWait = waitCount * 2;
297         for (int i = 0;
298              (i < targetWait) && (!pData1.finished && !pData2.finished); i++)
299         {
300             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
301         }
302         usleep(MINIMAL_EXTRA_SLEEP);
303
304         // only one should be finished
305         ca_mutex_lock(sharedMutex);
306         EXPECT_NE(pData1.finished, pData2.finished);
307         ca_mutex_unlock(sharedMutex);
308
309         DBG_printf("test    : signaling another thread\n");
310
311         ca_mutex_lock(sharedMutex);
312         ca_cond_signal(sharedCond);
313         ca_mutex_unlock(sharedMutex);
314
315         waitCount = 0;
316         while ((!pData1.finished || !pData2.finished)
317                && ((waitCount * MINIMAL_EXTRA_SLEEP) < MAX_WAIT_MS))
318         {
319             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
320             waitCount++;
321         }
322
323         // both should finally be finished
324         EXPECT_TRUE(pData1.finished);
325         EXPECT_TRUE(pData2.finished);
326
327         // Cleanup Everything
328
329         ca_mutex_free(pData1.mutex);
330     }
331
332     ca_cond_free(pData1.condition);
333
334     ca_thread_pool_free(mythreadpool);
335 }
336
337 TEST(ConditionTests, TC_03_BROADCAST)
338 {
339     const int MAX_WAIT_MS = 2000;
340     ca_thread_pool_t mythreadpool;
341
342     EXPECT_EQ(CA_STATUS_OK, ca_thread_pool_init(3, &mythreadpool));
343
344     ca_mutex sharedMutex = ca_mutex_new();
345     ca_cond sharedCond = ca_cond_new();
346
347     _func2_struct pData1 =
348     { 1, sharedMutex, sharedCond, false, false };
349     _func2_struct pData2 =
350     { 2, sharedMutex, sharedCond, false, false };
351
352     EXPECT_TRUE(pData1.mutex != NULL);
353     if (pData1.mutex != NULL)
354     {
355         DBG_printf("starting thread\n");
356         // start threads
357         EXPECT_EQ(CA_STATUS_OK,
358                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData1));
359         EXPECT_EQ(CA_STATUS_OK,
360                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData2));
361
362         //start thread
363         EXPECT_EQ(CA_STATUS_OK,
364                   ca_thread_pool_add_task(mythreadpool, condFunc, &pData2));
365
366         DBG_printf("test    : sleeping\n");
367
368         while (!pData1.thread_up || !pData2.thread_up)
369         {
370             // For test purposes only, use of condition variables is being
371             // avoided, so a minor sleep is used.
372             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
373         }
374         // At this point the threads are running and both have locked. One
375         // has already started waiting on the condition and the other is at
376         // least close.
377
378         DBG_printf("test    : signaling all threads\n");
379
380         ca_mutex_lock(sharedMutex);
381         // once the lock is acquired it means both threads were waiting.
382         ca_cond_broadcast(sharedCond);
383         ca_mutex_unlock(sharedMutex);
384
385         int waitCount = 0;
386         while ((!pData1.finished || !pData2.finished)
387                && ((waitCount * MINIMAL_EXTRA_SLEEP) < MAX_WAIT_MS))
388         {
389             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
390             waitCount++;
391         }
392
393         // both should finally be finished
394         EXPECT_TRUE(pData1.finished);
395         EXPECT_TRUE(pData2.finished);
396
397         // Cleanup Everything
398
399         ca_mutex_free(sharedMutex);
400     }
401
402     ca_cond_free(sharedCond);
403
404     ca_thread_pool_free(mythreadpool);
405 }
406
407 TEST(CondTests, TC_04_TIMECHECK)
408 {
409     uint64_t begin = getAbsTime();
410
411     usleep(1);
412
413     uint64_t end = getAbsTime();
414
415     EXPECT_LT(begin, end); // should never be the same value
416 }
417
418 void timedFunc(void *context)
419 {
420     _func2_struct* pData = (_func2_struct*) context;
421
422     DBG_printf("Thread_%d: waiting for timeout \n", pData->id);
423
424     ca_mutex_lock(pData->mutex);
425
426     uint64_t abs = USECS_PER_SEC / 2; // 1/2 seconds
427
428     // test UTIMEDOUT
429     CAWaitResult_t ret = ca_cond_wait_for(pData->condition,
430                                           pData->mutex, abs);
431     EXPECT_EQ(CA_WAIT_TIMEDOUT, ret);
432
433     pData->thread_up = true;
434
435     DBG_printf("Thread_%d: waiting for signal \n", pData->id);
436
437     abs = 5 * USECS_PER_SEC; // 5 seconds
438
439     // test signal
440     ret = ca_cond_wait_for(pData->condition, pData->mutex, abs);
441     EXPECT_EQ(CA_WAIT_SUCCESS, ret);
442
443     pData->finished = true; // assignment guarded by lock
444
445     ca_mutex_unlock(pData->mutex);
446
447     DBG_printf("Thread_%d: stopping\n", pData->id);
448 }
449
450 TEST(ConditionTests, TC_05_WAIT)
451 {
452     const int MAX_WAIT_MS = 5000;
453     ca_thread_pool_t mythreadpool;
454
455     EXPECT_EQ(CA_STATUS_OK, ca_thread_pool_init(3, &mythreadpool));
456
457     ca_mutex sharedMutex = ca_mutex_new();
458     ca_cond sharedCond = ca_cond_new();
459
460     _func2_struct pData1 =
461     { 1, sharedMutex, sharedCond, false, false };
462
463     EXPECT_TRUE(sharedMutex != NULL);
464     if (sharedMutex != NULL)
465     {
466         DBG_printf("test    : starting thread\n");
467         //start thread
468         EXPECT_EQ(CA_STATUS_OK,
469                   ca_thread_pool_add_task(mythreadpool, timedFunc, &pData1));
470
471         DBG_printf("test    : waiting for thread to timeout once.\n");
472
473         while (!pData1.thread_up)
474         {
475             // For test purposes only, use of condition variables is being
476             // avoided, so a minor sleep is used.
477             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
478         }
479
480
481         DBG_printf("test    : signaling first thread\n");
482
483         ca_mutex_lock(sharedMutex);
484         ca_cond_signal(sharedCond);
485         ca_mutex_unlock(sharedMutex);
486
487         int waitCount = 0;
488         while (!pData1.finished
489                && ((waitCount * MINIMAL_EXTRA_SLEEP) < MAX_WAIT_MS))
490         {
491             usleep(MINIMAL_LOOP_SLEEP * USECS_PER_MSEC);
492             waitCount++;
493         }
494
495         EXPECT_TRUE(pData1.finished); // thread should finally be finished
496
497         // Cleanup Everything
498
499         ca_mutex_free(sharedMutex);
500     }
501
502     ca_cond_free(sharedCond);
503
504     ca_thread_pool_free(mythreadpool);
505 }
506
507 // Disabled because this should no longer be a valid test
508 TEST(ConditionTests, DISABLED_TC_06_INVALIDWAIT)
509 {
510
511     ca_mutex sharedMutex = ca_mutex_new();
512     ca_cond sharedCond = ca_cond_new();
513
514     ca_mutex_lock(sharedMutex);
515
516     int ret = ca_cond_wait_for(NULL, sharedMutex, 5000);
517     EXPECT_EQ(CA_WAIT_INVAL,ret);
518
519     ret = ca_cond_wait_for(sharedCond, NULL, 5000);
520     EXPECT_EQ(CA_WAIT_INVAL,ret);
521
522     ret = ca_cond_wait_for(NULL, NULL, 5000);
523     EXPECT_EQ(CA_WAIT_INVAL,ret);
524
525     ca_mutex_unlock(sharedMutex);
526
527     // Cleanup Everything
528
529     ca_mutex_free(sharedMutex);
530
531     ca_cond_free(sharedCond);
532 }
533
534 TEST(ConditionTests, TC_07_WAITDURATION)
535 {
536     const double TARGET_WAIT = 1.125;
537
538     ca_mutex sharedMutex = ca_mutex_new();
539     ca_cond sharedCond = ca_cond_new();
540
541     ca_mutex_lock(sharedMutex);
542
543     uint64_t beg = getAbsTime();
544
545     CAWaitResult_t ret = ca_cond_wait_for(sharedCond, sharedMutex,
546                                           TARGET_WAIT * USECS_PER_SEC);
547     EXPECT_EQ(CA_WAIT_TIMEDOUT,ret);
548
549     uint64_t end = getAbsTime();
550
551     double secondsDiff = (end - beg) / (double) USECS_PER_SEC;
552
553     EXPECT_NEAR(TARGET_WAIT, secondsDiff, 0.05);
554
555     ca_mutex_unlock(sharedMutex);
556
557     // Cleanup Everything
558
559     ca_mutex_free(sharedMutex);
560
561     ca_cond_free(sharedCond);
562 }