Initialize Tizen 2.3
[framework/web/wrt-commons.git] / tests / unused / test_shm.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file    test_shm.h
18  * @author  Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for test cases for shared data framework
21  */
22
23 #include <stdlib.h>
24 #include <ctime>
25 #include <sys/shm.h>
26 #include <dpl/semaphore.h>
27 #include <dpl/test_runner.h>
28 #include <dpl/thread.h>
29 #include <dpl/controller.h>
30 #include <dpl/generic_event.h>
31 #include <dpl/log.h>
32 #include <dpl/shared_object.h>
33 #include <dpl/shared_property.h>
34 #include <memory>
35
36 RUNNER_TEST_GROUP_INIT(DPL)
37
38 using namespace DPL;
39
40 namespace {
41 const SharedMemory::Key SHM_KEY = 12345;
42 const char* SEM_NAME = "/wrt_engine_shared_object_semaphore";
43 const size_t VERSION = 1;
44
45 const size_t MAX_THREADS = 10;
46 const size_t TEST_AND_SET_REPEATS = 100;
47
48 const size_t SHARED_PROP_REPEATS = 3;
49
50 const size_t SINGLETON_TEST_REPEATS = 3;
51
52 // maximum random delay in singleton listener addition/removal
53 const size_t MAX_SINGLETON_LISTENER_DELAY = 50;
54
55 const int SINGLE_PROCESS_REPEATS = 50;
56
57 /*
58  * 5 seconds expected timeout for waitable events
59  * 30 seconds unexpected timeout for waitable events
60  * We don't want to block tests
61  */
62 const size_t EXPECTED_WAITABLE_TIMEOUT = 5 * 1000;
63 const size_t UNEXPECTED_WAITABLE_TIMEOUT = 30 * 1000;
64
65 bool g_enumTestCorrect = false;
66 bool g_enumTestIncorrect = false;
67 size_t g_delegateCalls = 0;
68
69 void Wait(DPL::WaitableEvent& event, bool expectedTimeout = false)
70 {
71     LogDebug("WaitForSingleHandle...");
72     DPL::WaitableHandleIndexList list = DPL::WaitForSingleHandle(
73             event.GetHandle(),
74             expectedTimeout ?
75             EXPECTED_WAITABLE_TIMEOUT : UNEXPECTED_WAITABLE_TIMEOUT);
76     if (list.size() == 0) {
77         LogDebug("...timeout.");
78     } else {
79         LogDebug("...signaled.");
80         event.Reset();
81     }
82
83     if (expectedTimeout) {
84         RUNNER_ASSERT(list.size() == 0);
85     } else {
86         RUNNER_ASSERT(list.size() == 1);
87     }
88 }
89
90 void RemoveIpcs()
91 {
92     Try {
93         SharedMemory::Remove(SHM_KEY);
94     }
95     Catch(SharedMemory::Exception::RemoveFailed) {
96         // ignore
97     }
98
99     Try {
100         DPL::Semaphore::Remove(SEM_NAME);
101     }
102     Catch(DPL::Semaphore::Exception::RemoveFailed) {
103         // ignore
104     }
105 }
106
107 typedef DPL::TypeListDecl<int, int, char, int[64]>::Type TestTypeList;
108 typedef DPL::TypeListDecl<int, int, char, int[63]>::Type TestTypeList2;
109 typedef DPL::TypeListDecl<int, int, char, int[63], int>::Type TestTypeList3;
110
111 typedef SharedObject<TestTypeList> TestSharedObject;
112 typedef SharedObject<TestTypeList2> TestSharedObject2;
113 typedef SharedObject<TestTypeList3> TestSharedObject3;
114
115 typedef std::shared_ptr<TestSharedObject> TestSharedObjectPtr;
116
117 const int INIT_EVENT = 0;
118 const int DESTROY_EVENT = 1;
119
120 int g_values[TestTypeList::Size];
121
122 /*
123  * helper listening controller
124  */
125 template <typename SharedType>
126 class ListeningController :
127     public DPL::Controller<DPL::TypeListDecl<int>::Type>
128 {
129   public:
130     explicit ListeningController(DPL::WaitableEvent* waitable);
131     ~ListeningController();
132
133     virtual void OnEventReceived(const int &event);
134
135     virtual void OnEvent(const int /*event*/) {}
136
137   protected:
138     std::shared_ptr<SharedType> m_so;
139     DPL::Thread m_thread;
140     DPL::WaitableEvent* m_waitable;
141 };
142
143 template <typename SharedType>
144 ListeningController<SharedType>::ListeningController(
145     DPL::WaitableEvent* waitable) :
146     m_waitable(waitable)
147 {
148     Touch();
149     m_thread.Run();
150     SwitchToThread(&m_thread);
151     PostEvent(INIT_EVENT);
152 }
153
154 template <typename SharedType>
155 ListeningController<SharedType>::~ListeningController()
156 {
157     m_thread.Quit();
158 }
159
160 template <typename SharedType>
161 void ListeningController<SharedType>::OnEventReceived(const int& event)
162 {
163     if (event == INIT_EVENT) {
164         m_so = SharedObjectFactory<SharedType>::Create(SHM_KEY, SEM_NAME);
165         OnEvent(event);
166         m_waitable->Signal();
167     } else if (event == DESTROY_EVENT) {
168         LogDebug("Destroying shared object");
169         OnEvent(event);
170
171         // deregister, destroy ad notify main thread
172         m_so.Reset();
173         LogDebug("4");
174         m_waitable->Signal();
175         LogDebug("5");
176     } else {
177         OnEvent(event);
178     }
179 }
180
181 typedef DPL::TypeListDecl<size_t, bool>::Type SharedTypeList;
182
183 class TestSharedObject4;
184 typedef std::shared_ptr<TestSharedObject4> TestSharedObject4Ptr;
185
186 class TestSharedObject4 : public SharedObject<SharedTypeList>
187 {
188   public:
189     enum
190     {
191         SIZE_T,
192         BOOLEAN
193     };
194
195     static TestSharedObject4Ptr Create()
196     {
197         return SharedObjectFactory<TestSharedObject4>::Create(SHM_KEY, SEM_NAME);
198     }
199
200     ~TestSharedObject4()
201     {
202         LogDebug("dtor");
203     }
204
205   protected:
206     explicit TestSharedObject4(const std::string& semaphore) :
207         SharedObject<SharedTypeList>(semaphore)
208     {}
209
210   private:
211     void Init()
212     {
213         SetPropertyInternal<BOOLEAN>(false);
214     }
215     friend class SharedObjectFactory<TestSharedObject4>;
216 };
217 } // anonymus namespace
218
219 //////////////////////////////////////////////
220
221 RUNNER_TEST(SharedMemory_002_AccessByType)
222 {
223     RemoveIpcs();
224
225     SharedData<TestTypeList> str;
226
227     // access by type
228     str.Embedded<0, int>::value = 4;
229     str.Embedded<1, int>::value = 5;
230     str.Embedded<2, char>::value = 'd';
231     str.Embedded<3, int[64]>::value[0] = 1;
232     str.Embedded<3, int[64]>::value[1] = 20;
233
234     RUNNER_ASSERT((str.Embedded<0, int>::value) == 4);
235     RUNNER_ASSERT((str.Embedded<1, int>::value) == 5);
236     RUNNER_ASSERT((str.Embedded<2, char>::value) == 'd');
237     RUNNER_ASSERT((str.Embedded<3, int[64]>::value[0]) == 1);
238     RUNNER_ASSERT((str.Embedded<3, int[64]>::value[1]) == 20);
239 }
240
241 //////////////////////////////////////////////
242
243 RUNNER_TEST(SharedMemory_003_AccessByIndex)
244 {
245     RemoveIpcs();
246
247     SharedData<TestTypeList> str;
248     // access by enum
249     str.Embedded<0, TestTypeList::Element<0>::Type>::value = 4;
250     str.Embedded<1, TestTypeList::Element<1>::Type>::value = 5;
251     str.Embedded<2, TestTypeList::Element<2>::Type>::value = 'd';
252     str.Embedded<3, TestTypeList::Element<3>::Type>::value[0] = 1;
253     str.Embedded<3, TestTypeList::Element<3>::Type>::value[1] = 20;
254
255     RUNNER_ASSERT(
256         (str.Embedded<0, TestTypeList::Element<0>::Type>::value) == 4);
257     RUNNER_ASSERT(
258         (str.Embedded<1, TestTypeList::Element<1>::Type>::value) == 5);
259     RUNNER_ASSERT(
260         (str.Embedded<2, TestTypeList::Element<2>::Type>::value) == 'd');
261     RUNNER_ASSERT(
262         (str.Embedded<3, TestTypeList::Element<3>::Type>::value[0]) == 1);
263     RUNNER_ASSERT(
264         (str.Embedded<3, TestTypeList::Element<3>::Type>::value[1]) == 20);
265 }
266
267 //////////////////////////////////////////////
268
269 RUNNER_TEST(SharedMemory_004_SimplifiedAccess)
270 {
271     RemoveIpcs();
272
273     SharedData<TestTypeList> str;
274
275     // access via PropertyRef
276     str.PropertyRef<1>() = 3;
277     RUNNER_ASSERT(str.PropertyRef<1>() == 3);
278
279     int (&array)[64] = str.PropertyRef<3>();
280     array[0] = 2;
281     RUNNER_ASSERT(str.PropertyRef<3>()[0] == 2);
282
283     str.PropertyRef<3>()[1] = 19;
284     RUNNER_ASSERT(str.PropertyRef<3>()[1] == 19);
285
286     // access via macro
287     str.SHARED_PROPERTY(0) = 2;
288     RUNNER_ASSERT(str.SHARED_PROPERTY(0) == 2);
289
290     str.SHARED_PROPERTY(2) = 'c';
291     RUNNER_ASSERT(str.SHARED_PROPERTY(2) == 'c');
292
293     str.SHARED_PROPERTY(3)[2] = 10;
294     RUNNER_ASSERT(str.SHARED_PROPERTY(3)[2] == 10);
295
296     // old style check
297     RUNNER_ASSERT((str.Embedded<0, int>::value) == 2);
298     RUNNER_ASSERT((str.Embedded<1, int>::value) == 3);
299     RUNNER_ASSERT((str.Embedded<2, char>::value) == 'c');
300     RUNNER_ASSERT((str.Embedded<3, int[64]>::value[0]) == 2);
301     RUNNER_ASSERT((str.Embedded<3, int[64]>::value[1]) == 19);
302     RUNNER_ASSERT((str.Embedded<3, int[64]>::value[2]) == 10);
303 }
304
305 //////////////////////////////////////////////
306
307 struct SharedStruct
308 {
309     int a;
310     int b;
311     char c;
312     int d[64];
313 };
314
315 typedef std::shared_ptr<SharedMemory::Segment<SharedStruct> > SharedStructPtr;
316
317 RUNNER_TEST(SharedMemory_010_BaseShmTest)
318 {
319     RemoveIpcs();
320
321     typedef std::unique_ptr<SharedMemory> SharedMemoryPtr;
322
323     // write
324     SharedMemoryPtr shm;
325     Try {
326         shm.Reset(SharedMemory::Create<SharedStruct>(SHM_KEY, false));
327     }
328     Catch(SharedMemory::Exception::NotFound) {
329         shm.Reset(SharedMemory::Create<SharedStruct>(SHM_KEY, true, true));
330     }
331
332     SharedStructPtr str = shm->Attach<SharedStruct>();
333
334     str->Data()->a = 1;
335     str->Data()->b = 2;
336     str->Data()->c = '3';
337     str->Data()->d[0] = 4;
338     str->Data()->d[1] = 5;
339
340     // read
341     SharedMemoryPtr shm2;
342     Try {
343         shm2.Reset(SharedMemory::Create<SharedStruct>(SHM_KEY, false));
344     }
345     Catch(SharedMemory::Exception::NotFound) {
346         shm2.Reset(SharedMemory::Create<SharedStruct>(SHM_KEY, true, true));
347     }
348
349     SharedStructPtr str2 = shm2->Attach<SharedStruct>();
350     SharedStructPtr str3 = shm2->Attach<SharedStruct>();
351
352     RUNNER_ASSERT(str2->Data()->a == 1);
353     RUNNER_ASSERT(str2->Data()->b == 2);
354     RUNNER_ASSERT(str2->Data()->c == '3');
355     RUNNER_ASSERT(str2->Data()->d[0] == 4);
356     RUNNER_ASSERT(str2->Data()->d[1] == 5);
357
358     RUNNER_ASSERT(str3->Data()->a == 1);
359     RUNNER_ASSERT(str3->Data()->b == 2);
360     RUNNER_ASSERT(str3->Data()->c == '3');
361     RUNNER_ASSERT(str3->Data()->d[0] == 4);
362     RUNNER_ASSERT(str3->Data()->d[1] == 5);
363
364     str2->Data()->b = 4;
365     str2->Data()->c = 'c';
366     str2->Data()->d[0] = 0;
367     RUNNER_ASSERT(str3->Data()->a == 1);
368     RUNNER_ASSERT(str3->Data()->b == 4);
369     RUNNER_ASSERT(str3->Data()->c == 'c');
370     RUNNER_ASSERT(str3->Data()->d[0] == 0);
371     RUNNER_ASSERT(str3->Data()->d[1] == 5);
372 }
373
374 //////////////////////////////////////////////
375
376 RUNNER_TEST(SharedMemory_020_SharedObjectTest)
377 {
378     RemoveIpcs();
379
380     typedef SharedObject<SharedTypeList> MySharedObj;
381
382     MySharedObj::Ptr so =
383         SharedObjectFactory<MySharedObj>::Create(SHM_KEY, SEM_NAME);
384
385     RUNNER_ASSERT((so->GetProperty<0, size_t>()) == 0);
386     so->SetProperty<0, size_t>(4);
387     RUNNER_ASSERT((so->GetProperty<0, size_t>()) == 4);
388 }
389
390 //////////////////////////////////////////////
391
392 class InitTestSharedObject : public TestSharedObject
393 {
394   protected:
395     explicit InitTestSharedObject(const std::string& semaphore) :
396         TestSharedObject(semaphore) {}
397
398     virtual void Init();    // from SharedObject
399
400   private:
401     friend class SharedObjectFactory<InitTestSharedObject>;
402 };
403
404 void InitTestSharedObject::Init()
405 {
406     SetPropertyInternal<0>(1);
407     SetPropertyInternal<1>(2);
408     SetPropertyInternal<2>('c');
409 }
410
411 RUNNER_TEST(SharedMemory_021_InitTest)
412 {
413     RemoveIpcs();   // we need non existing shm
414
415     std::shared_ptr<InitTestSharedObject> sho =
416         SharedObjectFactory<InitTestSharedObject>::Create(
417             SHM_KEY, SEM_NAME);
418     RUNNER_ASSERT((sho->GetProperty<0, int>()) == 1);
419     RUNNER_ASSERT((sho->GetProperty<1, int>()) == 2);
420     RUNNER_ASSERT((sho->GetProperty<2, char>()) == 'c');
421 }
422
423 //////////////////////////////////////////////
424
425 class VersionTestSO1 : public TestSharedObject
426 {
427   protected:
428     explicit VersionTestSO1(const std::string& semaphore) :
429         TestSharedObject(semaphore) {}
430
431     virtual SizeType GetVersion() const
432     {
433         return 1;
434     }                                                    // from SharedObject
435
436   private:
437     friend class SharedObjectFactory<VersionTestSO1>;
438 };
439
440 class VersionTestSO2 : public TestSharedObject
441 {
442   protected:
443     explicit VersionTestSO2(const std::string& semaphore) :
444         TestSharedObject(semaphore) {}
445
446     virtual SizeType GetVersion() const
447     {
448         return 2;
449     }                                                    // from SharedObject
450
451   private:
452     friend class SharedObjectFactory<VersionTestSO2>;
453 };
454
455 RUNNER_TEST(SharedMemory_022_InvalidVersionTest)
456 {
457     RemoveIpcs();   // we need non existing shm
458
459     std::shared_ptr<VersionTestSO1> sho =
460         SharedObjectFactory<VersionTestSO1>::Create(SHM_KEY, SEM_NAME);
461
462     Try {
463         std::shared_ptr<VersionTestSO2> sho2 =
464             SharedObjectFactory<VersionTestSO2>::Create(SHM_KEY, SEM_NAME);
465
466         RUNNER_ASSERT_MSG(false, "Invalid shm version has been accepted");
467     }
468     Catch(SharedObjectBase::Exception::InvalidVersion) {
469         RUNNER_ASSERT(true);
470     }
471 }
472
473 //////////////////////////////////////////////
474
475 RUNNER_TEST(SharedMemory_023_InvalidSizeTest)
476 {
477     RemoveIpcs();   // we need non existing shm
478
479     typedef SharedObject<TestTypeList> SO1;
480     typedef SharedObject<TestTypeList2> SO2;
481
482     SO1::Ptr sho = SharedObjectFactory<SO1>::Create(SHM_KEY, SEM_NAME);
483
484     Try {
485         SO2::Ptr sho2 = SharedObjectFactory<SO2>::Create(SHM_KEY, SEM_NAME);
486
487         RUNNER_ASSERT_MSG(false, "Invalid shm size has been accepted");
488     }
489     Catch(SharedObjectBase::Exception::InvalidSize) {
490         RUNNER_ASSERT(true);
491     }
492 }
493
494 //////////////////////////////////////////////
495
496 class MagicTestSO1 : public TestSharedObject
497 {
498   protected:
499     explicit MagicTestSO1(const std::string& semaphore) :
500         TestSharedObject(semaphore) {}
501
502     // from SharedObject
503     virtual MagicType GetMagicNumber() const
504     {
505         return 661;
506     }
507
508   private:
509     friend class SharedObjectFactory<MagicTestSO1>;
510 };
511
512 class MagicTestSO2 : public TestSharedObject
513 {
514   protected:
515     explicit MagicTestSO2(const std::string& semaphore) :
516         TestSharedObject(semaphore) {}
517
518     // from SharedObject
519     virtual MagicType GetMagicNumber() const
520     {
521         return 662;
522     }
523
524   private:
525     friend class SharedObjectFactory<MagicTestSO2>;
526 };
527
528 RUNNER_TEST(SharedMemory_024_InvalidMagicTest)
529 {
530     RemoveIpcs();   // we need non existing shm
531
532     std::shared_ptr<MagicTestSO1> sho =
533         SharedObjectFactory<MagicTestSO1>::Create(SHM_KEY, SEM_NAME);
534
535     Try {
536         std::shared_ptr<MagicTestSO2> sho2 =
537             SharedObjectFactory<MagicTestSO2>::Create(SHM_KEY, SEM_NAME);
538
539         RUNNER_ASSERT_MSG(false, "Invalid shm magic number has been accepted");
540     }
541     Catch(SharedObjectBase::Exception::InvalidMagicNumber) {
542         RUNNER_ASSERT(true);
543     }
544 }
545
546 //////////////////////////////////////////////
547
548 /*
549  * Listening shared object
550  */
551 class EnumTestSO1 : public TestSharedObject
552 {
553   public:
554     void SetWaitable(DPL::WaitableEvent* waitable)
555     {
556         m_waitable = waitable;
557     }
558
559   protected:
560     explicit EnumTestSO1(const std::string& semaphore) :
561         TestSharedObject(semaphore),
562         m_waitable(NULL)
563     {}
564
565
566     virtual void PropertyChanged(size_t propertyEnum);
567
568   private:
569     friend class SharedObjectFactory<EnumTestSO1>;
570
571     DPL::WaitableEvent* m_waitable;
572 };
573
574 void EnumTestSO1::PropertyChanged(size_t propertyEnum)
575 {
576     if (propertyEnum == 1) {
577         LogDebug("Property enum " << propertyEnum << " correctly set");
578         g_enumTestCorrect = true;
579     }
580     if (propertyEnum == 4) {
581         // This is bad. We only have 4 types
582         LogError("Property enum " << propertyEnum << " should be skipped");
583         g_enumTestIncorrect = true;
584     }
585     // confirm property change notification
586     m_waitable->Signal();
587 }
588
589 class EnumController : public ListeningController<EnumTestSO1>
590 {
591   public:
592     explicit EnumController(DPL::WaitableEvent* waitable) :
593         ListeningController<EnumTestSO1>(waitable) {}
594
595     virtual void OnEvent(const int event);
596 };
597
598 void EnumController::OnEvent(const int event)
599 {
600     if (event == INIT_EVENT) {
601         m_so->SetWaitable(m_waitable);
602     }
603 }
604
605 /*
606  * Writing shared object with correct size but different number of types
607  */
608 class EnumTestSO2 : public TestSharedObject3
609 {
610   protected:
611     explicit EnumTestSO2(const std::string& semaphore) :
612         TestSharedObject3(semaphore) {}
613
614   private:
615     friend class SharedObjectFactory<EnumTestSO2>;
616 };
617
618 RUNNER_TEST(SharedMemory_025_InvalidEnumTest)
619 {
620     RemoveIpcs();   // we need non existing shm
621
622     g_enumTestCorrect = false;
623     g_enumTestIncorrect = false;
624
625     DPL::WaitableEvent waitable;
626
627     // create listening controller and wait until it registers
628     EnumController controller(&waitable);
629     Wait(waitable);
630     LogDebug("Listening controller created");
631
632     // create writing shared object
633     std::shared_ptr<EnumTestSO2> sho2 =
634         SharedObjectFactory<EnumTestSO2>::Create(SHM_KEY, SEM_NAME);
635     DPL::WaitableHandleIndexList list;
636
637     // write property and wait for confirmation
638     sho2->SetProperty<1>(2);
639     Wait(waitable);
640
641     // write incorrect property and wait for confirmation
642     // we expect timeout
643     sho2->SetProperty<4>(2);
644     Wait(waitable, true);
645
646     // schedule listener deregistration and wait for confirmation
647     controller.PostEvent(DESTROY_EVENT);
648     Wait(waitable);
649
650     // check results
651     RUNNER_ASSERT(g_enumTestCorrect == true);
652     RUNNER_ASSERT(g_enumTestIncorrect == false);
653 }
654
655 //////////////////////////////////////////////
656
657 class MultiThreadSO : public TestSharedObject
658 {
659   public:
660     void TestAndSetProperty();
661
662   protected:
663     explicit MultiThreadSO(const std::string& semaphore) :
664         TestSharedObject(semaphore) {}
665
666   private:
667     friend class SharedObjectFactory<MultiThreadSO>;
668 };
669
670 void MultiThreadSO::TestAndSetProperty()
671 {
672     ScopedFlaggedLock lock(*this);
673
674     int value = PropertyRef<0, int>();
675     DPL::Thread::MicroSleep(100);
676     SetPropertyInternal<0>(value + 1);
677 }
678
679 class ShmController : public ListeningController<MultiThreadSO>
680 {
681   public:
682     explicit ShmController(DPL::WaitableEvent* event) :
683         ListeningController<MultiThreadSO>(event), m_counter(0)
684     {}
685
686     virtual void OnEventReceived(const int& event);
687
688   private:
689     size_t m_counter;
690 };
691
692 void ShmController::OnEventReceived(const int& event)
693 {
694     if (event == INIT_EVENT) {
695         m_so = SharedObjectFactory<MultiThreadSO>::Create(SHM_KEY, SEM_NAME);
696         PostEvent(2);
697     } else if (event == DESTROY_EVENT) {
698         LogDebug("Destroying shared object");
699         // deregister, destroy ad notify main thread
700         m_so.Reset();
701         m_waitable->Signal();
702     } else if (event == 2) {
703         m_so->TestAndSetProperty();
704         m_counter++;
705         if (m_counter >= TEST_AND_SET_REPEATS) {
706             LogDebug("Max tests reached. Finishing thread");
707             PostEvent(DESTROY_EVENT);
708             return;
709         }
710         PostEvent(2);
711     }
712 }
713
714 RUNNER_TEST(SharedMemory_030_MultithreadTest)
715 {
716     RemoveIpcs();   // we need non existing shm
717
718     typedef SharedObject<TestTypeList> SHO;
719     SHO::Ptr sho = SharedObjectFactory<SHO>::Create(SHM_KEY, SEM_NAME);
720
721     ShmController* controller[MAX_THREADS];
722     DPL::WaitableEvent finalEvent[MAX_THREADS];
723
724     for (size_t i = 0; i < MAX_THREADS; ++i) {
725         controller[i] = new ShmController(&finalEvent[i]);
726     }
727
728     for (size_t i = 0; i < MAX_THREADS; ++i) {
729         Wait(finalEvent[i]);
730     }
731
732     for (size_t i = 0; i < MAX_THREADS; ++i) {
733         delete controller[i];
734         controller[i] = NULL;
735     }
736
737     int value = sho->GetProperty<0, int>();
738     LogDebug("Final value is " << value << ", expected " <<
739              MAX_THREADS * TEST_AND_SET_REPEATS);
740     RUNNER_ASSERT(value == MAX_THREADS * TEST_AND_SET_REPEATS);
741 }
742
743 //////////////////////////////////////////////
744
745 class MyModel10 : public DPL::Model
746 {
747   public:
748     explicit MyModel10(const TestSharedObject4Ptr& shared_object) :
749         DPL::Model(), boolValue(this, shared_object) {}
750
751     SharedProperty<bool, TestSharedObject4::BOOLEAN, TestSharedObject4>
752     boolValue;
753 };
754
755 /*
756  * Listening controller
757  */
758 class ShmController3 : public ListeningController<TestSharedObject4>
759 {
760   public:
761     explicit ShmController3(DPL::WaitableEvent* event) :
762         ListeningController<TestSharedObject4>(event)
763     {}
764
765     virtual void OnEvent(const int event);
766
767     void OnValueChanged(const DPL::PropertyEvent<bool>& event);
768
769   private:
770     typedef std::unique_ptr<MyModel10> MyModelPtr;
771
772     // model with property bound to shared object
773     MyModelPtr m_model;
774 };
775
776 void ShmController3::OnEvent(const int event)
777 {
778     if (event == INIT_EVENT) {
779         m_model.Reset(new MyModel10(m_so));
780         m_model->boolValue.AddListener(
781             std::bind(&ShmController3::OnValueChanged, this));
782     } else if (event == DESTROY_EVENT) {
783         m_model->boolValue.RemoveListener(
784             std::bind(&ShmController3::OnValueChanged, this));
785         m_model.Reset();
786     }
787 }
788
789 void ShmController3::OnValueChanged(const DPL::PropertyEvent<bool>& event)
790 {
791     if (event.value) {
792         // change back
793         m_model->boolValue.Set(false);
794     } else {
795         LogError("Expected value = true, got false");
796     }
797
798     m_waitable->Signal();
799 }
800
801 RUNNER_TEST(SharedMemory_050_SharedProperty)
802 {
803     RemoveIpcs();
804
805     bool result = true;
806     DPL::WaitableEvent waitable;
807     // listener controller
808     ShmController3 controller(&waitable);
809     Wait(waitable);
810
811     TestSharedObject4Ptr sharedObject = TestSharedObject4::Create();
812
813     for (size_t i = 0; i < SHARED_PROP_REPEATS; ++i) {
814         sharedObject->SetProperty<TestSharedObject4::BOOLEAN>(true);
815         Wait(waitable);
816         result = sharedObject->GetProperty<TestSharedObject4::BOOLEAN,
817                                            bool>();
818         RUNNER_ASSERT(result == false);
819     }
820     controller.PostEvent(DESTROY_EVENT);
821     Wait(waitable);
822 }
823
824 //////////////////////////////////////////////
825
826 class MyModel2 : public DPL::Model
827 {
828   public:
829     explicit MyModel2(const TestSharedObjectPtr& shared_object) :
830         counter(this, shared_object) {}
831
832     SharedProperty<int, 0, TestSharedObject> counter;
833 };
834
835 class SPController : public ListeningController<TestSharedObject>
836 {
837   public:
838     explicit SPController(DPL::WaitableEvent* event) :
839         ListeningController<TestSharedObject>(event), m_repeats(1) {}
840
841     virtual void OnEvent(const int event);
842
843     void OnValueChanged1(const DPL::PropertyEvent<int>& event);
844     void OnValueChanged2(const DPL::PropertyEvent<int>& event);
845
846   private:
847     std::unique_ptr<MyModel2> m_model1;
848     std::unique_ptr<MyModel2> m_model2;
849
850     int m_repeats;
851     std::shared_ptr<TestSharedObject> m_so2;
852 };
853
854 void SPController::OnEvent(const int event)
855 {
856     if (event == INIT_EVENT) {
857         m_so2 = SharedObjectFactory<TestSharedObject>::Create(SHM_KEY,
858                                                               SEM_NAME);
859
860         // create and register 2 models sharing the same property
861         m_model1.Reset(new MyModel2(m_so));
862         m_model2.Reset(new MyModel2(m_so2));
863         m_model1->counter.AddListener(
864             std::bind(&SPController::OnValueChanged1, this));
865         m_model2->counter.AddListener(
866             std::bind(&SPController::OnValueChanged2, this));
867         m_model1->counter.Set(1);
868     } else if (event == DESTROY_EVENT) {
869         m_model1->counter.RemoveListener(
870             std::bind(&SPController::OnValueChanged1, this));
871         m_model2->counter.RemoveListener(
872             std::bind(&SPController::OnValueChanged2, this));
873
874         m_model1.Reset();
875         m_model2.Reset();
876         m_so2.Reset();
877     }
878 }
879
880 void SPController::OnValueChanged1(const DPL::PropertyEvent<int>& event)
881 {
882     if (m_repeats >= SINGLE_PROCESS_REPEATS) {
883         PostEvent(DESTROY_EVENT);
884         return;
885     }
886
887     LogDebug("[1] Value changed to " << event.value);
888     m_repeats++;
889     m_model1->counter.Set(event.value + 1);
890 }
891
892 void SPController::OnValueChanged2(const DPL::PropertyEvent<int>& event)
893 {
894     if (m_repeats >= SINGLE_PROCESS_REPEATS) {
895         PostEvent(DESTROY_EVENT);
896         return;
897     }
898
899     LogDebug("[2] Value changed to " << event.value);
900     m_repeats++;
901     m_model2->counter.Set(event.value + 1);
902 }
903
904 RUNNER_TEST(SharedMemory_060_SingleProcess)
905 {
906     RemoveIpcs();
907
908     DPL::WaitableEvent waitable;
909     SPController controller(&waitable);
910     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
911             SHM_KEY,
912             SEM_NAME);
913
914     // wait for creation
915     Wait(waitable);
916
917     // wait for destruction
918     Wait(waitable);
919
920     int value = sho->GetProperty<0, int>();
921
922     LogDebug("final value: " << value);
923
924     // check value
925     RUNNER_ASSERT(value == SINGLE_PROCESS_REPEATS);
926 }
927
928 //////////////////////////////////////////////
929
930 class ListenerTestController : public ListeningController<TestSharedObject>,
931     public ISharedObjectListener<0, int>,
932     public ISharedObjectListener<1, int>,
933     public ISharedObjectListener<2, char>,
934     public ISharedObjectListener<3, int[64]>
935 {
936   public:
937     explicit ListenerTestController(DPL::WaitableEvent* event) :
938         ListeningController<TestSharedObject>(event) {}
939
940     ~ListenerTestController();
941
942     virtual void OnEvent(const int event);
943
944     virtual void ValueChanged(size_t propertyEnum,
945                               const int& value,
946                               const void* info = NULL);
947     virtual void ValueChanged(size_t propertyEnum,
948                               const char& value,
949                               const void* info = NULL);
950     virtual void ValueChanged(size_t propertyEnum,
951                               const int(&value)[64],
952                               const void* info = NULL);
953 };
954
955 ListenerTestController::~ListenerTestController()
956 {}
957
958 void ListenerTestController::OnEvent(const int event)
959 {
960     if (event == INIT_EVENT) {
961         // add self as a listener to shared object
962         m_so->AddListener<0, int>(this);
963         m_so->AddListener<1, int>(this);
964         m_so->AddListener<2, char>(this);
965         m_so->AddListener<3, int[64]>(this);
966     } else if (event == DESTROY_EVENT) {
967         // remove self from listener list
968         m_so->RemoveListener<0, int>(this);
969         m_so->RemoveListener<1, int>(this);
970         m_so->RemoveListener<2, char>(this);
971         m_so->RemoveListener<3, int[64]>(this);
972     }
973 }
974
975 void ListenerTestController::ValueChanged(size_t propertyEnum,
976                                           const int& value,
977                                           const void* /*info*/)
978 {
979     LogDebug("ValueChanged(int) " << propertyEnum << " " << value);
980     if ((propertyEnum == 0 &&
981          value == 1) || (propertyEnum == 1 && value == 2))
982     {
983         g_values[propertyEnum]++;
984         if (g_values[propertyEnum] == 3) {
985             m_waitable->Signal();
986         }
987     }
988 }
989
990 void ListenerTestController::ValueChanged(size_t propertyEnum,
991                                           const char& value,
992                                           const void* /*info*/)
993 {
994     LogDebug("ValueChanged(char) " << propertyEnum << " " << value);
995     if (propertyEnum == 2 && value == 'c') {
996         g_values[propertyEnum]++;
997         if (g_values[propertyEnum] == 3) {
998             m_waitable->Signal();
999         }
1000     }
1001 }
1002
1003 void ListenerTestController::ValueChanged(size_t propertyEnum,
1004                                           const int(&value)[64],
1005                                           const void* /*info*/)
1006 {
1007     LogDebug("ValueChanged(int[64]) " << propertyEnum << " " << value[5]);
1008     if (propertyEnum == 3 && value[5] == 5) {
1009         g_values[propertyEnum]++;
1010         if (g_values[propertyEnum] == 3) {
1011             m_waitable->Signal();
1012         }
1013     }
1014 }
1015
1016 RUNNER_TEST(SharedMemory_070_SharedObjectListeners)
1017 {
1018     RemoveIpcs();
1019
1020     // setup global flags
1021     for (size_t i = 0; i < TestTypeList::Size; ++i) {
1022         g_values[i] = 0;
1023     }
1024
1025     // create shared object
1026     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1027             SHM_KEY, SEM_NAME);
1028
1029     // create 1st listener and wait for it
1030     DPL::WaitableEvent waitable;
1031     ListenerTestController c1(&waitable);
1032     Wait(waitable);
1033
1034     // create 2nd listener and wait for it
1035     ListenerTestController c2(&waitable);
1036     Wait(waitable);
1037
1038     // create 3rd listener and wait for it
1039     ListenerTestController c3(&waitable);
1040     Wait(waitable);
1041
1042     // set properties and wait for result
1043     sho->SetProperty<0, int>(1);
1044     Wait(waitable);
1045
1046     RUNNER_ASSERT(g_values[0] == 3);
1047
1048     sho->SetProperty<1, int>(2);
1049     Wait(waitable);
1050
1051     RUNNER_ASSERT(g_values[1] == 3);
1052
1053     sho->SetProperty<2, char>('c');
1054     Wait(waitable);
1055
1056     RUNNER_ASSERT(g_values[2] == 3);
1057
1058     int array[64];
1059     memset(array, 0, 64 * sizeof(array[0]));
1060     array[5] = 5;
1061     sho->SetProperty<3, int[64]>(array);
1062     Wait(waitable);
1063
1064     RUNNER_ASSERT(g_values[3] == 3);
1065
1066     // finalize listeners
1067     c1.PostEvent(DESTROY_EVENT);
1068     Wait(waitable);
1069
1070     c2.PostEvent(DESTROY_EVENT);
1071     Wait(waitable);
1072
1073     c3.PostEvent(DESTROY_EVENT);
1074     Wait(waitable);
1075 }
1076
1077 //////////////////////////////////////////////
1078
1079 /*
1080  * class simulating DB access
1081  */
1082 class DAO : public DPL::Noncopyable
1083 {
1084   public:
1085     DAO() : m_boolValue(false) {}
1086
1087     void SetBoolValue(const bool& value)
1088     {
1089         m_boolValue = value;
1090     }
1091
1092     bool GetBoolValue() const
1093     {
1094         return m_boolValue;
1095     }
1096
1097   private:
1098     bool m_boolValue;
1099 };
1100
1101 /*
1102  * Model with property having set delegate defined
1103  */
1104 class MyModel3 : public DPL::Model
1105 {
1106   public:
1107     typedef SharedPropertyEx<bool,
1108                              TestSharedObject4::BOOLEAN,
1109                              TestSharedObject4> PropertyType;
1110
1111     MyModel3(const TestSharedObject4Ptr& shared_object, DAO* dao) :
1112         boolValue(this,
1113                   shared_object,
1114                   PropertyType::SetDelegate(dao, &DAO::SetBoolValue))
1115     {}
1116
1117     PropertyType boolValue;
1118 };
1119
1120 RUNNER_TEST(SharedMemory_090_SetPropertyDelegate)
1121 {
1122     RemoveIpcs();
1123
1124     // dao object
1125     DAO dao;
1126
1127     // create shared object
1128     TestSharedObject4Ptr sho = TestSharedObject4::Create();
1129
1130     // set property but call dao delegate within semaphore
1131     sho->SetProperty<TestSharedObject4::BOOLEAN>(
1132         true,
1133         MyModel3::PropertyType::SetDelegate(&dao, &DAO::SetBoolValue));
1134
1135     // check dao value
1136     RUNNER_ASSERT(dao.GetBoolValue() == true);
1137
1138     // check shared object value
1139     bool shoValue = sho->GetProperty<TestSharedObject4::BOOLEAN, bool>();
1140     RUNNER_ASSERT(shoValue == true);
1141
1142     // try the same with shared property
1143     MyModel3 model(sho, &dao);
1144
1145     // set property
1146     model.boolValue.Set(false);
1147
1148     // check dao value
1149     RUNNER_ASSERT(dao.GetBoolValue() == false);
1150
1151     // check sho value
1152     shoValue = sho->GetProperty<TestSharedObject4::BOOLEAN, bool>();
1153     RUNNER_ASSERT(shoValue == false);
1154
1155     // check property value
1156     RUNNER_ASSERT(model.boolValue.Get() == false);
1157 }
1158
1159 //////////////////////////////////////////////
1160
1161 /*
1162  * Lazy initialization test shared object
1163  */
1164 class LazySharedObject : public SharedObject<TestTypeList>
1165 {
1166   private:
1167     LazySharedObject() :
1168         m_read(false)
1169     {}
1170
1171   public:
1172     explicit LazySharedObject(const std::string& semaphore) :
1173         SharedObject<TestTypeList>(semaphore)
1174         , m_read(false)
1175     {}
1176
1177     void Init();
1178
1179     bool IsRead() const
1180     {
1181         return m_read;
1182     }
1183
1184   private:
1185     friend class SharedObjectFactory<LazySharedObject>;
1186
1187     bool m_read;
1188 };
1189
1190 void LazySharedObject::Init()
1191 {
1192     SetPropertyInternal<0>(42);
1193     m_read = true;
1194 }
1195
1196 RUNNER_TEST(SharedMemory_100_LazyInit)
1197 {
1198     RemoveIpcs();
1199
1200     typedef std::shared_ptr<LazySharedObject> LazySharedObjectPtr;
1201
1202     // create shared object
1203     LazySharedObjectPtr sho = SharedObjectFactory<LazySharedObject>::Create(
1204             SHM_KEY, SEM_NAME);
1205
1206     RUNNER_ASSERT(sho->IsRead() == false);
1207
1208     // get property causing lazy init
1209     int value = sho->GetProperty<0, int>();
1210
1211     RUNNER_ASSERT(sho->IsRead() == true);
1212     RUNNER_ASSERT(value == 42);
1213
1214     // create another object
1215     LazySharedObjectPtr sho2 = SharedObjectFactory<LazySharedObject>::Create(
1216             SHM_KEY, SEM_NAME);
1217
1218     RUNNER_ASSERT(sho2->IsRead() == false);
1219
1220     // get property NOT causing lazy init
1221     value = sho2->GetProperty<0, int>();
1222
1223     RUNNER_ASSERT(sho2->IsRead() == false);
1224     RUNNER_ASSERT(value == 42);
1225
1226     // destroy both objects
1227     sho.Reset();
1228     sho2.Reset();
1229
1230     // create shared object
1231     LazySharedObjectPtr sho3 = SharedObjectFactory<LazySharedObject>::Create(
1232             SHM_KEY, SEM_NAME);
1233
1234     RUNNER_ASSERT(sho3->IsRead() == false);
1235
1236     // set property causing lazy init
1237     sho3->SetProperty<0>(43);
1238     value = sho3->GetProperty<0, int>();
1239
1240     RUNNER_ASSERT(sho3->IsRead() == true);
1241     RUNNER_ASSERT(value == 43);
1242 }
1243
1244 //////////////////////////////////////////////
1245
1246 bool SetCondition(const int& readValue, int& setValue);
1247 bool SetCondition(const int& readValue, int& setValue)
1248 {
1249     LogDebug("Condition delegate called with read value = " << readValue <<
1250              " and set value = " << setValue);
1251
1252     if (readValue > 3) {
1253         LogDebug("Condition is false");
1254         return false;
1255     }
1256
1257     LogDebug("Condition is true");
1258     if (4 == setValue) {
1259         setValue = 10;
1260         LogDebug("Changing set value to " << setValue);
1261     }
1262     return true;
1263 }
1264
1265 void SetDelegate(const int& readValue);
1266 void SetDelegate(const int& readValue)
1267 {
1268     LogDebug("Set delegate called " << readValue);
1269     g_delegateCalls++;
1270 }
1271
1272 RUNNER_TEST(SharedMemory_120_ConditionalSet)
1273 {
1274     RemoveIpcs();
1275
1276     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1277             SHM_KEY,
1278             SEM_NAME);
1279
1280     g_delegateCalls = 0;
1281
1282     RUNNER_ASSERT(0 == (sho->GetProperty<0, int>()));
1283
1284     std::function<bool (const int&, int&)> condition = SetCondition;
1285     std::function<void (const int&)> delegate = SetDelegate;
1286
1287     bool succeeded = false;
1288
1289     succeeded = sho->ConditionalSetProperty<0>(-2, condition);
1290
1291     RUNNER_ASSERT(succeeded);
1292     RUNNER_ASSERT(-2 == (sho->GetProperty<0, int>()));
1293
1294     succeeded = sho->ConditionalSetProperty<0>(4, condition, delegate);
1295
1296     RUNNER_ASSERT(succeeded);
1297     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1298     RUNNER_ASSERT(1 == g_delegateCalls);
1299
1300     succeeded = sho->ConditionalSetProperty<0>(5, condition);
1301
1302     RUNNER_ASSERT(!succeeded);
1303     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1304
1305     succeeded = sho->ConditionalSetProperty<0>(666, condition, delegate);
1306
1307     RUNNER_ASSERT(!succeeded);
1308     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1309     RUNNER_ASSERT(1 == g_delegateCalls);
1310 }
1311
1312 //////////////////////////////////////////////
1313
1314 /*
1315  * Shared object used by multiple threads as a singleton.
1316  */
1317 class MTSharedObject : public SharedObject<TestTypeList>
1318 {
1319   public:
1320     explicit MTSharedObject(const std::string& semaphore) :
1321         SharedObject<TestTypeList>(semaphore)
1322     {}
1323
1324     void Clear();
1325
1326   private:
1327     friend class SharedObjectFactory<MTSharedObject>;
1328 };
1329
1330 typedef std::shared_ptr<MTSharedObject> MTSharedObjectPtr;
1331
1332 void MTSharedObject::Clear()
1333 {
1334     int array[64] = {};
1335     SetProperty<0>(0);
1336     SetProperty<1>(0);
1337     SetProperty<2>(static_cast<char>(0));
1338     SetProperty<3>(array);
1339 }
1340
1341 /*
1342  * Shared object singleton
1343  */
1344 class SharedObjectSingleton
1345 {
1346   public:
1347     static MTSharedObjectPtr Instance();
1348     static void Destroy();
1349
1350   private:
1351     static MTSharedObjectPtr m_sho;
1352     static DPL::Mutex m_mutex;
1353 };
1354
1355 MTSharedObjectPtr SharedObjectSingleton::m_sho;
1356 DPL::Mutex SharedObjectSingleton::m_mutex;
1357
1358 MTSharedObjectPtr SharedObjectSingleton::Instance()
1359 {
1360     DPL::Mutex::ScopedLock lock(&m_mutex);
1361     if (!m_sho) {
1362         m_sho = SharedObjectFactory<MTSharedObject>::Create(SHM_KEY, SEM_NAME);
1363     }
1364     return m_sho;
1365 }
1366
1367 void SharedObjectSingleton::Destroy()
1368 {
1369     DPL::Mutex::ScopedLock lock(&m_mutex);
1370     m_sho.Reset();
1371 }
1372
1373 /*
1374  * Listening controller
1375  */
1376 class ShmController4 : public ListeningController<MTSharedObject>,
1377     public ISharedObjectListener<0, int>,
1378     public ISharedObjectListener<1, int>,
1379     public ISharedObjectListener<2, char>,
1380     public ISharedObjectListener<3, int[64]>
1381 {
1382   public:
1383     enum {
1384         ADD_LISTENERS = 2,
1385         REMOVE_LISTENERS = 3,
1386         DESTROY_SINGLETON = 4
1387     };
1388
1389     explicit ShmController4(DPL::WaitableEvent* event) :
1390         ListeningController<MTSharedObject>(event),
1391         m_counter(0)
1392     {}
1393
1394     virtual void OnEventReceived(const int& event);
1395
1396     virtual void ValueChanged(size_t propertyEnum,
1397                               const int& value,
1398                               const void* info = NULL);
1399     virtual void ValueChanged(size_t propertyEnum,
1400                               const char& value,
1401                               const void* info = NULL);
1402     virtual void ValueChanged(size_t propertyEnum,
1403                               const int(&value)[64],
1404                               const void* info = NULL);
1405
1406     bool NotRegistered();
1407
1408   private:
1409     void Sleep();
1410
1411     size_t m_counter;
1412     static unsigned int seed = time(NULL);
1413 };
1414
1415 void ShmController4::ValueChanged(size_t propertyEnum,
1416                                   const int& value,
1417                                   const void* /*info*/)
1418 {
1419     LogDebug("ValueChanged(int) " << propertyEnum << " " << value);
1420     if ((propertyEnum == 0 && value == 1) ||
1421         (propertyEnum == 1 && value == 11))
1422     {
1423         m_waitable->Signal();
1424     }
1425 }
1426
1427 void ShmController4::ValueChanged(size_t propertyEnum,
1428                                   const char& value,
1429                                   const void* /*info*/)
1430 {
1431     LogDebug("ValueChanged(char) " << propertyEnum << " " << value);
1432     if (propertyEnum == 2 && value == 'a') {
1433         m_waitable->Signal();
1434     }
1435 }
1436
1437 void ShmController4::ValueChanged(size_t propertyEnum,
1438                                   const int(&value)[64],
1439                                   const void* /*info*/)
1440 {
1441     LogDebug("ValueChanged(int[64]) " << propertyEnum << " " << value[5]);
1442     if (propertyEnum == 3 && value[0] == 0 && value[1] == 1 && value[2] == 2) {
1443         m_waitable->Signal();
1444     }
1445 }
1446
1447 void ShmController4::Sleep()
1448 {
1449     DPL::Thread::GetCurrentThread()->MiliSleep(
1450         rand_r(&seed) % MAX_SINGLETON_LISTENER_DELAY);
1451 }
1452
1453 void ShmController4::OnEventReceived(const int& event)
1454 {
1455     switch (event) {
1456     case INIT_EVENT:
1457         m_so = SharedObjectSingleton::Instance();
1458         m_waitable->Signal();
1459         break;
1460
1461     case DESTROY_EVENT:
1462         LogDebug("Destroying shared object");
1463         // deregister, destroy and notify main thread
1464         m_so.Reset();
1465         m_waitable->Signal();
1466         break;
1467
1468     case ADD_LISTENERS:
1469         // add listener and notify
1470         m_so->AddListener<0, int>(this);
1471         Sleep();
1472         m_so->AddListener<1, int>(this);
1473         Sleep();
1474         m_so->AddListener<2, char>(this);
1475         Sleep();
1476         m_so->AddListener<3, int[64]>(this);
1477         Sleep();
1478         m_waitable->Signal();
1479         break;
1480
1481     case REMOVE_LISTENERS:
1482         // remove listener and notify
1483         m_so->RemoveListener<0, int>(this);
1484         Sleep();
1485         m_so->RemoveListener<1, int>(this);
1486         Sleep();
1487         m_so->RemoveListener<2, char>(this);
1488         Sleep();
1489         m_so->RemoveListener<3, int[64]>(this);
1490         Sleep();
1491         m_waitable->Signal();
1492         break;
1493
1494     case DESTROY_SINGLETON:
1495         SharedObjectSingleton::Destroy();
1496         m_waitable->Signal();
1497         break;
1498
1499     default:
1500         LogError("Unsupported event received: " << event);
1501     }
1502 }
1503
1504 void MultipleWait(DPL::WaitableEvent(&event)[MAX_THREADS]);
1505 void MultipleWait(DPL::WaitableEvent(&event)[MAX_THREADS])
1506 {
1507     for (size_t i = 0; i < MAX_THREADS; ++i) {
1508         Wait(event[i]);
1509     }
1510 }
1511
1512 /*
1513  * Try to remove property listener. If there's no such listener an exception
1514  * should be thrown.
1515  */
1516 #define LISTENER_ASSERT(property) \
1517     Try { \
1518         singleton->RemoveListener<(property)>(controller[i]); \
1519         LogError("Controller " << i << " is still listening for property " \
1520                                << #property); \
1521         RUNNER_ASSERT_MSG(false, "No listeners expected"); \
1522     } \
1523     Catch(MTSharedObject::Exception::ListenerNotFound) { \
1524         RUNNER_ASSERT(true); \
1525     } \
1526
1527 // test
1528 RUNNER_TEST(SharedMemory_130_SharedObjectSingleton)
1529 {
1530     RemoveIpcs();   // we need non existing shm
1531
1532     // writer shared object
1533     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1534             SHM_KEY, SEM_NAME);
1535
1536     ShmController4* controller[MAX_THREADS];
1537     DPL::WaitableEvent waitable[MAX_THREADS];
1538
1539     const int array[64] = { 0, 1, 2 };
1540
1541     // Create and wait for notification. Make sure that the thread/controller 0
1542     // is created first
1543     LogDebug("Creating controllers");
1544     for (size_t i = 0; i < MAX_THREADS; ++i) {
1545         controller[i] = new ShmController4(&waitable[i]);
1546         Wait(waitable[i]);
1547     }
1548
1549     // singleton will be created by thread/controller 0 by now
1550     MTSharedObjectPtr singleton = SharedObjectSingleton::Instance();
1551
1552     for (size_t repeats = 0; repeats < SINGLETON_TEST_REPEATS; ++repeats) {
1553         LogDebug("%%%%%%%%%%%%%%%%%%%%%");
1554         LogDebug("Iteration " << repeats + 1 << " of " << SINGLETON_TEST_REPEATS);
1555         singleton->Clear();
1556
1557         // add listeners
1558         LogDebug("Adding listeners");
1559         for (size_t i = 0; i < MAX_THREADS; ++i) {
1560             controller[i]->PostEvent(ShmController4::ADD_LISTENERS);
1561         }
1562         // wait for listeners
1563         MultipleWait(waitable);
1564
1565         RUNNER_ASSERT((singleton->GetProperty<0, int>()) == 0);
1566         RUNNER_ASSERT((singleton->GetProperty<1, int>()) == 0);
1567         RUNNER_ASSERT((singleton->GetProperty<2, char>()) == 0);
1568
1569         int checkArray[64] = {};
1570         singleton->GetProperty<3>(checkArray);
1571         RUNNER_ASSERT(checkArray[0] == 0);
1572         RUNNER_ASSERT(checkArray[1] == 0);
1573         RUNNER_ASSERT(checkArray[2] == 0);
1574
1575         // change
1576         LogDebug("Setting property 0");
1577         sho->SetProperty<0>(1);
1578         // wait for confirmations
1579         MultipleWait(waitable);
1580
1581         // change
1582         LogDebug("Setting property 1");
1583         sho->SetProperty<1>(11);
1584         // wait for confirmations
1585         MultipleWait(waitable);
1586
1587         // change
1588         LogDebug("Setting property 2");
1589         sho->SetProperty<2>('a');
1590         // wait for confirmations
1591         MultipleWait(waitable);
1592
1593         // change
1594         LogDebug("Setting property 3");
1595         sho->SetProperty<3>(array);
1596         // wait for confirmations
1597         MultipleWait(waitable);
1598
1599         // remove listeners
1600         LogDebug("Removing listeners");
1601         for (size_t i = 0; i < MAX_THREADS; ++i) {
1602             controller[i]->PostEvent(ShmController4::REMOVE_LISTENERS);
1603         }
1604         // wait for listeners
1605         MultipleWait(waitable);
1606
1607         // check if listeners array is empty
1608         LogDebug("Checking listeners");
1609         for (size_t i = 0; i < MAX_THREADS; ++i) {
1610             LISTENER_ASSERT(0);
1611             LISTENER_ASSERT(1);
1612             LISTENER_ASSERT(2);
1613             LISTENER_ASSERT(3);
1614         }
1615
1616         RUNNER_ASSERT((singleton->GetProperty<0, int>()) == 1);
1617         RUNNER_ASSERT((singleton->GetProperty<1, int>()) == 11);
1618         RUNNER_ASSERT((singleton->GetProperty<2, char>()) == 'a');
1619         singleton->GetProperty<3>(checkArray);
1620         RUNNER_ASSERT(checkArray[0] == 0);
1621         RUNNER_ASSERT(checkArray[1] == 1);
1622         RUNNER_ASSERT(checkArray[2] == 2);
1623     }
1624
1625     singleton.Reset();
1626
1627     // Destroy controllers and wait for confirmation. Make sure that
1628     // thread/controller 0 is destroyed in the end
1629     LogDebug("Destroying controllers");
1630     for (int i = MAX_THREADS - 1; i >= 0; --i) {
1631         controller[i]->PostEvent(DESTROY_EVENT);
1632         Wait(waitable[i]);
1633         if (i == 0) {
1634             /*
1635              * Destroy singleton before thread that created it finishes.
1636              * This is to properly close all waitable handles opened by
1637              * SharedObject in thread 0.
1638              */
1639             LogDebug("Destroying singleton");
1640             controller[i]->PostEvent(ShmController4::DESTROY_SINGLETON);
1641             Wait(waitable[i]);
1642         }
1643         delete controller[i];
1644     }
1645 }
1646
1647 #undef LISTENER_ASSERT
1648
1649 /*
1650  *  test preconditions & postconditions:
1651  *   - no existing shared memory with given SHM_KEY
1652  *   - no existing semaphore of given SEM_NAME
1653  */
1654 RUNNER_TEST(SharedMemory_001_Preconditions) {
1655     RemoveIpcs();
1656 }
1657
1658 RUNNER_TEST(SharedMemory_999_Postconditions) {
1659     RemoveIpcs();
1660 }