ZNC  trunk
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Threads.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004-2014 ZNC, see the NOTICE file for details.
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 #ifndef _THREADS_H
18 #define _THREADS_H
19 
20 #include <znc/zncconfig.h>
21 
22 #ifdef HAVE_PTHREAD
23 
24 #include <znc/Utils.h>
25 
26 #include <cerrno>
27 #include <csignal>
28 #include <cstdlib>
29 #include <cstring>
30 #include <list>
31 #include <pthread.h>
32 
37 class CMutex {
38 public:
39  friend class CConditionVariable;
40 
41  CMutex() {
42  int i = pthread_mutex_init(&m_mutex, NULL);
43  if (i) {
44  CUtils::PrintError("Can't initialize mutex: " + CString(strerror(errno)));
45  exit(1);
46  }
47  }
48 
49  ~CMutex() {
50  int i = pthread_mutex_destroy(&m_mutex);
51  if (i) {
52  CUtils::PrintError("Can't destroy mutex: " + CString(strerror(errno)));
53  exit(1);
54  }
55  }
56 
57  void lock() {
58  int i = pthread_mutex_lock(&m_mutex);
59  if (i) {
60  CUtils::PrintError("Can't lock mutex: " + CString(strerror(errno)));
61  exit(1);
62  }
63  }
64 
65  void unlock() {
66  int i = pthread_mutex_unlock(&m_mutex);
67  if (i) {
68  CUtils::PrintError("Can't unlock mutex: " + CString(strerror(errno)));
69  exit(1);
70  }
71  }
72 
73 private:
74  pthread_mutex_t m_mutex;
75 };
76 
82 class CMutexLocker {
83 public:
84  CMutexLocker(CMutex& mutex, bool initiallyLocked = true)
85  : m_mutex(mutex), m_locked(false) {
86  if (initiallyLocked)
87  lock();
88  }
89 
90  ~CMutexLocker() {
91  if (m_locked)
92  unlock();
93  }
94 
95  void lock() {
96  assert(!m_locked);
97  m_mutex.lock();
98  m_locked = true;
99  }
100 
101  void unlock() {
102  assert(m_locked);
103  m_locked = false;
104  m_mutex.unlock();
105  }
106 
107 private:
108  CMutex &m_mutex;
109  bool m_locked;
110 };
111 
116 class CConditionVariable {
117 public:
118  CConditionVariable() {
119  int i = pthread_cond_init(&m_cond, NULL);
120  if (i) {
121  CUtils::PrintError("Can't initialize condition variable: "
122  + CString(strerror(errno)));
123  exit(1);
124  }
125  }
126 
127  ~CConditionVariable() {
128  int i = pthread_cond_destroy(&m_cond);
129  if (i) {
130  CUtils::PrintError("Can't destroy condition variable: "
131  + CString(strerror(errno)));
132  exit(1);
133  }
134  }
135 
136  void wait(CMutex& mutex) {
137  int i = pthread_cond_wait(&m_cond, &mutex.m_mutex);
138  if (i) {
139  CUtils::PrintError("Can't wait on condition variable: "
140  + CString(strerror(errno)));
141  exit(1);
142  }
143  }
144 
145  void signal() {
146  int i = pthread_cond_signal(&m_cond);
147  if (i) {
148  CUtils::PrintError("Can't signal condition variable: "
149  + CString(strerror(errno)));
150  exit(1);
151  }
152  }
153 
154  void broadcast() {
155  int i = pthread_cond_broadcast(&m_cond);
156  if (i) {
157  CUtils::PrintError("Can't broadcast condition variable: "
158  + CString(strerror(errno)));
159  exit(1);
160  }
161  }
162 
163 private:
164  pthread_cond_t m_cond;
165 };
166 
167 class CThread {
168 public:
169  typedef void *threadRoutine(void *);
170  static void startThread(threadRoutine *func, void *arg) {
171  pthread_t thr;
172  sigset_t old_sigmask, sigmask;
173 
174  /* Block all signals. The thread will inherit our signal mask
175  * and thus won't ever try to handle signals.
176  */
177  int i = sigfillset(&sigmask);
178  i |= pthread_sigmask(SIG_SETMASK, &sigmask, &old_sigmask);
179  i |= pthread_create(&thr, NULL, func, arg);
180  i |= pthread_sigmask(SIG_SETMASK, &old_sigmask, NULL);
181  i |= pthread_detach(thr);
182  if (i) {
183  CUtils::PrintError("Can't start new thread: "
184  + CString(strerror(errno)));
185  exit(1);
186  }
187  }
188 };
189 
190 class CJob {
191 public:
192  virtual ~CJob() {}
193  virtual void runThread() = 0;
194  virtual void runMain() = 0;
195 };
196 
197 class CThreadPool {
198 private:
199  CThreadPool();
200  ~CThreadPool();
201 
202 public:
203  static CThreadPool& Get();
204 
205  void addJob(CJob *job);
206 
207  int getReadFD() const {
208  return m_iJobPipe[0];
209  }
210 
211  void handlePipeReadable() const;
212 
213 private:
214  void jobDone(const CJob* pJob) const;
215 
216  // Check if the calling thread is still needed, must be called with m_mutex held
217  bool threadNeeded() const;
218 
219  void threadFunc();
220  static void *threadPoolFunc(void *arg) {
221  CThreadPool &pool = *reinterpret_cast<CThreadPool *>(arg);
222  pool.threadFunc();
223  return NULL;
224  }
225 
226  // mutex protecting all of these members
227  CMutex m_mutex;
228 
229  // condition variable for waiting idle threads
230  CConditionVariable m_cond;
231 
232  // when this is true, all threads should exit
233  bool m_done;
234 
235  // total number of running threads
236  size_t m_num_threads;
237 
238  // number of idle threads waiting on the condition variable
239  size_t m_num_idle;
240 
241  // pipe for waking up the main thread
242  int m_iJobPipe[2];
243 
244  // list of pending jobs
245  std::list<CJob *> m_jobs;
246 };
247 
248 #endif // HAVE_PTHREAD
249 #endif // !_THREADS_H
String class that is used inside ZNC.
Definition: ZNCString.h:67
static void PrintError(const CString &sMessage)