Skip to content

Commit

Permalink
fix: sync/cond and sync/pool noyields with timeout=0
Browse files Browse the repository at this point in the history
	* sync/cond: caller now can "check" whether value is present in
	  condvar with cond:recv(0) and it is guarantied that this call
	  never yields
	* sync/pool: caller now can send task with {wait_timeout=0} and
	  be sure, that execution will not yield and will return proper
	  message TASK_WAS_NOT_SCHEDULED if no free workers are
	  available in the pool right now
  • Loading branch information
Vladislav Grubov committed Mar 3, 2024
1 parent 00722df commit 8c67bb3
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
3 changes: 3 additions & 0 deletions sync/cond.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ function cond:recv(timeout)
if not self.lock then
self.lock = fiber.cond()
end
if timeout == 0 then
return nil, "Timed out"
end
self.lock:wait(timeout or self.timeout)
self.lock = nil
if self.sent then
Expand Down
15 changes: 10 additions & 5 deletions sync/pool.lua
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,16 @@ function pool:send(func, args, opts)

self.wg:start()

while not self.terminate_cb:recv(0) and fiber.time() < deadline do
fiber.testcancel()
if self.chan:put(task, math.min(1, deadline - fiber.time())) then
task.published_at = fiber.time()
break
if fiber.time() <= deadline then
while not self.terminate_cb:recv(0) do
fiber.testcancel()
if self.chan:put(task, math.min(1, math.max(0, deadline - fiber.time()))) then
task.published_at = fiber.time()
break
end
if fiber.time() >= deadline then
break
end
end
end

Expand Down
20 changes: 20 additions & 0 deletions test/01-cond.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,24 @@ test:deadline(function()

end, 3, 'recv -> send works')

test:deadline(function()
local cond = sync.cond()
local value = math.random()

test:noyield(function()
local no_val, err = cond:recv(0)
test:is(no_val, nil, 'empty cond returns no value')
test:is(err, 'Timed out', 'error message is timed out')
end)

cond:send(value)

test:noyield(function()
local recv, err = cond:recv(0)
test:is(recv, value, 'value received')
test:is(err, nil, 'no error returned')
end)

end, 1, 'recv noyields when timeout=0')

test:done_testing()
33 changes: 33 additions & 0 deletions test/05-pool.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,37 @@ test:deadline(function()
test:is(waited, 0, "task:wait on cancelled task must return instantly")
end, 1, "task autocancelled when wait_timeout is too low")

test:deadline(function()
local pool = sync.pool.new('pool', 1)

local executed
local task, err

test:noyield(function()
task, err = pool:send(function() executed = true end, {},
{ async = true, wait_timeout = 0 })
test:is(err, nil, "pool with one worker raises no error on send{wait_timeout=0}")
end)

---@cast task sync.pool.task
task:wait()
test:is(executed, true, "task was successfully executed")

-- pool still has free slot
test:noyield(function ()
task, err = pool:send(function() fiber.yield() executed = true end, {},
{ async = true, wait_timeout = 0 })

test:is(err, nil, "pool with one worker raises no error on send{wait_timeout=0}")

task, err = pool:send(function() executed = true end, {},
{ async = true, wait_timeout = 0 })

test:is(err, pool.errors.TASK_WAS_NOT_SCHEDULED, "second task was not scheduled")
end)

pool:terminate()
pool:wait()
end, 1, "pool:send allows put with timeout=0")

test:done_testing()

0 comments on commit 8c67bb3

Please sign in to comment.