Skip to content

Commit

Permalink
perf: automatically suspend the scheduler when all threads are waiting (
Browse files Browse the repository at this point in the history
#1591)

* perf: automatically suspend the scheduler when all threads are waiting

* ci: fix ci

* test: cleanup
  • Loading branch information
folke committed Jun 30, 2024
1 parent 0507e19 commit c7ed87f
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 53 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
./tests/run
docs:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'folke' }}
needs: tests
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -40,6 +41,7 @@ jobs:
run: gh workflow run "Deploy to Github Pages" --ref docs
community:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'folke' }}
steps:
- uses: actions/checkout@v4
- name: Install Neovim
Expand Down Expand Up @@ -70,7 +72,7 @@ jobs:
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
release:
name: release
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'folke' }}
needs:
- tests
- docs
Expand Down
83 changes: 52 additions & 31 deletions lua/lazy/async.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
local Util = require("lazy.core.util")

local M = {}

---@type Async[]
M._queue = {}
M._executor = assert(vim.loop.new_timer())
M._active = {}
---@type Async[]
M._suspended = {}
M._executor = assert(vim.loop.new_check())

M.TIMER = 10
M.BUDGET = 100
M.BUDGET = 10

---@type table<thread, Async>
M._threads = setmetatable({}, { __mode = "k" })
Expand Down Expand Up @@ -42,11 +45,6 @@ function Async:init(fn)
return M.add(self)
end

function Async:restart()
assert(not self:running(), "Cannot restart a running async")
self:init(self._fn)
end

---@param event AsyncEvent
---@param cb async fun(res:any, async:Async)
function Async:on(event, cb)
Expand Down Expand Up @@ -77,27 +75,41 @@ function Async:sleep(ms)
end

---@async
function Async:suspend()
---@param yield? boolean
function Async:suspend(yield)
self._suspended = true
if coroutine.running() == self._co then
if coroutine.running() == self._co and yield ~= false then
coroutine.yield()
end
end

function Async:resume()
self._suspended = false
M._run()
end

function Async:wait()
---@async
---@param yield? boolean
function Async:wake(yield)
local async = M.running()
assert(async, "Not in an async context")
self:on("done", function()
async:resume()
end)
async:suspend(yield)
end

---@async
function Async:wait()
if coroutine.running() == self._co then
error("Cannot wait on self")
end

while self:running() do
if async then
coroutine.yield()
else
local async = M.running()
if async then
self:wake()
else
while self:running() do
vim.wait(10)
end
end
Expand All @@ -121,33 +133,42 @@ function Async:step()
end

function M.step()
local budget = M.BUDGET * 1e6
local start = vim.uv.hrtime()
local count = #M._queue
local i = 0
while #M._queue > 0 and vim.uv.hrtime() - start < budget do
---@type Async
local state = table.remove(M._queue, 1)
if state:step() then
table.insert(M._queue, state)
end
i = i + 1
if i >= count then
for _ = 1, #M._active do
if vim.uv.hrtime() - start > M.BUDGET * 1e6 then
break
end
local state = table.remove(M._active, 1)
if state:step() then
if state._suspended then
table.insert(M._suspended, state)
else
table.insert(M._active, state)
end
end
end
if #M._queue == 0 then
for _ = 1, #M._suspended do
local state = table.remove(M._suspended, 1)
table.insert(state._suspended and M._suspended or M._active, state)
end

-- print("step", #M._active, #M._suspended)
if #M._active == 0 then
return M._executor:stop()
end
end

---@param async Async
function M.add(async)
table.insert(M._queue, async)
table.insert(M._active, async)
M._run()
return async
end

function M._run()
if not M._executor:is_active() then
M._executor:start(1, M.TIMER, vim.schedule_wrap(M.step))
M._executor:start(vim.schedule_wrap(M.step))
end
return async
end

function M.running()
Expand Down
39 changes: 24 additions & 15 deletions lua/lazy/manage/runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function Runner:_start()
---@type number?
local wait_step = nil

---@async
---@param resume? boolean
local function continue(resume)
active = 0
Expand Down Expand Up @@ -114,22 +115,30 @@ function Runner:_start()
end
local s = state[name]
local plugin = self:plugin(name)
if s.step == #self._pipeline then
-- done
s.task = nil
plugin._.working = false
elseif s.step < #self._pipeline then
-- next
s.step = s.step + 1
local step = self._pipeline[s.step]
if step.task == "wait" then
while s.step <= #self._pipeline do
if s.step == #self._pipeline then
-- done
s.task = nil
plugin._.working = false
waiting = waiting + 1
wait_step = s.step
else
s.task = self:queue(plugin, step)
plugin._.working = true
active = active + 1
break
elseif s.step < #self._pipeline then
-- next
s.step = s.step + 1
local step = self._pipeline[s.step]
if step.task == "wait" then
plugin._.working = false
waiting = waiting + 1
wait_step = s.step
break
else
s.task = self:queue(plugin, step)
plugin._.working = true
if s.task then
active = active + 1
s.task:wake(false)
break
end
end
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lua/lazy/manage/task/git.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ M.log = {
---@async
---@param opts {args?: string[], updated?:boolean, check?:boolean}
run = function(self, opts)
-- self:spawn({ "sleep", "5" })
local args = {
"log",
"--pretty=format:%h %s (%cr)",
Expand Down
1 change: 0 additions & 1 deletion tests/core/init_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---@module 'luassert'
local Util = require("lazy.core.util")

describe("init", function()
Expand Down
2 changes: 0 additions & 2 deletions tests/core/plugin_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
---@module 'luassert'

local Config = require("lazy.core.config")
local Handler = require("lazy.core.handler")
local Plugin = require("lazy.core.plugin")
Expand Down
1 change: 0 additions & 1 deletion tests/handlers/keys_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---@module 'luassert'
local Keys = require("lazy.core.handler.keys")

describe("keys", function()
Expand Down
1 change: 0 additions & 1 deletion tests/manage/process_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---@module 'luassert'
local Async = require("lazy.async")
local Process = require("lazy.manage.process")

Expand Down
1 change: 0 additions & 1 deletion tests/manage/task_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---@module 'luassert'
--# selene:allow(incorrect_standard_library_use)
local Task = require("lazy.manage.task")

Expand Down

0 comments on commit c7ed87f

Please sign in to comment.