146 lines
4.4 KiB
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!")
|