merge with master
[platform/framework/web/wrt-commons.git] / modules / event / include / dpl / event / inter_context_delegate.h
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        inter_context_delegate.h
18  * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
19  * @version     1.0
20  * @brief       This file is the header file of inter context delegate
21  */
22
23 #ifndef DPL_INTER_CONTEXT_DELEGATE_H_
24 #define DPL_INTER_CONTEXT_DELEGATE_H_
25
26 #ifndef __GXX_EXPERIMENTAL_CXX0X__ // C++11 compatibility check
27 # include <bits/c++0x_warning.h>
28 #else
29
30 #include <dpl/event/event_support.h>
31 #include <dpl/event/thread_event_dispatcher.h>
32 #include <dpl/event/main_event_dispatcher.h>
33 #include <dpl/fast_delegate.h>
34 #include <dpl/generic_event.h>
35 #include <dpl/foreach.h>
36 #include <dpl/recursive_mutex.h>
37 #include <dpl/mutex.h>
38 #include <dpl/noncopyable.h>
39 #include <dpl/log/log.h>
40 #include <dpl/assert.h>
41 #include <dpl/apply.h>
42 #include <tuple>
43 #include <list>
44 #include <memory>
45
46 /*
47  * - Created ICDelegate can be passed freely to other threads.
48  * - ICDelegate can be called just once. All following calls will be
49  *   silently ignored.
50  * - When ICDelegateSupport is destroyed, all its ICDelegates
51  *   are invalidated and safetly removed.
52  * - ICDelegate can be invalidated by call to disable method on it.
53  * - To use ICDelegate you have to do two steps:
54  *
55  * 1. Class that will be used to create delegate have to derive from templated
56  *    class ICDelegateSupport<T>
57  *
58  * 2. Create instance of ICDelegate by calling
59  *    makeICDelegate and passing to it pointer to method
60  *
61  *    class A : ICDelegateSupport<A>
62  *    {
63  *    void methodA(int) {}
64  *    void createDelegate() {
65  *        ICDelegate<int> dlg;
66  *        dlg = makeICDelegate(&A::MethodA);
67  *    };
68  */
69
70 namespace DPL {
71 namespace Event {
72 //forward declaration
73 template <typename ... ArgTypesList>
74 class ICDelegate;
75
76 namespace ICD {
77 // This Type defines whether ICDelegate should be destroyed after the call, or
78 // could be reused later.
79 enum class Reuse
80 {
81     Yes, No
82 };
83 }
84
85 namespace ICDPrivate {
86 // Base for all ICDSharedDatas. Needed for auto disabling and deleting of
87 // ICDSharedDatas.
88 // If ICDSharedData is disabled, delegate won't be called anymore.
89 class ICDSharedDataBase
90 {
91   public:
92     typedef std::shared_ptr<ICDSharedDataBase> ICDSharedDataBasePtr;
93     typedef std::list<ICDSharedDataBasePtr> ICDSharedDataBaseList;
94
95     class ScopedLock : DPL::Noncopyable
96     {
97       public:
98         explicit ScopedLock(ICDSharedDataBasePtr helperBase) :
99             m_scopedLock(&helperBase->m_mutex)
100         {}
101
102       private:
103         DPL::RecursiveMutex::ScopedLock m_scopedLock;
104     };
105
106     ICDSharedDataBase() : m_disabled(false)
107     {}
108     virtual ~ICDSharedDataBase()
109     {}
110
111     bool isDisabled() const
112     {
113         return m_disabled;
114     }
115     virtual void disable()
116     {
117         m_disabled = true;
118     }
119
120     void setIterator(ICDSharedDataBaseList::iterator pos)
121     {
122         m_position = pos;
123     }
124
125     ICDSharedDataBaseList::iterator getIterator() const
126     {
127         return m_position;
128     }
129
130   private:
131     bool m_disabled;
132     DPL::RecursiveMutex m_mutex;
133     ICDSharedDataBaseList::iterator m_position;
134 };
135
136 // Pure Event to remove ICDSharedData.
137 class DeleteICDSharedDataBaseEventCall : public DPL::Event::AbstractEventCall
138 {
139   public:
140     DeleteICDSharedDataBaseEventCall(
141         ICDSharedDataBase::ICDSharedDataBasePtr helperBase) :
142         m_helperBase(helperBase)
143     {}
144     virtual void Call()
145     {
146         m_helperBase.reset();
147     }
148
149   private:
150     ICDSharedDataBase::ICDSharedDataBasePtr m_helperBase;
151 };
152
153 class ICDelegateSupportInterface
154 {
155   protected:
156     virtual ~ICDelegateSupportInterface()
157     {}
158     virtual void unregisterICDSharedData(
159         ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0;
160     virtual void registerICDSharedData(
161         ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0;
162
163   private:
164     template <typename ... ArgTypesList>
165     friend class DPL::Event::ICDelegate;
166 };
167 } //ICDPrivate
168
169 // Better makeDelegate then DPL::MakeDelegate
170 template<typename ThisType, typename ... ArgTypesList>
171 FastDelegate<void (ArgTypesList ...)>
172 makeDelegate(ThisType* This,
173              void (ThisType::*Func)(ArgTypesList ...))
174 {
175     return FastDelegate<void (ArgTypesList ...)>(This, Func);
176 }
177
178 // ICDelegate class represents delegate that can be called from
179 // any context (thread). The actual calling context (thread) is allways the same
180 // as the context in which it was created.
181 template <typename ... ArgTypesList>
182 class ICDelegate
183 {
184   public:
185     ICDelegate()
186     {}
187     ICDelegate(ICDPrivate::ICDelegateSupportInterface* base,
188                DPL::FastDelegate<void (ArgTypesList ...)> outerDelegate,
189                ICD::Reuse reuse)
190     {
191         ICDSharedData* hlp = new ICDSharedData(base, outerDelegate, reuse);
192         m_helper.reset(hlp);
193     }
194
195     // Calling operator will pass all args passed to it to correct context and
196     // will call apropriate method that was registered with.
197     void operator()(ArgTypesList ... args)
198     {
199         Assert(m_helper);
200         ICDPrivate::ICDSharedDataBase::ScopedLock lock(
201             std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper));
202         m_helper->CallDelegate(args ...);
203     }
204
205     //Disable delegate (it won't be called anymore)
206     void disable()
207     {
208         Assert(m_helper);
209         ICDPrivate::ICDSharedDataBase::ScopedLock lock(
210             std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper));
211         m_helper->disable();
212     }
213
214   protected:
215     ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr
216     getRelatedICDSharedData() const
217     {
218         return std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper);
219     }
220
221   private:
222     template<typename ThisType>
223     friend class ICDelegateSupport;
224     class ICDSharedData;
225     typedef std::shared_ptr<ICDSharedData> ICDSharedDataPtr;
226
227     struct PrivateEvent
228     {
229         PrivateEvent(ICDSharedDataPtr a_helper,
230                      ArgTypesList ... arguments) :
231             helper(a_helper),
232             args(std::make_tuple(arguments ...))
233         {}
234
235         ICDSharedDataPtr helper;
236         std::tuple<ArgTypesList ...> args;
237     };
238
239     typedef DPL::FastDelegate<void (const PrivateEvent&)>
240     ICDSharedDataDelegateType;
241     class ICDSharedData : private DPL::Event::EventSupport<PrivateEvent>,
242         public std::enable_shared_from_this<ICDSharedData>,
243         public ICDPrivate::ICDSharedDataBase
244     {
245       public:
246         ICDSharedData(
247             ICDPrivate::ICDelegateSupportInterface *base,
248             DPL::FastDelegate<void (ArgTypesList ...)> outerDelegate,
249             ICD::Reuse reuse) :
250             m_base(base),
251             m_outerDelegate(outerDelegate),
252             m_reuse(reuse)
253         {
254             Assert(m_base);
255             // lock is not needed: this object is not shared at that moment
256             m_subDelegate =
257                 DPL::Event::makeDelegate(this,
258                                          &ICDSharedData::delegateForwarder);
259             EventSupport<PrivateEvent>::AddListener(m_subDelegate);
260         }
261
262         void CallDelegate(ArgTypesList ... args)
263         {
264             ICDPrivate::ICDSharedDataBase::ScopedLock lock(
265                 std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(
266                     this->shared_from_this()));
267             if (!isDisabled()) {
268                 EmitEvent(PrivateEvent(this->shared_from_this(),
269                                        args ...));
270             }
271         }
272
273         virtual void disable()
274         {
275             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr(
276                 std::static_pointer_cast<ICDSharedDataBase>(
277                     this->shared_from_this()));
278             ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr);
279             if (!isDisabled()) {
280                 ICDPrivate::ICDSharedDataBase::disable();
281                 EventSupport<PrivateEvent>::RemoveListener(m_subDelegate);
282                 m_base->unregisterICDSharedData(ptr);
283             }
284         }
285
286       private:
287         friend class std::shared_ptr<ICDSharedData>;
288         ICDSharedDataDelegateType m_subDelegate;
289         ICDPrivate::ICDelegateSupportInterface* m_base;
290         DPL::FastDelegate<void (ArgTypesList ...)> m_outerDelegate;
291         ICD::Reuse m_reuse;
292
293         void delegateForwarder(const PrivateEvent& event)
294         {
295             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr(
296                 std::static_pointer_cast<ICDSharedDataBase>(event.helper));
297             ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr);
298
299             Assert(!m_outerDelegate.empty());
300             if (ptr->isDisabled()) {
301                 LogPedantic("ICDSharedData has been disabled - call is ignored");
302             } else {
303                 DPL::Apply(m_outerDelegate, event.args);
304
305                 if (m_reuse == ICD::Reuse::Yes) {
306                     return;
307                 }
308
309                 disable();
310                 deleteICDSharedDataBase(ptr);
311             }
312         }
313     };
314
315     // Schedules helper removal.
316     static void deleteICDSharedDataBase(
317         ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
318     {
319         using namespace ICDPrivate;
320         ICDSharedDataBase::ScopedLock lock(helper);
321         DeleteICDSharedDataBaseEventCall* event =
322             new DeleteICDSharedDataBaseEventCall(helper);
323         if (DPL::Thread::GetCurrentThread() == NULL) {
324             DPL::Event::GetMainEventDispatcherInstance().AddEventCall(event);
325         } else {
326             DPL::Event::ThreadEventDispatcher dispatcher;
327             dispatcher.SetThread(DPL::Thread::GetCurrentThread());
328             dispatcher.AddEventCall(event);
329         }
330     }
331
332     ICDSharedDataPtr m_helper;
333 };
334
335 template <typename ThisType>
336 class ICDelegateSupport : public ICDPrivate::ICDelegateSupportInterface
337 {
338   protected:
339     template<typename ... ArgTypesList>
340     ICDelegate<ArgTypesList ...> makeICDelegate(
341         void (ThisType::*Func)(ArgTypesList ...),
342         ICD::Reuse reuse = ICD::Reuse::No)
343     {
344         ThisType* This = static_cast<ThisType*>(this);
345         ICDelegate<ArgTypesList ...> icdelegate(
346             This,
347             makeDelegate(This, Func),
348             reuse);
349         this->registerICDSharedData(icdelegate.getRelatedICDSharedData());
350         return icdelegate;
351     }
352
353     ICDelegateSupport()
354     {}
355
356     ~ICDelegateSupport()
357     {
358         ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList list =
359             m_ICDSharedDatas;
360         FOREACH(helper, list) {
361             ICDPrivate::ICDSharedDataBase::ScopedLock lock(
362                 std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(*helper));
363             (*helper)->disable();
364         }
365         m_ICDSharedDatas.clear();
366     }
367
368   private:
369     virtual void unregisterICDSharedData(
370         ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
371     {
372         m_ICDSharedDatas.erase(helper->getIterator());
373     }
374
375     virtual void registerICDSharedData(
376         ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
377     {
378         helper->setIterator(
379             m_ICDSharedDatas.insert(m_ICDSharedDatas.begin(),
380                                     helper));
381     }
382
383   private:
384     ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList m_ICDSharedDatas;
385 };
386 }
387 } //namespace
388
389 #endif // __GXX_EXPERIMENTAL_CXX0X__
390
391 #endif //DPL_INTER_CONTEXT_DELEGATE_H_