[Release] wrt-commons_0.2.139
[platform/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             DPL::FastDelegate<void (const DPL::PropertyEvent<bool>&)>(
782                 this,
783                 &ShmController3::OnValueChanged));
784     } else if (event == DESTROY_EVENT) {
785         m_model->boolValue.RemoveListener(
786             DPL::FastDelegate<void (const DPL::PropertyEvent<bool>&)>(
787                 this,
788                 &ShmController3::OnValueChanged));
789         m_model.Reset();
790     }
791 }
792
793 void ShmController3::OnValueChanged(const DPL::PropertyEvent<bool>& event)
794 {
795     if (event.value) {
796         // change back
797         m_model->boolValue.Set(false);
798     } else {
799         LogError("Expected value = true, got false");
800     }
801
802     m_waitable->Signal();
803 }
804
805 RUNNER_TEST(SharedMemory_050_SharedProperty)
806 {
807     RemoveIpcs();
808
809     bool result = true;
810     DPL::WaitableEvent waitable;
811     // listener controller
812     ShmController3 controller(&waitable);
813     Wait(waitable);
814
815     TestSharedObject4Ptr sharedObject = TestSharedObject4::Create();
816
817     for (size_t i = 0; i < SHARED_PROP_REPEATS; ++i) {
818         sharedObject->SetProperty<TestSharedObject4::BOOLEAN>(true);
819         Wait(waitable);
820         result = sharedObject->GetProperty<TestSharedObject4::BOOLEAN,
821                                            bool>();
822         RUNNER_ASSERT(result == false);
823     }
824     controller.PostEvent(DESTROY_EVENT);
825     Wait(waitable);
826 }
827
828 //////////////////////////////////////////////
829
830 class MyModel2 : public DPL::Model
831 {
832   public:
833     explicit MyModel2(const TestSharedObjectPtr& shared_object) :
834         counter(this, shared_object) {}
835
836     SharedProperty<int, 0, TestSharedObject> counter;
837 };
838
839 class SPController : public ListeningController<TestSharedObject>
840 {
841   public:
842     explicit SPController(DPL::WaitableEvent* event) :
843         ListeningController<TestSharedObject>(event), m_repeats(1) {}
844
845     virtual void OnEvent(const int event);
846
847     void OnValueChanged1(const DPL::PropertyEvent<int>& event);
848     void OnValueChanged2(const DPL::PropertyEvent<int>& event);
849
850   private:
851     std::unique_ptr<MyModel2> m_model1;
852     std::unique_ptr<MyModel2> m_model2;
853
854     int m_repeats;
855     std::shared_ptr<TestSharedObject> m_so2;
856 };
857
858 void SPController::OnEvent(const int event)
859 {
860     if (event == INIT_EVENT) {
861         m_so2 = SharedObjectFactory<TestSharedObject>::Create(SHM_KEY,
862                                                               SEM_NAME);
863
864         // create and register 2 models sharing the same property
865         m_model1.Reset(new MyModel2(m_so));
866         m_model2.Reset(new MyModel2(m_so2));
867         m_model1->counter.AddListener(
868             DPL::FastDelegate<void (const DPL::PropertyEvent<int>&)>(
869                 this,
870                 &SPController::OnValueChanged1));
871         m_model2->counter.AddListener(
872             DPL::FastDelegate<void (const DPL::PropertyEvent<int>&)>(
873                 this,
874                 &SPController::OnValueChanged2));
875         m_model1->counter.Set(1);
876     } else if (event == DESTROY_EVENT) {
877         m_model1->counter.RemoveListener(
878             DPL::FastDelegate<void (const DPL::PropertyEvent<int>&)>(
879                 this,
880                 &SPController::OnValueChanged1));
881         m_model2->counter.RemoveListener(
882             DPL::FastDelegate<void (const DPL::PropertyEvent<int>&)>(
883                 this,
884                 &SPController::OnValueChanged2));
885
886         m_model1.Reset();
887         m_model2.Reset();
888         m_so2.Reset();
889     }
890 }
891
892 void SPController::OnValueChanged1(const DPL::PropertyEvent<int>& event)
893 {
894     if (m_repeats >= SINGLE_PROCESS_REPEATS) {
895         PostEvent(DESTROY_EVENT);
896         return;
897     }
898
899     LogDebug("[1] Value changed to " << event.value);
900     m_repeats++;
901     m_model1->counter.Set(event.value + 1);
902 }
903
904 void SPController::OnValueChanged2(const DPL::PropertyEvent<int>& event)
905 {
906     if (m_repeats >= SINGLE_PROCESS_REPEATS) {
907         PostEvent(DESTROY_EVENT);
908         return;
909     }
910
911     LogDebug("[2] Value changed to " << event.value);
912     m_repeats++;
913     m_model2->counter.Set(event.value + 1);
914 }
915
916 RUNNER_TEST(SharedMemory_060_SingleProcess)
917 {
918     RemoveIpcs();
919
920     DPL::WaitableEvent waitable;
921     SPController controller(&waitable);
922     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
923             SHM_KEY,
924             SEM_NAME);
925
926     // wait for creation
927     Wait(waitable);
928
929     // wait for destruction
930     Wait(waitable);
931
932     int value = sho->GetProperty<0, int>();
933
934     LogDebug("final value: " << value);
935
936     // check value
937     RUNNER_ASSERT(value == SINGLE_PROCESS_REPEATS);
938 }
939
940 //////////////////////////////////////////////
941
942 class ListenerTestController : public ListeningController<TestSharedObject>,
943     public ISharedObjectListener<0, int>,
944     public ISharedObjectListener<1, int>,
945     public ISharedObjectListener<2, char>,
946     public ISharedObjectListener<3, int[64]>
947 {
948   public:
949     explicit ListenerTestController(DPL::WaitableEvent* event) :
950         ListeningController<TestSharedObject>(event) {}
951
952     ~ListenerTestController();
953
954     virtual void OnEvent(const int event);
955
956     virtual void ValueChanged(size_t propertyEnum,
957                               const int& value,
958                               const void* info = NULL);
959     virtual void ValueChanged(size_t propertyEnum,
960                               const char& value,
961                               const void* info = NULL);
962     virtual void ValueChanged(size_t propertyEnum,
963                               const int(&value)[64],
964                               const void* info = NULL);
965 };
966
967 ListenerTestController::~ListenerTestController()
968 {}
969
970 void ListenerTestController::OnEvent(const int event)
971 {
972     if (event == INIT_EVENT) {
973         // add self as a listener to shared object
974         m_so->AddListener<0, int>(this);
975         m_so->AddListener<1, int>(this);
976         m_so->AddListener<2, char>(this);
977         m_so->AddListener<3, int[64]>(this);
978     } else if (event == DESTROY_EVENT) {
979         // remove self from listener list
980         m_so->RemoveListener<0, int>(this);
981         m_so->RemoveListener<1, int>(this);
982         m_so->RemoveListener<2, char>(this);
983         m_so->RemoveListener<3, int[64]>(this);
984     }
985 }
986
987 void ListenerTestController::ValueChanged(size_t propertyEnum,
988                                           const int& value,
989                                           const void* /*info*/)
990 {
991     LogDebug("ValueChanged(int) " << propertyEnum << " " << value);
992     if ((propertyEnum == 0 &&
993          value == 1) || (propertyEnum == 1 && value == 2))
994     {
995         g_values[propertyEnum]++;
996         if (g_values[propertyEnum] == 3) {
997             m_waitable->Signal();
998         }
999     }
1000 }
1001
1002 void ListenerTestController::ValueChanged(size_t propertyEnum,
1003                                           const char& value,
1004                                           const void* /*info*/)
1005 {
1006     LogDebug("ValueChanged(char) " << propertyEnum << " " << value);
1007     if (propertyEnum == 2 && value == 'c') {
1008         g_values[propertyEnum]++;
1009         if (g_values[propertyEnum] == 3) {
1010             m_waitable->Signal();
1011         }
1012     }
1013 }
1014
1015 void ListenerTestController::ValueChanged(size_t propertyEnum,
1016                                           const int(&value)[64],
1017                                           const void* /*info*/)
1018 {
1019     LogDebug("ValueChanged(int[64]) " << propertyEnum << " " << value[5]);
1020     if (propertyEnum == 3 && value[5] == 5) {
1021         g_values[propertyEnum]++;
1022         if (g_values[propertyEnum] == 3) {
1023             m_waitable->Signal();
1024         }
1025     }
1026 }
1027
1028 RUNNER_TEST(SharedMemory_070_SharedObjectListeners)
1029 {
1030     RemoveIpcs();
1031
1032     // setup global flags
1033     for (size_t i = 0; i < TestTypeList::Size; ++i) {
1034         g_values[i] = 0;
1035     }
1036
1037     // create shared object
1038     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1039             SHM_KEY, SEM_NAME);
1040
1041     // create 1st listener and wait for it
1042     DPL::WaitableEvent waitable;
1043     ListenerTestController c1(&waitable);
1044     Wait(waitable);
1045
1046     // create 2nd listener and wait for it
1047     ListenerTestController c2(&waitable);
1048     Wait(waitable);
1049
1050     // create 3rd listener and wait for it
1051     ListenerTestController c3(&waitable);
1052     Wait(waitable);
1053
1054     // set properties and wait for result
1055     sho->SetProperty<0, int>(1);
1056     Wait(waitable);
1057
1058     RUNNER_ASSERT(g_values[0] == 3);
1059
1060     sho->SetProperty<1, int>(2);
1061     Wait(waitable);
1062
1063     RUNNER_ASSERT(g_values[1] == 3);
1064
1065     sho->SetProperty<2, char>('c');
1066     Wait(waitable);
1067
1068     RUNNER_ASSERT(g_values[2] == 3);
1069
1070     int array[64];
1071     memset(array, 0, 64 * sizeof(array[0]));
1072     array[5] = 5;
1073     sho->SetProperty<3, int[64]>(array);
1074     Wait(waitable);
1075
1076     RUNNER_ASSERT(g_values[3] == 3);
1077
1078     // finalize listeners
1079     c1.PostEvent(DESTROY_EVENT);
1080     Wait(waitable);
1081
1082     c2.PostEvent(DESTROY_EVENT);
1083     Wait(waitable);
1084
1085     c3.PostEvent(DESTROY_EVENT);
1086     Wait(waitable);
1087 }
1088
1089 //////////////////////////////////////////////
1090
1091 /*
1092  * class simulating DB access
1093  */
1094 class DAO : public DPL::Noncopyable
1095 {
1096   public:
1097     DAO() : m_boolValue(false) {}
1098
1099     void SetBoolValue(const bool& value)
1100     {
1101         m_boolValue = value;
1102     }
1103
1104     bool GetBoolValue() const
1105     {
1106         return m_boolValue;
1107     }
1108
1109   private:
1110     bool m_boolValue;
1111 };
1112
1113 /*
1114  * Model with property having set delegate defined
1115  */
1116 class MyModel3 : public DPL::Model
1117 {
1118   public:
1119     typedef SharedPropertyEx<bool,
1120                              TestSharedObject4::BOOLEAN,
1121                              TestSharedObject4> PropertyType;
1122
1123     MyModel3(const TestSharedObject4Ptr& shared_object, DAO* dao) :
1124         boolValue(this,
1125                   shared_object,
1126                   PropertyType::SetDelegate(dao, &DAO::SetBoolValue))
1127     {}
1128
1129     PropertyType boolValue;
1130 };
1131
1132 RUNNER_TEST(SharedMemory_090_SetPropertyDelegate)
1133 {
1134     RemoveIpcs();
1135
1136     // dao object
1137     DAO dao;
1138
1139     // create shared object
1140     TestSharedObject4Ptr sho = TestSharedObject4::Create();
1141
1142     // set property but call dao delegate within semaphore
1143     sho->SetProperty<TestSharedObject4::BOOLEAN>(
1144         true,
1145         MyModel3::PropertyType::SetDelegate(&dao, &DAO::SetBoolValue));
1146
1147     // check dao value
1148     RUNNER_ASSERT(dao.GetBoolValue() == true);
1149
1150     // check shared object value
1151     bool shoValue = sho->GetProperty<TestSharedObject4::BOOLEAN, bool>();
1152     RUNNER_ASSERT(shoValue == true);
1153
1154     // try the same with shared property
1155     MyModel3 model(sho, &dao);
1156
1157     // set property
1158     model.boolValue.Set(false);
1159
1160     // check dao value
1161     RUNNER_ASSERT(dao.GetBoolValue() == false);
1162
1163     // check sho value
1164     shoValue = sho->GetProperty<TestSharedObject4::BOOLEAN, bool>();
1165     RUNNER_ASSERT(shoValue == false);
1166
1167     // check property value
1168     RUNNER_ASSERT(model.boolValue.Get() == false);
1169 }
1170
1171 //////////////////////////////////////////////
1172
1173 /*
1174  * Lazy initialization test shared object
1175  */
1176 class LazySharedObject : public SharedObject<TestTypeList>
1177 {
1178   private:
1179     LazySharedObject() :
1180         m_read(false)
1181     {}
1182
1183   public:
1184     explicit LazySharedObject(const std::string& semaphore) :
1185         SharedObject<TestTypeList>(semaphore)
1186         , m_read(false)
1187     {}
1188
1189     void Init();
1190
1191     bool IsRead() const
1192     {
1193         return m_read;
1194     }
1195
1196   private:
1197     friend class SharedObjectFactory<LazySharedObject>;
1198
1199     bool m_read;
1200 };
1201
1202 void LazySharedObject::Init()
1203 {
1204     SetPropertyInternal<0>(42);
1205     m_read = true;
1206 }
1207
1208 RUNNER_TEST(SharedMemory_100_LazyInit)
1209 {
1210     RemoveIpcs();
1211
1212     typedef std::shared_ptr<LazySharedObject> LazySharedObjectPtr;
1213
1214     // create shared object
1215     LazySharedObjectPtr sho = SharedObjectFactory<LazySharedObject>::Create(
1216             SHM_KEY, SEM_NAME);
1217
1218     RUNNER_ASSERT(sho->IsRead() == false);
1219
1220     // get property causing lazy init
1221     int value = sho->GetProperty<0, int>();
1222
1223     RUNNER_ASSERT(sho->IsRead() == true);
1224     RUNNER_ASSERT(value == 42);
1225
1226     // create another object
1227     LazySharedObjectPtr sho2 = SharedObjectFactory<LazySharedObject>::Create(
1228             SHM_KEY, SEM_NAME);
1229
1230     RUNNER_ASSERT(sho2->IsRead() == false);
1231
1232     // get property NOT causing lazy init
1233     value = sho2->GetProperty<0, int>();
1234
1235     RUNNER_ASSERT(sho2->IsRead() == false);
1236     RUNNER_ASSERT(value == 42);
1237
1238     // destroy both objects
1239     sho.Reset();
1240     sho2.Reset();
1241
1242     // create shared object
1243     LazySharedObjectPtr sho3 = SharedObjectFactory<LazySharedObject>::Create(
1244             SHM_KEY, SEM_NAME);
1245
1246     RUNNER_ASSERT(sho3->IsRead() == false);
1247
1248     // set property causing lazy init
1249     sho3->SetProperty<0>(43);
1250     value = sho3->GetProperty<0, int>();
1251
1252     RUNNER_ASSERT(sho3->IsRead() == true);
1253     RUNNER_ASSERT(value == 43);
1254 }
1255
1256 //////////////////////////////////////////////
1257
1258 bool SetCondition(const int& readValue, int& setValue);
1259 bool SetCondition(const int& readValue, int& setValue)
1260 {
1261     LogDebug("Condition delegate called with read value = " << readValue <<
1262              " and set value = " << setValue);
1263
1264     if (readValue > 3) {
1265         LogDebug("Condition is false");
1266         return false;
1267     }
1268
1269     LogDebug("Condition is true");
1270     if (4 == setValue) {
1271         setValue = 10;
1272         LogDebug("Changing set value to " << setValue);
1273     }
1274     return true;
1275 }
1276
1277 void SetDelegate(const int& readValue);
1278 void SetDelegate(const int& readValue)
1279 {
1280     LogDebug("Set delegate called " << readValue);
1281     g_delegateCalls++;
1282 }
1283
1284 RUNNER_TEST(SharedMemory_120_ConditionalSet)
1285 {
1286     RemoveIpcs();
1287
1288     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1289             SHM_KEY,
1290             SEM_NAME);
1291
1292     g_delegateCalls = 0;
1293
1294     RUNNER_ASSERT(0 == (sho->GetProperty<0, int>()));
1295
1296     DPL::FastDelegate<bool (const int&, int&)> condition(&SetCondition);
1297     DPL::FastDelegate<void (const int&)> delegate(&SetDelegate);
1298
1299     bool succeeded = false;
1300
1301     succeeded = sho->ConditionalSetProperty<0>(-2, condition);
1302
1303     RUNNER_ASSERT(succeeded);
1304     RUNNER_ASSERT(-2 == (sho->GetProperty<0, int>()));
1305
1306     succeeded = sho->ConditionalSetProperty<0>(4, condition, delegate);
1307
1308     RUNNER_ASSERT(succeeded);
1309     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1310     RUNNER_ASSERT(1 == g_delegateCalls);
1311
1312     succeeded = sho->ConditionalSetProperty<0>(5, condition);
1313
1314     RUNNER_ASSERT(!succeeded);
1315     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1316
1317     succeeded = sho->ConditionalSetProperty<0>(666, condition, delegate);
1318
1319     RUNNER_ASSERT(!succeeded);
1320     RUNNER_ASSERT(10 == (sho->GetProperty<0, int>()));
1321     RUNNER_ASSERT(1 == g_delegateCalls);
1322 }
1323
1324 //////////////////////////////////////////////
1325
1326 /*
1327  * Shared object used by multiple threads as a singleton.
1328  */
1329 class MTSharedObject : public SharedObject<TestTypeList>
1330 {
1331   public:
1332     explicit MTSharedObject(const std::string& semaphore) :
1333         SharedObject<TestTypeList>(semaphore)
1334     {}
1335
1336     void Clear();
1337
1338   private:
1339     friend class SharedObjectFactory<MTSharedObject>;
1340 };
1341
1342 typedef std::shared_ptr<MTSharedObject> MTSharedObjectPtr;
1343
1344 void MTSharedObject::Clear()
1345 {
1346     int array[64] = {};
1347     SetProperty<0>(0);
1348     SetProperty<1>(0);
1349     SetProperty<2>(static_cast<char>(0));
1350     SetProperty<3>(array);
1351 }
1352
1353 /*
1354  * Shared object singleton
1355  */
1356 class SharedObjectSingleton
1357 {
1358   public:
1359     static MTSharedObjectPtr Instance();
1360     static void Destroy();
1361
1362   private:
1363     static MTSharedObjectPtr m_sho;
1364     static DPL::Mutex m_mutex;
1365 };
1366
1367 MTSharedObjectPtr SharedObjectSingleton::m_sho;
1368 DPL::Mutex SharedObjectSingleton::m_mutex;
1369
1370 MTSharedObjectPtr SharedObjectSingleton::Instance()
1371 {
1372     DPL::Mutex::ScopedLock lock(&m_mutex);
1373     if (!m_sho) {
1374         m_sho = SharedObjectFactory<MTSharedObject>::Create(SHM_KEY, SEM_NAME);
1375     }
1376     return m_sho;
1377 }
1378
1379 void SharedObjectSingleton::Destroy()
1380 {
1381     DPL::Mutex::ScopedLock lock(&m_mutex);
1382     m_sho.Reset();
1383 }
1384
1385 /*
1386  * Listening controller
1387  */
1388 class ShmController4 : public ListeningController<MTSharedObject>,
1389     public ISharedObjectListener<0, int>,
1390     public ISharedObjectListener<1, int>,
1391     public ISharedObjectListener<2, char>,
1392     public ISharedObjectListener<3, int[64]>
1393 {
1394   public:
1395     enum {
1396         ADD_LISTENERS = 2,
1397         REMOVE_LISTENERS = 3,
1398         DESTROY_SINGLETON = 4
1399     };
1400
1401     explicit ShmController4(DPL::WaitableEvent* event) :
1402         ListeningController<MTSharedObject>(event),
1403         m_counter(0)
1404     {}
1405
1406     virtual void OnEventReceived(const int& event);
1407
1408     virtual void ValueChanged(size_t propertyEnum,
1409                               const int& value,
1410                               const void* info = NULL);
1411     virtual void ValueChanged(size_t propertyEnum,
1412                               const char& value,
1413                               const void* info = NULL);
1414     virtual void ValueChanged(size_t propertyEnum,
1415                               const int(&value)[64],
1416                               const void* info = NULL);
1417
1418     bool NotRegistered();
1419
1420   private:
1421     void Sleep();
1422
1423     size_t m_counter;
1424     static unsigned int seed = time(NULL);
1425 };
1426
1427 void ShmController4::ValueChanged(size_t propertyEnum,
1428                                   const int& value,
1429                                   const void* /*info*/)
1430 {
1431     LogDebug("ValueChanged(int) " << propertyEnum << " " << value);
1432     if ((propertyEnum == 0 && value == 1) ||
1433         (propertyEnum == 1 && value == 11))
1434     {
1435         m_waitable->Signal();
1436     }
1437 }
1438
1439 void ShmController4::ValueChanged(size_t propertyEnum,
1440                                   const char& value,
1441                                   const void* /*info*/)
1442 {
1443     LogDebug("ValueChanged(char) " << propertyEnum << " " << value);
1444     if (propertyEnum == 2 && value == 'a') {
1445         m_waitable->Signal();
1446     }
1447 }
1448
1449 void ShmController4::ValueChanged(size_t propertyEnum,
1450                                   const int(&value)[64],
1451                                   const void* /*info*/)
1452 {
1453     LogDebug("ValueChanged(int[64]) " << propertyEnum << " " << value[5]);
1454     if (propertyEnum == 3 && value[0] == 0 && value[1] == 1 && value[2] == 2) {
1455         m_waitable->Signal();
1456     }
1457 }
1458
1459 void ShmController4::Sleep()
1460 {
1461     DPL::Thread::GetCurrentThread()->MiliSleep(
1462         rand_r(&seed) % MAX_SINGLETON_LISTENER_DELAY);
1463 }
1464
1465 void ShmController4::OnEventReceived(const int& event)
1466 {
1467     switch (event) {
1468     case INIT_EVENT:
1469         m_so = SharedObjectSingleton::Instance();
1470         m_waitable->Signal();
1471         break;
1472
1473     case DESTROY_EVENT:
1474         LogDebug("Destroying shared object");
1475         // deregister, destroy and notify main thread
1476         m_so.Reset();
1477         m_waitable->Signal();
1478         break;
1479
1480     case ADD_LISTENERS:
1481         // add listener and notify
1482         m_so->AddListener<0, int>(this);
1483         Sleep();
1484         m_so->AddListener<1, int>(this);
1485         Sleep();
1486         m_so->AddListener<2, char>(this);
1487         Sleep();
1488         m_so->AddListener<3, int[64]>(this);
1489         Sleep();
1490         m_waitable->Signal();
1491         break;
1492
1493     case REMOVE_LISTENERS:
1494         // remove listener and notify
1495         m_so->RemoveListener<0, int>(this);
1496         Sleep();
1497         m_so->RemoveListener<1, int>(this);
1498         Sleep();
1499         m_so->RemoveListener<2, char>(this);
1500         Sleep();
1501         m_so->RemoveListener<3, int[64]>(this);
1502         Sleep();
1503         m_waitable->Signal();
1504         break;
1505
1506     case DESTROY_SINGLETON:
1507         SharedObjectSingleton::Destroy();
1508         m_waitable->Signal();
1509         break;
1510
1511     default:
1512         LogError("Unsupported event received: " << event);
1513     }
1514 }
1515
1516 void MultipleWait(DPL::WaitableEvent(&event)[MAX_THREADS]);
1517 void MultipleWait(DPL::WaitableEvent(&event)[MAX_THREADS])
1518 {
1519     for (size_t i = 0; i < MAX_THREADS; ++i) {
1520         Wait(event[i]);
1521     }
1522 }
1523
1524 /*
1525  * Try to remove property listener. If there's no such listener an exception
1526  * should be thrown.
1527  */
1528 #define LISTENER_ASSERT(property) \
1529     Try { \
1530         singleton->RemoveListener<(property)>(controller[i]); \
1531         LogError("Controller " << i << " is still listening for property " \
1532                                << #property); \
1533         RUNNER_ASSERT_MSG(false, "No listeners expected"); \
1534     } \
1535     Catch(MTSharedObject::Exception::ListenerNotFound) { \
1536         RUNNER_ASSERT(true); \
1537     } \
1538
1539 // test
1540 RUNNER_TEST(SharedMemory_130_SharedObjectSingleton)
1541 {
1542     RemoveIpcs();   // we need non existing shm
1543
1544     // writer shared object
1545     TestSharedObjectPtr sho = SharedObjectFactory<TestSharedObject>::Create(
1546             SHM_KEY, SEM_NAME);
1547
1548     ShmController4* controller[MAX_THREADS];
1549     DPL::WaitableEvent waitable[MAX_THREADS];
1550
1551     const int array[64] = { 0, 1, 2 };
1552
1553     // Create and wait for notification. Make sure that the thread/controller 0
1554     // is created first
1555     LogDebug("Creating controllers");
1556     for (size_t i = 0; i < MAX_THREADS; ++i) {
1557         controller[i] = new ShmController4(&waitable[i]);
1558         Wait(waitable[i]);
1559     }
1560
1561     // singleton will be created by thread/controller 0 by now
1562     MTSharedObjectPtr singleton = SharedObjectSingleton::Instance();
1563
1564     for (size_t repeats = 0; repeats < SINGLETON_TEST_REPEATS; ++repeats) {
1565         LogDebug("%%%%%%%%%%%%%%%%%%%%%");
1566         LogDebug("Iteration " << repeats + 1 << " of " << SINGLETON_TEST_REPEATS);
1567         singleton->Clear();
1568
1569         // add listeners
1570         LogDebug("Adding listeners");
1571         for (size_t i = 0; i < MAX_THREADS; ++i) {
1572             controller[i]->PostEvent(ShmController4::ADD_LISTENERS);
1573         }
1574         // wait for listeners
1575         MultipleWait(waitable);
1576
1577         RUNNER_ASSERT((singleton->GetProperty<0, int>()) == 0);
1578         RUNNER_ASSERT((singleton->GetProperty<1, int>()) == 0);
1579         RUNNER_ASSERT((singleton->GetProperty<2, char>()) == 0);
1580
1581         int checkArray[64] = {};
1582         singleton->GetProperty<3>(checkArray);
1583         RUNNER_ASSERT(checkArray[0] == 0);
1584         RUNNER_ASSERT(checkArray[1] == 0);
1585         RUNNER_ASSERT(checkArray[2] == 0);
1586
1587         // change
1588         LogDebug("Setting property 0");
1589         sho->SetProperty<0>(1);
1590         // wait for confirmations
1591         MultipleWait(waitable);
1592
1593         // change
1594         LogDebug("Setting property 1");
1595         sho->SetProperty<1>(11);
1596         // wait for confirmations
1597         MultipleWait(waitable);
1598
1599         // change
1600         LogDebug("Setting property 2");
1601         sho->SetProperty<2>('a');
1602         // wait for confirmations
1603         MultipleWait(waitable);
1604
1605         // change
1606         LogDebug("Setting property 3");
1607         sho->SetProperty<3>(array);
1608         // wait for confirmations
1609         MultipleWait(waitable);
1610
1611         // remove listeners
1612         LogDebug("Removing listeners");
1613         for (size_t i = 0; i < MAX_THREADS; ++i) {
1614             controller[i]->PostEvent(ShmController4::REMOVE_LISTENERS);
1615         }
1616         // wait for listeners
1617         MultipleWait(waitable);
1618
1619         // check if listeners array is empty
1620         LogDebug("Checking listeners");
1621         for (size_t i = 0; i < MAX_THREADS; ++i) {
1622             LISTENER_ASSERT(0);
1623             LISTENER_ASSERT(1);
1624             LISTENER_ASSERT(2);
1625             LISTENER_ASSERT(3);
1626         }
1627
1628         RUNNER_ASSERT((singleton->GetProperty<0, int>()) == 1);
1629         RUNNER_ASSERT((singleton->GetProperty<1, int>()) == 11);
1630         RUNNER_ASSERT((singleton->GetProperty<2, char>()) == 'a');
1631         singleton->GetProperty<3>(checkArray);
1632         RUNNER_ASSERT(checkArray[0] == 0);
1633         RUNNER_ASSERT(checkArray[1] == 1);
1634         RUNNER_ASSERT(checkArray[2] == 2);
1635     }
1636
1637     singleton.Reset();
1638
1639     // Destroy controllers and wait for confirmation. Make sure that
1640     // thread/controller 0 is destroyed in the end
1641     LogDebug("Destroying controllers");
1642     for (int i = MAX_THREADS - 1; i >= 0; --i) {
1643         controller[i]->PostEvent(DESTROY_EVENT);
1644         Wait(waitable[i]);
1645         if (i == 0) {
1646             /*
1647              * Destroy singleton before thread that created it finishes.
1648              * This is to properly close all waitable handles opened by
1649              * SharedObject in thread 0.
1650              */
1651             LogDebug("Destroying singleton");
1652             controller[i]->PostEvent(ShmController4::DESTROY_SINGLETON);
1653             Wait(waitable[i]);
1654         }
1655         delete controller[i];
1656     }
1657 }
1658
1659 #undef LISTENER_ASSERT
1660
1661 /*
1662  *  test preconditions & postconditions:
1663  *   - no existing shared memory with given SHM_KEY
1664  *   - no existing semaphore of given SEM_NAME
1665  */
1666 RUNNER_TEST(SharedMemory_001_Preconditions) {
1667     RemoveIpcs();
1668 }
1669
1670 RUNNER_TEST(SharedMemory_999_Postconditions) {
1671     RemoveIpcs();
1672 }