Skip to content

Commit 6b11c18

Browse files
committed
fix: add queueing for jobstart preventing multiple concurrent jobs
1 parent 60de4f2 commit 6b11c18

File tree

1 file changed

+65
-4
lines changed

1 file changed

+65
-4
lines changed

lua/ccusage/cli.lua

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ local cache = {
1111

1212
local CACHE_TTL = 20000 -- 20 seconds in milliseconds
1313

14+
-- Job state tracking to prevent multiple concurrent jobs
15+
local job_state = {
16+
blocks = { job_id = nil, pending_callbacks = {}, is_running = false },
17+
availability = { job_id = nil, pending_callbacks = {}, is_running = false },
18+
}
19+
1420
---Check if cached value is still valid
1521
---@param cache_entry table cache entry with data and timestamp
1622
---@return boolean
@@ -31,6 +37,34 @@ local function update_cache(cache_entry, data)
3137
cache_entry.timestamp = vim.uv.now()
3238
end
3339

40+
---Queue callback for when job is already running
41+
---@param job_type string either "blocks" or "availability"
42+
---@param callback function callback to queue
43+
local function queue_callback(job_type, callback)
44+
if callback then
45+
table.insert(job_state[job_type].pending_callbacks, callback)
46+
end
47+
end
48+
49+
---Execute all queued callbacks with result
50+
---@param job_type string either "blocks" or "availability"
51+
---@param result any result to pass to callbacks
52+
local function execute_queued_callbacks(job_type, result)
53+
local callbacks = job_state[job_type].pending_callbacks
54+
job_state[job_type].pending_callbacks = {}
55+
56+
for _, callback in ipairs(callbacks) do
57+
callback(result)
58+
end
59+
end
60+
61+
---Reset job state when job completes
62+
---@param job_type string either "blocks" or "availability"
63+
local function reset_job_state(job_type)
64+
job_state[job_type].job_id = nil
65+
job_state[job_type].is_running = false
66+
end
67+
3468
---Get ccusage blocks data with simple caching
3569
---@param opts? {bypass_cache?: boolean, callback?: fun(data: CCUsage.Data?)}
3670
---@return CCUsage.Data?
@@ -45,9 +79,17 @@ M.ccusage_blocks = function(opts)
4579
return cache.blocks.data
4680
end
4781

82+
-- If job is already running, queue the callback and return cached data
83+
if job_state.blocks.is_running then
84+
queue_callback("blocks", opts.callback)
85+
return not opts.bypass_cache and cache.blocks.data or nil
86+
end
87+
88+
-- Start new job
89+
job_state.blocks.is_running = true
4890
local base_cmd = config.options.ccusage_cmd
4991

50-
vim.fn.jobstart({ base_cmd, "blocks", "--json", "--offline" }, {
92+
job_state.blocks.job_id = vim.fn.jobstart({ base_cmd, "blocks", "--json", "--offline" }, {
5193
stdout_buffered = true,
5294
on_stdout = function(_, data, _)
5395
if #data > 0 then
@@ -58,16 +100,23 @@ M.ccusage_blocks = function(opts)
58100
if not opts.bypass_cache then
59101
update_cache(cache.blocks, parsed)
60102
end
103+
104+
-- Execute current callback and all queued callbacks
61105
if opts.callback then
62106
opts.callback(parsed)
63107
end
108+
execute_queued_callbacks("blocks", parsed)
64109
end
65110
end
66111
end,
67112
on_exit = function(_, exit_code, _)
68-
if exit_code ~= 0 and opts.callback then
69-
opts.callback(nil)
113+
if exit_code ~= 0 then
114+
if opts.callback then
115+
opts.callback(nil)
116+
end
117+
execute_queued_callbacks("blocks", nil)
70118
end
119+
reset_job_state("blocks")
71120
end,
72121
})
73122

@@ -89,10 +138,18 @@ M.is_available = function(opts)
89138
return cache.availability.data
90139
end
91140

141+
-- If job is already running, queue the callback and return cached data
142+
if job_state.availability.is_running then
143+
queue_callback("availability", opts.callback)
144+
return not opts.bypass_cache and (cache.availability.data or false) or false
145+
end
146+
147+
-- Start new job
148+
job_state.availability.is_running = true
92149
local base_cmd = config.options.ccusage_cmd
93150
local found = false
94151

95-
vim.fn.jobstart({ base_cmd, "--version" }, {
152+
job_state.availability.job_id = vim.fn.jobstart({ base_cmd, "--version" }, {
96153
on_stdout = function(_, data, _)
97154
if #data > 0 and data[1] ~= "" then
98155
found = true
@@ -104,9 +161,13 @@ M.is_available = function(opts)
104161
if not opts.bypass_cache then
105162
update_cache(cache.availability, available)
106163
end
164+
165+
-- Execute current callback and all queued callbacks
107166
if opts.callback then
108167
opts.callback(available)
109168
end
169+
execute_queued_callbacks("availability", available)
170+
reset_job_state("availability")
110171
end,
111172
})
112173

0 commit comments

Comments
 (0)