-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathasync.lua
More file actions
144 lines (129 loc) · 3.58 KB
/
Copy pathasync.lua
File metadata and controls
144 lines (129 loc) · 3.58 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
if not love.thread then
LOG("ASYNC lib is not loaded (need love.thread)")
return setmetatable({},{
__index=function(_,k)
error("attempt to use ASYNC."..k..", but ASYNC lib is not loaded (need love.thread)")
end,
})
end
local resPool={}
local flagPool={}
local ASYNC={}
local resCHN=love.thread.newChannel()
local getCount=resCHN.getCount
---@type Map<love.Thread>
local threads={}
local function refreshThreads()
for k,v in next,threads do
if not v:isRunning() then
threads[k]=nil
end
end
end
---@language LUA
local thread_lua=[[
local resCHN,rtn,cmd,args,traceback=...
local func,err=loadstring(cmd,"<async>"..tostring(rtn))
if func then
local suc, res = pcall(func, args)
if suc then
resCHN:push({
rtn=rtn,
res=res,
})
else
resCHN:push({
rtn=rtn,
err=traceback:gsub('@@@',res),
})
end
else
resCHN:push({
rtn=rtn,
err=traceback:gsub('@@@',err),
})
end
]]
---Run a Lua function asynchronously in another Love2D thread
---
---Note: multiple calls with same `rtn` will override each other, only the latest result will be kept.
---@param rtn string | any Use ASYNC.get(rtn) to get the result later
---@param cmd string Lua code string (cannot be function because cannot pass non-data objects to Love2D thread)
---@param args? any parameter to pass to the Lua code
---@return boolean success whether the thread was started
function ASYNC.runLua(rtn,cmd,args)
refreshThreads()
if threads[rtn] then return false end
flagPool[rtn]=true
threads[rtn]=love.thread.newThread(thread_lua)
threads[rtn]:start(
resCHN,
rtn,
cmd,
args,
debug.traceback('@@@',2)
)
return true
end
---@language BAT|SH
local thread_cmd=[[
local resCHN,rtn,cmd=...
local f=io.popen(cmd,'r')
local res=f:read('*a')
f:close()
resCHN:push{
rtn=rtn,
res=res,
}
]]
---Run a system command asynchronously in another Love2D thread
---
---Note: multiple calls with same `rtn` will override each other, only the latest result will be kept.
---@param rtn string | any Use ASYNC.get(rtn) to get the result later
---@param cmd string command to run with io.popen
---@return boolean success whether the thread was started
function ASYNC.runCmd(rtn,cmd)
refreshThreads()
if threads[rtn] then return false end
flagPool[rtn]=true
threads[rtn]=love.thread.newThread(thread_cmd)
threads[rtn]:start(
resCHN,
rtn,
cmd
)
return true
end
local function update()
while getCount(resCHN)>0 do
local m=resCHN:pop()
if m.err then
LOG('error',m.err)
else
resPool[m.rtn]=m.res
flagPool[m.rtn]=true
end
end
end
---Get the result of an asynchronous operation started with ASYNC.runLua or ASYNC.runCmd
---@param rtn string | any The return key used in ASYNC.runLua or ASYNC.runCmd
function ASYNC.get(rtn)
refreshThreads()
update()
if resPool[rtn]~=nil then
local res=resPool[rtn]
resPool[rtn]=nil
flagPool[rtn]=nil
return res
end
end
---Check if the result of an asynchronous operation is retrieved, to avoid multiple ASYNC.run('task1') overriding each other's result
---
---`true` since ASYNC.run(), `false` after ASYNC.get()
---@param rtn string | any
---@return true?
function ASYNC.isBusy(rtn)
update()
return flagPool[rtn]
end
return ASYNC