1 /**
2     Native Threading Abstraction
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-2025, Inochi2D Project
7     
8     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9     Authors:   Luna Nielsen
10 */
11 module nulib.threading.internal.thread;
12 import core.attribute : weak;
13 import numem;
14 
15 /**
16     Thread ID
17 */
18 alias ThreadId = size_t;
19 
20 /**
21     A thread context.
22 */
23 struct ThreadContext {
24     void* userData;
25     void function(void* userData) callback;
26     Exception ex;
27 }
28 
29 /**
30     Base class of native threading implementations.
31 */
32 export
33 abstract
34 class NativeThread : NuObject {
35 public:
36 @nogc:
37 
38     /**
39         The Thread ID of the calling thread.
40     */
41     static @property ThreadId selfTid() @safe => _nu_thread_current_tid();
42 
43     /**
44         Creates a new native thread.
45     */
46     static NativeThread create(ThreadContext ctx) @safe => _nu_thread_new(ctx);
47 
48     /**
49         Makes the calling thread sleep for the specified amount
50         of time.
51 
52         Params:
53             ms = Miliseconds that the thread should sleep.
54     */
55     static void sleep(uint ms) @safe {
56         _nu_thread_sleep(ms);
57     }
58 
59     /**
60         ID of the thread.    
61     */
62     abstract @property ThreadId tid() @safe;
63 
64     /**
65         Whether the thread is currently running.
66     */
67     abstract @property bool isRunning() @safe;
68 
69     /**
70         Starts the given thread.
71     */
72     abstract void start() @safe;
73 
74     /**
75         Forcefully cancels the thread, stopping execution.
76 
77         This is not a safe operation, as it may lead to memory
78         leaks and corrupt state. Only use when ABSOLUTELY neccesary.
79     */
80     abstract void cancel() @system;
81 
82     /**
83         Waits for the thread to finish execution.
84     
85         Params:
86             timeout = How long to wait for the thread to exit.
87             rethrow = Whether execptions thrown in the thread should be rethrown.
88     */
89     abstract bool join(uint timeout, bool rethrow) @safe;
90 }
91 
92 /**
93     Gets the total number of CPUs in the system.
94 
95     Returns:
96         The number of CPUs in the system.
97 */
98 int totalCPUs() @weak nothrow @nogc {
99     version(Windows) {
100         static struct SYSTEM_INFO {
101             short wProcessorArchitecture;
102             short wReserved;
103             int dwPageSize;
104             void* lpMinimumApplicationAddress;
105             void* lpMaximumApplicationAddress;
106             int* dwActiveProcessorMask;
107             int dwNumberOfProcessors;
108             int dwProcessorType;
109             int dwAllocationGranularity;
110             short  wProcessorLevel;
111             short  wProcessorRevision;
112         }
113 
114         pragma(mangle, "GetSystemInfo")
115         static 
116         extern(System) extern void GetSystemInfo(SYSTEM_INFO*) @nogc nothrow;
117 
118         SYSTEM_INFO inf;
119         GetSystemInfo(&inf);
120         return inf.dwNumberOfProcessors;
121     } else version(Darwin) {
122 
123         pragma(mangle, "sysctlbyname")
124         static
125         extern(C) int sysctlbyname(const(char)*, void*, size_t*, const(void)*, size_t);
126         
127         const(char)* nstr = "machdep.cpu.core_count\0";
128         size_t len = uint.max;
129         uint result;
130         cast(void)sysctlbyname(nstr, &result, &len, null, 0);
131         return cast(int)result;
132     } else version(Posix) {
133 
134         import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
135         return cast(int)sysconf(_SC_NPROCESSORS_ONLN);
136     } else {
137         pragma(msg, "totalCPUs: Platform not supported, will return 1.");
138 
139         // Not supported.
140         return 1;
141     }
142 }
143 
144 //
145 //          FOR IMPLEMENTORS
146 //
147 
148 private extern(C):
149 
150 version(DigitalMars) version = WeakIsBroken;
151 version(linux) version = WeakIsBroken;
152 version(WeakIsBroken) {
153 
154     // Needed on Linux because we can't specify load order.
155     extern(C) extern NativeThread _nu_thread_new(ThreadContext) @trusted @nogc nothrow;
156     extern(C) extern ThreadId _nu_thread_current_tid() @trusted @nogc nothrow;
157     extern(C) extern void _nu_thread_sleep(uint) @trusted @nogc nothrow;
158 } else {
159 
160     /*
161         Function which creates a new native thread.
162     */
163     NativeThread _nu_thread_new(ThreadContext) @trusted @weak @nogc nothrow { return null; }
164 
165     /*
166         Function which gets the current Thread ID.
167     */
168     ThreadId _nu_thread_current_tid() @trusted @weak @nogc nothrow { return 0; }
169 
170     /*
171         Function which sleeps the calling thread.
172     */
173     void _nu_thread_sleep(uint) @trusted @weak @nogc nothrow { }
174 }