Repository navigation
Expand file tree
/
Copy pathTask.lua
More file actions
456 lines (411 loc) · 11.5 KB
/
Copy pathTask.lua
File metadata and controls
456 lines (411 loc) · 11.5 KB
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
--[[
Copyright 2017 YANG Huan ([email protected]).
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local System = System
local define = System.define
local throw = System.throw
local try = System.try
local trunc = System.trunc
local post = System.post
local addTimer = System.addTimer
local removeTimer = System.removeTimer
local waitTask = System.Thread.waitTask
local Exception = System.Exception
local NotImplementedException = System.NotImplementedException
local ArgumentNullException = System.ArgumentNullException
local ArgumentOutOfRangeException = System.ArgumentOutOfRangeException
local InvalidOperationException = System.InvalidOperationException
local type = type
local table = table
local tremove = table.remove
local setmetatable = setmetatable
local assert = assert
local ipairs = ipairs
local coroutine = coroutine
local ccreate = coroutine.create
local cresume = coroutine.resume
local cyield = coroutine.yield
local TaskCanceledException = define("System.TaskCanceledException", {
__tostring = Exception.ToString,
__inherits__ = { Exception },
__ctor__ = function(this, task)
this.task = task
Exception.__ctor__(this, "A task was canceled.")
end,
getTask = function(this)
return this.task
end,
})
local TaskStatusCreated = 0
local TaskStatusWaitingForActivation = 1
local TaskStatusWaitingToRun = 2
local TaskStatusRunning = 3
local TaskStatusWaitingForChildrenToComplete = 4
local TaskStatusRanToCompletion = 5
local TaskStatusCanceled = 6
local TaskStatusFaulted = 7
System.defEnum("System.TaskStatus", {
Created = TaskStatusCreated,
WaitingForActivation = TaskStatusWaitingForActivation,
WaitingToRun = TaskStatusWaitingToRun,
Running = TaskStatusRunning,
WaitingForChildrenToComplete = TaskStatusWaitingForChildrenToComplete,
RanToCompletion = TaskStatusRanToCompletion,
Canceled = TaskStatusCanceled,
Faulted = TaskStatusFaulted,
})
local UnobservedTaskExceptionEventArgs = define("System.UnobservedTaskExceptionEventArgs", {
__ctor__ = function (this, exception)
this.exception = exception
end,
SetObserved = function (this)
this.observed = true
end,
getObserved = function (this)
if this.observed then
return true
end
return false
end,
getException = function (this)
return this.exception
end
})
local unobservedTaskException
local function publishUnobservedTaskException(sender, ueea)
local handler = unobservedTaskException
if handler then
handler(sender, ueea)
end
end
local TaskScheduler = define("System.TaskScheduler", {
addUnobservedTaskException = function (value)
unobservedTaskException = unobservedTaskException + value
end,
removeUnobservedTaskException = function (value)
unobservedTaskException = unobservedTaskException - value
end
})
local TaskExceptionHolder = {
__index = false,
__gc = function (this)
if not this.isHandled then
local e = this.exception
if e then
local ueea = UnobservedTaskExceptionEventArgs(e)
publishUnobservedTaskException(this.task, ueea)
if not ueea.observed then
print("Warning: TaskExceptionHolder" , e)
end
end
end
end
}
TaskExceptionHolder.__index = TaskExceptionHolder
local function newTaskExceptionHolder(task, exception)
return setmetatable({ task = task, exception = exception }, TaskExceptionHolder)
end
local function createExceptionObject(this)
if not this.isHandled then
this.isHandled = true
end
return this.exception
end
local Task
local nextTaskId = 1
local currentTask
local completedTask
local function getNewId()
local id = nextTaskId
nextTaskId = nextTaskId + 1
return id
end
local function getId(this)
local id = this.id
if id == nil then
id = getNewId()
this.id = id
end
return id
end
local function isCompleted(this)
local status = this.status
return status == TaskStatusRanToCompletion or status == TaskStatusFaulted or status == TaskStatusCanceled
end
local function newTask(status, data)
return setmetatable({ status = status, data = data }, Task)
end
local function fromResult(result)
return newTask(TaskStatusRanToCompletion, result)
end
local function fromCanceled(cancellationToken)
if cancellationToken and cancellationToken:getIsCancellationRequested() then
throw(ArgumentOutOfRangeException("cancellationToken"))
end
return newTask(TaskStatusCanceled, cancellationToken)
end
local function fromException(exception)
local data = newTaskExceptionHolder(false, exception)
local t = newTask(TaskStatusFaulted, data)
data.task = t
return t
end
local function getCompletedTask()
local t = completedTask
if t == nil then
t = fromResult()
completedTask = t
end
return t
end
local function trySetComplete(this, status, data)
if isCompleted(this) then
return false
end
this.status = status
this.data = data
local continueActions = this.continueActions
if continueActions then
for _, action in ipairs(continueActions) do
action(this)
end
this.continueActions = nil
end
return true
end
local function trySetResult(this, result)
return trySetComplete(this, TaskStatusRanToCompletion, result)
end
local function trySetException(this, exception)
if this.data == true then
throw(exception)
end
return trySetComplete(this, TaskStatusFaulted, newTaskExceptionHolder(this, exception))
end
local function trySetCanceled(this, cancellationToken)
return trySetComplete(this, TaskStatusCanceled, cancellationToken)
end
local function newWaitingTask(isVoid)
return newTask(TaskStatusWaitingForActivation, isVoid)
end
local function getContinueActions(task)
local continueActions = task.continueActions
if continueActions == nil then
continueActions = {}
task.continueActions = continueActions
end
return continueActions
end
local waitToken = {}
local function getResult(this)
local status = this.status
if status == TaskStatusRanToCompletion then
return this.data
elseif status == TaskStatusFaulted then
throw(createExceptionObject(this.data))
elseif status == TaskStatusCanceled then
throw(TaskCanceledException(this))
end
return waitToken
end
local function awaitTask(t, task)
assert(t.co)
local continueActions = getContinueActions(task)
continueActions[#continueActions + 1] = function (task)
local status = task.status
local ok, v
if status == TaskStatusRanToCompletion then
ok, v = true, task.data
elseif status == TaskStatusFaulted then
ok, v = false, createExceptionObject(task.data)
elseif status == TaskStatusCanceled then
ok, v = false, TaskCanceledException(task)
else
assert(false)
end
ok, v = cresume(t.co, ok, v)
if not ok then
assert(trySetException(t, v))
end
end
local ok, v = cyield()
if ok then
return v
else
throw(v)
end
end
local factory = {
StartNew = function (f, state)
local t = newWaitingTask()
post(function ()
try(function ()
assert(trySetResult(t, f(state)))
end, function (e)
assert(trySetException(t, e))
end)
end)
return t
end
}
Task = define("System.Task", {
__ctor__ = function (this, action, state)
if action == nil then throw(ArgumentNullException("action")) end
this.status = TaskStatusCreated
this.data = function ()
action(state)
end
end,
getId = getId,
getCurrentId = function ()
local t = currentTask
if t then
return getId(t)
end
end,
getFactory = function ()
return factory
end,
getStatus = function (this)
return this.status
end,
getException = function (this)
if this.status == TaskStatusFaulted then
return createExceptionObject(this.data)
end
return nil
end,
getResult = function (this)
local result = getResult(this)
if result == waitToken then
waitTask(getContinueActions(this))
result = getResult(this)
assert(result ~= waitToken)
end
return result
end,
getIsCompleted = isCompleted,
getIsCanceled = function (this)
return this.status == TaskStatusCanceled
end,
getIsFaulted = function (this)
return this.status == TaskStatusFaulted
end,
FromResult = fromResult,
FromCanceled = fromCanceled,
FromException = fromException,
getCompletedTask = getCompletedTask,
Delay = function (delay, cancellationToken)
if type(delay) == "table" then
delay = trunc(delay:getTotalMilliseconds())
if delay < -1 or delay > 2147483647 then
throw(ArgumentOutOfRangeException("delay"))
end
elseif delay < -1 then
throw(ArgumentOutOfRangeException("millisecondsDelay"))
end
if cancellationToken and cancellationToken:getIsCancellationRequested() then
return fromCanceled(cancellationToken)
elseif delay == 0 then
return getCompletedTask()
end
local t = newWaitingTask()
local timerId, registration
if cancellationToken and cancellationToken:getCanBeCanceled() then
registration = cancellationToken.source:register(function ()
local success = trySetCanceled(t, cancellationToken)
if success and timerId then
removeTimer(timerId)
end
end)
end
if delay ~= -1 then
timerId = addTimer(function ()
local success = trySetResult(t)
if success and registration then
registration:Dispose()
end
end, delay)
end
return t
end,
ContinueWith = function (this, continuationAction)
if continuationAction == nil then throw(ArgumentNullException("continuationAction")) end
local t = newWaitingTask()
local function f()
try(function ()
assert(trySetResult(t, continuationAction()))
end, function (e)
assert(trySetException(t, e))
end)
end
if isCompleted(this) then
post(f)
else
local continueActions = getContinueActions(task)
continueActions[#continueActions + 1] = f
end
return t
end,
Start = function (this)
if this.status ~= TaskStatusCreated then throw(InvalidOperationException("Task was already started.")) end
post(function ()
try(function ()
assert(trySetResult(t, this.data()))
end, function (e)
assert(trySetException(t, e))
end)
end)
end,
Wait = function (this)
waitTask(getContinueActions(this))
end,
await = function (this, task)
local result = getResult(task)
if result ~= waitToken then
return result
end
return awaitTask(this, task)
end
})
local taskCoroutinePool = {}
local function taskCoroutineCreate(t, f)
local co = tremove(taskCoroutinePool)
if co == nil then
co = ccreate(function (...)
local r = f(t, ...)
assert(trySetResult(t, r))
while true do
t = nil
f = nil
taskCoroutinePool[#taskCoroutinePool + 1] = co
t, f = cyield()
r = f(t, cyield())
assert(trySetResult(t, r))
end
end)
t.co = co
else
t.co = co
cresume(co, t, f)
end
return co
end
function System.async(f, void, ...)
local t = newWaitingTask(void)
local co = taskCoroutineCreate(t, f)
local ok, v = cresume(co, ...)
if not ok then
assert(trySetException(t, v))
end
return t
end