1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.system.Memory;
13 
14 import core.atomic;
15 import core.memory;
16 
17 import std.traits;
18 
19 version (OSX) {
20     version = useSysctlbyname;
21 }
22 else version (FreeBSD) {
23     version = useSysctlbyname;
24 }
25 else version (DragonFlyBSD) {
26     version = useSysctlbyname;
27 }
28 else version (NetBSD) {
29     version = useSysctlbyname;
30 }
31 
32 version(useSysctlbyname)
33     private extern(C) int sysctlbyname(
34         const char *, void *, size_t *, void *, size_t
35     ) @nogc nothrow;
36 
37 /*
38 (For now public undocumented with reserved name.)
39 
40 A lazily initialized global constant. The underlying value is a shared global
41 statically initialized to `outOfBandValue` which must not be a legit value of
42 the constant. Upon the first call the situation is detected and the global is
43 initialized by calling `initializer`. The initializer is assumed to be pure
44 (even if not marked as such), i.e. return the same value upon repeated calls.
45 For that reason, no special precautions are taken so `initializer` may be called
46 more than one time leading to benign races on the cached value.
47 
48 In the quiescent state the cost of the function is an atomic load from a global.
49 
50 Params:
51     T = The type of the pseudo-constant (may be qualified)
52     outOfBandValue = A value that cannot be valid, it is used for initialization
53     initializer = The function performing initialization; must be `nothrow`
54 
55 Returns:
56     The lazily initialized value
57 */
58 @property pure T __lazilyInitializedConstant(T, alias outOfBandValue, alias initializer)()
59         if (is(Unqual!T : T) && is(typeof(initializer()) : T) && is(typeof(outOfBandValue) : T)) {
60     static T impl() nothrow {
61         // Thread-local cache
62         static Unqual!T tls = outOfBandValue;
63         auto local = tls;
64         // Shortest path, no atomic operations
65         if (local != outOfBandValue)
66             return local;
67         // Process-level cache
68         static shared Unqual!T result = outOfBandValue;
69         // Initialize both process-level cache and tls
70         local = atomicLoad(result);
71         if (local == outOfBandValue) {
72             local = initializer();
73             atomicStore(result, local);
74         }
75         tls = local;
76         return local;
77     }
78 
79     // import std.traits : SetFunctionAttributes;
80     alias Fun = SetFunctionAttributes!(typeof(&impl), "D",
81             functionAttributes!(typeof(&impl)) | FunctionAttribute.pure_);
82     auto purified = (() @trusted => cast(Fun)&impl)();
83     return purified();
84 }
85 
86 /**
87 The total number of CPU cores available on the current machine, as reported by
88 the operating system.
89 */
90 alias totalCPUs = __lazilyInitializedConstant!(immutable(uint), uint.max, totalCPUsImpl);
91 
92 uint totalCPUsImpl() @nogc nothrow @trusted {
93     version (Windows) {
94         // BUGS:  Only works on Windows 2000 and above.
95         import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo;
96         import std.algorithm.comparison : max;
97 
98         SYSTEM_INFO si;
99         GetSystemInfo(&si);
100         return max(1, cast(uint) si.dwNumberOfProcessors);
101     }
102     else version (linux) {
103         import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
104 
105         return cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
106     }
107     else version (Solaris) {
108         import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
109 
110         return cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
111     }
112     else version (useSysctlbyname) {
113         version (OSX) {
114             auto nameStr = "machdep.cpu.core_count\0".ptr;
115         }
116         else version (FreeBSD) {
117             auto nameStr = "hw.ncpu\0".ptr;
118         }
119         else version (DragonFlyBSD) {
120             auto nameStr = "hw.ncpu\0".ptr;
121         }
122         else version (NetBSD) {
123             auto nameStr = "hw.ncpu\0".ptr;
124         }
125 
126         uint result;
127         size_t len = result.sizeof;
128         sysctlbyname(nameStr, &result, &len, null, 0);
129         return result;
130     }
131     else {
132         static assert(0, "Don't know how to get N CPUs on this OS.");
133     }
134 }
135 
136 /**
137 */
138 size_t pageSize() @safe pure nothrow @nogc {
139     return _pageSize;
140 }
141 
142 static immutable size_t _pageSize;
143 
144 shared static this() {
145     version (Windows) {
146         import core.sys.windows.winbase;
147 
148         SYSTEM_INFO info;
149         GetSystemInfo(&info);
150 
151         _pageSize = info.dwPageSize;
152         assert(_pageSize < int.max);
153     }
154     else version (Posix) {
155         import core.sys.posix.unistd;
156 
157         _pageSize = cast(size_t) sysconf(_SC_PAGESIZE);
158     }
159     else {
160         static assert(0, "unimplemented");
161     }
162 }
163 
164 /**
165  * Reference queues, to which registered reference objects are appended by the
166  * garbage collector after the appropriate reachability changes are detected.
167  *
168  * @author   Mark Reinhold
169  */
170 
171 class ReferenceQueue(T) {
172     // TODO: Tasks pending completion -@zxp at 8/10/2018, 4:15:28 PM
173     // 
174     /**
175      * Constructs a new reference-object queue.
176      */
177     this() {
178     }
179 
180     // private static class Null<S> extends ReferenceQueue<S> {
181     //     bool enqueue(Reference<S> r) {
182     //         return false;
183     //     }
184     // }
185 
186     // static ReferenceQueue<Object> NULL = new Null<>();
187     // static ReferenceQueue<Object> ENQUEUED = new Null<>();
188 
189     // static private class Lock { };
190     // private Lock lock = new Lock();
191     // private Reference<T> head = null;
192     // private long queueLength = 0;
193 
194     // bool enqueue(Reference<T> r) { /* Called only by Reference class */
195     //     synchronized (lock) {
196     //         // Check that since getting the lock this reference hasn't already been
197     //         // enqueued (and even then removed)
198     //         ReferenceQueue<?> queue = r.queue;
199     //         if ((queue == NULL) || (queue == ENQUEUED)) {
200     //             return false;
201     //         }
202     //         assert queue == this;
203     //         r.queue = ENQUEUED;
204     //         r.next = (head is null) ? r : head;
205     //         head = r;
206     //         queueLength++;
207     //         if (r instanceof FinalReference) {
208     //             sun.misc.VM.addFinalRefCount(1);
209     //         }
210     //         lock.notifyAll();
211     //         return true;
212     //     }
213     // }
214 
215     // @SuppressWarnings("unchecked")
216     // private Reference<T> reallyPoll() {       /* Must hold lock */
217     //     Reference<T> r = head;
218     //     if (r !is null) {
219     //         head = (r.next == r) ?
220     //             null :
221     //             r.next; // Unchecked due to the next field having a raw type in Reference
222     //         r.queue = NULL;
223     //         r.next = r;
224     //         queueLength--;
225     //         if (r instanceof FinalReference) {
226     //             sun.misc.VM.addFinalRefCount(-1);
227     //         }
228     //         return r;
229     //     }
230     //     return null;
231     // }
232 
233     // /**
234     //  * Polls this queue to see if a reference object is available.  If one is
235     //  * available without further delay then it is removed from the queue and
236     //  * returned.  Otherwise this method immediately returns <tt>null</tt>.
237     //  *
238     //  * @return  A reference object, if one was immediately available,
239     //  *          otherwise <code>null</code>
240     //  */
241     // Reference<T> poll() {
242     //     if (head is null)
243     //         return null;
244     //     synchronized (lock) {
245     //         return reallyPoll();
246     //     }
247     // }
248 
249     // /**
250     //  * Removes the next reference object in this queue, blocking until either
251     //  * one becomes available or the given timeout period expires.
252     //  *
253     //  * <p> This method does not offer real-time guarantees: It schedules the
254     //  * timeout as if by invoking the {@link Object#wait(long)} method.
255     //  *
256     //  * @param  timeout  If positive, block for up to <code>timeout</code>
257     //  *                  milliseconds while waiting for a reference to be
258     //  *                  added to this queue.  If zero, block indefinitely.
259     //  *
260     //  * @return  A reference object, if one was available within the specified
261     //  *          timeout period, otherwise <code>null</code>
262     //  *
263     //  * @throws  IllegalArgumentException
264     //  *          If the value of the timeout argument is negative
265     //  *
266     //  * @throws  InterruptedException
267     //  *          If the timeout wait is interrupted
268     //  */
269     // Reference<T> remove(long timeout)
270     //     throws IllegalArgumentException, InterruptedException
271     // {
272     //     if (timeout < 0) {
273     //         throw new IllegalArgumentException("Negative timeout value");
274     //     }
275     //     synchronized (lock) {
276     //         Reference<T> r = reallyPoll();
277     //         if (r !is null) return r;
278     //         long start = (timeout == 0) ? 0 : System.nanoTime();
279     //         for (;;) {
280     //             lock.wait(timeout);
281     //             r = reallyPoll();
282     //             if (r !is null) return r;
283     //             if (timeout != 0) {
284     //                 long end = System.nanoTime();
285     //                 timeout -= (end - start) / 1000_000;
286     //                 if (timeout <= 0) return null;
287     //                 start = end;
288     //             }
289     //         }
290     //     }
291     // }
292 
293     // /**
294     //  * Removes the next reference object in this queue, blocking until one
295     //  * becomes available.
296     //  *
297     //  * @return A reference object, blocking until one becomes available
298     //  * @throws  InterruptedException  If the wait is interrupted
299     //  */
300     // Reference<T> remove() throws InterruptedException {
301     //     return remove(0);
302     // }
303 
304 }