1 module mecca.platform.os.linux.time; 2 3 version (linux): 4 package(mecca): 5 6 struct Timer 7 { 8 @nogc: 9 10 import core.sys.posix.signal : siginfo_t; 11 import core.sys.posix.time : timer_t; 12 import core.time : Duration; 13 14 import mecca.platform.os.linux : OSSignal; 15 16 alias Callback = extern (C) void function(); 17 18 private 19 { 20 Duration interval; 21 Callback callback; 22 23 OSSignal hangDetectorSig; 24 timer_t hangDetectorTimerId; 25 } 26 27 this(Duration interval, Callback callback) nothrow 28 in 29 { 30 assert(callback !is null); 31 } 32 do 33 { 34 this.interval = interval; 35 this.callback = callback; 36 } 37 38 void start() @trusted 39 { 40 import core.sys.posix.signal : SA_ONSTACK, 41 SA_RESTART, SA_SIGINFO, sigaction, sigaction_t, sigevent, signal, 42 SIGRTMIN, SIG_DFL; 43 44 import core.sys.posix.time : CLOCK_MONOTONIC, itimerspec, timer_create, 45 timer_delete, timer_settime; 46 47 import mecca.lib.exception : DBG_ASSERT, ASSERT, errnoEnforceNGC; 48 import mecca.log : INFO; 49 import mecca.platform.os.linux : gettid; 50 51 hangDetectorSig = cast(OSSignal)SIGRTMIN; 52 scope(failure) hangDetectorSig = OSSignal.init; 53 54 sigaction_t sa; 55 sa.sa_flags = SA_RESTART | SA_ONSTACK | SA_SIGINFO; 56 sa.sa_sigaction = cast(typeof(sa.sa_sigaction)) callback; 57 errnoEnforceNGC(sigaction(hangDetectorSig, &sa, null) == 0, "sigaction() for registering hang detector signal failed"); 58 scope(failure) signal(hangDetectorSig, SIG_DFL); 59 60 enum SIGEV_THREAD_ID = 4; 61 // SIGEV_THREAD_ID (Linux-specific) 62 // As for SIGEV_SIGNAL, but the signal is targeted at the thread whose ID is given in sigev_notify_thread_id, 63 // which must be a thread in the same process as the caller. The sigev_notify_thread_id field specifies a kernel 64 // thread ID, that is, the value returned by clone(2) or gettid(2). This flag is intended only for use by 65 // threading libraries. 66 67 sigevent sev; 68 sev.sigev_notify = SIGEV_THREAD_ID; 69 sev.sigev_signo = hangDetectorSig; 70 sev.sigev_value.sival_ptr = &hangDetectorTimerId; 71 sev._sigev_un._tid = gettid(); 72 73 errnoEnforceNGC(timer_create(CLOCK_MONOTONIC, &sev, &hangDetectorTimerId) == 0, 74 "timer_create for hang detector"); 75 ASSERT!"hangDetectorTimerId is null"(hangDetectorTimerId !is timer_t.init); 76 scope(failure) timer_delete(hangDetectorTimerId); 77 78 itimerspec its; 79 80 interval.split!("seconds", "nsecs")(its.it_value.tv_sec, its.it_value.tv_nsec); 81 its.it_interval = its.it_value; 82 INFO!"Hang detector will wake up every %s seconds and %s nsecs"(its.it_interval.tv_sec, its.it_interval.tv_nsec); 83 84 errnoEnforceNGC(timer_settime(hangDetectorTimerId, 0, &its, null) == 0, "timer_settime"); 85 } 86 87 void cancel() @trusted nothrow 88 { 89 import core.sys.posix.signal : signal, SIG_DFL; 90 import core.sys.posix.time : timer_delete; 91 92 if (hangDetectorSig is OSSignal.SIGNONE) 93 return; // Hang detector was not initialized 94 95 timer_delete(hangDetectorTimerId); 96 signal(hangDetectorSig, SIG_DFL); 97 hangDetectorSig = OSSignal.init; 98 } 99 100 bool isSet() const pure @safe nothrow 101 { 102 return hangDetectorSig != OSSignal.SIGNONE; 103 } 104 } 105 106 auto calculateCycles() 107 { 108 import core.sys.posix.signal : timespec; 109 import core.sys.posix.time: clock_gettime, CLOCK_MONOTONIC, nanosleep; 110 111 import std.exception: enforce, errnoEnforce; 112 import std.file: readText; 113 import std..string: indexOf; 114 import std.typecons: tuple; 115 116 import mecca.platform.x86: readTSC; 117 118 enforce(readText("/proc/cpuinfo").indexOf("constant_tsc") >= 0, 119 "constant_tsc not supported"); 120 121 timespec sleepTime = timespec(0, 200_000_000); 122 timespec t0, t1; 123 124 auto rc1 = clock_gettime(CLOCK_MONOTONIC, &t0); 125 auto cyc0 = readTSC(); 126 auto rc2 = nanosleep(&sleepTime, null); 127 auto rc3 = clock_gettime(CLOCK_MONOTONIC, &t1); 128 auto cyc1 = readTSC(); 129 130 errnoEnforce(rc1 == 0, "clock_gettime"); 131 errnoEnforce(rc2 == 0, "nanosleep"); // we hope we won't be interrupted by a signal here 132 errnoEnforce(rc3 == 0, "clock_gettime"); 133 134 const nsecs = (t1.tv_sec - t0.tv_sec) * 1_000_000_000UL + 135 (t1.tv_nsec - t0.tv_nsec); 136 const cyclesPerSecond = cast(long)((cyc1 - cyc0) / (nsecs / 1E9)); 137 const cyclesPerMsec = cyclesPerSecond / 1_000; 138 const cyclesPerUsec = cyclesPerSecond / 1_000_000; 139 140 return tuple!("perSecond", "perMsec", "perUsec")( 141 cyclesPerSecond, cyclesPerMsec, cyclesPerUsec); 142 }