-
-
Notifications
You must be signed in to change notification settings - Fork 288
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
Conversation
In general I like the design, but I see one perf/complexity oversight. Consider the following code from the test: local read_file = async(function(path)
local err, fd = await(a.uv.fs_open(path, "r", 438))
assert(not err, err)
local err, stat = await(a.uv.fs_fstat(fd))
assert(not err, err)
...
return data
end)
local first_bench = async(function()
local contents = await(read_file(the_path))
....
end) When you |
@bfredl I am not sure I understand you. |
No, i meant the inner function without async which even is what you quoted here. This will run inside the co-routine and still be able to await "leaf" futures (or nest even deeper) |
Sorry, I still do not understand. It has to allocate another coroutine or it cannot |
Ok, I can show you some actual code example tomorrow :) |
Ok here is a hack demonstration of the equivalent control flow the optimization should result in: diff --git a/tests/plenary/async_lib/work_bench.lua b/tests/plenary/async_lib/work_bench.lua
index 7411ccb..76f8365 100644
--- a/tests/plenary/async_lib/work_bench.lua
+++ b/tests/plenary/async_lib/work_bench.lua
@@ -9,7 +9,7 @@ local plenary_init = vim.api.nvim_get_runtime_file('lua/plenary/init.lua', false
local plenary_dir = vim.fn.fnamemodify(plenary_init, ":h:h:h")
local assets_dir = plenary_dir .. '/' .. 'tests/plenary/async_lib/assets/'
-local read_file = async(function(path)
+local read_file_inner = function(path)
local err, fd = await(a.uv.fs_open(path, "r", 438))
assert(not err, err)
@@ -23,7 +23,8 @@ local read_file = async(function(path)
assert(not err, err)
return data
-end)
+end
+local read_file = async(read_file_inner)
local test = async(function()
local res = await(work.string.match('abcdefg', 'b..'))
@@ -51,7 +52,8 @@ end)
--- readfile asynchronously, string process not async
local second_bench = async(function()
- local contents = await(read_file(assets_dir .. 'README.md'))
+ --local contents = await(read_file(assets_dir .. 'README.md'))
+ --the above should be made equivalent to:
+ local contents = read_file_inner(assets_dir .. 'README.md')
local lines = vim.split(contents, '\n')
This executes just fine as you can yield/await a cross a "normal" function call inside a co-routine, and is cheaper than allocating a second co-routine. Essentially, a co-routine should only executed per async "task", not per each async function running inside the same task ( await_all still needs to do the current behavior obviously, as it structurally forks the current task into several when invoked with several async functions) The problem here is |
@bfredl I think understand what you mean now. So just to make sure the root future will allocate a coroutine. |
@bfredl I implemented your idea. |
Does this PR make #46 obsolete? |
it does |
🎉🎉🎉 |
This would be a great thing to upstream, if @tjdevries or anyone else is amenable. I'd love to replace |
Yes, the plan is to test drive the library here and bike shed for a bit. Then we can merge to core. |
Looking forward to it, then! |
* started branch * added bench * added plenary. ahead * changed naming * added work future and test * fixed await_all, added more benches and tests * ntoes * more notes * added doc * added M * added some more uv functions * start of counting semaphore * more docs * use join in run_all * started branch * fixed tests * removed unneeded * small changes * async: refactor futures without object * maded naming more consistent * added argc * added argc for wrap * added argc for all functions * put in main loop * made timeout work * added runned * removed convert * added nvim future to be able to call api * added select * fixed wrong argc in select function * added block on * updated waiting time for blockon * added protect and block_on * added api helper * updated benchs for api * fixed protected * validate sender * add in_fast_event check * removed unneeded asset file * removed comment * change name to scheduler * removed idle and work related stuff for now * removed work tests and changed name to util * added scope and void * added check to condvar * removed unnecesary concats * removed long bench file * added better errors * added many docs * moved block_on and fixed oneshot channel * added async tests * updated tests and added describe it * fixed channel and added more tests * more tests * added counter channel * changed counter api and added tests * added more deque methods and tests * added mspc channel * woops forgot to commit * remove runned Co-authored-by: Björn Linse <[email protected]>
This pr adds async await from this library that I made here. Take a look at the README to see the enormous ergonomic improvement. It is inspired from neovim-async-tutorial and by some parts of neogit.
Features
sleep
(better than vim.wait, does not block interface)Let me know what you think. This can be thought as a continuation of my previous pr. This will allow much better async abstractions and prevent from going into callback hell. I saw a pr where Tj wanted to use more callbacks and I think that this is much better and will allow to make every part of telescope async. This pr also abstracts over async blocking from things such as condvar and channels which is much more ergonimic using async await than callbacks.