diff --git a/runtime/lua/vim/_async.lua b/runtime/lua/vim/_async.lua deleted file mode 100644 index f99ec97fb9..0000000000 --- a/runtime/lua/vim/_async.lua +++ /dev/null @@ -1,110 +0,0 @@ -local M = {} - -local max_timeout = 120000 -local copcall = package.loaded.jit and pcall or require('coxpcall').pcall - ---- @param thread thread ---- @param on_finish fun(err: string?, ...:any) ---- @param ... any -local function resume(thread, on_finish, ...) - --- @type {n: integer, [1]:boolean, [2]:string|function} - local ret = vim.F.pack_len(coroutine.resume(thread, ...)) - local stat = ret[1] - - if not stat then - -- Coroutine had error - on_finish(ret[2] --[[@as string]]) - elseif coroutine.status(thread) == 'dead' then - -- Coroutine finished - on_finish(nil, unpack(ret, 2, ret.n)) - else - local fn = ret[2] - --- @cast fn -string - - --- @type boolean, string? - local ok, err = copcall(fn, function(...) - resume(thread, on_finish, ...) - end) - - if not ok then - on_finish(err) - end - end -end - ---- @param func async fun(): ...:any ---- @param on_finish? fun(err: string?, ...:any) -function M.run(func, on_finish) - local res --- @type {n:integer, [integer]:any}? - resume(coroutine.create(func), function(err, ...) - res = vim.F.pack_len(err, ...) - if on_finish then - on_finish(err, ...) - end - end) - - return { - --- @param timeout? integer - --- @return any ... return values of `func` - wait = function(_self, timeout) - vim.wait(timeout or max_timeout, function() - return res ~= nil - end) - assert(res, 'timeout') - if res[1] then - error(res[1]) - end - return unpack(res, 2, res.n) - end, - } -end - ---- Asynchronous blocking wait ---- @async ---- @param argc integer ---- @param fun function ---- @param ... any func arguments ---- @return any ... -function M.await(argc, fun, ...) - assert(coroutine.running(), 'Async.await() must be called from an async function') - local args = vim.F.pack_len(...) --- @type {n:integer, [integer]:any} - - --- @param callback fun(...:any) - return coroutine.yield(function(callback) - args[argc] = assert(callback) - fun(unpack(args, 1, math.max(argc, args.n))) - end) -end - ---- @async ---- @param max_jobs integer ---- @param funs (async fun())[] -function M.join(max_jobs, funs) - if #funs == 0 then - return - end - - max_jobs = math.min(max_jobs, #funs) - - --- @type (async fun())[] - local remaining = { select(max_jobs + 1, unpack(funs)) } - local to_go = #funs - - M.await(1, function(on_finish) - local function run_next() - to_go = to_go - 1 - if to_go == 0 then - on_finish() - elseif #remaining > 0 then - local next_fun = table.remove(remaining) - M.run(next_fun, run_next) - end - end - - for i = 1, max_jobs do - M.run(funs[i], run_next) - end - end) -end - -return M diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index 5c7cb7fda6..91126b9fb8 100644 --- a/runtime/lua/vim/pack.lua +++ b/runtime/lua/vim/pack.lua @@ -268,7 +268,8 @@ local api = vim.api local uv = vim.uv -local async = require('vim._async') +---@diagnostic disable-next-line: no-unknown +local async = require('vim.async') local util = require('vim._core.util') local nvim_on = util.nvim_on local N_ = vim.fn.gettext @@ -650,14 +651,24 @@ local function new_progress_report(action) end local copcall = package.loaded.jit and pcall or require('coxpcall').pcall +local max_timeout = 120000 +--- @param funs (async fun())[] local function async_join_run_wait(funs) local n_threads = 2 * (uv.available_parallelism() or 1) --- @async local function joined_f() - async.join(n_threads, funs) + ---@diagnostic disable-next-line: no-unknown + local semaphore = async.semaphore(n_threads) + local function run_one(f) + -- Isolate job failures. Task return still observes cancellation. + copcall(semaphore.with, semaphore, f) + end + for _, f in ipairs(funs) do + async.run(run_one, f) + end end - async.run(joined_f):wait() + async.run(joined_f):wait(max_timeout) end --- Execute function in parallel for each non-errored plugin in the list @@ -675,7 +686,8 @@ local function run_list(plug_list, f, progress_action) if p.info.err == '' then --- @async funs[#funs + 1] = function() - local ok, err = copcall(f, p) --[[@as string]] + ---@diagnostic disable-next-line: no-unknown + local ok, err = async.pawait(async.run(f, p)) if not ok then p.info.err = err --- @as string end @@ -767,7 +779,8 @@ local function resolve_version(p) local tags = git_get_tags(p.path) if type(version) == 'string' then local is_branch = vim.tbl_contains(branches, version) - local is_tag_or_hash = copcall(git_get_hash, version, p.path) + ---@diagnostic disable-next-line: no-unknown + local is_tag_or_hash = async.pawait(async.run(git_get_hash, version, p.path)) if not (is_branch or is_tag_or_hash) then local err = ('`%s` is not a branch/tag/commit. Available:'):format(version) .. list_in_line('Tags', tags) @@ -992,7 +1005,7 @@ local function lock_repair(names, plug_dir) plugin_lock.plugins[name] = data end end - async.run(f):wait() + async.run(f):wait(max_timeout) end --- Sync lockfile data and installed plugins: