1 /* 2 Copyright (c) 2015-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Cross-platform thread class. Supports Windows and Posix 31 * 32 * Description: 33 * This module provides a platform-independent multithreading API. 34 * For now, it supports Windows and Posix threads (pthreads). The interface 35 * is greatly inspired by core.thread from Phobos, but dlib.core.thread 36 * is fully GC-free and can be used with dlib.core.memory allocators. 37 * 38 * Implementation notes: 39 * 40 * - No TLS support, sorry. Any global variables should be marked with 41 * __gshared for correct access from threads. 42 * 43 * - Internals of Thread class are platform-dependent, so be aware of that 44 * when inheriting. 45 * 46 * Copyright: Timur Gafarov 2015-2021. 47 * License: $(LINK2 https://boost.org/LICENSE_1_0.txt, Boost License 1.0). 48 * Authors: Timur Gafarov 49 */ 50 module dlib.core.thread; 51 52 version(Windows) 53 { 54 extern(Windows): 55 56 struct SECURITY_ATTRIBUTES 57 { 58 uint nLength; 59 void* lpSecurityDescriptor; 60 int bInheritHandle; 61 } 62 63 alias LPTHREAD_START_ROUTINE = extern(Windows) uint function(in void*); 64 65 void* CreateThread( 66 in SECURITY_ATTRIBUTES* lpThreadAttributes, 67 in size_t dwStackSize, 68 in LPTHREAD_START_ROUTINE lpStartAddress, 69 in void* lpParameter, 70 in uint dwCreationFlags, 71 uint* lpThreadId 72 ); 73 74 uint WaitForMultipleObjects( 75 in uint nCount, 76 in void** lpHandles, 77 in int bWaitAll, 78 in uint dwMilliseconds 79 ); 80 81 int TerminateThread( 82 void* hThread, 83 in uint dwExitCode 84 ); 85 86 int GetExitCodeThread( 87 in void* hThread, 88 uint* lpExitCode 89 ); 90 91 int CloseHandle( 92 in void* hObject 93 ); 94 95 enum INFINITE = uint.max; 96 enum STILL_ACTIVE = 259; 97 } 98 99 version(Posix) 100 { 101 import core.sys.posix.pthread; 102 } 103 104 version(Windows) 105 { 106 private extern(Windows) void Sleep(uint msec); 107 } 108 else version(Posix) 109 { 110 import core.stdc.time; 111 import core.sys.posix.time; 112 import core.sys.posix.signal; 113 } 114 115 /** 116 * Base class for creating threads 117 */ 118 class Thread 119 { 120 private void function() func; 121 private void delegate() dlgt; 122 private bool callFunc; 123 private bool initialized = false; 124 125 version(Windows) 126 { 127 private void* winThread; 128 } 129 130 version(Posix) 131 { 132 private pthread_t posixThread; 133 private bool running = false; 134 } 135 136 /// Constructor. Initializes Thread using a function pointer 137 this(void function() func) 138 { 139 this.func = func; 140 callFunc = true; 141 } 142 143 /// Constructor. Initializes Thread using a delegate 144 this(void delegate() dlgt) 145 { 146 this.dlgt = dlgt; 147 callFunc = false; 148 } 149 150 /// Destructor 151 ~this() 152 { 153 version(Windows) 154 { 155 if (winThread) 156 CloseHandle(winThread); 157 } 158 159 version(Posix) 160 { 161 if (initialized) 162 pthread_detach(posixThread); 163 } 164 } 165 166 /// Starts the thread 167 void start() 168 { 169 version(Windows) 170 { 171 if (winThread) 172 CloseHandle(winThread); 173 uint threadId; 174 winThread = CreateThread(null, cast(size_t)0, &winThreadFunc, cast(void*)this, cast(uint)0, &threadId); 175 assert(winThread !is null); 176 initialized = true; 177 } 178 179 version(Posix) 180 { 181 running = true; 182 pthread_create(&posixThread, null, &posixThreadFunc, cast(void*)this); 183 // TODO: validate thread creation 184 initialized = true; 185 } 186 } 187 188 /// Waits for the thread to terminate 189 void join() 190 { 191 version(Windows) 192 { 193 WaitForMultipleObjects(1, &winThread, 1, INFINITE); 194 } 195 196 version(Posix) 197 { 198 pthread_join(posixThread, null); 199 } 200 } 201 202 /// Checks if thread is running 203 bool isRunning() 204 { 205 version(Windows) 206 { 207 uint c = 0; 208 GetExitCodeThread(winThread, &c); 209 return (c == STILL_ACTIVE); 210 } 211 212 version(Posix) 213 { 214 return running; 215 } 216 } 217 218 /// Stops the thread immediately. This functionality is unsafe, use with care 219 void terminate() 220 { 221 version(Windows) 222 { 223 TerminateThread(winThread, 1); 224 } 225 226 version(Posix) 227 { 228 pthread_cancel(posixThread); 229 running = false; 230 } 231 } 232 233 version(Windows) 234 { 235 extern(Windows) static uint winThreadFunc(in void* lpParam) 236 { 237 Thread t = cast(Thread)lpParam; 238 if (t.callFunc) 239 t.func(); 240 else 241 t.dlgt(); 242 return 0; 243 } 244 } 245 246 version(Posix) 247 { 248 extern(C) static void* posixThreadFunc(void* arg) 249 { 250 Thread t = cast(Thread)arg; 251 if (t.callFunc) 252 t.func(); 253 else 254 t.dlgt(); 255 t.running = false; 256 return null; 257 } 258 } 259 260 /// Wait for specified amout of milliseconds 261 static void sleep(uint msec) 262 { 263 version(Windows) 264 { 265 Sleep(msec); 266 } 267 else version(Posix) 268 { 269 timespec ts; 270 ts.tv_sec = msec / 1000; 271 ts.tv_nsec = (msec % 1000) * 1000000; 272 nanosleep(&ts, null); 273 } 274 } 275 276 /// 277 unittest 278 { 279 Thread.sleep(100); 280 assert(true); 281 } 282 }