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