xthread.h
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 #ifndef threadH
15 #define threadH
16 //---------------------------------------------------------------------------
17 #include "support.h"
18 #include "atomic.h"
19 
20 #ifdef USE_QTHREAD
21  #include <QThread>
22  #define threadid_t Qt::HANDLE
23  inline threadid_t threadID() {return QThread::currentThreadId();}
24  #define is_thread_equal(x,y) ((x) == (y))
25  #include <QMutex>
26  #include <QWaitCondition>
27  #include <QThread>
28 #elif defined USE_PTHREAD
29  #define threadid_t pthread_t
30  inline threadid_t threadID() {return pthread_self();}
31  #define is_thread_equal(x,y) (pthread_equal(x,y))
32  #include <sys/mman.h>
33 #else
34  #error
35 #endif
36 
37 #include "threadlocal.h"
38 
39 //! Lock mutex during its life time.
40 template <class Mutex>
41 struct XScopedLock {
42  explicit XScopedLock(Mutex &mutex) : m_mutex(mutex) {
43  m_mutex.lock();
44  }
45  ~XScopedLock() {
46  m_mutex.unlock();
47  }
48 private:
49  Mutex &m_mutex;
50 };
51 
52 //! Lock mutex during its life time.
53 template <class Mutex>
55  explicit XScopedTryLock(Mutex &mutex) : m_mutex(mutex) {
56  m_bLocking = m_mutex.trylock();
57  }
58  ~XScopedTryLock() {
59  if(m_bLocking) m_mutex.unlock();
60  }
61  bool operator!() const {
62  return !m_bLocking;
63  }
64  operator bool() const {
65  return m_bLocking;
66  }
67 private:
68  Mutex &m_mutex;
69  bool m_bLocking;
70 };
71 
72 /*! non-recursive mutex.
73  * double lock is inhibited.
74  * \sa XRecursiveMutex.
75  */
76 class DECLSPEC_KAME XMutex {
77 public:
78  XMutex();
79  ~XMutex();
80 
81  void lock();
82  void unlock();
83  //! \return true if locked.
84  bool trylock();
85 protected:
86 #ifdef USE_QTHREAD
87  QMutex m_mutex;
88 #elif defined USE_PTHREAD
89  pthread_mutex_t m_mutex;
90 #endif
91 };
92 
93 //! condition class.
94 class DECLSPEC_KAME XCondition : public XMutex
95 {
96 public:
97  XCondition();
98  ~XCondition();
99  //! Lock me before calling me.
100  //! go asleep until signal is emitted.
101  //! \param usec if non-zero, timeout occurs after \a usec.
102  //! \return zero if locked thread is waked up.
103  int wait(int usec = 0);
104  //! wake-up at most one thread.
105  //! \sa broadcast()
106  void signal();
107  //! wake-up all waiting threads.
108  //! \sa signal()
109  void broadcast();
110 private:
111 #ifdef USE_QTHREAD
112  QWaitCondition m_cond;
113 #elif defined USE_PTHREAD
114  pthread_cond_t m_cond;
115 #endif
116 };
117 
118 //! recursive mutex.
119 class DECLSPEC_KAME XRecursiveMutex {
120 public:
121  XRecursiveMutex() {
122  m_lockingthread = (threadid_t)-1;
123  }
124  ~XRecursiveMutex() {}
125 
126  void lock() {
127  if(!is_thread_equal(m_lockingthread, threadID())) {
128  m_mutex.lock();
129  m_lockcount = 1;
130  m_lockingthread = threadID();
131  }
132  else
133  m_lockcount++;
134  }
135  //! unlock me with locking thread.
136  void unlock() {
137  m_lockcount--;
138  if(m_lockcount == 0) {
139  m_lockingthread = (threadid_t)-1;
140  m_mutex.unlock();
141  }
142  }
143  //! \return true if locked.
144  bool trylock() {
145  if(!is_thread_equal(m_lockingthread, threadID())) {
146  if(m_mutex.trylock()) {
147  m_lockcount = 1;
148  m_lockingthread = threadID();
149  }
150  else
151  return false;
152  }
153  else
154  m_lockcount++;
155  return true;
156  }
157  //! \return true if the current thread is locking mutex.
158  bool isLockedByCurrentThread() const {
159  return is_thread_equal(m_lockingthread, threadID());
160  }
161 private:
162  XMutex m_mutex;
163  threadid_t m_lockingthread;
164  int m_lockcount;
165 };
166 
167 
168 //! create a new thread.
169 template <class T>
170 class XThread {
171 public:
172  /*! use resume() to start a thread.
173  * \p X must be super class of \p T.
174  */
175  template <class X>
176  XThread(const shared_ptr<X> &t, void *(T::*func)(const atomic<bool> &));
177  ~XThread();
178  //! resume a new thread.
179  void resume();
180  /*! join a running thread.
181  * should be called before destruction.
182  * \param retval a pointer to return value from a thread.
183  */
184  void waitFor(void **retval = 0L);
185  //! set termination flag.
186  void terminate();
187  //! fetch termination flag.
188  bool isTerminated() const {return m_startarg->is_terminated;}
189 private:
190  struct targ{
191  shared_ptr<targ> this_ptr;
192  shared_ptr<T> obj;
193  void *(T::*func)(const atomic<bool> &);
194  atomic<bool> is_terminated;
195  };
196  shared_ptr<targ> m_startarg;
197  static void * xthread_start_routine(void *);
198 #ifdef USE_PTHREAD
199  pthread_t m_threadid;
200 #elif defined USE_STD_THREAD
201  std::thread m_thread;
202 #else
203  #error
204 #endif
205 };
206 
207 template <class T>
208 template <class X>
209 XThread<T>::XThread(const shared_ptr<X> &t, void *(T::*func)(const atomic<bool> &))
210  : m_startarg(std::make_shared<targ>()) {
211  m_startarg->obj = dynamic_pointer_cast<T>(t);
212  assert(m_startarg->obj);
213  m_startarg->func = func;
214  m_startarg->is_terminated = false;
215 }
216 
217 template <class T>
219  terminate();
220 #if defined USE_STD_THREAD
221  if(m_thread.joinable()) {
222  m_thread.detach();
223  }
224 #endif
225 }
226 
227 template <class T>
228 void
230  m_startarg->this_ptr = m_startarg;
231 #ifdef USE_PTHREAD
232  int ret =
233  pthread_create((pthread_t*)&m_threadid, NULL,
234  &XThread<T>::xthread_start_routine , m_startarg.get());
235  assert( !ret);
236 #elif defined USE_STD_THREAD
237  std::thread th(&XThread<T>::xthread_start_routine , m_startarg.get());
238  m_thread.swap(th);
239 #endif
240 }
241 template <class T>
242 void *
244  shared_ptr<targ> arg = ((targ *)x)->this_ptr;
245  if(isMemLockAvailable())
246  mlock(&arg, 8192uL); //reserve stack.
247 
248  arg->this_ptr.reset();
249  void *p = ((arg->obj.get())->*(arg->func))(arg->is_terminated);
250  arg->obj.reset();
251 
252  return p;
253 }
254 
255 #include <system_error>
256 
257 template <class T>
258 void
259 XThread<T>::waitFor(void **retval) {
260 #ifdef USE_PTHREAD
261  pthread_join(m_threadid, retval);
262 #elif defined USE_STD_THREAD
263  m_thread.join();
264 #endif
265 }
266 template <class T>
267 void
269  m_startarg->is_terminated = true;
270 }
271 
272 #endif

Generated for KAME4 by  doxygen 1.8.3