1 /* 2 * Geario - A cross-platform abstraction library with asynchronous I/O. 3 * 4 * Copyright (C) 2021-2022 Kerisy.com 5 * 6 * Website: https://www.kerisy.com 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module geario.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 }