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