Advertisement
FluttyProger

superthreads.lua

Jan 19th, 2017
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.89 KB | None | 0 0
  1. -- Credits to RoCoWa @ github.com/Akuukis/RobotColorWars
  2.  
  3. local thread = {}
  4.  
  5. -- Sanitization happens here
  6. function thread.spawn(fn, ...)
  7. local err
  8. if type(fn) == "function" then
  9. fn = fn
  10. elseif type(fn) == "string" then
  11. fn, err = load(fn)
  12. if not fn then return false, err end
  13. elseif false then -- TODO: Add support for file handles
  14. else
  15. return false, "Not valid function or string"
  16. end
  17. local co = coroutine.create(fn)
  18. if not co then return false, "Failed to create thread" end
  19. return coroutine.yield({
  20. ["flag"] = "thread_spawn",
  21. ["co"] = co,
  22. ["args"] = { ... },
  23. ["name"] = "anonymous",
  24. })
  25. end
  26. function thread.yield( sleep_filter, filter_sleep )
  27. local sleep, filter
  28. if type(sleep_filter) == "number" then sleep = sleep_filter; filter = filter_sleep end
  29. if type(sleep_filter) == "string" then sleep = filter_sleep; filter = sleep_filter end
  30.  
  31. return coroutine.yield({
  32. ["flag"] = "thread_yield",
  33. ["filter"] = filter,
  34. ["sleep"] = sleep
  35. })
  36. end
  37. function thread.pause(uid, amount)
  38. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  39. if type(amount) ~= "number" then amount = 1 end
  40. return coroutine.yield({
  41. ["flag"] = "thread_pause",
  42. ["uid"] = uid,
  43. ["amount"] = amount,
  44. })
  45. end
  46. function thread.unpause(uid, amount)
  47. local amount = -(amount or 1)
  48. return thread.pause( uid, amount )
  49. end
  50. function thread.kill(uid)
  51. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  52. return coroutine.yield({
  53. ["flag"] = "thread_kill",
  54. ["uid"] = uid,
  55. })
  56. end
  57. function thread.dig(uid)
  58. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  59. return coroutine.yield({
  60. ["flag"] = "thread_dig",
  61. ["uid"] = uid,
  62. })
  63. end
  64. function thread.setName(uid, name)
  65. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  66. local ok, err = pcall(checkArg, 1, name, "string"); if not ok then return ok, err end
  67. return coroutine.yield({
  68. ["flag"] = "thread_setName",
  69. ["uid"] = uid,
  70. ["name"] = name,
  71. })
  72. end
  73. function thread.getName(uid)
  74. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  75. return coroutine.yield({
  76. ["flag"] = "thread_getName",
  77. ["uid"] = uid,
  78. })
  79. end
  80. function thread.setAlarm( uid, alarm )
  81. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  82. local ok, err = pcall(checkArg, 1, alarm, "number", "nil"); if not ok then return ok, err end
  83. return coroutine.yield({
  84. ["flag"] = "thread_setAlarm",
  85. ["uid"] = uid,
  86. ["alarm"] = alarm,
  87. })
  88. end
  89. function thread.getAlarm( uid )
  90. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  91. return coroutine.yield({
  92. ["flag"] = "thread_getAlarm",
  93. ["uid"] = uid,
  94. })
  95. end
  96. function thread.setFilter( uid, filter )
  97. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  98. local ok, err = pcall(checkArg, 1, filter, "string", "nil"); if not ok then return ok, err end
  99. return coroutine.yield({
  100. ["flag"] = "thread_setFilter",
  101. ["uid"] = uid,
  102. ["filter"] = filter,
  103. })
  104. end
  105. function thread.getFilter( uid )
  106. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  107. return coroutine.yield({
  108. ["flag"] = "thread_getFilter",
  109. ["uid"] = uid,
  110. })
  111. end
  112. function thread.setAnswer(uid, answer)
  113. if answer ~= nil then
  114. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  115. else
  116. answer = uid
  117. uid = thread.getThread()
  118. end
  119. return coroutine.yield({
  120. ["flag"] = "thread_setAnswer",
  121. ["uid"] = uid,
  122. ["answer"] = answer,
  123. })
  124. end
  125. function thread.getAnswer(uid, msg)
  126. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  127. return coroutine.yield({
  128. ["flag"] = "thread_getAnswer",
  129. ["uid"] = uid,
  130. ["msg"] = msg,
  131. })
  132. end
  133. function thread.ask(uid, msg)
  134. return thread.getAnswer(uid, msg)
  135. end
  136. function thread.whisper(uid, ...)
  137. local ok, err = pcall(checkArg, 1, uid, "string"); if not ok then return ok, err end
  138. return coroutine.yield({
  139. ["flag"] = "thread_whisper",
  140. ["uid"] = uid,
  141. ["args"] = { ... },
  142. })
  143. end
  144. function thread.getThreads()
  145. return coroutine.yield({
  146. ["flag"] = "thread_getThreads"
  147. })
  148. end
  149. function thread.getThread( uid )
  150. return coroutine.yield({
  151. ["flag"] = "thread_getThread",
  152. ["uid"] = uid,
  153. })
  154. end
  155. function thread.exit()
  156. return coroutine.yield({
  157. ["flag"] = "thread_exit",
  158. })
  159. end
  160.  
  161. -- Magic happens here
  162. function thread.manager( firstThread )
  163. local computer = require("computer")
  164. -- You should provide better versions of these functions.
  165. local countUid = 0
  166. local getUid = getUid or function ()
  167. countUid = countUid + 1
  168. return tostring(countUid)
  169. end
  170. local utils = utils or false
  171. if not utils then
  172. utils = {
  173. getTime = function () return os.clock()*60 end,
  174. }
  175. end
  176. local logger = logger or false
  177. if not logger then
  178. logger = {
  179. fatal = function (...) io.write(string.format(...)) end,
  180. warning = function (...) io.write(string.format(...)) end,
  181. info = function (...) io.write(string.format(...)) end,
  182. spam = function (...) io.write(string.format(...)) end,
  183. }
  184. end
  185. if not firstThread then
  186. firstThread = {
  187. ["name"] = "Default console",
  188. ["uid"] = getUid("coroutine"),
  189. ["co"] = coroutine.create(function()
  190.  
  191. local upenv = _ENV
  192. local setmetatable = setmetatable
  193. local _ENV = {}
  194. setmetatable(_ENV, {__index=upenv})
  195. local upenv, setmetatable = nil, nil
  196.  
  197. local function read(history)
  198. local component = require("component")
  199. local prefix = "P# "
  200. local history = history
  201. if type(history) ~= "table" then history = {} end
  202. history[#history] = ""
  203. local command = ""
  204. local historyCursor = #history
  205. local cursorX, cursorY = 1,1
  206. while true do
  207. local newline = false
  208. local resX, resY = component.gpu.getResolution()
  209. for i=1,#prefix do
  210. if string.sub(prefix,i,i) ~= component.gpu.get(i,resY) then newline = true end
  211. end
  212. if newline then
  213. component.gpu.copy(1,1,resX,resY,0,-1)
  214. component.gpu.fill(1,resY,resX,1," ")
  215. end
  216. local output = prefix..string.sub(command or "",-(resX-3))
  217. for i=1,resX-#output do output = output.." " end
  218. component.gpu.set(1,resY,output)
  219.  
  220. local name, address, charOrValue, code, player = coroutine.yield("[kc][el][yi][_p][db][oo][wa][nr]d?")
  221. if name == "clipboard" then player = code; code = nil end
  222.  
  223. if name == "key_down" then
  224. if code == 28 then -- enter
  225. component.gpu.fill(1,resY,resX,1," ")
  226. return command
  227. elseif code == 14 then -- backspace
  228. command = string.sub(history[historyCursor],1,#history[historyCursor]-1)
  229. history[#history] = command
  230. historyCursor = #history
  231. elseif code == 211 then -- del
  232. command = ""
  233. history[#history] = command
  234. historyCursor = #history
  235. elseif code == 200 then -- arrow up
  236. historyCursor = math.max(1, historyCursor-1)
  237. command = history[historyCursor]
  238. elseif code == 208 then -- arrow down
  239. historyCursor = math.min(#history, historyCursor+1)
  240. command = history[historyCursor]
  241. historyCursor = #history
  242. elseif not(type(charOrValue) == "number" and (charOrValue < 0x20 or (charOrValue >= 0x7F and charOrValue <= 0x9F))) then -- is normal char
  243. command = history[historyCursor]..string.char(charOrValue) -- TODO: crashed once with "value out of range", unicode problems?
  244. history[#history] = command
  245. historyCursor = #history
  246. end
  247. elseif name == "clipboard" then
  248. command = history[historyCursor]..charOrValue
  249. history[#history] = command
  250. historyCursor = #history
  251. if string.find(command,"\n",-1, true) then
  252. component.gpu.fill(1,resY,resX,1," ")
  253. return command
  254. end
  255. end
  256. end
  257. end
  258. local history = {}
  259. while true do
  260. local str = read(history) -- yields here!
  261. history[#history+1] = str
  262. if str == "quit" then
  263. print("Quit!")
  264. return history
  265. end
  266. --local formattedTime = ""
  267. --if os.clock()*60 >= 60 * 60 then formattedTime = formattedTime .. string.format("%dh",os.clock/60) end
  268. --if os.clock()*60 >= 60 then formattedTime = formattedTime .. string.format("%dm",os.clock()-math.floor(os.clock()/60)) end
  269. --formattedTime = formattedTime .. string.format("%is",os.clock()*60-math.floor(os.clock())*60)
  270. local fn, err = load(str) -- ,"console@"..formattedTime
  271. if fn and str ~="" and str ~="\n" then
  272. print("executed:",pcall(fn))
  273. else
  274. if err then print("load error:", err) end
  275. end
  276. end
  277. end
  278. ),
  279. ["filter"] = nil,
  280. ["pause"] = 0,
  281. ["sleep"] = false,
  282. ["answer"] = nil,
  283. ["argSets"] = {},
  284. ["lastArgSet"] = 1,
  285. }
  286. end
  287.  
  288. local event = {"thread_dummy"}
  289. local focus, lastThread = 1, 1
  290. local threads = { [1] = firstThread } -- TODO: Sanitize
  291. local graveyard = {}
  292. local graveyardTime = {}
  293.  
  294. local operations = {
  295. thread_yield = function (data)
  296. threads[focus].filter = data.filter or false
  297. if data.sleep then threads[focus].alarm = data.sleep + utils.getTime() end
  298. return nil -- Cycle to next thread
  299. end,
  300. thread_spawn = function (data)
  301. lastThread = lastThread + 1
  302. threads[ lastThread ] = {
  303. ["name"] = data.name or "anonymous",
  304. ["filter"] = data.filter or nil,
  305. ["pause"] = 0,
  306. ["argSets"] = {
  307. [1] = data.args or nil },
  308. ["uid"] = getUid("coroutine"),
  309. ["co"] = data.co,
  310. }
  311. if data.args then threads[ lastThread ].lastArgSet = 1 else threads[ lastThread ].lastArgSet = 0 end
  312. return threads[ lastThread ].uid
  313. end,
  314. thread_whisper = function (data)
  315. for _,thread in pairs(threads) do
  316. if thread.uid == data.uid then
  317. if
  318. ((not thread.filter) or (thread.filter == data.args[1]))
  319. and ((not thread.alarm) or thread.alarm < utils.getTime())
  320. then
  321. thread.filter = nil
  322. thread.lastArgSet = thread.lastArgSet + 1
  323. thread.argSets[ thread.lastArgSet ] = data.args or nil
  324. return true
  325. end
  326. local err = "Thread found"
  327. if thread.filter then err = err..", but filters for "..type(thread.filter).." "..thread.filter end
  328. if thread.alarm then err = err..", but has alarm on "..thread.alarm.." after "..(utils.getTime() - thread.alarm).." seconds" end
  329. return false, err, thread.filter, thread.alarm, utils.getTime() - thread.alarm
  330. end
  331. end
  332. return false, "No running thread found, try to dig"
  333. end,
  334. thread_pause = function (data)
  335. for _,thread in pairs(threads) do
  336. if thread.uid == data.uid then
  337. if thread.pause == 0 and data.amount > 0 then
  338. if thread.alarm then
  339. thread.alarm = thread.alarm - utils.getTime()
  340. if thread.alarm < 0 then thread.alarm = false end
  341. end
  342. end
  343. if thread.pause > 0 and thread.pause + data.amount <= 0 then
  344. if thread.alarm then thread.alarm = thread.alarm + utils.getTime() end
  345. end
  346. thread.pause = math.max(0, thread.pause + data.amount)
  347. return true
  348. end
  349. end
  350. return false, "No running thread found, try to dig"
  351. end,
  352. thread_setAnswer = function (data)
  353. for i=1,lastThread do
  354. if threads[i] and threads[i].uid == data.uid then
  355. threads[i].answer = data.answer
  356. return true
  357. end
  358. end
  359. return false, "No running thread found, try to dig"
  360. end,
  361. thread_getAnswer = function (data)
  362. for _,thread in pairs(threads) do
  363. if thread.uid == data.uid and thread.answer then
  364. if type(thread.answer) == "function" then
  365. return thread.answer(data.msg)
  366. else
  367. return thread.answer
  368. end
  369. end
  370. end
  371. return false, "No running thread found, try to dig"
  372. end,
  373. thread_kill = function (data)
  374. for i=1,lastThread do
  375. if threads[i] and threads[i].uid == data.uid then
  376. graveyard[ threads[i].uid ] = { false, "killed", threads[focus].uid, utils.getTime() }
  377. graveyardTime[ threads[i].uid ] = utils.getTime()
  378. threads[i] = nil
  379. return true
  380. end
  381. end
  382. return false, "No running thread found, try to dig"
  383. end,
  384. thread_dig = function (data)
  385. if graveyard[data.uid] then
  386. return true, graveyard[data.uid]
  387. end
  388. return false, "Not found in Graveyard, check uid or try again later"
  389. end,
  390. thread_setAlarm = function (data)
  391. for i=1,lastThread do
  392. if threads[i] and threads[i].uid == data.uid then
  393. threads[i].alarm = data.alarm
  394. return true
  395. end
  396. end
  397. return false, "No running thread found, try to dig"
  398. end,
  399. thread_getAlarm = function (data)
  400. for i=1,lastThread do
  401. if threads[i] and threads[i].uid == data.uid then
  402. return threads[i].alarm, threads[i].alarm and "No alarm"
  403. end
  404. end
  405. return false, "No running thread found, try to dig"
  406. end,
  407. thread_setFilter = function (data)
  408. for i=1,lastThread do
  409. if threads[i] and threads[i].uid == data.uid then
  410. threads[i].filter = data.filter
  411. return true
  412. end
  413. end
  414. return false, "No running thread found, try to dig"
  415. end,
  416. thread_getFilter = function (data)
  417. for i=1,lastThread do
  418. if threads[i] and threads[i].uid == data.uid then
  419. return threads[i].filter, threads[i].filter and "No filter"
  420. end
  421. end
  422. return false, "No running thread found, try to dig"
  423. end,
  424. thread_setName = function (data)
  425. if data == nil then
  426. threads[focus] = data.name
  427. return true
  428. end
  429. for i=1,lastThread do
  430. if threads[i] and threads[i].uid == data.uid then
  431. threads[i].name = data.name
  432. return true
  433. end
  434. end
  435. return false, "No running thread found, try to dig"
  436. end,
  437. thread_getName = function (data)
  438. for i=1,lastThread do
  439. if threads[i] and threads[i].uid == data.uid then
  440. return threads[i].name
  441. end
  442. end
  443. return false, "No running thread found, try to dig"
  444. end,
  445. thread_getThread = function (data)
  446. if data.uid == nil then
  447. local result = {}
  448. for key,value in pairs(threads[focus]) do
  449. if key ~= "argSets" and key ~= "co" then result[key] = value end
  450. end
  451. return result
  452. else
  453. for _,thread in pairs(threads) do
  454. if thread.uid == data.uid then
  455. local result = {}
  456. for key,value in pairs(thread) do
  457. if key ~= "argSets" and key ~= "co" then result[key] = value end
  458. end
  459. return result
  460. end
  461. end
  462. return false, "No running thread found, try to dig"
  463. end
  464. end,
  465. thread_getThreads = function ()
  466. local result = {}
  467. local count = 0
  468. for _,thread in pairs(threads) do
  469. count = count + 1
  470. result[count] = {}
  471. for key,value in pairs(thread) do
  472. if key ~= "argSets" and key ~= "co" then result[count][key] = value end
  473. end
  474. end
  475. return result
  476. end,
  477. thread_exit = function ()
  478. lastThread = -1
  479. return nil
  480. end,
  481. }
  482. while lastThread > 0 do -- Run until no threads left (won't happen normally)
  483. local alarm = 0
  484. --logger.spam("\nThread %s/%s (%s,%s,%s)", focus, lastThread ,event[1], event[3], event[4])
  485. if type(threads[focus]) == "table" then
  486. --logger.spam("%s,%s!", threads[focus].pause, threads[focus].lastArgSet)
  487.  
  488. local i = 1
  489. while
  490. threads[focus]
  491. and threads[focus].pause == 0
  492. and ((not threads[focus].alarm) or threads[focus].alarm < utils.getTime())
  493. and i <= threads[focus].lastArgSet
  494. do
  495. --logger.spam("\n i=%s..",i)
  496.  
  497. local result = table.pack( coroutine.resume( threads[focus].co, table.unpack( threads[focus].argSets[i] or {} ) ) )
  498. --for k,v in ipairs(result) do logger.spam("\n [%s]: %s",k,v) end
  499. local ok, args = result[1], result[2]
  500. threads[focus].argSets[i] = nil
  501. threads[focus].filter = false
  502. threads[focus].alarm = false
  503.  
  504. if not ok then
  505. logger.warning("thread failed! %s\n", args )
  506. graveyard[ threads[focus].uid ] = result
  507. graveyardTime[ threads[focus].uid ] = utils.getTime()
  508. threads[focus] = nil
  509. elseif coroutine.status(threads[focus].co) == "dead" then
  510. graveyard[ threads[focus].uid ] = result
  511. graveyardTime[ threads[focus].uid ] = utils.getTime()
  512. threads[focus] = nil
  513. else
  514. if args and type(args) == "string" and args ~= "" then
  515. threads[focus].filter = args
  516. elseif args and type(args) == "number" and args ~= "" then
  517. threads[focus].alarm = args + utils.getTime()
  518. elseif args and type(args) == "table" then
  519. if args.flag and operations[ args.flag ] then
  520. threads[focus].argSets[i] = { operations[ args.flag ]( args ) }
  521. if threads[focus].argSets[i][1] == nil then threads[focus].argSets[i] = nil end
  522. end
  523. local computer = require("computer")
  524. end
  525. end
  526.  
  527. if threads[focus] and threads[focus].argSets[i] == nil then i = i + 1 end
  528. end
  529. if threads[focus] then threads[focus].lastArgSet = 0 end
  530. else
  531. if focus == lastThread then lastThread = lastThread - 1 end
  532. end
  533. if type(threads[focus+1]) ~= "table" then
  534. threads[focus+1] = threads[focus+2]
  535. threads[focus+2] = nil
  536. end
  537.  
  538. if threads[focus]
  539. and not threads[focus].filter
  540. and (threads[focus].alarm or 0) < alarm
  541. then alarm = threads[focus].alarm or 0 end
  542.  
  543. --logger.spam("\n")
  544. if focus < lastThread then
  545. focus = focus + 1
  546. else
  547. focus = 1
  548. event = table.pack(computer.pullSignal(math.max(0,alarm - utils.getTime())))
  549. for i=1,lastThread do
  550. if
  551. threads[i]
  552. and ((not threads[i].filter) or string.match((type(event[1])=="string" and event[1]) or "",threads[i].filter))
  553. and ((not threads[i].alarm) or threads[i].alarm < utils.getTime())
  554. then
  555. threads[i].argSets[ threads[i].lastArgSet+1 ] = event
  556. threads[i].lastArgSet = threads[i].lastArgSet + 1
  557. end
  558. end
  559. end
  560.  
  561. end
  562.  
  563. logger.info("Out of threads\n")
  564. return false, "Out of threads"
  565. end
  566. return thread
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement