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 }