/
MainLoop.hx
181 lines (161 loc) · 3.61 KB
/
MainLoop.hx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package haxe;
import haxe.EntryPoint;
class MainEvent {
var f:Void->Void;
var prev:MainEvent;
var next:MainEvent;
/**
Tells if the event can lock the process from exiting (default:true)
**/
public var isBlocking:Bool = true;
public var nextRun(default, null):Float;
public var priority(default, null):Int;
function new(f, p) {
this.f = f;
this.priority = p;
nextRun = Math.NEGATIVE_INFINITY;
}
/**
Delay the execution of the event for the given time, in seconds.
If t is null, the event will be run at tick() time.
**/
public function delay(t:Null<Float>) {
nextRun = t == null ? Math.NEGATIVE_INFINITY : haxe.Timer.stamp() + t;
}
/**
Call the event. Will do nothing if the event has been stopped.
**/
public inline function call() {
if (f != null)
f();
}
/**
Stop the event from firing anymore.
**/
public function stop() {
if (f == null)
return;
f = null;
nextRun = Math.NEGATIVE_INFINITY;
if (prev == null)
@:privateAccess MainLoop.pending = next;
else
prev.next = next;
if (next != null)
next.prev = prev;
}
}
@:access(haxe.MainEvent)
class MainLoop {
static var pending:MainEvent;
public static var threadCount(get, never):Int;
inline static function get_threadCount()
return EntryPoint.threadCount;
public static function hasEvents() {
var p = pending;
while (p != null) {
if (p.isBlocking)
return true;
p = p.next;
}
return false;
}
public static function addThread(f:Void->Void) {
EntryPoint.addThread(f);
}
public static function runInMainThread(f:Void->Void) {
EntryPoint.runInMainThread(f);
}
/**
Add a pending event to be run into the main loop.
**/
public static function add(f:Void->Void, priority = 0):MainEvent@:privateAccess {
if (f == null)
throw "Event function is null";
var e = new MainEvent(f, priority);
var head = pending;
if (head != null)
head.prev = e;
e.next = head;
pending = e;
return e;
}
static function sortEvents() {
// pending = haxe.ds.ListSort.sort(pending, function(e1, e2) return e1.nextRun > e2.nextRun ? -1 : 1);
// we can't use directly ListSort because it requires prev/next to be public, which we don't want here
// we do then a manual inline, this also allow use to do a Float comparison of nextRun
var list = pending;
if (list == null)
return;
var insize = 1, nmerges, psize = 0, qsize = 0;
var p, q, e, tail:MainEvent;
while (true) {
p = list;
list = null;
tail = null;
nmerges = 0;
while (p != null) {
nmerges++;
q = p;
psize = 0;
for (i in 0...insize) {
psize++;
q = q.next;
if (q == null)
break;
}
qsize = insize;
while (psize > 0 || (qsize > 0 && q != null)) {
if (psize == 0) {
e = q;
q = q.next;
qsize--;
} else if (qsize == 0
|| q == null
|| (p.priority > q.priority || (p.priority == q.priority && p.nextRun <= q.nextRun))) {
e = p;
p = p.next;
psize--;
} else {
e = q;
q = q.next;
qsize--;
}
if (tail != null)
tail.next = e;
else
list = e;
e.prev = tail;
tail = e;
}
p = q;
}
tail.next = null;
if (nmerges <= 1)
break;
insize *= 2;
}
list.prev = null; // not cycling
pending = list;
}
/**
Run the pending events. Return the time for next event.
**/
static function tick() {
sortEvents();
var e = pending;
var now = haxe.Timer.stamp();
var wait = 1e9;
while (e != null) {
var next = e.next;
var wt = e.nextRun - now;
if (wt <= 0) {
wait = 0;
e.call();
} else if (wait > wt)
wait = wt;
e = next;
}
return wait;
}
}