1 module mecca.platform.os.darwin.time;
2 
3 version (Darwin):
4 package(mecca):
5 
6 import mecca.platform.os.darwin.dispatch;
7 
8 struct Timer
9 {
10 @nogc:
11 nothrow:
12 
13     import core.time : Duration;
14 
15     alias Callback = extern (C) void function();
16 
17     private
18     {
19         Duration interval;
20         Callback callback;
21 
22         dispatch_source_t timer;
23         dispatch_queue_t queue;
24     }
25 
26     this(Duration interval, Callback callback)
27     in
28     {
29         assert(callback !is null);
30     }
31     do
32     {
33         this.callback = callback;
34         this.interval = interval;
35     }
36 
37     void start() @trusted
38     {
39         import mecca.log : INFO;
40 
41         const interval = this.interval.total!"nsecs";
42 
43         queue = dispatch_queue_create("com.github.weka-io.mecca.timer", null);
44         timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
45 
46         dispatch_source_set_event_handler_f(timer, &handler);
47         dispatch_set_context(timer, &this);
48         dispatch_source_set_cancel_handler_f(timer, &cancelHandler);
49 
50         INFO!"Hang detector will wake up every %s nsecs"(interval);
51 
52         const start = dispatch_time(DISPATCH_TIME_NOW, interval);
53         dispatch_source_set_timer(timer, start, interval, 0);
54 
55         dispatch_activate(timer);
56     }
57 
58     void cancel() @trusted
59     {
60         dispatch_source_cancel(timer);
61     }
62 
63     bool isSet() const pure @safe
64     {
65         return timer !is null;
66     }
67 
68 private:
69 
70     void release()
71     {
72         dispatch_release(timer);
73         dispatch_release(queue);
74     }
75 
76     extern (C) static void handler(void* timer)
77     {
78         (cast(Timer*) timer).callback();
79     }
80 
81     extern (C) static void cancelHandler(void* timer)
82     {
83         (cast(Timer*) timer).release();
84     }
85 }
86 
87 auto calculateCycles()
88 {
89     import core.sys.darwin.mach.kern_return : KERN_SUCCESS;
90     import core.sys.posix.time : nanosleep, timespec;
91     import core.time: mach_absolute_time, mach_timebase_info_data_t,
92         mach_timebase_info;
93 
94     import std.exception: enforce, errnoEnforce;
95     import std.typecons: tuple;
96 
97     import mecca.platform.x86: readTSC;
98 
99     const sleepTime = timespec(0, 200_000_000);
100 
101     const start = mach_absolute_time();
102     const cyc0 = readTSC();
103     const rcNanosleep = nanosleep(&sleepTime, null);
104     const end = mach_absolute_time();
105     const cyc1 = readTSC();
106 
107     errnoEnforce(rcNanosleep == 0, "nanosleep"); // we hope we won't be interrupted by a signal here
108 
109     const elapsed = end - start;
110 
111     mach_timebase_info_data_t timebaseInfo;
112     enforce(mach_timebase_info(&timebaseInfo) == KERN_SUCCESS);
113 
114     const nsecs = elapsed * timebaseInfo.numer / timebaseInfo.denom;
115 
116     const cyclesPerSecond = cast(long)((cyc1 - cyc0) / (nsecs / 1E9));
117     const cyclesPerMsec = cyclesPerSecond / 1_000;
118     const cyclesPerUsec = cyclesPerSecond / 1_000_000;
119 
120     return tuple!("perSecond", "perMsec", "perUsec")(
121         cyclesPerSecond, cyclesPerMsec, cyclesPerUsec);
122 }