├── .gitignore ├── CMakeLists.txt ├── README.md ├── fast_mutex.h ├── tests ├── fractal.cpp ├── future.cpp ├── hello.cpp ├── pool.cpp ├── test.cpp └── timer.h ├── tinythread.cpp ├── tinythread.h ├── tinythread_async.h ├── tinythread_future.h ├── tinythread_pool.cpp ├── tinythread_pool.h ├── tinythread_queue.h └── tinythread_t.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /build_64 3 | /build 4 | /bin 5 | /mingw 6 | /mingw_64 7 | /threadpool_test 8 | /build_clang64 9 | /build_64 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(tthread) 4 | 5 | if(MSVC10) 6 | set(tthread_SUFFIX _vc10) 7 | elseif(MSVC11) 8 | set(tthread_SUFFIX _vc11) 9 | else() 10 | set(tthread_SUFFIX) 11 | endif() 12 | 13 | if(CMAKE_SIZEOF_VOID_P MATCHES 4) 14 | set(tthread_SIZE_TYPE x86) 15 | else() 16 | set(tthread_SIZE_TYPE x64) 17 | endif() 18 | 19 | set(tthread_BINARY_PATH ${CMAKE_HOME_DIRECTORY}/bin/${tthread_SIZE_TYPE}${tthread_SUFFIX}) 20 | 21 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY 22 | ${tthread_BINARY_PATH} 23 | CACHE PATH 24 | "Single Directory for all Executables.") 25 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY 26 | ${tthread_BINARY_PATH} 27 | CACHE PATH 28 | "Single Directory for all Libraries") 29 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY 30 | ${tthread_LIBRARY_PATH} 31 | CACHE PATH 32 | "Single Directory for all static libraries.") 33 | 34 | SET(TREAT_WARNINGS_AS_ERRORS OFF CACHE BOOL "Treat compiler warnings as errors") 35 | 36 | if(UNIX OR MINGW) 37 | set(STOP_ON_FIRST_ERROR TRUE CACHE BOOL "Stop compilation on first error") 38 | if(TREAT_WARNINGS_AS_ERRORS) 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 40 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") 41 | endif() 42 | if (STOP_ON_FIRST_ERROR) 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfatal-errors") 44 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfatal-errors") 45 | endif() 46 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -std=c++0x -Wall -Wno-return-type") 47 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wno-deprecated") 49 | add_definitions( "-DDEBUG" ) 50 | elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") 51 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -fno-omit-frame-pointer") 52 | elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithProfile") 53 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -g -O3 -fno-omit-frame-pointer") 54 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") 55 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg -fPIC") 56 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pg") 57 | else() 58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wno-unused-variable -Wno-write-strings -fno-strict-aliasing -Wno-parentheses -Wno-deprecated -fno-omit-frame-pointer") 59 | endif() 60 | endif() 61 | 62 | if(MSVC) 63 | SET(LINK_STATIC_RUNTIME OFF CACHE BOOL "Link statically against C++ runtime") 64 | if(TREAT_WARNINGS_AS_ERRORS) 65 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") 66 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") 67 | endif() 68 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DNOMINMAX /MP /W3 /EHa") 69 | if(MSVC10 OR MSVC11) 70 | SET(ITERATOR_DEBUG TRUE CACHE BOOL "Use iterator debug level 2") 71 | #add_definitions("-D_ITERATOR_DEBUG_LEVEL=0") 72 | if (ITERATOR_DEBUG) 73 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=2") 74 | else() 75 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=0") 76 | endif() 77 | #add_definitions("-D_ITERATOR_DEBUG_LEVEL=0") 78 | endif() 79 | # Extra speed optimisation options 80 | set(MSVC_EXTRA_OPTIMIZE ON CACHE BOOL "Use extra optimization flags in release builds") 81 | if(MSVC_EXTRA_OPTIMIZE) 82 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oi /Ot /Oy /GL /GS- /fp:fast") 83 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /Ot /Oy /GL /GS- /fp:fast") 84 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /LTCG") 85 | set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /LTCG") 86 | endif() 87 | # Even more speed optimisations 88 | set(MSVC_OPTIMIZE_SSE2 ON CACHE BOOL "Use SSE2 instruction set in release builds") 89 | if(MSVC_OPTIMIZE_SSE2) 90 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /arch:SSE2") 91 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:SSE2") 92 | endif() 93 | if(LINK_STATIC_RUNTIME AND NOT BUILD_SHARED_LIBS) 94 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") 95 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 96 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:msvcrt /NODEFAULTLIB:msvcrtd") 97 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:msvcrt /NODEFAULTLIB:msvcrtd") 98 | endif() 99 | endif() 100 | 101 | 102 | file(GLOB TTHREAD_HEADERS *.h) 103 | file(GLOB TTHREAD_SOURCES *.cpp) 104 | 105 | add_library(tthread STATIC ${TTHREAD_HEADERS} ${TTHREAD_SOURCES}) 106 | if(UNIX) 107 | target_link_libraries(tthread pthread) 108 | set(TTHREAD_LIBS tthread pthread) 109 | else() 110 | set(TTHREAD_LIBS tthread) 111 | endif() 112 | 113 | set(BUILD_TESTS FALSE CACHE BOOL "Build unit tests") 114 | 115 | if(BUILD_TESTS) 116 | include_directories(.) 117 | 118 | add_executable(test tests/test.cpp) 119 | target_link_libraries(test ${TTHREAD_LIBS}) 120 | 121 | add_executable(hello tests/hello.cpp) 122 | target_link_libraries(hello ${TTHREAD_LIBS}) 123 | 124 | add_executable(fractal tests/fractal.cpp) 125 | target_link_libraries(fractal ${TTHREAD_LIBS}) 126 | 127 | add_executable(future tests/future.cpp) 128 | target_link_libraries(future ${TTHREAD_LIBS}) 129 | 130 | add_executable(pool tests/pool.cpp) 131 | target_link_libraries(pool ${TTHREAD_LIBS}) 132 | endif() 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TinyThread++ 2 | ============= 3 | 4 | TinyThread++ (tthread, http://tinythread.sourceforge.net/) is a lightweight and portable C++ threading library. 5 | 6 | This project extends TinyThread++ by providing 7 | 8 | * Lambda, function and function object execution support 9 | * tthread::future and tthread::async, lightweight analogs to std::future and std::async 10 | * future.then() for task continuations 11 | * basic fifo threadpooling with thread_pool 12 | -------------------------------------------------------------------------------- /fast_mutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Marcus Geelnard 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef _FAST_MUTEX_H_ 25 | #define _FAST_MUTEX_H_ 26 | 27 | /// @file 28 | 29 | // Which platform are we on? 30 | #if !defined(_TTHREAD_PLATFORM_DEFINED_) 31 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 32 | #define _TTHREAD_WIN32_ 33 | #else 34 | #define _TTHREAD_POSIX_ 35 | #endif 36 | #define _TTHREAD_PLATFORM_DEFINED_ 37 | #endif 38 | 39 | // Check if we can support the assembly language level implementation (otherwise 40 | // revert to the system API) 41 | #if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \ 42 | ((defined(_MSC_VER) && _MSC_VER < 1600) && (defined(_M_IX86) || defined(_M_X64))) || \ 43 | (defined(__GNUC__) && (defined(__ppc__))) 44 | #define _FAST_MUTEX_ASM_ 45 | #else 46 | #define _FAST_MUTEX_SYS_ 47 | #endif 48 | 49 | #if defined(_TTHREAD_WIN32_) 50 | #include 51 | #else 52 | #ifdef _FAST_MUTEX_ASM_ 53 | #include 54 | #else 55 | #include 56 | #endif 57 | #endif 58 | 59 | namespace tthread { 60 | 61 | /// Fast mutex class. 62 | /// This is a mutual exclusion object for synchronizing access to shared 63 | /// memory areas for several threads. It is similar to the tthread::mutex class, 64 | /// but instead of using system level functions, it is implemented as an atomic 65 | /// spin lock with very low CPU overhead. 66 | /// 67 | /// The \c fast_mutex class is NOT compatible with the \c condition_variable 68 | /// class (however, it IS compatible with the \c lock_guard class). It should 69 | /// also be noted that the \c fast_mutex class typically does not provide 70 | /// as accurate thread scheduling as a the standard \c mutex class does. 71 | /// 72 | /// Because of the limitations of the class, it should only be used in 73 | /// situations where the mutex needs to be locked/unlocked very frequently. 74 | /// 75 | /// @note The "fast" version of this class relies on inline assembler language, 76 | /// which is currently only supported for 32/64-bit Intel x86/AMD64 and 77 | /// PowerPC architectures on a limited number of compilers (GNU g++ and MS 78 | /// Visual C++). 79 | /// For other architectures/compilers, system functions are used instead. 80 | class fast_mutex { 81 | public: 82 | /// Constructor. 83 | #if defined(_FAST_MUTEX_ASM_) 84 | fast_mutex() : mLock(0) {} 85 | #else 86 | fast_mutex() 87 | { 88 | #if defined(_TTHREAD_WIN32_) 89 | InitializeCriticalSection(&mHandle); 90 | #elif defined(_TTHREAD_POSIX_) 91 | pthread_mutex_init(&mHandle, NULL); 92 | #endif 93 | } 94 | #endif 95 | 96 | #if !defined(_FAST_MUTEX_ASM_) 97 | /// Destructor. 98 | ~fast_mutex() 99 | { 100 | #if defined(_TTHREAD_WIN32_) 101 | DeleteCriticalSection(&mHandle); 102 | #elif defined(_TTHREAD_POSIX_) 103 | pthread_mutex_destroy(&mHandle); 104 | #endif 105 | } 106 | #endif 107 | 108 | /// Lock the mutex. 109 | /// The method will block the calling thread until a lock on the mutex can 110 | /// be obtained. The mutex remains locked until \c unlock() is called. 111 | /// @see lock_guard 112 | inline void lock() 113 | { 114 | #if defined(_FAST_MUTEX_ASM_) 115 | bool gotLock; 116 | do { 117 | gotLock = try_lock(); 118 | if(!gotLock) 119 | { 120 | #if defined(_TTHREAD_WIN32_) 121 | Sleep(0); 122 | #elif defined(_TTHREAD_POSIX_) 123 | sched_yield(); 124 | #endif 125 | } 126 | } while(!gotLock); 127 | #else 128 | #if defined(_TTHREAD_WIN32_) 129 | EnterCriticalSection(&mHandle); 130 | #elif defined(_TTHREAD_POSIX_) 131 | pthread_mutex_lock(&mHandle); 132 | #endif 133 | #endif 134 | } 135 | 136 | /// Try to lock the mutex. 137 | /// The method will try to lock the mutex. If it fails, the function will 138 | /// return immediately (non-blocking). 139 | /// @return \c true if the lock was acquired, or \c false if the lock could 140 | /// not be acquired. 141 | inline bool try_lock() 142 | { 143 | #if defined(_FAST_MUTEX_ASM_) 144 | int oldLock; 145 | #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 146 | asm volatile ( 147 | "movl $1,%%eax\n\t" 148 | "xchg %%eax,%0\n\t" 149 | "movl %%eax,%1\n\t" 150 | : "=m" (mLock), "=m" (oldLock) 151 | : 152 | : "%eax", "memory" 153 | ); 154 | #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 155 | int *ptrLock = &mLock; 156 | __asm { 157 | mov eax,1 158 | mov ecx,ptrLock 159 | xchg eax,[ecx] 160 | mov oldLock,eax 161 | } 162 | #elif defined(__GNUC__) && (defined(__ppc__)) 163 | int newLock = 1; 164 | asm volatile ( 165 | "\n1:\n\t" 166 | "lwarx %0,0,%1\n\t" 167 | "cmpwi 0,%0,0\n\t" 168 | "bne- 2f\n\t" 169 | "stwcx. %2,0,%1\n\t" 170 | "bne- 1b\n\t" 171 | "isync\n" 172 | "2:\n\t" 173 | : "=&r" (oldLock) 174 | : "r" (&mLock), "r" (newLock) 175 | : "cr0", "memory" 176 | ); 177 | #endif 178 | return (oldLock == 0); 179 | #else 180 | #if defined(_TTHREAD_WIN32_) 181 | return TryEnterCriticalSection(&mHandle) ? true : false; 182 | #elif defined(_TTHREAD_POSIX_) 183 | return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; 184 | #endif 185 | #endif 186 | } 187 | 188 | /// Unlock the mutex. 189 | /// If any threads are waiting for the lock on this mutex, one of them will 190 | /// be unblocked. 191 | inline void unlock() 192 | { 193 | #if defined(_FAST_MUTEX_ASM_) 194 | #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 195 | asm volatile ( 196 | "movl $0,%%eax\n\t" 197 | "xchg %%eax,%0\n\t" 198 | : "=m" (mLock) 199 | : 200 | : "%eax", "memory" 201 | ); 202 | #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 203 | int *ptrLock = &mLock; 204 | __asm { 205 | mov eax,0 206 | mov ecx,ptrLock 207 | xchg eax,[ecx] 208 | } 209 | #elif defined(__GNUC__) && (defined(__ppc__)) 210 | asm volatile ( 211 | "sync\n\t" // Replace with lwsync where possible? 212 | : : : "memory" 213 | ); 214 | mLock = 0; 215 | #endif 216 | #else 217 | #if defined(_TTHREAD_WIN32_) 218 | LeaveCriticalSection(&mHandle); 219 | #elif defined(_TTHREAD_POSIX_) 220 | pthread_mutex_unlock(&mHandle); 221 | #endif 222 | #endif 223 | } 224 | 225 | private: 226 | #if defined(_FAST_MUTEX_ASM_) 227 | int mLock; 228 | #else 229 | #if defined(_TTHREAD_WIN32_) 230 | CRITICAL_SECTION mHandle; 231 | #elif defined(_TTHREAD_POSIX_) 232 | pthread_mutex_t mHandle; 233 | #endif 234 | #endif 235 | }; 236 | 237 | } 238 | 239 | #endif // _FAST_MUTEX_H_ 240 | -------------------------------------------------------------------------------- /tests/fractal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Marcus Geelnard 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | using namespace tthread; 34 | 35 | 36 | // Mandelbrot fractal settings 37 | #define MAND_MID_RE -0.69795 38 | #define MAND_MID_IM -0.34865 39 | #define MAND_SIZE 0.003 40 | #define MAND_MAX_ITER 4000 41 | #define MAND_RESOLUTION 1024 42 | 43 | 44 | /// BGR pixel class. 45 | class Pixel { 46 | public: 47 | Pixel() : r(0), g(0), b(0) {} 48 | 49 | Pixel(unsigned char red, unsigned char green, unsigned char blue) 50 | { 51 | r = red; 52 | g = green; 53 | b = blue; 54 | } 55 | 56 | unsigned char b, g, r; 57 | }; 58 | 59 | /// Simple 2D BGR image class. 60 | class Image { 61 | public: 62 | /// Create a new image with the dimensions aWidth x aHeight. 63 | Image(int aWidth, int aHeight) 64 | { 65 | mData = new Pixel[aWidth * aHeight]; 66 | mWidth = aWidth; 67 | mHeight = aHeight; 68 | } 69 | 70 | ~Image() 71 | { 72 | delete mData; 73 | } 74 | 75 | /// Write the image to a TGA file. 76 | void WriteToTGAFile(const char * aFileName) 77 | { 78 | // Prepare TGA file header 79 | unsigned char header[18] = {0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0}; 80 | header[12] = mWidth & 255; // Image width (16 bits) 81 | header[13] = (mWidth >> 8) & 255; // -"- 82 | header[14] = mHeight & 255; // Image width (16 bits) 83 | header[15] = (mHeight >> 8) & 255; // -"- 84 | 85 | // Write output file 86 | ofstream f(aFileName, ios_base::out | ios_base::binary); 87 | f.write((const char *) header, 18); 88 | f.write((const char *) mData, (mWidth * mHeight) * 3); 89 | } 90 | 91 | Pixel& operator[](const int idx) 92 | { 93 | return mData[idx]; 94 | } 95 | 96 | inline int Width() const 97 | { 98 | return mWidth; 99 | } 100 | 101 | inline int Height() const 102 | { 103 | return mHeight; 104 | } 105 | 106 | private: 107 | int mWidth; ///< Image width. 108 | int mHeight; ///< Image height. 109 | Pixel * mData; ///< Data pointer. 110 | }; 111 | 112 | /// The RowDispatcher class manages the "job pool" for the fractal 113 | /// calculation threads. 114 | class RowDispatcher { 115 | public: 116 | RowDispatcher(Image * aImage) : mNextRow(0) 117 | { 118 | mImage = aImage; 119 | } 120 | 121 | Image * GetImage() 122 | { 123 | return mImage; 124 | } 125 | 126 | int NextRow() 127 | { 128 | lock_guard guard(mMutex); 129 | if(mNextRow >= mImage->Height()) 130 | return -1; 131 | int result = mNextRow; 132 | ++ mNextRow; 133 | return result; 134 | } 135 | 136 | private: 137 | mutex mMutex; 138 | int mNextRow; 139 | Image * mImage; 140 | }; 141 | 142 | // Core iteration function 143 | Pixel Iterate(const double &cre, const double &cim, int aIterMax) 144 | { 145 | double zre = 0.0; 146 | double zim = 0.0; 147 | int n = 0; 148 | double absZSqr = 0.0; 149 | while((absZSqr < 4.0) && (n < aIterMax)) 150 | { 151 | double tmp = zre * zre - zim * zim + cre; 152 | zim = 2.0 * zre * zim + cim; 153 | zre = tmp; 154 | absZSqr = zre * zre + zim * zim; 155 | ++ n; 156 | } 157 | if(n >= aIterMax) 158 | return Pixel(0,0,0); 159 | else 160 | { 161 | double nSmooth = n + 1 - log(log(sqrt(absZSqr))) / log(2.0); 162 | return Pixel( 163 | (unsigned char)(128.0 - 127.0 * cos(0.02 * nSmooth + 0.3)), 164 | (unsigned char)(128.0 - 127.0 * cos(0.016 * nSmooth + 1.2)), 165 | (unsigned char)(128.0 - 127.0 * cos(0.013 * nSmooth + 2.6)) 166 | ); 167 | } 168 | } 169 | 170 | // Calculation thread 171 | void CalcThread(void * arg) 172 | { 173 | RowDispatcher * dispatcher = (RowDispatcher *) arg; 174 | Image * img = dispatcher->GetImage(); 175 | 176 | // Set min/max interval for the fractal set 177 | double xMin = MAND_MID_RE - MAND_SIZE * 0.5; 178 | double yMin = MAND_MID_IM - MAND_SIZE * 0.5; 179 | double xMax = MAND_MID_RE + MAND_SIZE * 0.5; 180 | double yMax = MAND_MID_IM + MAND_SIZE * 0.5; 181 | 182 | // Set step size (distance between two adjacent pixels) 183 | double xStep = (xMax - xMin) / img->Width(); 184 | double yStep = (yMax - yMin) / img->Height(); 185 | 186 | // Until no more rows to process... 187 | while(true) 188 | { 189 | // Get the next row to calculate 190 | int y = dispatcher->NextRow(); 191 | 192 | // Done? 193 | if(y < 0) 194 | break; 195 | 196 | // Generate one row of the image 197 | Pixel * line = &(*img)[y * img->Width()]; 198 | double cim = y * yStep + yMin; 199 | double cre = xMin; 200 | for(int x = 0; x < img->Width(); ++ x) 201 | { 202 | *line ++ = Iterate(cre, cim, MAND_MAX_ITER); 203 | cre += xStep; 204 | } 205 | } 206 | } 207 | 208 | int main(int argc, char **argv) 209 | { 210 | // Show some information about this program... 211 | cout << "This is a small multi threaded Mandelbrot fractal generator." << endl; 212 | cout << endl; 213 | cout << "The program will generate a " << MAND_RESOLUTION << "x" << MAND_RESOLUTION << " image, using one calculation thread per" << endl; 214 | cout << "processor core. The result is written to a TGA format image, \"fractal.tga\"." << endl; 215 | cout << endl; 216 | cout << "Type \"" << argv[0] << " -st\" to force single threaded operation." << endl; 217 | cout << endl; 218 | 219 | // Check arguments 220 | bool singleThreaded = false; 221 | if((argc >= 2) && (string(argv[1]) == string("-st"))) 222 | singleThreaded = true; 223 | 224 | // Init image and row dispatcher 225 | Image img(MAND_RESOLUTION, MAND_RESOLUTION); 226 | RowDispatcher dispatcher(&img); 227 | 228 | // Determine the number of calculation threads to use 229 | int numThreads; 230 | if(!singleThreaded) 231 | { 232 | numThreads = thread::hardware_concurrency(); 233 | if(numThreads < 1) 234 | numThreads = 1; 235 | } 236 | else 237 | numThreads = 1; 238 | 239 | // Start calculation threads (we run one thread on each processor core) 240 | cout << "Running " << numThreads << " calculation thread(s)..." << flush; 241 | list threadList; 242 | for(int i = 0; i < numThreads; ++ i) 243 | { 244 | thread * t = new thread(CalcThread, (void *) &dispatcher); 245 | threadList.push_back(t); 246 | } 247 | 248 | // Wait for the threads to complete... 249 | for(list::iterator i = threadList.begin(); i != threadList.end(); ++ i) 250 | { 251 | thread * t = *i; 252 | t->join(); 253 | delete t; 254 | } 255 | cout << "done!" << endl; 256 | 257 | // Write the final image to a file 258 | cout << "Writing final image..." << flush; 259 | img.WriteToTGAFile("fractal.tga"); 260 | cout << "done!" << endl; 261 | } 262 | -------------------------------------------------------------------------------- /tests/future.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #define USE_TTHREAD 1 30 | #if USE_TTHREAD 31 | #include 32 | #include 33 | #include 34 | #define USE_CONTINUATIONS 35 | using namespace tthread; 36 | #else 37 | #include 38 | #include 39 | #include 40 | #endif 41 | 42 | using namespace std; 43 | 44 | int ackermann(int m, int n) { 45 | if (m == 0) return n + 1; 46 | if (n == 0) return ackermann(m - 1, 1); 47 | return ackermann(m - 1, ackermann(m, n - 1)); 48 | } 49 | 50 | int main() { 51 | 52 | /////////////////////////////////////////////////////////////////////////// 53 | 54 | #if defined(USE_CONTINUATIONS) 55 | try { 56 | cout << "f(g0(g1(g2(g3()))) = 1*(2*(3*(4*(5)))) = " << 57 | async([]() { 58 | return 5; 59 | }).then([](int x) { 60 | return x * 4; 61 | }).then([](int x) { 62 | return x * 3; 63 | }).then([](int x) { 64 | return x * 2; 65 | }).then([](int x) { 66 | return x; 67 | }).get() << endl << endl; 68 | } catch (...) { 69 | cout << "Error in async continuation." << endl << endl; 70 | } 71 | #endif 72 | 73 | /////////////////////////////////////////////////////////////////////////// 74 | 75 | cout << "Main thread id: " << this_thread::get_id() << endl; 76 | vector> futures; 77 | for (int i = 0; i < 8; ++i) { 78 | futures.emplace_back(async([] { 79 | this_thread::sleep_for(chrono::milliseconds(100)); 80 | cout << this_thread::get_id() << " "; 81 | })); 82 | } 83 | for_each(futures.begin(), futures.end(), [](future& f) { 84 | f.wait(); 85 | }); 86 | cout << endl << endl; 87 | 88 | /////////////////////////////////////////////////////////////////////////// 89 | 90 | packaged_task task(bind(&ackermann, 3, 11)); 91 | auto f = task.get_future(); 92 | task(); 93 | cout << "Sync: Ackerman(3,11) = " << f.get() << endl << endl; 94 | 95 | /////////////////////////////////////////////////////////////////////////// 96 | 97 | vector> futures2; 98 | for (int i = 0; i < 8; ++i) { 99 | futures2.emplace_back(async(bind(&ackermann, 3, 11))); 100 | } 101 | for_each(futures2.begin(), futures2.end(), [=](future& f) { 102 | std::cout << "Async: Ackerman(3,11) = " << f.get() << endl; 103 | }); 104 | cout << endl << endl; 105 | 106 | /////////////////////////////////////////////////////////////////////////// 107 | 108 | } 109 | -------------------------------------------------------------------------------- /tests/hello.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Marcus Geelnard 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | using namespace tthread; 29 | 30 | 31 | // This is the child thread function 32 | void HelloThread(void * aArg) 33 | { 34 | cout << "Hello world!" << endl; 35 | } 36 | 37 | // This is the main program (i.e. the main thread) 38 | int main() 39 | { 40 | // Start the child thread 41 | thread t(HelloThread, 0); 42 | 43 | // Wait for the thread to finish 44 | t.join(); 45 | } 46 | -------------------------------------------------------------------------------- /tests/pool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "timer.h" 32 | 33 | using namespace tthread; 34 | using namespace std; 35 | 36 | /////////////////////////////////////////////////////////////////////////// 37 | 38 | template 39 | struct ackermann_t { 40 | enum { value = ackermann_t::value>::value }; 41 | }; 42 | 43 | template struct ackermann_t { 44 | enum { value = ackermann_t::value }; 45 | }; 46 | 47 | template struct ackermann_t<0, n> { 48 | enum { value = n + 1 }; 49 | }; 50 | 51 | /////////////////////////////////////////////////////////////////////////// 52 | 53 | int ackermann(int m, int n) { 54 | if (m == 0) return n + 1; 55 | if (n == 0) return ackermann(m - 1, 1); 56 | return ackermann(m - 1, ackermann(m, n - 1)); 57 | } 58 | 59 | /////////////////////////////////////////////////////////////////////////// 60 | 61 | int ackermann2(int m, int n) { 62 | while (m > 0) { 63 | if (n == 0) 64 | n = 1; 65 | else { 66 | n = ackermann2(m, n - 1); 67 | } 68 | m = m - 1; 69 | } 70 | 71 | return n + 1; 72 | } 73 | /////////////////////////////////////////////////////////////////////////// 74 | 75 | int main(int argc, char* argv[]) { 76 | 77 | std::cout << "AckermanT: " << ackermann_t<3, 6>::value << std::endl; 78 | 79 | unsigned pool_size = thread_pool::DEFAULT_POOL_SIZE; 80 | if (argc > 1) { 81 | std::istringstream ss(argv[1]); 82 | ss >> pool_size; 83 | } 84 | 85 | thread_pool pool(pool_size); 86 | 87 | { 88 | vector> mFutures; 89 | 90 | timed_run session("Ackerman 10"); 91 | 92 | for (int i = 0; i < 10; ++i) { 93 | mFutures.emplace_back( 94 | pool.submit_task([]() { 95 | std::cout << "Ackerman(" << this_thread::get_id() << ") = " 96 | << ackermann(3, 11) 97 | << std::endl; 98 | })); 99 | } 100 | 101 | for (auto it = begin(mFutures); it != end(mFutures); ++it) { 102 | it->wait(); 103 | } 104 | } 105 | 106 | { 107 | vector> mFutures; 108 | 109 | timed_run session("SimpleThread 500000"); 110 | 111 | for (int i = 0; i < 500000; ++i) { 112 | mFutures.emplace_back(pool.submit_task([]() { return ackermann(3,4); })); 113 | } 114 | 115 | for (auto it = begin(mFutures); it != end(mFutures); ++it) { 116 | it->wait(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Marcus Geelnard 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | using namespace tthread; 31 | 32 | // HACK: Mac OS X and early MinGW do not support thread-local storage 33 | #if defined(__APPLE__) || (defined(__MINGW32__) || (__GNUC__ < 4)) 34 | #define NO_TLS 35 | #endif 36 | 37 | 38 | // Thread local storage variable 39 | #ifndef NO_TLS 40 | thread_local int gLocalVar; 41 | #endif 42 | 43 | // Mutex + global count variable 44 | mutex gMutex; 45 | fast_mutex gFastMutex; 46 | int gCount; 47 | 48 | // Condition variable 49 | condition_variable gCond; 50 | 51 | // Thread function: Thread ID 52 | void ThreadIDs(void * aArg) 53 | { 54 | cout << " My thread id is " << this_thread::get_id() << "." << endl; 55 | } 56 | 57 | #ifndef NO_TLS 58 | // Thread function: Thread-local storage 59 | void ThreadTLS(void * aArg) 60 | { 61 | gLocalVar = 2; 62 | cout << " My gLocalVar is " << gLocalVar << "." << endl; 63 | } 64 | #endif 65 | 66 | // Thread function: Mutex locking 67 | void ThreadLock(void * aArg) 68 | { 69 | for(int i = 0; i < 10000; ++ i) 70 | { 71 | lock_guard lock(gMutex); 72 | ++ gCount; 73 | } 74 | } 75 | 76 | // Thread function: Mutex locking 77 | void ThreadLock2(void * aArg) 78 | { 79 | for(int i = 0; i < 10000; ++ i) 80 | { 81 | lock_guard lock(gFastMutex); 82 | ++ gCount; 83 | } 84 | } 85 | 86 | // Thread function: Condition notifier 87 | void ThreadCondition1(void * aArg) 88 | { 89 | lock_guard lock(gMutex); 90 | -- gCount; 91 | gCond.notify_all(); 92 | } 93 | 94 | // Thread function: Condition waiter 95 | void ThreadCondition2(void * aArg) 96 | { 97 | cout << " Wating..." << flush; 98 | lock_guard lock(gMutex); 99 | while(gCount > 0) 100 | { 101 | cout << "." << flush; 102 | gCond.wait(gMutex); 103 | } 104 | cout << "." << endl; 105 | } 106 | 107 | // Thread function: Yield 108 | void ThreadYield(void * aArg) 109 | { 110 | // Yield... 111 | this_thread::yield(); 112 | } 113 | 114 | // Thread function: Detach 115 | void ThreadDetach(void * aArg) 116 | { 117 | // We don't do anything much, just sleep a little... 118 | this_thread::sleep_for(chrono::milliseconds(100)); 119 | } 120 | 121 | 122 | // This is the main program (i.e. the main thread) 123 | int main() 124 | { 125 | // Test 1: Show number of CPU cores in the system 126 | cout << "PART I: Info" << endl; 127 | cout << " Number of processor cores: " << thread::hardware_concurrency() << endl; 128 | 129 | // Test 2: thread IDs 130 | cout << endl << "PART II: Thread IDs" << endl; 131 | { 132 | // Show the main thread ID 133 | cout << " Main thread id is " << this_thread::get_id() << "." << endl; 134 | 135 | // Start a bunch of child threads - only run a single thread at a time 136 | thread t1(ThreadIDs, 0); 137 | t1.join(); 138 | thread t2(ThreadIDs, 0); 139 | t2.join(); 140 | thread t3(ThreadIDs, 0); 141 | t3.join(); 142 | thread t4(ThreadIDs, 0); 143 | t4.join(); 144 | } 145 | 146 | // Test 3: thread local storage 147 | cout << endl << "PART III: Thread local storage" << endl; 148 | #ifndef NO_TLS 149 | { 150 | // Clear the TLS variable (it should keep this value after all threads are 151 | // finished). 152 | gLocalVar = 1; 153 | cout << " Main gLocalVar is " << gLocalVar << "." << endl; 154 | 155 | // Start a child thread that modifies gLocalVar 156 | thread t1(ThreadTLS, 0); 157 | t1.join(); 158 | 159 | // Check if the TLS variable has changed 160 | if(gLocalVar == 1) 161 | cout << " Main gLocalID was not changed by the child thread - OK!" << endl; 162 | else 163 | cout << " Main gLocalID was changed by the child thread - FAIL!" << endl; 164 | } 165 | #else 166 | cout << " TLS is not supported on this platform..." << endl; 167 | #endif 168 | 169 | // Test 4: mutex locking 170 | cout << endl << "PART IV: Mutex locking (100 threads x 10000 iterations)" << endl; 171 | { 172 | // Clear the global counter. 173 | gCount = 0; 174 | 175 | // Start a bunch of child threads 176 | list threadList; 177 | for(int i = 0; i < 100; ++ i) 178 | threadList.push_back(new thread(ThreadLock, 0)); 179 | 180 | // Wait for the threads to finish 181 | list::iterator it; 182 | for(it = threadList.begin(); it != threadList.end(); ++ it) 183 | { 184 | thread * t = *it; 185 | t->join(); 186 | delete t; 187 | } 188 | 189 | // Check the global count 190 | cout << " gCount = " << gCount << endl; 191 | } 192 | 193 | // Test 5: fast_mutex locking 194 | cout << endl << "PART V: Fast mutex locking (100 threads x 10000 iterations)" << endl; 195 | { 196 | // Clear the global counter. 197 | gCount = 0; 198 | 199 | // Start a bunch of child threads 200 | list threadList; 201 | for(int i = 0; i < 100; ++ i) 202 | threadList.push_back(new thread(ThreadLock2, 0)); 203 | 204 | // Wait for the threads to finish 205 | list::iterator it; 206 | for(it = threadList.begin(); it != threadList.end(); ++ it) 207 | { 208 | thread * t = *it; 209 | t->join(); 210 | delete t; 211 | } 212 | 213 | // Check the global count 214 | cout << " gCount = " << gCount << endl; 215 | } 216 | 217 | // Test 6: condition variable 218 | cout << endl << "PART VI: Condition variable (40 + 1 threads)" << endl; 219 | { 220 | // Set the global counter to the number of threads to run. 221 | gCount = 40; 222 | 223 | // Start the waiting thread (it will wait for gCount to reach zero). 224 | thread t1(ThreadCondition2, 0); 225 | 226 | // Start a bunch of child threads (these will decrease gCount by 1 when they 227 | // finish) 228 | list threadList; 229 | for(int i = 0; i < 40; ++ i) 230 | threadList.push_back(new thread(ThreadCondition1, 0)); 231 | 232 | // Wait for the waiting thread to finish 233 | t1.join(); 234 | 235 | // Wait for the other threads to finish 236 | list::iterator it; 237 | for(it = threadList.begin(); it != threadList.end(); ++ it) 238 | { 239 | thread * t = *it; 240 | t->join(); 241 | delete t; 242 | } 243 | } 244 | 245 | // Test 7: yield 246 | cout << endl << "PART VII: Yield (40 + 1 threads)" << endl; 247 | { 248 | // Start a bunch of child threads 249 | list threadList; 250 | for(int i = 0; i < 40; ++ i) 251 | threadList.push_back(new thread(ThreadYield, 0)); 252 | 253 | // Yield... 254 | this_thread::yield(); 255 | 256 | // Wait for the threads to finish 257 | list::iterator it; 258 | for(it = threadList.begin(); it != threadList.end(); ++ it) 259 | { 260 | thread * t = *it; 261 | t->join(); 262 | delete t; 263 | } 264 | } 265 | 266 | // Test 8: sleep 267 | cout << endl << "PART VIII: Sleep (10 x 100 ms)" << endl; 268 | { 269 | // Sleep... 270 | cout << " Sleeping" << flush; 271 | for(int i = 0; i < 10; ++ i) 272 | { 273 | this_thread::sleep_for(chrono::milliseconds(100)); 274 | cout << "." << flush; 275 | } 276 | cout << endl; 277 | } 278 | 279 | // Test 9: detach 280 | cout << endl << "PART IX: Detach" << endl; 281 | { 282 | thread t(ThreadDetach, 0); 283 | t.detach(); 284 | cout << " Detached from thread." << endl; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /tests/timer.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2012, Jared Duke. 3 | // This code is released under the MIT License. 4 | // www.opensource.org/licenses/mit-license.php 5 | ///////////////////////////////////////////////////////////////////////////// 6 | 7 | #ifndef _TIMER_H_ 8 | #define _TIMER_H_ 9 | 10 | #include 11 | #if defined(_WIN32) 12 | #include 13 | #else 14 | #include 15 | #include 16 | #endif 17 | 18 | class timer { 19 | public: 20 | 21 | timer(); 22 | ~timer(); 23 | void restart(); 24 | double elapsed(); 25 | 26 | private: 27 | 28 | #if defined(_WIN32) 29 | long long int startTime; 30 | long long int frequency; 31 | #else 32 | unsigned long startTime[2]; 33 | #endif 34 | }; 35 | 36 | /////////////////////////////////////////////////////////////////////////// 37 | 38 | #if defined(_WIN32) 39 | 40 | timer::timer() { 41 | DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1); 42 | QueryPerformanceFrequency((LARGE_INTEGER *)&frequency); 43 | SetThreadAffinityMask(GetCurrentThread(), oldmask); 44 | restart(); 45 | } 46 | 47 | timer::~timer() { 48 | 49 | } 50 | 51 | void timer::restart() { 52 | DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1); 53 | QueryPerformanceCounter((LARGE_INTEGER *)&startTime); 54 | SetThreadAffinityMask(GetCurrentThread(), oldmask); 55 | } 56 | 57 | double timer::elapsed() { 58 | long long int tempTime; 59 | DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1); 60 | QueryPerformanceCounter((LARGE_INTEGER *)&tempTime); 61 | SetThreadAffinityMask(GetCurrentThread(), oldmask); 62 | if (tempTime 30 | #include "tinythread.h" 31 | 32 | #if defined(_TTHREAD_POSIX_) 33 | #include 34 | #include 35 | #elif defined(_TTHREAD_WIN32_) 36 | #include 37 | #endif 38 | 39 | 40 | namespace tthread { 41 | 42 | //------------------------------------------------------------------------------ 43 | // condition_variable 44 | //------------------------------------------------------------------------------ 45 | // NOTE 1: The Win32 implementation of the condition_variable class is based on 46 | // the corresponding implementation in GLFW, which in turn is based on a 47 | // description by Douglas C. Schmidt and Irfan Pyarali: 48 | // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html 49 | // 50 | // NOTE 2: Windows Vista actually has native support for condition variables 51 | // (InitializeConditionVariable, WakeConditionVariable, etc), but we want to 52 | // be portable with pre-Vista Windows versions, so TinyThread++ does not use 53 | // Vista condition variables. 54 | //------------------------------------------------------------------------------ 55 | 56 | #if defined(_TTHREAD_WIN32_) 57 | #define _CONDITION_EVENT_ONE 0 58 | #define _CONDITION_EVENT_ALL 1 59 | #endif 60 | 61 | #if defined(_TTHREAD_WIN32_) 62 | condition_variable::condition_variable() : mWaitersCount(0) { 63 | mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); 64 | mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); 65 | InitializeCriticalSection(&mWaitersCountLock); 66 | } 67 | #endif 68 | 69 | #if defined(_TTHREAD_WIN32_) 70 | condition_variable::~condition_variable() { 71 | CloseHandle(mEvents[_CONDITION_EVENT_ONE]); 72 | CloseHandle(mEvents[_CONDITION_EVENT_ALL]); 73 | DeleteCriticalSection(&mWaitersCountLock); 74 | } 75 | #endif 76 | 77 | #if defined(_TTHREAD_WIN32_) 78 | void condition_variable::_wait() { 79 | // Wait for either event to become signaled due to notify_one() or 80 | // notify_all() being called 81 | int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); 82 | 83 | // Check if we are the last waiter 84 | EnterCriticalSection(&mWaitersCountLock); 85 | -- mWaitersCount; 86 | bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && 87 | (mWaitersCount == 0); 88 | LeaveCriticalSection(&mWaitersCountLock); 89 | 90 | // If we are the last waiter to be notified to stop waiting, reset the event 91 | if(lastWaiter) 92 | ResetEvent(mEvents[_CONDITION_EVENT_ALL]); 93 | } 94 | #endif 95 | 96 | #if defined(_TTHREAD_WIN32_) 97 | void condition_variable::notify_one() { 98 | // Are there any waiters? 99 | EnterCriticalSection(&mWaitersCountLock); 100 | bool haveWaiters = (mWaitersCount > 0); 101 | LeaveCriticalSection(&mWaitersCountLock); 102 | 103 | // If we have any waiting threads, send them a signal 104 | if(haveWaiters) 105 | SetEvent(mEvents[_CONDITION_EVENT_ONE]); 106 | } 107 | #endif 108 | 109 | #if defined(_TTHREAD_WIN32_) 110 | void condition_variable::notify_all() { 111 | // Are there any waiters? 112 | EnterCriticalSection(&mWaitersCountLock); 113 | bool haveWaiters = (mWaitersCount > 0); 114 | LeaveCriticalSection(&mWaitersCountLock); 115 | 116 | // If we have any waiting threads, send them a signal 117 | if(haveWaiters) 118 | SetEvent(mEvents[_CONDITION_EVENT_ALL]); 119 | } 120 | #endif 121 | 122 | 123 | //------------------------------------------------------------------------------ 124 | // POSIX pthread_t to unique thread::id mapping logic. 125 | // Note: Here we use a global thread safe std::map to convert instances of 126 | // pthread_t to small thread identifier numbers (unique within one process). 127 | // This method should be portable across different POSIX implementations. 128 | //------------------------------------------------------------------------------ 129 | 130 | #if defined(_TTHREAD_POSIX_) 131 | static thread::id _pthread_t_to_ID(const pthread_t &aHandle) { 132 | static mutex idMapLock; 133 | static std::map idMap; 134 | static unsigned long int idCount(1); 135 | 136 | lock_guard guard(idMapLock); 137 | if(idMap.find(aHandle) == idMap.end()) 138 | idMap[aHandle] = idCount ++; 139 | return thread::id(idMap[aHandle]); 140 | } 141 | #endif // _TTHREAD_POSIX_ 142 | 143 | 144 | //------------------------------------------------------------------------------ 145 | // thread 146 | //------------------------------------------------------------------------------ 147 | 148 | /// Information to pass to the new thread (what to run). 149 | struct _thread_start_info { 150 | thread_func mFunction; ///< Pointer to the function to be executed. 151 | void * mArg; ///< Function argument for the thread function. 152 | thread::data_ptr mThreadData; ///< Pointer to the thread data. 153 | _thread_start_info(thread_func func, void * arg, thread::data_ptr threadData) 154 | : mFunction(func), mArg(arg), mThreadData(threadData) { } 155 | }; 156 | 157 | // Thread wrapper function. 158 | #if defined(_TTHREAD_WIN32_) 159 | unsigned WINAPI thread::wrapper_function(void * aArg) 160 | #elif defined(_TTHREAD_POSIX_) 161 | void * thread::wrapper_function(void * aArg) 162 | #endif 163 | { 164 | // Get thread startup information 165 | _thread_start_info * ti = (_thread_start_info *) aArg; 166 | 167 | try { 168 | // Call the actual client thread function 169 | ti->mFunction(ti->mArg); 170 | } catch(...) { 171 | // Uncaught exceptions will terminate the application (default behavior 172 | // according to the C++0x draft) 173 | std::terminate(); 174 | } 175 | 176 | // The thread is no longer executing 177 | lock_guard guard(ti->mThreadData->mMutex); 178 | ti->mThreadData->mNotAThread = true; 179 | 180 | // The thread is responsible for freeing the startup information 181 | delete ti; 182 | 183 | return 0; 184 | } 185 | 186 | thread::thread(thread_func func, void * aArg) : mData( new thread_data() ) { 187 | 188 | // Serialize access to this thread structure 189 | lock_guard guard(mData->mMutex); 190 | 191 | // Fill out the thread startup information (passed to the thread wrapper, 192 | // which will eventually free it) 193 | _thread_start_info * ti = new _thread_start_info(func, aArg, mData); 194 | 195 | thread_data& data = *mData; 196 | 197 | // The thread is now alive 198 | data.mNotAThread = false; 199 | 200 | // Create the thread 201 | #if defined(_TTHREAD_WIN32_) 202 | data.mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &data.mWin32ThreadID); 203 | #elif defined(_TTHREAD_POSIX_) 204 | if(pthread_create(&data.mHandle, NULL, wrapper_function, (void *) ti) != 0) 205 | data.mHandle = 0; 206 | #endif 207 | 208 | // Did we fail to create the thread? 209 | if(!data.mHandle) 210 | { 211 | data.mNotAThread = true; 212 | delete ti; 213 | } 214 | } 215 | 216 | thread::thread_data::thread_data() 217 | : mHandle(0) 218 | , mNotAThread(true) 219 | #if defined(_TTHREAD_WIN32_) 220 | , mWin32ThreadID(0) 221 | #endif 222 | {} 223 | 224 | thread::thread() 225 | : mData( new thread_data() ) { } 226 | 227 | thread::~thread() { 228 | if(joinable()) 229 | std::terminate(); 230 | } 231 | 232 | void thread::join() { 233 | if(joinable()) { 234 | #if defined(_TTHREAD_WIN32_) 235 | WaitForSingleObject(mData->mHandle, INFINITE); 236 | #elif defined(_TTHREAD_POSIX_) 237 | pthread_join(mData->mHandle, NULL); 238 | #endif 239 | } 240 | } 241 | 242 | void thread::detach() { 243 | lock_guard guard(mData->mMutex); 244 | mData->mNotAThread = true; 245 | } 246 | 247 | bool thread::joinable() const { 248 | if (!mData) 249 | return false; 250 | mData->mMutex.lock(); 251 | bool result = !mData->mNotAThread; 252 | mData->mMutex.unlock(); 253 | return result; 254 | } 255 | 256 | thread::id thread::get_id() const { 257 | if(!joinable()) 258 | return id(); 259 | #if defined(_TTHREAD_WIN32_) 260 | return id((unsigned long int) mData->mWin32ThreadID); 261 | #elif defined(_TTHREAD_POSIX_) 262 | return _pthread_t_to_ID(mData->mHandle); 263 | #endif 264 | } 265 | 266 | unsigned thread::hardware_concurrency() { 267 | #if defined(_TTHREAD_WIN32_) 268 | SYSTEM_INFO si; 269 | GetSystemInfo(&si); 270 | return (int) si.dwNumberOfProcessors; 271 | #elif defined(_SC_NPROCESSORS_ONLN) 272 | return (int) sysconf(_SC_NPROCESSORS_ONLN); 273 | #elif defined(_SC_NPROC_ONLN) 274 | return (int) sysconf(_SC_NPROC_ONLN); 275 | #else 276 | // The standard requires this function to return zero if the number of 277 | // hardware cores could not be determined. 278 | return 0; 279 | #endif 280 | } 281 | 282 | 283 | //------------------------------------------------------------------------------ 284 | // this_thread 285 | //------------------------------------------------------------------------------ 286 | 287 | thread::id this_thread::get_id() { 288 | #if defined(_TTHREAD_WIN32_) 289 | return thread::id((unsigned long int) GetCurrentThreadId()); 290 | #elif defined(_TTHREAD_POSIX_) 291 | return _pthread_t_to_ID(pthread_self()); 292 | #endif 293 | } 294 | 295 | 296 | } 297 | -------------------------------------------------------------------------------- /tinythread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Marcus Geelnard 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | /* 25 | Modified (m) 2011 Jared Duke 26 | ** Added support for lambdas/function objects via generic std::function compatible arguments 27 | */ 28 | 29 | #ifndef _TINYTHREAD_H_ 30 | #define _TINYTHREAD_H_ 31 | 32 | /// @file 33 | /// @mainpage TinyThread++ API Reference 34 | /// 35 | /// @section intro_sec Introduction 36 | /// TinyThread++ is a minimal, portable implementation of basic threading 37 | /// classes for C++. 38 | /// 39 | /// They closely mimic the functionality and naming of the C++0x standard, and 40 | /// should be easily replaceable with the corresponding std:: variants. 41 | /// 42 | /// @section port_sec Portability 43 | /// The Win32 variant uses the native Win32 API for implementing the thread 44 | /// classes, while for other systems, the POSIX threads API (pthread) is used. 45 | /// 46 | /// @section class_sec Classes 47 | /// In order to mimic the threading API of the C++0x standard, subsets of 48 | /// several classes are provided. The fundamental classes are: 49 | /// @li tthread::thread 50 | /// @li tthread::mutex 51 | /// @li tthread::recursive_mutex 52 | /// @li tthread::condition_variable 53 | /// @li tthread::lock_guard 54 | /// @li tthread::fast_mutex 55 | /// 56 | /// @section misc_sec Miscellaneous 57 | /// The following special keywords are available: #thread_local. 58 | /// 59 | /// For more detailed information (including additional classes), browse the 60 | /// different sections of this documentation. A good place to start is: 61 | /// tinythread.h. 62 | 63 | // Which platform are we on? 64 | #if !defined(_TTHREAD_PLATFORM_DEFINED_) 65 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 66 | #define _TTHREAD_WIN32_ 67 | #else 68 | #define _TTHREAD_POSIX_ 69 | #endif 70 | #define _TTHREAD_PLATFORM_DEFINED_ 71 | #endif 72 | 73 | // Platform specific includes 74 | #if defined(_TTHREAD_WIN32_) 75 | #include 76 | #else 77 | #include 78 | #include 79 | #include 80 | #include 81 | #endif 82 | 83 | // Generic includes 84 | #include 85 | #include 86 | 87 | /// TinyThread++ version (major number). 88 | #define TINYTHREAD_VERSION_MAJOR 1 89 | /// TinyThread++ version (minor number). 90 | #define TINYTHREAD_VERSION_MINOR 2 91 | /// TinyThread++ version (full version). 92 | #define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) 93 | 94 | // Do we have a fully featured C++0x compiler? 95 | #if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) 96 | #define _TTHREAD_CPP0X_ 97 | #define _TTHREAD_FUNCTIONAL_ 98 | #define _TTHREAD_RVALUES_ 99 | #include 100 | #elif (defined(_MSC_VER) && _MSC_VER >= 1600) 101 | #define _TTHREAD_FUNCTIONAL_ 102 | #define _TTHREAD_RVALUES_ 103 | #include 104 | #elif (defined __MINGW32__) 105 | #define _TTHREAD_FUNCTIONAL_ 106 | #define _TTHREAD_RVALUES_ 107 | #include 108 | #endif 109 | 110 | #if defined(_TTHREAD_FUNCTIONAL_) 111 | typedef std::function thread_func; 112 | #else 113 | typedef void (*thread_func)(void *); 114 | #endif 115 | 116 | // ...at least partial C++0x? 117 | #if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) 118 | #define _TTHREAD_CPP0X_PARTIAL_ 119 | #endif 120 | 121 | // Macro for disabling assignments of objects. 122 | #ifdef _TTHREAD_CPP0X_PARTIAL_ 123 | #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ 124 | name(const name&) = delete; \ 125 | name& operator=(const name&) = delete; 126 | #else 127 | #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ 128 | name(const name&); \ 129 | name& operator=(const name&); 130 | #endif 131 | 132 | /// @def thread_local 133 | /// Thread local storage keyword. 134 | /// A variable that is declared with the \c thread_local keyword makes the 135 | /// value of the variable local to each thread (known as thread-local storage, 136 | /// or TLS). Example usage: 137 | /// @code 138 | /// // This variable is local to each thread. 139 | /// thread_local int variable; 140 | /// @endcode 141 | /// @note The \c thread_local keyword is a macro that maps to the corresponding 142 | /// compiler directive (e.g. \c __declspec(thread)). While the C++0x standard 143 | /// allows for non-trivial types (e.g. classes with constructors and 144 | /// destructors) to be declared with the \c thread_local keyword, most pre-C++0x 145 | /// compilers only allow for trivial types (e.g. \c int). So, to guarantee 146 | /// portable code, only use trivial types for thread local storage. 147 | /// @note This directive is currently not supported on Mac OS X (it will give 148 | /// a compiler error), since compile-time TLS is not supported in the Mac OS X 149 | /// executable format. Also, some older versions of MinGW (before GCC 4.x) do 150 | /// not support this directive. 151 | /// @hideinitializer 152 | 153 | #if !defined(_TTHREAD_CPP0X_) && !defined(thread_local) 154 | #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) 155 | #define thread_local __thread 156 | #else 157 | #define thread_local __declspec(thread) 158 | #endif 159 | #endif 160 | 161 | 162 | /// Main name space for TinyThread++. 163 | /// This namespace is more or less equivalent to the \c std namespace for the 164 | /// C++0x thread classes. For instance, the tthread::mutex class corresponds to 165 | /// the std::mutex class. 166 | namespace tthread { 167 | 168 | /// Mutex class. 169 | /// This is a mutual exclusion object for synchronizing access to shared 170 | /// memory areas for several threads. The mutex is non-recursive (i.e. a 171 | /// program may deadlock if the thread that owns a mutex object calls lock() 172 | /// on that object). 173 | /// @see recursive_mutex 174 | class mutex { 175 | public: 176 | /// Constructor. 177 | mutex() 178 | #if defined(_TTHREAD_WIN32_) 179 | : mAlreadyLocked(false) 180 | #endif 181 | { 182 | #if defined(_TTHREAD_WIN32_) 183 | InitializeCriticalSection(&mHandle); 184 | #else 185 | pthread_mutex_init(&mHandle, NULL); 186 | #endif 187 | } 188 | 189 | /// Destructor. 190 | ~mutex() 191 | { 192 | #if defined(_TTHREAD_WIN32_) 193 | DeleteCriticalSection(&mHandle); 194 | #else 195 | pthread_mutex_destroy(&mHandle); 196 | #endif 197 | } 198 | 199 | /// Lock the mutex. 200 | /// The method will block the calling thread until a lock on the mutex can 201 | /// be obtained. The mutex remains locked until \c unlock() is called. 202 | /// @see lock_guard 203 | inline void lock() 204 | { 205 | #if defined(_TTHREAD_WIN32_) 206 | EnterCriticalSection(&mHandle); 207 | while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... 208 | mAlreadyLocked = true; 209 | #else 210 | pthread_mutex_lock(&mHandle); 211 | #endif 212 | } 213 | 214 | /// Try to lock the mutex. 215 | /// The method will try to lock the mutex. If it fails, the function will 216 | /// return immediately (non-blocking). 217 | /// @return \c true if the lock was acquired, or \c false if the lock could 218 | /// not be acquired. 219 | inline bool try_lock() 220 | { 221 | #if defined(_TTHREAD_WIN32_) 222 | bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); 223 | if(ret && mAlreadyLocked) 224 | { 225 | LeaveCriticalSection(&mHandle); 226 | ret = false; 227 | } 228 | return ret; 229 | #else 230 | return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; 231 | #endif 232 | } 233 | 234 | /// Unlock the mutex. 235 | /// If any threads are waiting for the lock on this mutex, one of them will 236 | /// be unblocked. 237 | inline void unlock() 238 | { 239 | #if defined(_TTHREAD_WIN32_) 240 | mAlreadyLocked = false; 241 | LeaveCriticalSection(&mHandle); 242 | #else 243 | pthread_mutex_unlock(&mHandle); 244 | #endif 245 | } 246 | 247 | _TTHREAD_DISABLE_ASSIGNMENT(mutex) 248 | 249 | private: 250 | #if defined(_TTHREAD_WIN32_) 251 | CRITICAL_SECTION mHandle; 252 | bool mAlreadyLocked; 253 | #else 254 | pthread_mutex_t mHandle; 255 | #endif 256 | 257 | friend class condition_variable; 258 | }; 259 | 260 | /// Recursive mutex class. 261 | /// This is a mutual exclusion object for synchronizing access to shared 262 | /// memory areas for several threads. The mutex is recursive (i.e. a thread 263 | /// may lock the mutex several times, as long as it unlocks the mutex the same 264 | /// number of times). 265 | /// @see mutex 266 | class recursive_mutex { 267 | public: 268 | /// Constructor. 269 | recursive_mutex() 270 | { 271 | #if defined(_TTHREAD_WIN32_) 272 | InitializeCriticalSection(&mHandle); 273 | #else 274 | pthread_mutexattr_t attr; 275 | pthread_mutexattr_init(&attr); 276 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 277 | pthread_mutex_init(&mHandle, &attr); 278 | #endif 279 | } 280 | 281 | /// Destructor. 282 | ~recursive_mutex() 283 | { 284 | #if defined(_TTHREAD_WIN32_) 285 | DeleteCriticalSection(&mHandle); 286 | #else 287 | pthread_mutex_destroy(&mHandle); 288 | #endif 289 | } 290 | 291 | /// Lock the mutex. 292 | /// The method will block the calling thread until a lock on the mutex can 293 | /// be obtained. The mutex remains locked until \c unlock() is called. 294 | /// @see lock_guard 295 | inline void lock() 296 | { 297 | #if defined(_TTHREAD_WIN32_) 298 | EnterCriticalSection(&mHandle); 299 | #else 300 | pthread_mutex_lock(&mHandle); 301 | #endif 302 | } 303 | 304 | /// Try to lock the mutex. 305 | /// The method will try to lock the mutex. If it fails, the function will 306 | /// return immediately (non-blocking). 307 | /// @return \c true if the lock was acquired, or \c false if the lock could 308 | /// not be acquired. 309 | inline bool try_lock() 310 | { 311 | #if defined(_TTHREAD_WIN32_) 312 | return TryEnterCriticalSection(&mHandle) ? true : false; 313 | #else 314 | return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; 315 | #endif 316 | } 317 | 318 | /// Unlock the mutex. 319 | /// If any threads are waiting for the lock on this mutex, one of them will 320 | /// be unblocked. 321 | inline void unlock() 322 | { 323 | #if defined(_TTHREAD_WIN32_) 324 | LeaveCriticalSection(&mHandle); 325 | #else 326 | pthread_mutex_unlock(&mHandle); 327 | #endif 328 | } 329 | 330 | _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) 331 | 332 | private: 333 | #if defined(_TTHREAD_WIN32_) 334 | CRITICAL_SECTION mHandle; 335 | #else 336 | pthread_mutex_t mHandle; 337 | #endif 338 | 339 | friend class condition_variable; 340 | }; 341 | 342 | /// Lock guard class. 343 | /// The constructor locks the mutex, and the destructor unlocks the mutex, so 344 | /// the mutex will automatically be unlocked when the lock guard goes out of 345 | /// scope. Example usage: 346 | /// @code 347 | /// mutex m; 348 | /// int counter; 349 | /// 350 | /// void increment() 351 | /// { 352 | /// lock_guard guard(m); 353 | /// ++ counter; 354 | /// } 355 | /// @endcode 356 | 357 | template 358 | class lock_guard { 359 | public: 360 | typedef T mutex_type; 361 | 362 | lock_guard() : mMutex(0) {} 363 | 364 | /// The constructor locks the mutex. 365 | explicit lock_guard(mutex_type &aMutex) 366 | { 367 | mMutex = &aMutex; 368 | mMutex->lock(); 369 | } 370 | 371 | /// The destructor unlocks the mutex. 372 | ~lock_guard() 373 | { 374 | if(mMutex) 375 | mMutex->unlock(); 376 | } 377 | 378 | private: 379 | mutex_type * mMutex; 380 | }; 381 | 382 | /// Condition variable class. 383 | /// This is a signalling object for synchronizing the execution flow for 384 | /// several threads. Example usage: 385 | /// @code 386 | /// // Shared data and associated mutex and condition variable objects 387 | /// int count; 388 | /// mutex m; 389 | /// condition_variable cond; 390 | /// 391 | /// // Wait for the counter to reach a certain number 392 | /// void wait_counter(int targetCount) 393 | /// { 394 | /// lock_guard guard(m); 395 | /// while(count < targetCount) 396 | /// cond.wait(m); 397 | /// } 398 | /// 399 | /// // Increment the counter, and notify waiting threads 400 | /// void increment() 401 | /// { 402 | /// lock_guard guard(m); 403 | /// ++ count; 404 | /// cond.notify_all(); 405 | /// } 406 | /// @endcode 407 | class condition_variable { 408 | public: 409 | /// Constructor. 410 | #if defined(_TTHREAD_WIN32_) 411 | condition_variable(); 412 | #else 413 | condition_variable() 414 | { 415 | pthread_cond_init(&mHandle, NULL); 416 | } 417 | #endif 418 | 419 | /// Destructor. 420 | #if defined(_TTHREAD_WIN32_) 421 | ~condition_variable(); 422 | #else 423 | ~condition_variable() 424 | { 425 | pthread_cond_destroy(&mHandle); 426 | } 427 | #endif 428 | 429 | /// Wait for the condition. 430 | /// The function will block the calling thread until the condition variable 431 | /// is woken by \c notify_one(), \c notify_all() or a spurious wake up. 432 | /// @param[in] aMutex A mutex that will be unlocked when the wait operation 433 | /// starts, an locked again as soon as the wait operation is finished. 434 | template 435 | inline void wait(_mutexT &aMutex) 436 | { 437 | #if defined(_TTHREAD_WIN32_) 438 | // Increment number of waiters 439 | EnterCriticalSection(&mWaitersCountLock); 440 | ++ mWaitersCount; 441 | LeaveCriticalSection(&mWaitersCountLock); 442 | 443 | // Release the mutex while waiting for the condition (will decrease 444 | // the number of waiters when done)... 445 | aMutex.unlock(); 446 | _wait(); 447 | aMutex.lock(); 448 | #else 449 | pthread_cond_wait(&mHandle, &aMutex.mHandle); 450 | #endif 451 | } 452 | 453 | /// Notify one thread that is waiting for the condition. 454 | /// If at least one thread is blocked waiting for this condition variable, 455 | /// one will be woken up. 456 | /// @note Only threads that started waiting prior to this call will be 457 | /// woken up. 458 | #if defined(_TTHREAD_WIN32_) 459 | void notify_one(); 460 | #else 461 | inline void notify_one() 462 | { 463 | pthread_cond_signal(&mHandle); 464 | } 465 | #endif 466 | 467 | /// Notify all threads that are waiting for the condition. 468 | /// All threads that are blocked waiting for this condition variable will 469 | /// be woken up. 470 | /// @note Only threads that started waiting prior to this call will be 471 | /// woken up. 472 | #if defined(_TTHREAD_WIN32_) 473 | void notify_all(); 474 | #else 475 | inline void notify_all() 476 | { 477 | pthread_cond_broadcast(&mHandle); 478 | } 479 | #endif 480 | 481 | _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) 482 | 483 | private: 484 | #if defined(_TTHREAD_WIN32_) 485 | void _wait(); 486 | HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. 487 | unsigned int mWaitersCount; ///< Count of the number of waiters. 488 | CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. 489 | #else 490 | pthread_cond_t mHandle; 491 | #endif 492 | }; 493 | 494 | 495 | /// Thread class. 496 | class thread { 497 | public: 498 | #if defined(_TTHREAD_WIN32_) 499 | typedef HANDLE native_handle_type; 500 | #else 501 | typedef pthread_t native_handle_type; 502 | #endif 503 | 504 | class id; 505 | 506 | /// Default constructor. 507 | /// Construct a \c thread object without an associated thread of execution 508 | /// (i.e. non-joinable). 509 | thread(); 510 | 511 | #if defined(_TTHREAD_RVALUES_) 512 | 513 | /// Move constructor. 514 | /// Construct a \c thread object from an existing thread object 515 | thread(thread&& other) { 516 | *this = std::move(other); 517 | } 518 | 519 | thread& operator=(thread&& other) { 520 | swap(std::move(other)); 521 | return *this; 522 | } 523 | 524 | void swap(thread&& other) { 525 | std::swap(mData, other.mData); 526 | } 527 | 528 | #endif 529 | 530 | /// Thread starting constructor. 531 | /// Construct a \c thread object with a new thread of execution. 532 | /// @param[in] thread_func A function pointer to a function of type: 533 | /// void fun(void * arg) 534 | /// @param[in] aArg Argument to the thread function. 535 | /// @note This constructor is not fully compatible with the standard C++ 536 | /// thread class. It is more similar to the pthread_create() (POSIX) and 537 | /// CreateThread() (Windows) functions. 538 | thread(thread_func func, void * aArg); 539 | 540 | /// Destructor. 541 | /// @note If the thread is joinable upon destruction, \c std::terminate() 542 | /// will be called, which terminates the process. It is always wise to do 543 | /// \c join() before deleting a thread object. 544 | ~thread(); 545 | 546 | /// Wait for the thread to finish (join execution flows). 547 | void join(); 548 | 549 | /// Detach the thread object from the executing thread 550 | void detach(); 551 | 552 | /// Check if the thread is joinable. 553 | /// A thread object is joinable if it has an associated thread of execution. 554 | bool joinable() const; 555 | 556 | /// Return the thread ID of a thread object. 557 | id get_id() const; 558 | 559 | /// Get the native handle for this thread. 560 | /// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this 561 | /// is a \c pthread_t. 562 | inline native_handle_type native_handle() 563 | { 564 | return mData->mHandle; 565 | } 566 | 567 | /// Determine the number of threads which can possibly execute concurrently. 568 | /// This function is useful for determining the optimal number of threads to 569 | /// use for a task. 570 | /// @return The number of hardware thread contexts in the system. 571 | /// @note If this value is not defined, the function returns zero (0). 572 | static unsigned hardware_concurrency(); 573 | 574 | _TTHREAD_DISABLE_ASSIGNMENT(thread) 575 | 576 | struct thread_data { 577 | thread_data(); 578 | _TTHREAD_DISABLE_ASSIGNMENT(thread_data); 579 | 580 | native_handle_type mHandle; ///< Thread handle. 581 | mutable mutex mMutex; ///< Serializer for access to the thread private data. 582 | bool mNotAThread; ///< True if this object is not a thread of execution. 583 | #if defined(_TTHREAD_WIN32_) 584 | unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). 585 | #endif 586 | }; 587 | 588 | typedef std::shared_ptr data_ptr; 589 | 590 | protected: 591 | 592 | data_ptr mData; 593 | 594 | // This is the internal thread wrapper function. 595 | #if defined(_TTHREAD_WIN32_) 596 | static unsigned WINAPI wrapper_function(void * aArg); 597 | #else 598 | static void * wrapper_function(void * aArg); 599 | #endif 600 | }; 601 | 602 | /// Thread ID. 603 | /// The thread ID is a unique identifier for each thread. 604 | /// @see thread::get_id() 605 | class thread::id { 606 | public: 607 | /// Default constructor. 608 | /// The default constructed ID is that of thread without a thread of 609 | /// execution. 610 | id() : mId(0) {}; 611 | 612 | id(unsigned long int aId) : mId(aId) {}; 613 | 614 | id(const id& aId) : mId(aId.mId) {}; 615 | 616 | inline id & operator=(const id &aId) 617 | { 618 | mId = aId.mId; 619 | return *this; 620 | } 621 | 622 | inline friend bool operator==(const id &aId1, const id &aId2) 623 | { 624 | return (aId1.mId == aId2.mId); 625 | } 626 | 627 | inline friend bool operator!=(const id &aId1, const id &aId2) 628 | { 629 | return (aId1.mId != aId2.mId); 630 | } 631 | 632 | inline friend bool operator<=(const id &aId1, const id &aId2) 633 | { 634 | return (aId1.mId <= aId2.mId); 635 | } 636 | 637 | inline friend bool operator<(const id &aId1, const id &aId2) 638 | { 639 | return (aId1.mId < aId2.mId); 640 | } 641 | 642 | inline friend bool operator>=(const id &aId1, const id &aId2) 643 | { 644 | return (aId1.mId >= aId2.mId); 645 | } 646 | 647 | inline friend bool operator>(const id &aId1, const id &aId2) 648 | { 649 | return (aId1.mId > aId2.mId); 650 | } 651 | 652 | inline friend std::ostream& operator <<(std::ostream &os, const id &obj) 653 | { 654 | os << obj.mId; 655 | return os; 656 | } 657 | 658 | private: 659 | unsigned long int mId; 660 | }; 661 | 662 | 663 | // Related to - minimal to be able to support chrono. 664 | typedef long long __intmax_t; 665 | 666 | /// Minimal implementation of the \c ratio class. This class provides enough 667 | /// functionality to implement some basic \c chrono classes. 668 | template <__intmax_t N, __intmax_t D = 1> class ratio { 669 | public: 670 | static double _as_double() { return double(N) / double(D); } 671 | }; 672 | 673 | /// Minimal implementation of the \c chrono namespace. 674 | /// The \c chrono namespace provides types for specifying time intervals. 675 | namespace chrono { 676 | 677 | /// Duration template class. This class provides enough functionality to 678 | /// implement \c this_thread::sleep_for(). 679 | template > class duration { 680 | private: 681 | _Rep rep_; 682 | public: 683 | typedef _Rep rep; 684 | typedef _Period period; 685 | 686 | /// Construct a duration object with the given duration. 687 | template 688 | explicit duration(const _Rep2& r) : rep_(r) {}; 689 | 690 | /// Return the value of the duration object. 691 | rep count() const 692 | { 693 | return rep_; 694 | } 695 | }; 696 | 697 | // Standard duration types. 698 | typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. 699 | typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. 700 | typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. 701 | typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. 702 | typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. 703 | typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. 704 | 705 | } 706 | 707 | /// The namespace \c this_thread provides methods for dealing with the 708 | /// calling thread. 709 | namespace this_thread { 710 | 711 | /// Return the thread ID of the calling thread. 712 | thread::id get_id(); 713 | 714 | /// Yield execution to another thread. 715 | /// Offers the operating system the opportunity to schedule another thread 716 | /// that is ready to run on the current processor. 717 | inline void yield() 718 | { 719 | #if defined(_TTHREAD_WIN32_) 720 | Sleep(0); 721 | #else 722 | sched_yield(); 723 | #endif 724 | } 725 | 726 | /// Blocks the calling thread for a period of time. 727 | /// @param[in] aTime Minimum time to put the thread to sleep. 728 | /// Example usage: 729 | /// @code 730 | /// // Sleep for 100 milliseconds 731 | /// this_thread::sleep_for(chrono::milliseconds(100)); 732 | /// @endcode 733 | /// @note Supported duration types are: nanoseconds, microseconds, 734 | /// milliseconds, seconds, minutes and hours. 735 | template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) 736 | { 737 | #if defined(_TTHREAD_WIN32_) 738 | Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); 739 | #else 740 | usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); 741 | #endif 742 | } 743 | 744 | } 745 | 746 | } 747 | 748 | #endif // _TINYTHREAD_H_ 749 | -------------------------------------------------------------------------------- /tinythread_async.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef _TINYTHREAD_ASYNC_H_ 25 | #define _TINYTHREAD_ASYNC_H_ 26 | 27 | /// @file 28 | 29 | #include "tinythread_future.h" 30 | #include "tinythread_pool.h" 31 | #include "tinythread_t.h" 32 | 33 | namespace tthread { 34 | 35 | /////////////////////////////////////////////////////////////////////////// 36 | // launch 37 | namespace launch { 38 | enum policy { 39 | async = 0x01, 40 | deferred = 0x02, 41 | pooled = 0x04, 42 | sync = deferred, 43 | any = async | deferred | pooled 44 | }; 45 | }; 46 | 47 | /////////////////////////////////////////////////////////////////////////// 48 | 49 | template< typename Task > 50 | auto async_impl(launch::policy policy, Task&& task) -> decltype(task.get_future()) { 51 | auto future = task.get_future(); 52 | if ((policy & launch::async) != 0) { 53 | threadt thread(std::move(task)); 54 | thread.detach(); 55 | } 56 | return future; 57 | } 58 | 59 | ///////////////////////////////////////////////////////////////////////// 60 | 61 | template< typename F > 62 | auto async(launch::policy policy, F f) -> future { 63 | typedef decltype(f()) result_type; 64 | typedef packaged_task task_type; 65 | return 66 | #if !defined(NO_GENERIC_POOL) 67 | ((policy & launch::pooled) != 0) ? thread_pool::instance().submit_task(std::move(f)) : 68 | #endif 69 | async_impl(policy, task_type(std::move(f))); 70 | } 71 | 72 | template< typename F > 73 | auto async(F f) -> future { 74 | return async(launch::any, std::move(f)); 75 | } 76 | 77 | #if defined(_TTHREAD_VARIADIC_) 78 | 79 | template< typename F, typename... Args > 80 | auto async(F f, Args... args) -> future { 81 | return async(std::bind(f, std::move(args)...)); 82 | } 83 | 84 | #else 85 | 86 | template< typename F, typename T > 87 | auto async(F f, T t) -> future { 88 | return async(std::bind(f, std::move(t))); 89 | } 90 | 91 | template< typename F, typename T, typename U > 92 | auto async(F f, T t, U u) -> future { 93 | return async(std::bind(f, std::move(t), std::move(u))); 94 | } 95 | 96 | template< typename F, typename T, typename U, typename V > 97 | auto async(F f, T t, U u, V v) -> future { 98 | return async(std::bind(f, std::move(t), std::move(u), std::move(v))); 99 | } 100 | 101 | template< typename F, typename T, typename U, typename V, typename W > 102 | auto async(F f, T t, U u, V v, W w) -> future { 103 | return async(std::bind(f, std::move(t), std::move(u), std::move(v))); 104 | } 105 | 106 | #endif 107 | 108 | } // namespace tthread 109 | 110 | #endif // _TINYTHREAD_ASYNC_H_ 111 | -------------------------------------------------------------------------------- /tinythread_future.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef _TINYTHREAD_FUTURE_H_ 25 | #define _TINYTHREAD_FUTURE_H_ 26 | 27 | /// @file 28 | 29 | #include "tinythread.h" 30 | #include "tinythread_t.h" 31 | #include "fast_mutex.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #if defined(_MSC_VER) 38 | namespace std { 39 | template 40 | typename std::add_rvalue_reference::type declval(); 41 | } 42 | #else 43 | #define _TTHREAD_VARIADIC_ 44 | #endif 45 | 46 | namespace tthread { 47 | 48 | /////////////////////////////////////////////////////////////////////////// 49 | // typedefs 50 | typedef fast_mutex future_mutex; 51 | typedef lock_guard lock; 52 | 53 | /////////////////////////////////////////////////////////////////////////// 54 | // forward declarations 55 | 56 | #if defined(_TTHREAD_VARIADIC_) 57 | 58 | template< typename... > 59 | class packaged_task; 60 | 61 | #else 62 | 63 | template< typename > 64 | class packaged_task; 65 | 66 | #endif 67 | 68 | template< typename > 69 | class packaged_task_continuation; 70 | 71 | template< typename > 72 | class future; 73 | 74 | /////////////////////////////////////////////////////////////////////////// 75 | // async_result 76 | 77 | template< typename R > 78 | struct result_helper { 79 | typedef R type; 80 | 81 | template< typename T, typename F > 82 | static void store(T& receiver, F& func) { 83 | receiver.set(func()); 84 | } 85 | 86 | #if defined(_TTHREAD_VARIADIC_) 87 | template< typename T, typename F, typename... Args > 88 | static void store(T& receiver, F& func, Args&&... args) { 89 | receiver.set(func(std::move(args)...)); 90 | } 91 | #else 92 | template< typename T, typename F, typename Arg > 93 | static void store(T& receiver, F& func, Arg&& arg) { 94 | receiver.set(func(std::move(arg))); 95 | } 96 | #endif 97 | static R fetch(R* r) { 98 | return *r; 99 | } 100 | 101 | template< typename F, typename Arg > 102 | static void run(F& f, Arg& r) { 103 | f(r); 104 | } 105 | }; 106 | 107 | template<> 108 | struct result_helper { 109 | typedef bool type; 110 | 111 | template< typename T, typename F > 112 | static void store(T& receiver, F& func) { 113 | func(); 114 | receiver.set(true); 115 | } 116 | 117 | #if defined(_TTHREAD_VARIADIC_) 118 | template< typename T, typename F, typename... Args > 119 | static void store(T& receiver, F& func, Args&& ... args) { 120 | func(std::move(args)...); 121 | receiver.set(true); 122 | } 123 | #else 124 | template< typename T, typename F, typename Arg > 125 | static void store(T& receiver, F& func, Arg&& arg) { 126 | func(std::move(arg)); 127 | receiver.set(true); 128 | } 129 | #endif 130 | 131 | static void fetch(...) { 132 | 133 | } 134 | 135 | template< typename F > 136 | static void run(F& f, ...) { 137 | f(); 138 | } 139 | }; 140 | 141 | /////////////////////////////////////////////////////////////////////////// 142 | 143 | template 144 | class locked_ptr { 145 | public: 146 | locked_ptr(volatile T& obj, const volatile Mutex& lock) 147 | : mPtr(const_cast(&obj)), 148 | mLock(*const_cast(&lock)) { 149 | mLock.lock(); 150 | } 151 | 152 | ~locked_ptr() { 153 | mLock.unlock(); 154 | } 155 | 156 | T* operator->() const { 157 | return mPtr; 158 | } 159 | 160 | T& operator*() const { 161 | return *mPtr; 162 | } 163 | 164 | private: 165 | T* mPtr; 166 | Mutex& mLock; 167 | }; 168 | 169 | /////////////////////////////////////////////////////////////////////////// 170 | 171 | template< typename R > 172 | struct async_result { 173 | 174 | typedef typename result_helper::type result_type; 175 | typedef async_result this_type; 176 | 177 | ~async_result() { 178 | //lock guard(mLock); 179 | //mCondition.notify_all(); 180 | } 181 | 182 | bool ready() const volatile { 183 | return mReady; 184 | } 185 | 186 | void wait() const volatile { 187 | const this_type* self = const_cast(this); 188 | lock guard(self->mLock); 189 | while (!mReady) { 190 | self->mCondition.wait(self->mLock); 191 | } 192 | } 193 | 194 | result_type operator()() const volatile { 195 | wait(); 196 | 197 | return mResult; 198 | } 199 | 200 | void set(result_type&& r) volatile { 201 | locked_ptr lockedSelf(*this, mLock); 202 | if (!mReady) { 203 | lockedSelf->mResult = std::move(r); 204 | lockedSelf->mReady = true; 205 | lockedSelf->mCondition.notify_all(); 206 | if (lockedSelf->mContinuation) 207 | result_helper::run(*lockedSelf->mContinuation, lockedSelf->mResult); 208 | } 209 | } 210 | 211 | void setExecuting(bool executing) volatile { 212 | mExecuting = executing; 213 | } 214 | 215 | void setContinuation(packaged_task_continuation* continuation) volatile { 216 | this_type* self = const_cast(this); 217 | lock guard(self->mLock); 218 | self->mContinuation.reset(continuation); 219 | if (self->mContinuation && mReady) 220 | (*self->mContinuation)(mResult); 221 | } 222 | 223 | template friend class packaged_task_impl; 224 | #if defined(_TTHREAD_VARIADIC_) 225 | template friend class packaged_task; 226 | #endif 227 | 228 | protected: 229 | async_result() 230 | : mReady(false), mExecuting(false), mException(false) { } 231 | 232 | _TTHREAD_DISABLE_ASSIGNMENT(async_result); 233 | 234 | volatile bool mReady; 235 | volatile bool mExecuting; 236 | volatile bool mException; 237 | 238 | volatile result_type mResult; 239 | 240 | mutable future_mutex mLock; 241 | mutable condition_variable mCondition; 242 | 243 | std::unique_ptr< packaged_task_continuation > mContinuation; 244 | 245 | }; 246 | 247 | /////////////////////////////////////////////////////////////////////////// 248 | // packaged_task 249 | 250 | template< typename Arg > 251 | class packaged_task_continuation { 252 | public: 253 | virtual void operator()(Arg) = 0; 254 | virtual ~packaged_task_continuation() { } 255 | }; 256 | template< > 257 | class packaged_task_continuation< void > { 258 | public: 259 | virtual void operator()(void) = 0; 260 | virtual ~packaged_task_continuation() { } 261 | }; 262 | 263 | #if defined(_TTHREAD_VARIADIC_) 264 | 265 | template < size_t N > 266 | struct apply_func { 267 | template < typename F, typename... ArgsT, typename... Args > 268 | static void applyTuple(F f, 269 | const std::tuple& t, 270 | Args... args) { 271 | apply_func < N - 1 >::applyTuple(f, t, std::get < N - 1 > (t), args...); 272 | } 273 | }; 274 | 275 | template <> 276 | struct apply_func<0> { 277 | template < typename F, typename... ArgsT, typename... Args > 278 | static void applyTuple(F f, 279 | const std::tuple& /* t */, 280 | Args... args) { 281 | f(args...); 282 | } 283 | }; 284 | 285 | template < typename F, typename... ArgsT > 286 | void apply_tuple(F f, 287 | const std::tuple& t) { 288 | apply_func::applyTuple(f, t); 289 | } 290 | 291 | template < typename... Args > 292 | struct first; 293 | template < typename First, typename... Rest > 294 | struct first { typedef First type; }; 295 | template < > 296 | struct first<> { typedef void type; }; 297 | 298 | template< typename R, typename... Args > 299 | class packaged_task : 300 | public packaged_task_continuation< typename first::type > { 301 | public: 302 | typedef R result_type; 303 | 304 | /////////////////////////////////////////////////////////////////////////// 305 | // construction and destruction 306 | 307 | packaged_task() { } 308 | ~packaged_task() { } 309 | 310 | explicit packaged_task(R(*f)(Args...)) : mFunc(f) { } 311 | template < typename F > 312 | explicit packaged_task(F f) : mFunc(std::move(f)) { } 313 | 314 | /////////////////////////////////////////////////////////////////////////// 315 | // move support 316 | 317 | packaged_task(packaged_task&& other) { 318 | *this = std::move(other); 319 | } 320 | 321 | packaged_task& operator=(packaged_task&& other) { 322 | swap(std::move(other)); 323 | return *this; 324 | } 325 | 326 | void swap(packaged_task&& other) { 327 | lock guard(mLock); 328 | std::swap(mFunc, other.mFunc); 329 | std::swap(mResult, other.mResult); 330 | } 331 | 332 | /////////////////////////////////////////////////////////////////////////// 333 | // result retrieval 334 | 335 | operator bool() const { 336 | lock guard(mLock); 337 | return !!mFunc; 338 | } 339 | 340 | future get_future() { 341 | lock guard(mLock); 342 | if (!mResult) 343 | mResult.reset(new async_result()); 344 | return future(mResult); 345 | } 346 | 347 | /////////////////////////////////////////////////////////////////////////// 348 | // execution 349 | 350 | void operator()(Args... args) { 351 | std::shared_ptr< async_result > result; 352 | { 353 | lock guard(mLock); 354 | if (!!mFunc) { 355 | if (!mResult) 356 | mResult.reset(new async_result()); 357 | if (!mResult->mExecuting) { 358 | mResult->setExecuting(true); 359 | result = mResult; 360 | } 361 | } 362 | } 363 | 364 | if (result) 365 | result_helper::store(*result, mFunc, args...); 366 | } 367 | 368 | private: 369 | _TTHREAD_DISABLE_ASSIGNMENT(packaged_task); 370 | 371 | mutable future_mutex mLock; 372 | std::function mFunc; 373 | std::shared_ptr< async_result > mResult; 374 | }; 375 | 376 | #else 377 | 378 | template< typename R, typename Arg > 379 | class packaged_task_impl : public packaged_task_continuation { 380 | public: 381 | typedef R result_type; 382 | typedef std::shared_ptr< async_result > async_result_ptr; 383 | 384 | /////////////////////////////////////////////////////////////////////////// 385 | // construction and destruction 386 | 387 | packaged_task_impl() { } 388 | ~packaged_task_impl() { } 389 | 390 | explicit packaged_task_impl(R(*f)(Arg)) : mFunc(f) { } 391 | template < typename F > 392 | explicit packaged_task_impl(F f) : mFunc(std::move(f)) { } 393 | 394 | /////////////////////////////////////////////////////////////////////////// 395 | // move support 396 | 397 | packaged_task_impl(packaged_task_impl && other) { 398 | *this = std::move(other); 399 | } 400 | 401 | packaged_task_impl& operator=(packaged_task_impl && other) { 402 | swap(std::move(other)); 403 | return *this; 404 | } 405 | 406 | void swap(packaged_task_impl&& other) { 407 | lock guard(mLock); 408 | std::swap(mFunc, other.mFunc); 409 | std::swap(mResult, other.mResult); 410 | } 411 | 412 | /////////////////////////////////////////////////////////////////////////// 413 | // result retrieval 414 | 415 | operator bool() const { 416 | lock guard(mLock); 417 | return !!mFunc; 418 | } 419 | 420 | future get_future() { 421 | lock guard(mLock); 422 | if (!mResult) 423 | mResult.reset(new async_result()); 424 | return future(mResult); 425 | } 426 | protected: 427 | _TTHREAD_DISABLE_ASSIGNMENT(packaged_task_impl); 428 | 429 | async_result_ptr& process(async_result_ptr& result) { 430 | if (*this) { 431 | lock guard(mLock); 432 | if (!mResult) 433 | mResult.reset(new async_result()); 434 | if (!mResult->mExecuting) { 435 | mResult->setExecuting(true); 436 | result = mResult; 437 | } 438 | } 439 | return result; 440 | } 441 | 442 | mutable future_mutex mLock; 443 | std::function mFunc; 444 | async_result_ptr mResult; 445 | }; 446 | 447 | /////////////////////////////////////////////////////////////////////////// 448 | 449 | template< typename R > 450 | class packaged_task : public packaged_task_impl { 451 | public: 452 | 453 | /////////////////////////////////////////////////////////////////////////// 454 | // construction and destruction 455 | 456 | typedef packaged_task_impl base; 457 | 458 | packaged_task() { } 459 | explicit packaged_task(R(*f)(void)) : base(f) { } 460 | template < typename F > 461 | explicit packaged_task(F f) : base(std::move(f)) { } 462 | 463 | /////////////////////////////////////////////////////////////////////////// 464 | // move support 465 | 466 | packaged_task(packaged_task&& other) { 467 | *this = std::move(other); 468 | } 469 | 470 | packaged_task& operator=(packaged_task&& other) { 471 | swap(std::move(other)); 472 | return *this; 473 | } 474 | 475 | /////////////////////////////////////////////////////////////////////////// 476 | // execution 477 | 478 | void operator()(); 479 | 480 | private: 481 | _TTHREAD_DISABLE_ASSIGNMENT(packaged_task); 482 | }; 483 | 484 | template< typename R > 485 | void packaged_task::operator()() { 486 | std::shared_ptr< async_result > result; 487 | if (process(result)) { 488 | result_helper::store(*result, mFunc); 489 | } 490 | } 491 | 492 | /////////////////////////////////////////////////////////////////////////// 493 | 494 | template< typename R, typename Arg > 495 | class packaged_task : public packaged_task_impl { 496 | public: 497 | 498 | /////////////////////////////////////////////////////////////////////////// 499 | // construction and destruction 500 | 501 | typedef packaged_task_impl base; 502 | 503 | packaged_task() { } 504 | explicit packaged_task(R(*f)(Arg)) : base(f) { } 505 | template < typename F > 506 | explicit packaged_task(F f) : base(std::move(f)) { } 507 | 508 | /////////////////////////////////////////////////////////////////////////// 509 | // move support 510 | 511 | packaged_task(packaged_task&& other) { 512 | *this = std::move(other); 513 | } 514 | 515 | packaged_task& operator=(packaged_task && other) { 516 | swap(std::move(other)); 517 | return *this; 518 | } 519 | 520 | /////////////////////////////////////////////////////////////////////////// 521 | // execution 522 | 523 | void operator()(Arg); 524 | 525 | private: 526 | _TTHREAD_DISABLE_ASSIGNMENT(packaged_task); 527 | }; 528 | 529 | template< typename R, typename Arg > 530 | void packaged_task::operator()(Arg arg) { 531 | std::shared_ptr< async_result > result; 532 | if (process(result)) { 533 | result_helper::store(*result, mFunc, arg); 534 | } 535 | } 536 | 537 | #endif 538 | 539 | /////////////////////////////////////////////////////////////////////////// 540 | /// Future class. 541 | 542 | template< typename R > 543 | class future { 544 | public: 545 | typedef std::shared_ptr< async_result > async_result_ptr; 546 | 547 | future(future&& other) { 548 | std::swap(mResult, other.mResult); 549 | } 550 | future(async_result_ptr result) : mResult(result) { } 551 | ~future() { } 552 | 553 | future& operator=(future&& other) { 554 | mResult.swap(other.mResult); 555 | return *this; 556 | } 557 | 558 | bool valid() const { 559 | return !!mResult; 560 | } 561 | 562 | bool is_ready() const { 563 | return valid() && mResult->ready(); 564 | } 565 | 566 | bool has_value() const { 567 | return is_ready(); 568 | } 569 | 570 | R get(); 571 | void wait(); 572 | 573 | template< typename F > 574 | auto then(F f) -> future()))>; 575 | 576 | protected: 577 | 578 | future() { } 579 | _TTHREAD_DISABLE_ASSIGNMENT(future) 580 | 581 | async_result_ptr mResult; 582 | }; 583 | 584 | /////////////////////////////////////////////////////////////////////////// 585 | 586 | template< typename R > 587 | R future::get() { 588 | return (*mResult)(); 589 | } 590 | 591 | template< typename R > 592 | void future::wait() { 593 | mResult->wait(); 594 | } 595 | 596 | template< typename R > 597 | template< typename F > 598 | auto future::then(F f) -> future()))> { 599 | typedef decltype(f(std::declval())) result_type; 600 | typedef packaged_task< result_type(R) > task_type; 601 | 602 | std::unique_ptr continuation(new task_type(std::move(f))); 603 | mResult->setContinuation(continuation.get()); 604 | 605 | return continuation.release()->get_future(); 606 | } 607 | 608 | } // namespace tthread 609 | 610 | #endif // _TINYTHREAD_FUTURE_H_ 611 | -------------------------------------------------------------------------------- /tinythread_pool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "tinythread_pool.h" 25 | 26 | #include 27 | 28 | using namespace tthread; 29 | 30 | thread_pool::thread_pool( unsigned thread_count ) 31 | : mFunctionQueue( new FunctionQueue() ) 32 | { 33 | FunctionQueuePtr q = mFunctionQueue; 34 | 35 | for (unsigned i = 0; i < thread_count; ++i) { 36 | mThreads.emplace_back( threadt( [=]() { 37 | Function f; 38 | while (q->wait_and_pop(f)) { 39 | #if defined(NO_GENERIC_POOL) 40 | f(); 41 | #else 42 | (*f)(); 43 | #endif 44 | } 45 | }) ); 46 | } 47 | 48 | } 49 | 50 | tthread::thread_pool::~thread_pool() { 51 | mFunctionQueue->destroy(); 52 | for (size_t i = 0; i < mThreads.size(); ++i) 53 | mThreads[i].join(); 54 | } -------------------------------------------------------------------------------- /tinythread_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef _TINYTHREAD_POOL_H_ 25 | #define _TINYTHREAD_POOL_H_ 26 | 27 | /// @file 28 | 29 | #include "tinythread_t.h" 30 | #include "tinythread_future.h" 31 | #include "tinythread_queue.h" 32 | 33 | #include 34 | 35 | //#define NO_GENERIC_POOL 36 | 37 | namespace tthread { 38 | 39 | /////////////////////////////////////////////////////////////////////////// 40 | 41 | template< typename T > 42 | class concurrent_queue; 43 | 44 | class thread_pool { 45 | public: 46 | enum { 47 | DEFAULT_POOL_SIZE = 4, 48 | }; 49 | 50 | // Construction and Destruction 51 | explicit thread_pool(unsigned thread_count = DEFAULT_POOL_SIZE); 52 | ~thread_pool(); 53 | 54 | // Submitting tasks for execution 55 | template 56 | auto submit_task(const F& f) -> future; 57 | 58 | template 59 | auto submit_task(F&& f) -> future; 60 | 61 | static thread_pool& instance() { 62 | static thread_pool pool(DEFAULT_POOL_SIZE); 63 | return pool; 64 | } 65 | 66 | #if defined(NO_GENERIC_POOL) 67 | typedef packaged_task Function; 68 | #else 69 | typedef std::shared_ptr > Function; 70 | #endif 71 | typedef concurrent_queue FunctionQueue; 72 | typedef std::shared_ptr FunctionQueuePtr; 73 | 74 | private: 75 | _TTHREAD_DISABLE_ASSIGNMENT(thread_pool); 76 | 77 | // TODO: Add lock for concurrent access from producers 78 | FunctionQueuePtr mFunctionQueue; 79 | std::vector mThreads; 80 | }; 81 | 82 | /////////////////////////////////////////////////////////////////////////// 83 | 84 | template< typename Queue, typename Task > 85 | auto submit_task_impl(Queue& q, Task&& task) -> decltype(task.get_future()) { 86 | auto future = task.get_future(); 87 | #if defined(NO_GENERIC_POOL) 88 | q.push(std::move(task)); 89 | #else 90 | q.push(thread_pool::Function(new Task(std::move(task)))); 91 | #endif 92 | return future; 93 | } 94 | 95 | template< typename F > 96 | auto thread_pool::submit_task(F&& f) -> future { 97 | typedef decltype(f()) result_type; 98 | typedef packaged_task task_type; 99 | return submit_task_impl(*mFunctionQueue, task_type(std::move(f))); 100 | } 101 | 102 | template< typename F > 103 | auto tthread::thread_pool::submit_task(const F& f) -> future { 104 | typedef decltype(f()) result_type; 105 | typedef packaged_task task_type; 106 | return submit_task_impl(*mFunctionQueue, task_type(f)); 107 | } 108 | 109 | } // namespace tthread 110 | 111 | #endif // _TINYTHREAD_POOL_H_ 112 | -------------------------------------------------------------------------------- /tinythread_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef _TINYTHREAD_QUEUE_H_ 25 | #define _TINYTHREAD_QUEUE_H_ 26 | 27 | #include "tinythread.h" 28 | 29 | #include 30 | 31 | namespace tthread { 32 | 33 | template 34 | class concurrent_queue { 35 | public: 36 | concurrent_queue() : mDestroy(false) { } 37 | ~concurrent_queue() { } 38 | 39 | void push(const T& data) { 40 | lock guard(mLock); 41 | if (mDestroy) 42 | return; 43 | 44 | mQ.push(data); 45 | // Unlock? 46 | mCondition.notify_one(); 47 | } 48 | void push(T&& data) { 49 | lock guard(mLock); 50 | if (mDestroy) 51 | return; 52 | 53 | mQ.emplace(std::move(data)); 54 | // Unlock? 55 | mCondition.notify_one(); 56 | } 57 | 58 | bool empty() const { 59 | lock guard(mLock); 60 | return mQ.empty(); 61 | } 62 | 63 | bool try_pop(T& value) { 64 | lock guard(mLock); 65 | 66 | if(mDestroy || mQ.empty()) { 67 | return false; 68 | } 69 | 70 | value = mQ.front(); 71 | mQ.pop(); 72 | return true; 73 | } 74 | 75 | bool wait_and_pop(T& value) { 76 | lock guard(mLock); 77 | 78 | while(!mDestroy && mQ.empty()) { 79 | mCondition.wait(mLock); 80 | } 81 | 82 | if (mDestroy) 83 | return false; 84 | 85 | value = std::move(mQ.front()); 86 | mQ.pop(); 87 | return true; 88 | } 89 | 90 | void destroy() { 91 | lock guard(mLock); 92 | mDestroy = true; 93 | while (!mQ.empty()) 94 | mQ.pop(); 95 | mCondition.notify_all(); 96 | } 97 | 98 | private: 99 | _TTHREAD_DISABLE_ASSIGNMENT(concurrent_queue); 100 | 101 | std::queue mQ; 102 | mutable future_mutex mLock; 103 | condition_variable mCondition; 104 | bool mDestroy; 105 | }; 106 | 107 | } 108 | 109 | #endif // _TINYTHREAD_QUEUE_H_ -------------------------------------------------------------------------------- /tinythread_t.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Jared Duke 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | 25 | #ifndef _TINYTHREAD_T_H_ 26 | #define _TINYTHREAD_T_H_ 27 | 28 | #include "tinythread.h" 29 | 30 | #if defined(_TTHREAD_POSIX_) 31 | #include 32 | #include 33 | #elif defined(_TTHREAD_WIN32_) 34 | #include 35 | #endif 36 | 37 | #include 38 | 39 | /// @file 40 | namespace tthread { 41 | 42 | /// Thread class with template argument support. 43 | class threadt : public thread { 44 | public: 45 | 46 | threadt() : thread() { } 47 | threadt(threadt&& other) : thread(std::move(other)) { } 48 | threadt& operator=(threadt&& other) { 49 | thread::swap(std::move(other)); 50 | return *this; 51 | } 52 | 53 | template< typename thread_func_t > 54 | threadt(thread_func_t func) : thread() { 55 | init(std::move(func)); 56 | } 57 | 58 | #if defined(_TTHREAD_VARIADIC_) 59 | template< typename thread_func_t, typename Args... > 60 | threadt(thread_func_t func, Args... args) : thread() { 61 | init(std::bind(std::move(func), std::move(args)...)); 62 | } 63 | #endif 64 | 65 | protected: 66 | 67 | _TTHREAD_DISABLE_ASSIGNMENT(threadt); 68 | 69 | template< typename thread_func_t > 70 | void init(thread_func_t func); 71 | 72 | template< typename thread_func_t > 73 | struct _thread_start_info_t { 74 | thread_func_t mFunction; ///< Handle to the function to be executed. 75 | thread::data_ptr mThreadData; ///< Pointer to the thread data. 76 | _thread_start_info_t(thread_func_t&& func, thread::data_ptr threadData) 77 | : mFunction(std::move(func)), mThreadData(threadData) { } 78 | }; 79 | 80 | template< class thread_func_t > 81 | #if defined(_TTHREAD_WIN32_) 82 | static unsigned WINAPI wrapper_function(void* aArg); 83 | #elif defined(_TTHREAD_POSIX_) 84 | static void* wrapper_function(void* aArg); 85 | #endif 86 | 87 | }; 88 | 89 | template< typename thread_func_t > 90 | void threadt::init(thread_func_t func) { 91 | // Serialize access to this thread structure 92 | lock_guard guard(mData->mMutex); 93 | 94 | // Fill out the thread startup information (passed to the thread wrapper, 95 | // which will eventually free it) 96 | typedef _thread_start_info_t thread_info; 97 | std::unique_ptr ti( new thread_info(std::move(func), mData) ); 98 | 99 | thread_data& data = *mData; 100 | 101 | // The thread is now alive 102 | data.mNotAThread = false; 103 | 104 | // Create the thread 105 | #if defined(_TTHREAD_WIN32_) 106 | data.mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void*) ti.get(), 0, &data.mWin32ThreadID); 107 | #elif defined(_TTHREAD_POSIX_) 108 | if (pthread_create(&data.mHandle, NULL, wrapper_function, (void*) ti.get()) != 0) 109 | data.mHandle = 0; 110 | #endif 111 | 112 | // Did we fail to create the thread? 113 | if (!data.mHandle) { 114 | data.mNotAThread = true; 115 | } else { 116 | // Release ownership of the thread info object 117 | ti.release(); 118 | } 119 | } 120 | 121 | template< class thread_func_t > 122 | #if defined(_TTHREAD_WIN32_) 123 | unsigned WINAPI threadt::wrapper_function(void* aArg) 124 | #elif defined(_TTHREAD_POSIX_) 125 | void* threadt::wrapper_function(void* aArg) 126 | #endif 127 | { 128 | typedef _thread_start_info_t thread_info; 129 | 130 | // Get thread startup information 131 | std::unique_ptr ti((thread_info*)aArg); 132 | 133 | try { 134 | ti->mFunction(); 135 | } catch (...) { 136 | std::terminate(); 137 | } 138 | 139 | lock_guard guard(ti->mThreadData->mMutex); 140 | ti->mThreadData->mNotAThread = true; 141 | 142 | return 0; 143 | } 144 | 145 | } 146 | 147 | #endif // _TINYTHREAD_T_H_ 148 | --------------------------------------------------------------------------------