1 | // |
2 | // Redistribution and use in source and binary forms, with or without |
3 | // modification, are permitted provided that the following conditions |
4 | // are met: |
5 | // * Redistributions of source code must retain the above copyright |
6 | // notice, this list of conditions and the following disclaimer. |
7 | // * Redistributions in binary form must reproduce the above copyright |
8 | // notice, this list of conditions and the following disclaimer in the |
9 | // documentation and/or other materials provided with the distribution. |
10 | // * Neither the name of NVIDIA CORPORATION nor the names of its |
11 | // contributors may be used to endorse or promote products derived |
12 | // from this software without specific prior written permission. |
13 | // |
14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | // |
26 | // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. |
27 | // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. |
28 | // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. |
29 | |
30 | #ifndef PSFOUNDATION_PSTHREAD_H |
31 | #define PSFOUNDATION_PSTHREAD_H |
32 | |
33 | #include "PsUserAllocated.h" |
34 | |
35 | // dsequeira: according to existing comment here (David Black would be my guess) |
36 | // "This is useful to reduce bus contention on tight spin locks. And it needs |
37 | // to be a macro as the xenon compiler often ignores even __forceinline." What's not |
38 | // clear is why a pause function needs inlining...? (TODO: check with XBox team) |
39 | |
40 | // todo: these need to go somewhere else |
41 | |
42 | #if PX_WINDOWS_FAMILY || PX_XBOXONE || PX_XBOX_SERIES_X |
43 | #define PxSpinLockPause() __asm pause |
44 | #elif PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY || PX_SWITCH |
45 | #define PxSpinLockPause() asm("nop") |
46 | #else |
47 | #error "Platform not supported!" |
48 | #endif |
49 | |
50 | namespace physx |
51 | { |
52 | namespace shdfnd |
53 | { |
54 | struct ThreadPriority // todo: put in some other header file |
55 | { |
56 | enum Enum |
57 | { |
58 | /** |
59 | \brief High priority |
60 | */ |
61 | eHIGH = 0, |
62 | |
63 | /** |
64 | \brief Above Normal priority |
65 | */ |
66 | eABOVE_NORMAL = 1, |
67 | |
68 | /** |
69 | \brief Normal/default priority |
70 | */ |
71 | eNORMAL = 2, |
72 | |
73 | /** |
74 | \brief Below Normal priority |
75 | */ |
76 | eBELOW_NORMAL = 3, |
77 | |
78 | /** |
79 | \brief Low priority. |
80 | */ |
81 | eLOW = 4, |
82 | eFORCE_DWORD = 0xffFFffFF |
83 | }; |
84 | }; |
85 | |
86 | class Runnable |
87 | { |
88 | public: |
89 | Runnable() |
90 | { |
91 | } |
92 | virtual ~Runnable() |
93 | { |
94 | } |
95 | virtual void execute(void) |
96 | { |
97 | } |
98 | }; |
99 | |
100 | class PX_FOUNDATION_API ThreadImpl |
101 | { |
102 | public: |
103 | typedef size_t Id; // space for a pointer or an integer |
104 | typedef void* (*ExecuteFn)(void*); |
105 | |
106 | static uint32_t getDefaultStackSize(); |
107 | static Id getId(); |
108 | |
109 | /** |
110 | Construct (but do not start) the thread object. The OS thread object will not be created |
111 | until start() is called. Executes in the context |
112 | of the spawning thread. |
113 | */ |
114 | |
115 | ThreadImpl(); |
116 | |
117 | /** |
118 | Construct and start the the thread, passing the given arg to the given fn. (pthread style) |
119 | */ |
120 | |
121 | ThreadImpl(ExecuteFn fn, void* arg, const char* name); |
122 | |
123 | /** |
124 | Deallocate all resources associated with the thread. Should be called in the |
125 | context of the spawning thread. |
126 | */ |
127 | |
128 | ~ThreadImpl(); |
129 | |
130 | /** |
131 | Create the OS thread and start it running. Called in the context of the spawning thread. |
132 | If an affinity mask has previously been set then it will be applied after the |
133 | thread has been created. |
134 | */ |
135 | |
136 | void start(uint32_t stackSize, Runnable* r); |
137 | |
138 | /** |
139 | Violently kill the current thread. Blunt instrument, not recommended since |
140 | it can leave all kinds of things unreleased (stack, memory, mutexes...) Should |
141 | be called in the context of the spawning thread. |
142 | */ |
143 | |
144 | void kill(); |
145 | |
146 | /** |
147 | Stop the thread. Signals the spawned thread that it should stop, so the |
148 | thread should check regularly |
149 | */ |
150 | |
151 | void signalQuit(); |
152 | |
153 | /** |
154 | Wait for a thread to stop. Should be called in the context of the spawning |
155 | thread. Returns false if the thread has not been started. |
156 | */ |
157 | |
158 | bool waitForQuit(); |
159 | |
160 | /** |
161 | check whether the thread is signalled to quit. Called in the context of the |
162 | spawned thread. |
163 | */ |
164 | |
165 | bool quitIsSignalled(); |
166 | |
167 | /** |
168 | Cleanly shut down this thread. Called in the context of the spawned thread. |
169 | */ |
170 | void quit(); |
171 | |
172 | /** |
173 | Change the affinity mask for this thread. The mask is a platform |
174 | specific value. |
175 | |
176 | On Windows, Linux, PS4, XboxOne and Switch platforms, each set mask bit represents |
177 | the index of a logical processor that the OS may schedule thread execution on. |
178 | Bits outside the range of valid logical processors may be ignored or cause |
179 | the function to return an error. |
180 | |
181 | On Apple platforms, this function has no effect. |
182 | |
183 | If the thread has not yet been started then the mask is stored |
184 | and applied when the thread is started. |
185 | |
186 | If the thread has already been started then this method returns the |
187 | previous affinity mask on success, otherwise it returns zero. |
188 | */ |
189 | uint32_t setAffinityMask(uint32_t mask); |
190 | |
191 | static ThreadPriority::Enum getPriority(Id threadId); |
192 | |
193 | /** Set thread priority. */ |
194 | void setPriority(ThreadPriority::Enum prio); |
195 | |
196 | /** set the thread's name */ |
197 | void setName(const char* name); |
198 | |
199 | /** Put the current thread to sleep for the given number of milliseconds */ |
200 | static void sleep(uint32_t ms); |
201 | |
202 | /** Yield the current thread's slot on the CPU */ |
203 | static void yield(); |
204 | |
205 | /** Return the number of physical cores (does not include hyper-threaded cores), returns 0 on failure */ |
206 | static uint32_t getNbPhysicalCores(); |
207 | |
208 | /** |
209 | Size of this class. |
210 | */ |
211 | static uint32_t getSize(); |
212 | }; |
213 | |
214 | /** |
215 | Thread abstraction API |
216 | */ |
217 | template <typename Alloc = ReflectionAllocator<ThreadImpl> > |
218 | class ThreadT : protected Alloc, public UserAllocated, public Runnable |
219 | { |
220 | public: |
221 | typedef ThreadImpl::Id Id; // space for a pointer or an integer |
222 | |
223 | /** |
224 | Construct (but do not start) the thread object. Executes in the context |
225 | of the spawning thread |
226 | */ |
227 | ThreadT(const Alloc& alloc = Alloc()) : Alloc(alloc) |
228 | { |
229 | mImpl = reinterpret_cast<ThreadImpl*>(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); |
230 | PX_PLACEMENT_NEW(mImpl, ThreadImpl)(); |
231 | } |
232 | |
233 | /** |
234 | Construct and start the the thread, passing the given arg to the given fn. (pthread style) |
235 | */ |
236 | ThreadT(ThreadImpl::ExecuteFn fn, void* arg, const char* name, const Alloc& alloc = Alloc()) : Alloc(alloc) |
237 | { |
238 | mImpl = reinterpret_cast<ThreadImpl*>(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); |
239 | PX_PLACEMENT_NEW(mImpl, ThreadImpl)(fn, arg, name); |
240 | } |
241 | |
242 | /** |
243 | Deallocate all resources associated with the thread. Should be called in the |
244 | context of the spawning thread. |
245 | */ |
246 | virtual ~ThreadT() |
247 | { |
248 | mImpl->~ThreadImpl(); |
249 | Alloc::deallocate(mImpl); |
250 | } |
251 | |
252 | /** |
253 | start the thread running. Called in the context of the spawning thread. |
254 | */ |
255 | |
256 | void start(uint32_t stackSize = ThreadImpl::getDefaultStackSize()) |
257 | { |
258 | mImpl->start(stackSize, r: this); |
259 | } |
260 | |
261 | /** |
262 | Violently kill the current thread. Blunt instrument, not recommended since |
263 | it can leave all kinds of things unreleased (stack, memory, mutexes...) Should |
264 | be called in the context of the spawning thread. |
265 | */ |
266 | |
267 | void kill() |
268 | { |
269 | mImpl->kill(); |
270 | } |
271 | |
272 | /** |
273 | The virtual execute() method is the user defined function that will |
274 | run in the new thread. Called in the context of the spawned thread. |
275 | */ |
276 | |
277 | virtual void execute(void) |
278 | { |
279 | } |
280 | |
281 | /** |
282 | stop the thread. Signals the spawned thread that it should stop, so the |
283 | thread should check regularly |
284 | */ |
285 | |
286 | void signalQuit() |
287 | { |
288 | mImpl->signalQuit(); |
289 | } |
290 | |
291 | /** |
292 | Wait for a thread to stop. Should be called in the context of the spawning |
293 | thread. Returns false if the thread has not been started. |
294 | */ |
295 | |
296 | bool waitForQuit() |
297 | { |
298 | return mImpl->waitForQuit(); |
299 | } |
300 | |
301 | /** |
302 | check whether the thread is signalled to quit. Called in the context of the |
303 | spawned thread. |
304 | */ |
305 | |
306 | bool quitIsSignalled() |
307 | { |
308 | return mImpl->quitIsSignalled(); |
309 | } |
310 | |
311 | /** |
312 | Cleanly shut down this thread. Called in the context of the spawned thread. |
313 | */ |
314 | void quit() |
315 | { |
316 | mImpl->quit(); |
317 | } |
318 | |
319 | uint32_t setAffinityMask(uint32_t mask) |
320 | { |
321 | return mImpl->setAffinityMask(mask); |
322 | } |
323 | |
324 | static ThreadPriority::Enum getPriority(ThreadImpl::Id threadId) |
325 | { |
326 | return ThreadImpl::getPriority(threadId); |
327 | } |
328 | |
329 | /** Set thread priority. */ |
330 | void setPriority(ThreadPriority::Enum prio) |
331 | { |
332 | mImpl->setPriority(prio); |
333 | } |
334 | |
335 | /** set the thread's name */ |
336 | void setName(const char* name) |
337 | { |
338 | mImpl->setName(name); |
339 | } |
340 | |
341 | /** Put the current thread to sleep for the given number of milliseconds */ |
342 | static void sleep(uint32_t ms) |
343 | { |
344 | ThreadImpl::sleep(ms); |
345 | } |
346 | |
347 | /** Yield the current thread's slot on the CPU */ |
348 | static void yield() |
349 | { |
350 | ThreadImpl::yield(); |
351 | } |
352 | |
353 | static uint32_t getDefaultStackSize() |
354 | { |
355 | return ThreadImpl::getDefaultStackSize(); |
356 | } |
357 | |
358 | static ThreadImpl::Id getId() |
359 | { |
360 | return ThreadImpl::getId(); |
361 | } |
362 | |
363 | static uint32_t getNbPhysicalCores() |
364 | { |
365 | return ThreadImpl::getNbPhysicalCores(); |
366 | } |
367 | |
368 | private: |
369 | class ThreadImpl* mImpl; |
370 | }; |
371 | |
372 | typedef ThreadT<> Thread; |
373 | |
374 | PX_FOUNDATION_API uint32_t TlsAlloc(); |
375 | PX_FOUNDATION_API void TlsFree(uint32_t index); |
376 | PX_FOUNDATION_API void* TlsGet(uint32_t index); |
377 | PX_FOUNDATION_API size_t TlsGetValue(uint32_t index); |
378 | PX_FOUNDATION_API uint32_t TlsSet(uint32_t index, void* value); |
379 | PX_FOUNDATION_API uint32_t TlsSetValue(uint32_t index, size_t value); |
380 | |
381 | } // namespace shdfnd |
382 | } // namespace physx |
383 | |
384 | #endif // #ifndef PSFOUNDATION_PSTHREAD_H |
385 | |