singe/thirdparty/copas/tests/semaphore.lua

146 lines
4.4 KiB
Lua

-- make sure we are pointing to the local copas first
package.path = string.format("../src/?.lua;%s", package.path)
local now = require("socket").gettime
local copas = require "copas"
local semaphore = copas.semaphore
local test_complete = false
copas.loop(function()
local sema = semaphore.new(10, 5, 1)
assert(sema:get_count() == 5)
assert(sema:take(3))
assert(sema:get_count() == 2)
local ok, _, err
local start = now()
_, err = sema:take(3, 0) -- 1 too many, immediate timeout
assert(err == "timeout", "expected a timeout")
assert(now() - start < 0.001, "expected it not to block with timeout = 0")
start = now()
_, err = sema:take(10, 0) -- way too many, immediate timeout
assert(err == "timeout", "expected a timeout")
assert(now() - start < 0.001, "expected it not to block with timeout = 0")
start = now()
_, err = sema:take(11) -- more than 'max'; "too many" error
assert(err == "too many", "expected a 'too many' error")
assert(now() - start < 0.001, "expected it not to block")
start = now()
_, err = sema:take(10) -- not too many, let's timeout
assert(err == "timeout", "expected a 'timeout' error")
assert(now() - start > 1, "expected it to block for 1s")
assert(sema:get_count() == 2)
--validate async threads
local state = 0
copas.addthread(function()
assert(sema:take(5))
print("got the first 5!")
state = state + 1
end)
copas.addthread(function()
assert(sema:take(5))
print("got the next 5!")
state = state + 2
end)
copas.pause(0.1)
assert(state == 0, "expected state to still be 0")
assert(sema:get_count() == 2, "expected count to still have 2 resources")
assert(sema:give(4))
assert(sema:get_count() == 1, "expected count to now have 1 resource")
copas.pause(0.1)
assert(state == 1, "expected 1 from the first thread to be added to state")
assert(sema:give(4))
assert(sema:get_count() == 0, "gave 4 more, so 5 in total, releasing 5, leaves 0 as expected")
copas.pause(0.1)
assert(state == 3, "expected 2 from the 2nd thread to be added to state")
ok, err = sema:give(100)
assert(not ok)
assert(err == "too many")
assert(sema:get_count() == 10)
-- validate destroying
assert(sema:take(sema:get_count())) -- empty the semaphore
assert(sema:get_count() == 0, "should be empty now")
local state = 0
copas.addthread(function()
local ok, err = sema:take(5)
if ok then
print("got 5, this is unexpected")
elseif err == "destroyed" then
state = state + 1
end
end)
copas.addthread(function()
local ok, err = sema:take(5)
if ok then
print("got 5, this is unexpected")
elseif err == "destroyed" then
state = state + 1
end
end)
copas.pause(0.1)
assert(sema:destroy())
copas.pause(0.1)
assert(state == 2, "expected 2 threads to error with 'destroyed'")
-- only returns errors from now on, on all methods
ok, err = sema:destroy(); assert(ok == nil and err == "destroyed", "expected an error")
ok, err = sema:give(1); assert(ok == nil and err == "destroyed", "expected an error")
ok, err = sema:take(1); assert(ok == nil and err == "destroyed", "expected an error")
ok, err = sema:get_count(); assert(ok == nil and err == "destroyed", "expected an error")
-- timeouts get cancelled upon destruction
-- we set a timeout to 0.5 seconds, then destroy the semaphore
-- the timeout should not execute
-- Reproduce https://github.com/lunarmodules/copas/issues/118
local track_table = setmetatable({}, { __mode = "v" })
local sema = semaphore.new(10, 0, 0.5)
track_table.sema = sema
local state = 0
track_table.coro = copas.addthread(function()
local ok, err = sema:take(1)
if ok then
print("got one, this is unexpected")
elseif err == "destroyed" then
state = state + 1
end
end)
copas.pause(0.1)
assert(sema:destroy())
copas.pause(0.1)
assert(state == 1, "expected 1 thread to error with 'destroyed'")
sema = nil
local errors = 0
copas.setErrorHandler(function(msg)
print("got error: "..tostring(msg))
print("--------------------------------------")
errors = errors + 1
end, true)
collectgarbage() -- collect garbage to force eviction from the semaphore registry
collectgarbage()
copas.pause(0.5) -- wait for the timeout to expire if it is still set
assert(errors == 0, "expected no errors")
test_complete = true
end)
assert(test_complete, "test did not complete!")
print("test success!")