1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 #ifndef __AThread_H__
21 #define __AThread_H__
22 
23 #include "../../config/common.h"
24 
25 /*
26  * This is pthread and win32-thread wrapper that isn't very functional, but serves for my purposes.
27  *
28  * The public interface looks like:
29  *
gbt_int4gt(const void * a,const void * b,FmgrInfo * flinfo)30  * WARNING! PITFALL! When a thread pointer is deleted or a stack thread object goes out of scope, the destructor of the derived class is called
31  *   At this point the derived class's data members are destroyed and AThread's destructor is waiting on the thread to end.  If main() is still
32  *   using those now-destroyed data-members, then a crash will likely follow.  The solution is to always arrange for the thread to have ended
33  *   before the derived class's destructor returns.  This can be done either explicitly before being to destroy the object, or as the first thing
34  *   in the [most-]derived class's destructor.  This would be accomplished by flagging the thread to end and then calling wait() to wait for it
35  *   to finish.
36  *
37  * Also, no means of terminating a thread exists.  This is because terminating a thread in mid-execution can result in half-way modified data,
38  * locked resources never getting unlocked and such.  Rather, it is best to design the thread execution path to check often for requests for the
39  * thread to end, and then it to end gracefully.
40  *
41 
42 	class AThread
43 	{
44 	public:
45 		AThread();
46 		virtual ~AThread();
47 
48 		void start(size_t stackSize= a platform specific default);
49 
50 		// returns true if the thread ended (or was already not running)
51 		// returns false on timeout
52 		bool wait(int timeout_ms=-1);
53 
54 		static void yield();
55 
56 		// gets a uniq thread identifier for the currently running thread
57 		static void *getCurrentThreadID();
58 
59 		// returns a pointer to the AThread object for the currently running thread or NULL if AThread didn't create the thread
60 		static AThread *getCurrentThread();
61 
62 		bool isRunning() const;
63 
64 		void *getThreadID() const;
65 
66 		// This method is used to set thread-specific data with the given key to the given value.
67 		// The value object must be allocated on the heap and will be cleaned up by the AThread system.
68 		// If the value assocated with the key needs to be cleaned up sooner then call setSpecific(key, NULL) or
69 		// just delete the CThreadSpecificObject pointer whose dtor will do the same.
70 		// If 'persistent' is true, then the value will not be cleared out with clearThreadSpecific().
71 		// NOTE: A created CThreadSpecificObject must be associated with no more than 1 key value.
72 		// NOTE: The actual object given is what is associated for the life-time of that data-value.  No copies of the object will/can be made.
73 		// NOTE: The key value 0 is reserved.
74 		static void setSpecific(unsigned key, CThreadSpecificObject *value, bool persistent = false);
75 
76 		// returns the thread-specific data object previously set by setSpecific() with the same key
77 		// if no value is found with the given key, then NULL will be returned.
78 		static CThreadSpecificObject *getSpecific(unsigned key);
79 
80 		// cleans up all non-persistent thread specific data
81 		// If needed, this can be called before the thread actually ends to reset most thread specific information,
82 		// but a complete cleanup of all thread specific data will be performed when the thread ends.
83 		static void clearThreadSpecific();
84 
85 		// NOTE: no thread specific data is preserved between subsequent starts() of the same AThread object (not
86 		//       even values flagged as persistent)
87 
88 
89 		// sets the name of the thread which is a thread specific data.
90 		static void setThreadName(const std::string &threadName);
91 
92 		// gets the name of the thread; returns "" if no name has been set
93 		static const std::string &getThreadName();
94 
95 
96 		// These methods are used to set / determine if the thread has been requested to quit
97 		// The isCancelled() method returns a reference to the bool flag in case it needs to be passed down to non-AThread-aware code
98 		void setCancelled(bool cancelled);
99 		const bool &isCancelled() const;
100 
101 	protected:
102 		// derive and implement this method
103 		virtual void main()=0;
104 	};
105  */
106 
107 #include "CMutex.h"
108 #include <string>
109 
110 // In order to use thread specific data, a sub-class of this object should be used.
111 // When thread specific data is fetched, an CThreadSpecificObject pointer is returned
112 // which should be cast to the specific sub-class type to obtain custom data values.
113 // The purpose of the class is so that the sub-class's destructor can do any custom cleanup
114 class CThreadSpecificObject
115 {
116 public:
117 	CThreadSpecificObject();
118 
119 	// NOTE: behavior is undefined if thread specific data is
120 	// set/fetched during or after the destructor of AThread has run
121 	virtual ~CThreadSpecificObject();
122 
123 private:
124 	// non copiable
125 	explicit CThreadSpecificObject(const CThreadSpecificObject &src);
126 	CThreadSpecificObject &operator=(const CThreadSpecificObject &rhs);
127 
128 	friend class AThread;
129 	unsigned mKey;
130 	bool mPersistent;
131 };
132 
133 
134 // private type
135 #if defined(__GNUC__)
136 #	include <unordered_map>
gbt_int4_consistent(PG_FUNCTION_ARGS)137 	typedef std::unordered_map<unsigned, CThreadSpecificObject *> ThreadSpecificMap_t;
138 #else
139 # 	include <map>
140 	typedef std::map<unsigned, CThreadSpecificObject *> ThreadSpecificMap_t;
141 #endif
142 
143 
144 #ifdef _WIN32
145 
146 	// *** WIN32 implementation ***
147 
148 	#define ATHREAD_DEFAULT_STACK_SIZE 0
149 
150 	class AThread
151 	{
152 	public:
153 		AThread();
154 		virtual ~AThread();
155 
156 		void start(size_t stackSize=ATHREAD_DEFAULT_STACK_SIZE);
157 
158 		bool wait(int timeout_ms=-1) const;
159 
160 		static void yield();
gbt_int4_distance(PG_FUNCTION_ARGS)161 		static void *getCurrentThreadID();
162 		static AThread *getCurrentThread();
163 		static void sleep(const int secs=0, const int msecs=0);
164 
165 		bool isRunning() const;
166 
167 		void *getThreadID() const;
168 		void *getHandle() const;
169 
170 		static void setSpecific(unsigned key, CThreadSpecificObject *value, bool persistent = false);
171 		static CThreadSpecificObject *getSpecific(unsigned key);
172 		static void clearThreadSpecific() throw();
173 
174 		static void setThreadName(const std::string &threadName);
175 		static const std::string &getThreadName();
176 
177 		void setCancelled(bool cancelled) { mCancelled = cancelled; }
178 		const bool& isCancelled() const { return mCancelled; }
179 		static const bool& isCurrentThreadCancelled() { return getCurrentThread()->isCancelled(); }
gbt_int4_union(PG_FUNCTION_ARGS)180 
181 		// this should only be set if you are abandoning the thread
182 		// and understand the implications (eg. all thread vars must be local)
183 		void setDeleteOnExit(bool del) { mDeleteOnExit = del; }
184 
185 	protected:
186 		// just derive and implement this method
187 		// don't make this pure virtual, since it causes issues with exceptions and destructors!
188 		virtual void main(){}
189 
190 	private:
gbt_int4_penalty(PG_FUNCTION_ARGS)191 		friend class CRawThreadHelper;
192 
193 		ThreadSpecificMap_t mThreadSpecificMap;
194 		std::string mThreadName;
195 
196 		void *mThreadHandle;
197 		unsigned mThreadID;
198 		bool mRunning;
199 		bool mCancelled;
200 		bool mDeleteOnExit;
201 
202 		static unsigned __stdcall AThreadStart(void *temp);
gbt_int4_picksplit(PG_FUNCTION_ARGS)203 		static void clearAllThreadSpecific(bool clearPersistent) throw();
204 	};
205 
206 #else
207 	// *** posix implementation ***
208 
209 	#include <pthread.h>
210 	#include "CConditionVariable.h"
211 
212 	#define ATHREAD_DEFAULT_STACK_SIZE (4 * 1024 * 1024)
gbt_int4_same(PG_FUNCTION_ARGS)213 
214 	class AThread
215 	{
216 	public:
217 		AThread();
218 		virtual ~AThread();
219 
220 		void start(size_t stackSize=ATHREAD_DEFAULT_STACK_SIZE);
221 
222 		bool wait(int timeout_ms=-1) const;
223 
224 		static void yield();
225 		static void *getCurrentThreadID();
226 		static AThread *getCurrentThread();
227 		static void sleep(const int secs=0, const int msecs=0);
228 
229 		bool isRunning() const;
230 
231 		void *getThreadID() const;
232 
233 		static void setSpecific(unsigned key, CThreadSpecificObject *value, bool persistent = false);
234 		static CThreadSpecificObject *getSpecific(unsigned key);
235 		static void clearThreadSpecific() throw();
236 
237 		static void setThreadName(const std::string &threadName);
238 		static const std::string &getThreadName();
239 
240 		void setCancelled(bool cancelled) { mCancelled = cancelled; }
241 		const bool& isCancelled() const { return mCancelled; }
242 		static const bool& isCurrentThreadCancelled() { return getCurrentThread()->isCancelled(); }
243 
244 		// this method arranges for AThread::alarmHandler() to be called within the thread that called AThread::alarm()
245 		// after the given number of seconds.  The subclass should override alarmHandler() and implement the application
246 		// specific code.
247 		//
248 		// The implementation sets the process's signal handler for SIGALRM, therefore the application should not alter
249 		// the signal handler else the behavior will be unpredictable.  Also, do NOT use ::alarm() in the same process
250 		// that uses AThread::alarm()
251 		//
252 		// PITFALL:
253 		// In the case the a AThread subclass is going to use an alarm for different reason at different times.  It is
254 		// probably undesirable to call AThread::alarm(n) where n>0 without having called AThread::alarm(0) first.
255 		// The problem is that if the subclass's implementation of alarmHandler() uses any state information out of the
256 		// thread object, without a call to alarm(0) it will be ambiguous as to which call to alarm() this is a timeout for.
257 		//
258 		// For example:
259 		//
260 		// class foo : public AThread {
261 		// protected:
262 		//    void alarmHandler() {
263 		//       if(f==1)
264 		//         // CASE A
265 		//       else if(f==2)
266 		//         // CASE B
267 		//    }
268 		//
269 		//    void main() {
270 		//       ...
271 		//       f=1;
272 		//       alarm(3);
273 		//       ...
274 		//       alarm(0);
275 		//       f=2;
276 		//       alarm(10);
277 		//       ...
278 		//    }
279 		// }
280 		//
281 		// In the above class, if alarm(0) in main() were not in place, then when alarmHandler() is called, it is possible that f will equal 2 but was a timeout for the call to alarm(3)
282 		//
283 		// alarm(0) is automatically called when main() has returned normally or abnormally.  But it is still necessary to ensure that AThread::alarmHandler() will not be called and use
284 		// something that is destroyed when main returns.
285 		//
286 		// calling alarm(0) from the destructor is probably not desired since it would clear the alarm() in the thread that's calling the destroying the object (NOT clearing the alarm
287 		// for that object's system thread)
288 		//
289 		static void alarm(int seconds);
290 
291 		// this should only be set if you are abandoning the thread
292 		// and understand the implications (eg. all thread vars must be local)
293 		void setDeleteOnExit(bool del) { mDeleteOnExit = del; }
294 
295 	protected:
296 		// just derive and implement this method
297 		// don't make this pure virtual, since it causes issues with exceptions and destructors!
298 		virtual void main() {}
299 
300 		// this may be overridden to handle the receipt of an alarm scheduled using AThread::alarm()
301 		virtual void alarmHandler() throw() {}
302 
303 	private:
304 		friend class CRawThreadHelper;
305 
306 		ThreadSpecificMap_t mThreadSpecificMap;
307 		std::string mThreadName;
308 
309 		pthread_t mThreadID;
310 		bool mRunning;
311 		bool mCancelled;
312 		bool mDeleteOnExit;
313 
314 		CMutex mStartSyncMut;
315 		CConditionVariable mStartSyncCond;
316 		CMutex mRunningLock;
317 		// The first two are used to synchronize the locking of the third, so it
318 		// can be locked reliably when the thread starts, and unlocked when it
319 		// finishes.  (It would be easier if mRunningLock could be locked by the
320 		// parent, it needs to be unlocked by the child.  But the locker and
321 		// unlocker need to be the same thread.)
322 
323 		static void clearAllThreadSpecific(bool clearPersistent) throw();
324 
325 		static void *AThreadStart(void *temp);
326 		static void SIGALRM_handler(int sig);
327 	};
328 
329 #endif
330 
331 // All threads that are not created using AThread must create a CRawThreadHelper object
332 // inside of the thread.  The helper object is responsible for initializing thread-specific
333 // data. Its constructor initializes the data and its destructor de-initializes the data.
334 // Therefore, the object should be destructed right before the thread terminates.
335 class CRawThreadHelper
336 {
337 public:
338 	CRawThreadHelper();
339 	virtual ~CRawThreadHelper();
340 private:
341 	AThread *mDummyThread;
342 };
343 
344 #endif // __AThread_H__
345 
346