Tizen 2.0 Release
[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
73 //forward declaration
74 template <typename ... ArgTypesList>
75 class ICDelegate;
76
77 namespace ICD{
78 // This Type defines whether ICDelegate should be destroyed after the call, or
79 // could be reused later.
80 enum class Reuse{ Yes, No };
81 }
82
83 namespace ICDPrivate {
84 // Base for all ICDSharedDatas. Needed for auto disabling and deleting of
85 // ICDSharedDatas.
86 // If ICDSharedData is disabled, delegate won't be called anymore.
87 class ICDSharedDataBase
88 {
89   public:
90     typedef std::shared_ptr<ICDSharedDataBase> ICDSharedDataBasePtr;
91     typedef std::list<ICDSharedDataBasePtr> ICDSharedDataBaseList;
92
93     class ScopedLock : DPL::Noncopyable
94     {
95       public:
96         explicit ScopedLock(ICDSharedDataBasePtr helperBase) :
97             m_scopedLock(&helperBase->m_mutex)
98         {
99         }
100
101       private:
102         DPL::RecursiveMutex::ScopedLock m_scopedLock;
103     };
104
105     ICDSharedDataBase() : m_disabled(false)
106     {
107     }
108     virtual ~ICDSharedDataBase()
109     {
110     }
111
112     bool isDisabled() const
113     {
114         return m_disabled;
115     }
116     virtual void disable()
117     {
118         m_disabled = true;
119     }
120
121     void setIterator(ICDSharedDataBaseList::iterator pos)
122     {
123         m_position = pos;
124     }
125
126     ICDSharedDataBaseList::iterator getIterator() const
127     {
128         return m_position;
129     }
130
131   private:
132     bool m_disabled;
133     DPL::RecursiveMutex m_mutex;
134     ICDSharedDataBaseList::iterator m_position;
135 };
136
137 // Pure Event to remove ICDSharedData.
138 class DeleteICDSharedDataBaseEventCall : public DPL::Event::AbstractEventCall
139 {
140   public:
141     DeleteICDSharedDataBaseEventCall(
142             ICDSharedDataBase::ICDSharedDataBasePtr helperBase) :
143         m_helperBase(helperBase)
144     {
145     }
146     virtual void Call()
147     {
148         m_helperBase.reset();
149     }
150
151   private:
152     ICDSharedDataBase::ICDSharedDataBasePtr m_helperBase;
153 };
154
155
156 class ICDelegateSupportInterface
157 {
158   protected:
159     virtual ~ICDelegateSupportInterface()
160     {
161     }
162     virtual void unregisterICDSharedData(
163             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0;
164     virtual void registerICDSharedData(
165             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0;
166   private:
167     template <typename ... ArgTypesList>
168     friend class DPL::Event::ICDelegate;
169 };
170 } //ICDPrivate
171
172 // Better makeDelegate then DPL::MakeDelegate
173 template<typename ThisType, typename ... ArgTypesList>
174 FastDelegate<void (ArgTypesList ...)>
175 makeDelegate(ThisType* This,
176         void (ThisType::*Func)(ArgTypesList ...))
177 {
178     return FastDelegate<void (ArgTypesList ...)>(This, Func);
179 }
180
181 // ICDelegate class represents delegate that can be called from
182 // any context (thread). The actual calling context (thread) is allways the same
183 // as the context in which it was created.
184 template <typename ... ArgTypesList>
185 class ICDelegate
186 {
187   public:
188     ICDelegate()
189     {
190     }
191     ICDelegate(ICDPrivate::ICDelegateSupportInterface* base,
192                DPL::FastDelegate<void (ArgTypesList ...)> outerDelegate,
193                ICD::Reuse reuse)
194     {
195         ICDSharedData* hlp = new ICDSharedData(base, outerDelegate, reuse);
196         m_helper.reset(hlp);
197     }
198
199     // Calling operator will pass all args passed to it to correct context and
200     // will call apropriate method that was registered with.
201     void operator()(ArgTypesList ... args)
202     {
203         Assert(m_helper);
204         ICDPrivate::ICDSharedDataBase::ScopedLock lock(
205             std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper));
206         m_helper->CallDelegate(args ...);
207     }
208
209     //Disable delegate (it won't be called anymore)
210     void disable()
211     {
212         Assert(m_helper);
213         ICDPrivate::ICDSharedDataBase::ScopedLock lock(
214             std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper));
215         m_helper->disable();
216     }
217
218   protected:
219     ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr
220     getRelatedICDSharedData() const
221     {
222         return std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(m_helper);
223     }
224
225   private:
226     template<typename ThisType>
227     friend class ICDelegateSupport;
228     class ICDSharedData;
229     typedef std::shared_ptr<ICDSharedData> ICDSharedDataPtr;
230
231     struct PrivateEvent
232     {
233         PrivateEvent(ICDSharedDataPtr a_helper,
234                 ArgTypesList ... arguments) :
235             helper(a_helper),
236             args(std::make_tuple(arguments ...))
237         {
238         }
239
240         ICDSharedDataPtr helper;
241         std::tuple<ArgTypesList ...> args;
242     };
243
244     typedef DPL::FastDelegate<void (const PrivateEvent&)>
245     ICDSharedDataDelegateType;
246     class ICDSharedData : private DPL::Event::EventSupport<PrivateEvent>,
247         public std::enable_shared_from_this<ICDSharedData>,
248         public ICDPrivate::ICDSharedDataBase
249     {
250       public:
251         ICDSharedData(
252             ICDPrivate::ICDelegateSupportInterface *base,
253             DPL::FastDelegate<void (ArgTypesList ...)> outerDelegate,
254             ICD::Reuse reuse) :
255             m_base(base),
256             m_outerDelegate(outerDelegate),
257             m_reuse(reuse)
258         {
259             Assert(m_base);
260             // lock is not needed: this object is not shared at that moment
261             m_subDelegate =
262                 DPL::Event::makeDelegate(this,
263                                          &ICDSharedData::delegateForwarder);
264             EventSupport<PrivateEvent>::AddListener(m_subDelegate);
265         }
266
267         void CallDelegate(ArgTypesList ... args)
268         {
269             ICDPrivate::ICDSharedDataBase::ScopedLock lock(
270                 std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(
271                     this->shared_from_this()));
272             if (!isDisabled()) {
273                 EmitEvent(PrivateEvent(this->shared_from_this(),
274                                        args ...));
275             }
276         }
277
278         virtual void disable()
279         {
280             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr(
281                 std::static_pointer_cast<ICDSharedDataBase>(
282                     this->shared_from_this()));
283             ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr);
284             if (!isDisabled()) {
285                 ICDPrivate::ICDSharedDataBase::disable();
286                 EventSupport<PrivateEvent>::RemoveListener(m_subDelegate);
287                 m_base->unregisterICDSharedData(ptr);
288             }
289         }
290
291       private:
292         friend class std::shared_ptr<ICDSharedData>;
293         ICDSharedDataDelegateType m_subDelegate;
294         ICDPrivate::ICDelegateSupportInterface* m_base;
295         DPL::FastDelegate<void (ArgTypesList ...)> m_outerDelegate;
296         ICD::Reuse m_reuse;
297
298         void delegateForwarder(const PrivateEvent& event)
299         {
300             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr(
301                 std::static_pointer_cast<ICDSharedDataBase>(event.helper));
302             ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr);
303
304             Assert(!m_outerDelegate.empty());
305             if (ptr->isDisabled()) {
306                 LogPedantic("ICDSharedData has been disabled - call is ignored");
307             } else {
308                 DPL::Apply(m_outerDelegate, event.args);
309
310                 if(m_reuse == ICD::Reuse::Yes)
311                     return;
312
313                 disable();
314                 deleteICDSharedDataBase(ptr);
315             }
316         }
317     };
318
319     // Schedules helper removal.
320     static void deleteICDSharedDataBase(ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
321     {
322         using namespace ICDPrivate;
323         ICDSharedDataBase::ScopedLock lock(helper);
324         DeleteICDSharedDataBaseEventCall* event =
325           new DeleteICDSharedDataBaseEventCall(helper);
326         if (DPL::Thread::GetCurrentThread() == NULL) {
327             DPL::Event::GetMainEventDispatcherInstance().AddEventCall(event);
328         } else {
329             DPL::Event::ThreadEventDispatcher dispatcher;
330             dispatcher.SetThread(DPL::Thread::GetCurrentThread());
331             dispatcher.AddEventCall(event);
332         }
333     }
334
335     ICDSharedDataPtr m_helper;
336 };
337
338 template <typename ThisType>
339 class ICDelegateSupport : public ICDPrivate::ICDelegateSupportInterface
340 {
341   protected:
342     template<typename ... ArgTypesList>
343     ICDelegate<ArgTypesList ...> makeICDelegate(
344             void (ThisType::*Func)(ArgTypesList ...),
345             ICD::Reuse reuse = ICD::Reuse::No)
346     {
347         ThisType* This = static_cast<ThisType*>(this);
348         ICDelegate<ArgTypesList ...> icdelegate(
349             This,
350             makeDelegate(This, Func),
351             reuse);
352         this->registerICDSharedData(icdelegate.getRelatedICDSharedData());
353         return icdelegate;
354     }
355
356     ICDelegateSupport()
357     {
358     }
359
360     ~ICDelegateSupport()
361     {
362         ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList list =
363             m_ICDSharedDatas;
364         FOREACH(helper, list) {
365             ICDPrivate::ICDSharedDataBase::ScopedLock lock(
366                 std::static_pointer_cast<ICDPrivate::ICDSharedDataBase>(*helper));
367             (*helper)->disable();
368         }
369         m_ICDSharedDatas.clear();
370     }
371
372   private:
373     virtual void unregisterICDSharedData(
374             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
375     {
376         m_ICDSharedDatas.erase(helper->getIterator());
377     }
378
379     virtual void registerICDSharedData(
380             ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper)
381     {
382         helper->setIterator(
383             m_ICDSharedDatas.insert(m_ICDSharedDatas.begin(),
384                                     helper));
385     }
386
387   private:
388     ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList m_ICDSharedDatas;
389 };
390
391 }
392 } //namespace
393
394 #endif // __GXX_EXPERIMENTAL_CXX0X__
395
396 #endif //DPL_INTER_CONTEXT_DELEGATE_H_