-
-
Notifications
You must be signed in to change notification settings - Fork 317
async await using libuv #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 26 commits
a096073
b363ee0
621bfb9
b226549
eea0b24
3fe213a
db2f958
02e938e
7b9c2ac
2ccda6b
4fe5f84
23553f3
7062489
dab2d92
de8ac26
430a2d0
1bc875a
68af03b
8915094
e1e44b1
9234048
6906da0
60368ff
53ab866
b682e78
7fc1214
02fe675
1c4559b
0fd0f1a
dc68752
c5efd4d
83378a0
2e3ef66
bab7509
578cd63
0c96921
8384d3d
fbd402c
1666282
e24a703
f74b094
522a7b8
ffcf332
2c9f3a9
58ac59d
73c4c81
e3e3e8f
14c734d
79640e9
92afbb9
fc4557f
4767627
60ea7d9
1752ae5
df921db
374d7cd
981a291
2765ee5
6dfa6fc
5f2d6e5
5233604
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| local co = coroutine | ||
| local uv = vim.loop | ||
|
|
||
| local M = {} | ||
|
|
||
| --- WIP idle stuff | ||
| local thread_loop = function(thread, callback) | ||
| local idle = uv.new_idle() | ||
| idle:start(function() | ||
| local success = co.resume(thread) | ||
| assert(success, "Coroutine failed") | ||
|
|
||
| if co.status(thread) == "dead" then | ||
| idle:stop() | ||
| callback() | ||
| end | ||
| end) | ||
| end | ||
|
|
||
| -- use with wrap | ||
| local execute = function(future, callback) | ||
| assert(type(future) == "function", "type error :: expected func") | ||
| local thread = co.create(future) | ||
|
|
||
| local step | ||
| step = function(...) | ||
| local res = {co.resume(thread, ...)} | ||
| local stat = res[1] | ||
| local ret = {select(2, unpack(res))} | ||
|
|
||
| assert(stat, string.format("The coroutine failed with this message: %s", ret[1])) | ||
|
|
||
| if co.status(thread) == "dead" then | ||
| (callback or function() end)(unpack(ret)) | ||
| else | ||
| assert(#ret == 1, "expected a single return value") | ||
| local returned_future = ret[1] | ||
| assert(type(returned_future) == "function", "type error :: expected func") | ||
| returned_future(step) | ||
| end | ||
| end | ||
|
|
||
| step() | ||
| end | ||
|
|
||
| -- use with CPS function, creates future factory | ||
| -- must have argc for arity checking | ||
| M.wrap = function(func, argc) | ||
| assert(type(func) == "function", "type error :: expected func, got " .. type(func)) | ||
| assert(type(argc) == "number" or argc == "vararg", "expected argc to be a number or string literal 'vararg'") | ||
|
|
||
| return function(...) | ||
| local params = {...} | ||
|
|
||
| local function future(step) | ||
| if step then | ||
| if type(argc) == "number" then | ||
| params[argc] = step | ||
| else | ||
| table.insert(params, step) -- change once not optional | ||
| end | ||
oberblastmeister marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return func(unpack(params)) | ||
| else | ||
| return co.yield(future) | ||
| end | ||
| end | ||
| return future | ||
| end | ||
| end | ||
|
|
||
| --- WIP | ||
| local thread_loop_async = M.wrap(thread_loop, 2) | ||
|
|
||
| -- many futures -> single future | ||
| M.join = M.wrap(function(futures, step) | ||
| local len = #futures | ||
| local results = {} | ||
| local done = 0 | ||
|
|
||
| if len == 0 then | ||
| return step(results) | ||
| end | ||
|
|
||
| for i, future in ipairs(futures) do | ||
| assert(type(future) == "function", "type error :: future must be function") | ||
| local callback = function(...) | ||
| results[i] = {...} -- should we set this to a table | ||
| done = done + 1 | ||
| if done == len then | ||
| -- step(unpack(results)) | ||
| step(results) -- should we unpack? | ||
oberblastmeister marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| end | ||
| end | ||
| future(callback) | ||
| end | ||
| end, 2) | ||
|
|
||
| --- use this over running a future by calling it with no callback argument because it is more explicit | ||
| M.run = function(future, callback) | ||
| future(callback or function() end) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about making |
||
| end | ||
|
|
||
| M.run_all = function(futures, callback) | ||
| M.run(M.join(futures), callback) | ||
| end | ||
|
|
||
| M.await = function(future) | ||
| return future(nil) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems a bit weird that here we pass
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are supposed to be completely opposite functions which is why they are not consistent. |
||
| end | ||
|
|
||
| M.await_all = function(futures) | ||
| assert(type(futures) == "table", "type error :: expected table") | ||
| return M.await(M.join(futures)) | ||
| end | ||
|
|
||
| -- suspend co-routine, call function with its continuation (like call/cc) | ||
| M.suspend = co.yield | ||
|
|
||
| M.async = function(func) | ||
| return function(...) | ||
| local args = {...} | ||
| local function future(step) | ||
| if step == nil then | ||
| return func(unpack(args)) | ||
| else | ||
| execute(future, step) | ||
| end | ||
| end | ||
| return future | ||
| end | ||
| end | ||
|
|
||
| -- converts an async function to callback based function | ||
| M.convert = function(async_func) | ||
| return function(...) | ||
| local args = {...} | ||
| local callback = table.remove(args) | ||
| assert(type(callback) == "function" or type(callback) == "nil", "type error :: expected function as last vararg") | ||
oberblastmeister marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| M.run(async_func(unpack(args)), callback) | ||
| end | ||
| end | ||
|
|
||
| M.future = function(func) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this create a future? I'd like to rename this maybe to |
||
| return M.async(func)() | ||
| end | ||
|
|
||
| --- WIP | ||
| local execute_loop = M.async(function(func, callback) | ||
| assert(type(func) == "function", "type error :: expected func") | ||
| local thread = co.create(func) | ||
|
|
||
| local _step | ||
| _step = function(...) | ||
| local res = {co.resume(thread, ...)} | ||
| local stat = res[1] | ||
| local ret = {select(2, unpack(res))} | ||
| assert(stat, "Status should be true") | ||
| if co.status(thread) == "dead" then | ||
| (callback or function() end)(unpack(ret)) | ||
| else | ||
| assert(#ret == 1, "expected a single return value") | ||
| assert(type(ret[1]) == "function", "type error :: expected func") | ||
| -- yield before calling the next one | ||
| co.yield() | ||
| ret[1](_step) | ||
| end | ||
| end | ||
|
|
||
| local step = function() | ||
| thread_loop(co.create(_step)) | ||
| end | ||
|
|
||
| step() | ||
| end) | ||
|
|
||
| --- WIP | ||
| --- because idle is a bad name | ||
| M.spawn = M.wrap(execute_loop, 2) | ||
oberblastmeister marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return M | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| local M = {} | ||
|
|
||
| VecDeque = {} | ||
| VecDeque.__index = VecDeque | ||
|
|
||
| function VecDeque.new() | ||
| return setmetatable({first = 0, last = -1}, VecDeque) | ||
| end | ||
|
|
||
| function VecDeque:pushleft(value) | ||
| local first = self.first - 1 | ||
| self.first = first | ||
| self[first] = value | ||
| end | ||
|
|
||
| function VecDeque:pushright(value) | ||
| local last = self.last + 1 | ||
| self.last = last | ||
| self[last] = value | ||
| end | ||
|
|
||
| function VecDeque:popleft() | ||
| local first = self.first | ||
| if first > self.last then return nil end | ||
| local value = self[first] | ||
| self[first] = nil -- to allow garbage collection | ||
| self.first = first + 1 | ||
| return value | ||
| end | ||
|
|
||
| function VecDeque:is_empty() | ||
| return self.first > self.last | ||
| end | ||
|
|
||
| function VecDeque:popright() | ||
| local last = self.last | ||
| if self.first > last then return nil end | ||
| local value = self[last] | ||
| self[last] = nil -- to allow garbage collection | ||
| self.last = last - 1 | ||
| return value | ||
| end | ||
|
|
||
| function VecDeque:len() | ||
| return self.last - self.first | ||
| end | ||
|
|
||
| M.VecDeque = VecDeque | ||
|
|
||
| return M |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| local exports = require('plenary.async_lib.async') | ||
| exports.uv = require('plenary.async_lib.uv_async') | ||
| exports.utils = require('plenary.async_lib.utils') | ||
| exports.lsp = require('plenary.async_lib.lsp') | ||
| exports.work = require('plenary.async_lib.work') | ||
|
|
||
| return exports |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| local a = require('plenary.async_lib.async') | ||
|
|
||
| local M = {} | ||
|
|
||
| M.buf_request = a.wrap(vim.lsp.buf_request, 4) | ||
|
|
||
| return M |
Uh oh!
There was an error while loading. Please reload this page.