Skip to content
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

async await using libuv #83

Merged
merged 61 commits into from
Apr 1, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
a096073
started branch
oberblastmeister Feb 23, 2021
b363ee0
added bench
oberblastmeister Feb 23, 2021
621bfb9
added plenary. ahead
oberblastmeister Feb 23, 2021
b226549
changed naming
oberblastmeister Feb 24, 2021
eea0b24
added work future and test
oberblastmeister Feb 24, 2021
3fe213a
fixed await_all, added more benches and tests
oberblastmeister Feb 24, 2021
db2f958
ntoes
oberblastmeister Feb 24, 2021
02e938e
more notes
oberblastmeister Feb 24, 2021
7b9c2ac
added doc
oberblastmeister Feb 24, 2021
2ccda6b
added M
oberblastmeister Feb 24, 2021
4fe5f84
added some more uv functions
oberblastmeister Feb 24, 2021
23553f3
start of counting semaphore
oberblastmeister Feb 26, 2021
7062489
more docs
oberblastmeister Feb 26, 2021
dab2d92
use join in run_all
oberblastmeister Feb 26, 2021
de8ac26
started branch
oberblastmeister Feb 27, 2021
430a2d0
fixed tests
oberblastmeister Feb 27, 2021
1bc875a
removed unneeded
oberblastmeister Feb 27, 2021
68af03b
small changes
oberblastmeister Feb 28, 2021
8915094
async: refactor futures without object
bfredl Feb 28, 2021
e1e44b1
maded naming more consistent
oberblastmeister Feb 28, 2021
9234048
added argc
oberblastmeister Feb 28, 2021
6906da0
added argc for wrap
oberblastmeister Mar 5, 2021
60368ff
added argc for all functions
oberblastmeister Mar 6, 2021
53ab866
put in main loop
oberblastmeister Mar 9, 2021
b682e78
made timeout work
oberblastmeister Mar 9, 2021
7fc1214
added runned
oberblastmeister Mar 9, 2021
02fe675
removed convert
oberblastmeister Mar 13, 2021
1c4559b
added nvim future to be able to call api
oberblastmeister Mar 18, 2021
0fd0f1a
added select
oberblastmeister Mar 18, 2021
dc68752
fixed wrong argc in select function
oberblastmeister Mar 18, 2021
c5efd4d
added block on
oberblastmeister Mar 20, 2021
83378a0
updated waiting time for blockon
oberblastmeister Mar 20, 2021
2e3ef66
Merge remote-tracking branch 'upstream/master' into async
oberblastmeister Mar 20, 2021
bab7509
added protect and block_on
oberblastmeister Mar 20, 2021
578cd63
added api helper
oberblastmeister Mar 20, 2021
0c96921
updated benchs for api
oberblastmeister Mar 20, 2021
8384d3d
fixed protected
oberblastmeister Mar 20, 2021
fbd402c
validate sender
oberblastmeister Mar 20, 2021
1666282
add in_fast_event check
oberblastmeister Mar 20, 2021
e24a703
removed unneeded asset file
oberblastmeister Mar 21, 2021
f74b094
removed comment
oberblastmeister Mar 21, 2021
522a7b8
change name to scheduler
oberblastmeister Mar 21, 2021
ffcf332
removed idle and work related stuff for now
oberblastmeister Mar 21, 2021
2c9f3a9
removed work tests and changed name to util
oberblastmeister Mar 21, 2021
58ac59d
added scope and void
oberblastmeister Mar 21, 2021
73c4c81
added check to condvar
oberblastmeister Mar 21, 2021
e3e3e8f
removed unnecesary concats
oberblastmeister Mar 22, 2021
14c734d
removed long bench file
oberblastmeister Mar 22, 2021
79640e9
added better errors
oberblastmeister Mar 22, 2021
92afbb9
added many docs
oberblastmeister Mar 22, 2021
fc4557f
moved block_on and fixed oneshot channel
oberblastmeister Mar 23, 2021
4767627
added async tests
oberblastmeister Mar 23, 2021
60ea7d9
updated tests and added describe it
oberblastmeister Mar 23, 2021
1752ae5
fixed channel and added more tests
oberblastmeister Mar 23, 2021
df921db
more tests
oberblastmeister Mar 23, 2021
374d7cd
added counter channel
oberblastmeister Mar 24, 2021
981a291
changed counter api and added tests
oberblastmeister Mar 27, 2021
2765ee5
added more deque methods and tests
oberblastmeister Mar 27, 2021
6dfa6fc
added mspc channel
oberblastmeister Mar 27, 2021
5f2d6e5
woops forgot to commit
oberblastmeister Mar 28, 2021
5233604
remove runned
oberblastmeister Mar 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions lua/plenary/async_lib/async.lua
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))}
oberblastmeister marked this conversation as resolved.
Show resolved Hide resolved

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.
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about making callback required and making a new function like start or something? Idk, I'm not sure I like having it optional callback (haven't thought a lot about it yet tho)

end

M.run_all = function(futures, callback)
M.run(M.join(futures), callback)
end

M.await = function(future)
return future(nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit weird that here we pass nil and above (line 119) we pass callback or function() end -- I'd like to make it more consistent perhaps. Seems fine though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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. await will yield the function and run a future asynchronously and then resume when it is done. Passing in nil to any future will await it in an async context. callback or function() end is used because passing a function into a future will run it in a none async function meaning it does not yield. Not passing in a function will still run it, hence the or. Using the or basically forces it to be a function. Futures are created using the wrap function and async function. In those functions you can see that there are if statements checking if we passed in nil or a function. What type of argument we passed in controls the behavior.

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.
Show resolved Hide resolved
M.run(async_func(unpack(args)), callback)
end
end

M.future = function(func)
Copy link
Member

Choose a reason for hiding this comment

The 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 to_future ? idk, what are your thoughts?

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.
Show resolved Hide resolved

return M
50 changes: 50 additions & 0 deletions lua/plenary/async_lib/helpers.lua
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
7 changes: 7 additions & 0 deletions lua/plenary/async_lib/init.lua
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
7 changes: 7 additions & 0 deletions lua/plenary/async_lib/lsp.lua
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
Loading