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