tizen 2.4 release
[framework/web/wrt-commons.git] / modules / core / include / dpl / thread.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        thread.h
18  * @author      Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @version     1.0
20  * @brief       This file is the implementation file of thread
21  */
22 #ifndef DPL_THREAD_H
23 #define DPL_THREAD_H
24
25 #include <dpl/waitable_handle_watch_support.h>
26 #include <dpl/noncopyable.h>
27 #include <dpl/exception.h>
28 #include <dpl/assert.h>
29 #include <boost/optional.hpp>
30 #include <stdint.h>
31 #include <cstdlib>
32 #include <pthread.h>
33 #include <thread>
34 #include <vector>
35 #include <list>
36 #include <mutex>
37
38 namespace DPL {
39 class Thread :
40     private Noncopyable,
41     public WaitableHandleWatchSupport
42 {
43   public:
44     class Exception
45     {
46       public:
47         DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
48         DECLARE_EXCEPTION_TYPE(Base, CreateFailed)
49         DECLARE_EXCEPTION_TYPE(Base, DestroyFailed)
50         DECLARE_EXCEPTION_TYPE(Base, RunFailed)
51         DECLARE_EXCEPTION_TYPE(Base, QuitFailed)
52         DECLARE_EXCEPTION_TYPE(Base, UnmanagedThread)
53     };
54
55     typedef void (*EventDeleteProc)(void *event, void *userParam);
56     typedef void (*EventDispatchProc)(void *event, void *userParam);
57
58   protected:
59     /**
60      * Main thread entry
61      * The method is intended to be overloaded with custom code.
62      * Default implementation just executes Exec method to process
63      * all thread exents
64      */
65     virtual int ThreadEntry();
66
67     /**
68      * Start processing of thread events
69      */
70     int Exec();
71
72   private:
73     struct InternalEvent
74     {
75         void *event;
76         void *userParam;
77         EventDispatchProc eventDispatchProc;
78         EventDeleteProc eventDeleteProc;
79
80         InternalEvent(void *eventArg,
81                       void *userParamArg,
82                       EventDispatchProc eventDispatchProcArg,
83                       EventDeleteProc eventDeleteProcArg) :
84             event(eventArg),
85             userParam(userParamArg),
86             eventDispatchProc(eventDispatchProcArg),
87             eventDeleteProc(eventDeleteProcArg)
88         {}
89     };
90
91     struct InternalTimedEvent :
92         InternalEvent
93     {
94         unsigned long dueTimeMiliseconds;
95         unsigned long registerTimeMiliseconds;
96
97         InternalTimedEvent(void *eventArg,
98                            void *userParamArg,
99                            unsigned long dueTimeMilisecondsArg,
100                            unsigned long registerTimeMilisecondsArg,
101                            EventDispatchProc eventDispatchProcArg,
102                            EventDeleteProc eventDeleteProcArg) :
103             InternalEvent(eventArg,
104                           userParamArg,
105                           eventDispatchProcArg,
106                           eventDeleteProcArg),
107             dueTimeMiliseconds(dueTimeMilisecondsArg),
108             registerTimeMiliseconds(registerTimeMilisecondsArg)
109         {}
110
111         bool operator<(const InternalTimedEvent &other)
112         {
113             return registerTimeMiliseconds + dueTimeMiliseconds >
114                    other.registerTimeMiliseconds + other.dueTimeMiliseconds;
115         }
116     };
117
118     // Internal event list
119     typedef std::list<InternalEvent> InternalEventList;
120
121     // Internal timed event list
122     typedef std::vector<InternalTimedEvent> InternalTimedEventVector;
123
124     // State managment
125     std::thread m_thread;
126     volatile bool m_abandon;
127     volatile bool m_running;
128     std::mutex m_stateMutex;
129     WaitableEvent m_quitEvent;
130
131     // Event processing
132     std::mutex m_eventMutex;
133     InternalEventList m_eventList;
134     WaitableEvent m_eventInvoker;
135
136     // Timed events processing
137     std::mutex m_timedEventMutex;
138     InternalTimedEventVector m_timedEventVector;
139     WaitableEvent m_timedEventInvoker;
140
141     // WaitableHandleWatchSupport
142     virtual Thread *GetInvokerThread();
143     virtual void HandleDirectInvoker();
144     bool m_directInvoke;
145
146     // Internals
147     unsigned long GetCurrentTimeMiliseconds() const;
148     void ProcessEvents();
149     void ProcessTimedEvents();
150
151     static void *StaticThreadEntry(void *param);
152
153   public:
154     explicit Thread();
155     virtual ~Thread();
156
157     /**
158      * Run thread. Does nothing if thread is already running
159      */
160     void Run();
161
162     /**
163      * Send quit message to thread and wait for its end
164      * Does nothing is thread is not running
165      */
166     void Quit();
167
168     /**
169      * Checks if current thread is main one
170      * Returns true if it is main program thread, false otherwise
171      */
172     static bool IsMainThread();
173
174     /**
175      * Current thread retrieval
176      * Returns DPL thread handle or NULL if it is main program thread
177      */
178     static Thread *GetCurrentThread();
179
180     /**
181      * Low-level event push, usually used only by EventSupport
182      */
183     void PushEvent(void *event,
184                    EventDispatchProc eventDispatchProc,
185                    EventDeleteProc eventDeleteProc,
186                    void *userParam);
187
188     /**
189      * Low-level timed event push, usually used only by EventSupport
190      */
191     void PushTimedEvent(void *event,
192                         double dueTimeSeconds,
193                         EventDispatchProc eventDispatchProc,
194                         EventDeleteProc eventDeleteProc,
195                         void *userParam);
196
197     /**
198      * Sleep for a number of seconds
199      */
200     static void Sleep(uint64_t seconds);
201
202     /**
203      * Sleep for a number of miliseconds
204      */
205     static void MiliSleep(uint64_t miliseconds);
206
207     /**
208      * Sleep for a number of microseconds
209      */
210     static void MicroSleep(uint64_t microseconds);
211
212     /**
213      * Sleep for a number of nanoseconds
214      */
215     static void NanoSleep(uint64_t nanoseconds);
216 };
217
218 extern bool g_TLSforMainCreated;
219
220 // In case of using TLV in main thread, pthread_exit(NULL) has to be called in
221 // this thread explicitly.
222 // On the other hand, possibly, because of the kernel bug, there exist
223 // a problem, if any other thread than main exist during pthread_exit call
224 // (process can become non-responsive)
225 // TODO further investigation is required.
226 template<typename Type>
227 class ThreadLocalVariable :
228     public Noncopyable
229 {
230   public:
231     typedef Type ValueType;
232
233     class Exception
234     {
235       public:
236         DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
237         DECLARE_EXCEPTION_TYPE(Base, NullReference)
238         DECLARE_EXCEPTION_TYPE(Base, KeyCreateFailed)
239     };
240
241   private:
242     pthread_key_t m_key;
243
244     struct ManagedValue
245     {
246         ValueType value;
247         boost::optional<pthread_key_t> guardKey;
248     };
249
250     static void MainThreadExitClean()
251     {
252         // There is a possible bug in kernel. If this function is called
253         // before ALL threads are closed, process will hang!
254         // Because of that, by default this function has to be called in well
255         // known "threads state".
256
257         // pthread_exit(NULL);
258     }
259
260     static void InternalDestroy(void *specific)
261     {
262         // Destroy underlying type
263         ManagedValue *instance = static_cast<ManagedValue *>(specific);
264         if (!instance->guardKey) {
265             delete instance;
266         } else {
267             int result = pthread_setspecific(*(instance->guardKey), instance);
268
269             Assert(result == 0 &&
270                    "Failed to set thread local variable");
271         }
272     }
273
274     Type &Reference(bool allowInstantiate = false)
275     {
276         ManagedValue *instance =
277             static_cast<ManagedValue *>(pthread_getspecific(m_key));
278
279         if (!instance) {
280             // Check if it is allowed to instantiate
281             if (!allowInstantiate) {
282                 Throw(typename Exception::NullReference);
283             }
284
285             // checking, if specific data is created for Main thread
286             // If yes, pthread_exit(NULL) is required
287             if (!g_TLSforMainCreated) {
288                 if (Thread::IsMainThread()) {
289                     g_TLSforMainCreated = true;
290                     atexit(&MainThreadExitClean);
291                 }
292             }
293
294             // Need to instantiate underlying type
295             instance = new ManagedValue();
296
297             int result = pthread_setspecific(m_key, instance);
298
299             Assert(result == 0 &&
300                    "Failed to set thread local variable");
301         }
302
303         return instance->value;
304     }
305
306   public:
307     ThreadLocalVariable()
308     {
309         int result = pthread_key_create(&m_key, &InternalDestroy);
310         if (result != 0) {
311             ThrowMsg(typename Exception::KeyCreateFailed,
312                      "Failed to allocate thread local variable: " << result);
313         }
314     }
315
316     ~ThreadLocalVariable()
317     {
318         pthread_key_delete(m_key);
319     }
320
321     Type &operator=(const Type &other)
322     {
323         Type &reference = Reference(true);
324         reference = other;
325         return reference;
326     }
327
328     bool IsNull() const
329     {
330         return pthread_getspecific(m_key) == NULL;
331     }
332
333     Type& operator*()
334     {
335         return Reference();
336     }
337
338     const Type& operator*() const
339     {
340         return Reference();
341     }
342
343     const Type* operator->() const
344     {
345         return &Reference();
346     }
347
348     Type* operator->()
349     {
350         return &Reference();
351     }
352
353     bool operator!() const
354     {
355         return IsNull();
356     }
357
358     void Reset()
359     {
360         ManagedValue *specific =
361             static_cast<ManagedValue *>(pthread_getspecific(m_key));
362
363         if (!specific) {
364             return;
365         }
366
367         // TODO Should be an assert? is it developers fault to Reset Guarded
368         // value?
369         specific->guardKey = boost::optional<pthread_key_t>();
370
371         InternalDestroy(specific);
372
373         int result = pthread_setspecific(m_key, NULL);
374
375         Assert(result == 0 &&
376                "Failed to reset thread local variable");
377     }
378
379     // GuardValue(true) allows to defer destroy (by pthread internal
380     // functionality) thread specific value until GuardValue(false) will be
381     // called.
382     void GuardValue(bool guard)
383     {
384         ManagedValue *instance =
385             static_cast<ManagedValue *>(pthread_getspecific(m_key));
386
387         Assert(instance && "Failed to get the value");
388
389         instance->guardKey = guard ? m_key : boost::optional<pthread_key_t>();
390     }
391 };
392 } // namespace DPL
393
394 #endif // DPL_THREAD_H