diff --git a/Makefile b/Makefile index bbb7277..7c0b584 100644 --- a/Makefile +++ b/Makefile @@ -50,13 +50,13 @@ test: certs $(LUA) $(DELIM) $(PKGPATH) tests/largetransfer.lua $(LUA) $(DELIM) $(PKGPATH) tests/lock.lua $(LUA) $(DELIM) $(PKGPATH) tests/loop_starter.lua + $(LUA) $(DELIM) $(PKGPATH) tests/pause.lua $(LUA) $(DELIM) $(PKGPATH) tests/queue.lua $(LUA) $(DELIM) $(PKGPATH) tests/removeserver.lua $(LUA) $(DELIM) $(PKGPATH) tests/removethread.lua $(LUA) $(DELIM) $(PKGPATH) tests/request.lua 'http://www.google.com' $(LUA) $(DELIM) $(PKGPATH) tests/request.lua 'https://www.google.nl' true $(LUA) $(DELIM) $(PKGPATH) tests/semaphore.lua - $(LUA) $(DELIM) $(PKGPATH) tests/sleep.lua $(LUA) $(DELIM) $(PKGPATH) tests/starve.lua $(LUA) $(DELIM) $(PKGPATH) tests/tcptimeout.lua $(LUA) $(DELIM) $(PKGPATH) tests/timer.lua diff --git a/docs/index.html b/docs/index.html index 092dbaa..d5fd87a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -105,6 +105,8 @@

History

  • Fix: an error in the timer callback would kill the timer.
  • Added: copas.geterrorhandler to retrieve the active errorhandler.
  • Added: option errorhandler for timer objects.
  • +
  • Added: copas.pause and copas.pauseforever to replace copas.sleep. The latter + method can accidentally sleep-forever if time arithmetic returns a negative result.
  • Change: renamed copas.setErrorHandler to copas.seterrorhandler.
  • Change: renamed copas.useSocketTimeoutErrors to copas.usesockettimeouterrors.
  • diff --git a/docs/manual.html b/docs/manual.html index 9cd1c51..76f28a7 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -381,7 +381,7 @@

    Synchronization primitives

    end print("doing something on my own") - copas.sleep() -- allow to yield, while inside the lock + copas.pause() -- allow to yield, while inside the lock print("after " .. ok .. " seconds waiting") lock:release() diff --git a/docs/reference.html b/docs/reference.html index 7176151..d31e35c 100644 --- a/docs/reference.html +++ b/docs/reference.html @@ -298,14 +298,23 @@

    Non-blocking data exchange and timer/sleep functions

    exchange data with other services.

    +
    copas.pause([delay])
    +
    Pauses the current co-routine. Parameter delay (in seconds) is optional + and defaults to 0. If delay <= 0 then it will pause for 0 seconds. +
    + +
    copas.pauseforever()
    +
    Pauses the current co-routine until explicitly woken by a call to + copas.wakeup(). +
    +
    copas.sleep([sleeptime])
    -
    Pauses the current co-routine. Parameter sleeptime (in seconds) is optional - and defaults to 0. If sleeptime < 0 - then it will sleep until explicitly woken by a call to copas.wakeup(). +
    Deprecated: use copas.pause and copas.pauseforever + instead.
    copas.wakeup(co)
    -
    co is the coroutine to wakeup, see copas.sleep(). +
    co is the coroutine to wakeup, see copas.pauseforever().
    sock:close()
    diff --git a/src/copas.lua b/src/copas.lua index ced7c1d..24f2b67 100644 --- a/src/copas.lua +++ b/src/copas.lua @@ -497,7 +497,7 @@ function copas.receive(client, pattern, part) -- guarantees that high throughput doesn't take other threads to starvation if (math.random(100) > 90) then - copas.sleep(0) + copas.pause() end if s then @@ -541,7 +541,7 @@ function copas.receivefrom(client, size) -- garantees that high throughput doesn't take other threads to starvation if (math.random(100) > 90) then - copas.sleep(0) + copas.pause() end if s then @@ -578,7 +578,7 @@ function copas.receivepartial(client, pattern, part) -- guarantees that high throughput doesn't take other threads to starvation if (math.random(100) > 90) then - copas.sleep(0) + copas.pause() end if s or (type(part) == "string" and #part > orig_size) then @@ -626,7 +626,7 @@ function copas.send(client, data, from, to) -- guarantees that high throughput doesn't take other threads to starvation if (math.random(100) > 90) then - copas.sleep(0) + copas.pause() end if s then @@ -1198,7 +1198,7 @@ function copas.addnamedthread(name, handler, ...) -- create a coroutine that skips the first argument, which is always the socket -- passed by the scheduler, but `nil` in case of a task/thread local thread = coroutine_create(function(_, ...) - copas.sleep(0) + copas.pause() return handler(...) end) if name then @@ -1230,10 +1230,29 @@ end -- yields the current coroutine and wakes it after 'sleeptime' seconds. -- If sleeptime < 0 then it sleeps until explicitly woken up using 'wakeup' +-- TODO: deprecated, remove in next major function copas.sleep(sleeptime) coroutine_yield((sleeptime or 0), _sleeping) end + +-- yields the current coroutine and wakes it after 'sleeptime' seconds. +-- if sleeptime < 0 then it sleeps 0 seconds. +function copas.pause(sleeptime) + if sleeptime and sleeptime > 0 then + coroutine_yield(sleeptime, _sleeping) + else + coroutine_yield(0, _sleeping) + end +end + + +-- yields the current coroutine until explicitly woken up using 'wakeup' +function copas.pauseforever() + coroutine_yield(-1, _sleeping) +end + + -- Wakes up a sleeping coroutine 'co'. function copas.wakeup(co) _sleeping:wakeup(co) @@ -1258,7 +1277,7 @@ do time_out_thread = copas.addnamedthread("copas_core_timer", function() while true do - copas.sleep(TIMEOUT_PRECISION) + copas.pause(TIMEOUT_PRECISION) timerwheel:step() end end) @@ -1358,7 +1377,7 @@ local _sleeping_task = {} do local co = _sleeping:pop(now) while co do -- we're pushing them to _resumable, since that list will be replaced before - -- executing. This prevents tasks running twice in a row with sleep(0) for example. + -- executing. This prevents tasks running twice in a row with pause(0) for example. -- So here we won't execute, but at _resumable step which is next _resumable:push(co) co = _sleeping:pop(now) diff --git a/src/copas/lock.lua b/src/copas/lock.lua index b16a03d..3a8dadd 100644 --- a/src/copas/lock.lua +++ b/src/copas/lock.lua @@ -124,7 +124,7 @@ function lock:get(timeout) copas.timeout(timeout, timeout_handler) start_time = gettime() - copas.sleep(-1) + copas.pauseforever() local err = self.errors[co] self.errors[co] = nil diff --git a/src/copas/semaphore.lua b/src/copas/semaphore.lua index f88d3d0..db7eaa3 100644 --- a/src/copas/semaphore.lua +++ b/src/copas/semaphore.lua @@ -160,7 +160,7 @@ function semaphore:take(requested, timeout) } self.q_tail = self.q_tail + 1 - copas.sleep(-1) -- block until woken + copas.pauseforever() -- block until woken if self.to_flags[co] then -- a timeout happened self.to_flags[co] = nil diff --git a/src/copas/timer.lua b/src/copas/timer.lua index 91c5b84..ba96711 100644 --- a/src/copas/timer.lua +++ b/src/copas/timer.lua @@ -28,7 +28,7 @@ do if self.errorhandler then copas.seterrorhandler(self.errorhandler) end - copas.sleep(initial_delay) + copas.pause(initial_delay) while true do if not self.cancelled then if not self.recurring then @@ -52,7 +52,7 @@ do return end - copas.sleep(self.delay) + copas.pause(self.delay) end end diff --git a/tests/close.lua b/tests/close.lua index c26d406..cbc31b3 100644 --- a/tests/close.lua +++ b/tests/close.lua @@ -34,13 +34,13 @@ copas.loop(function() copas.addserver(server, copas.handler(function(conn_skt) -- client connected, we're not doing anything, let the client -- wait in the read/write queues - copas.sleep(2) + copas.pause(2) -- now we're closing the connecting_socket close_time = socket.gettime() print("closing client socket now, client receive and send operation should immediately error out now") client_socket:close() - copas.sleep(10) + copas.pause(10) conn_skt:close() copas.removeserver(server) print "timeout, test failed" diff --git a/tests/errhandlers.lua b/tests/errhandlers.lua index 25755d0..cc5de10 100644 --- a/tests/errhandlers.lua +++ b/tests/errhandlers.lua @@ -63,7 +63,7 @@ if _VERSION ~= "Lua 5.1" then end f() end) - copas.sleep(1) + copas.pause(1) end) print = old_print --luacheck: ignore @@ -83,7 +83,7 @@ tests.yielding_from_user_code_fails = function() end copas.loop(function() - copas.sleep(1) + copas.pause(1) coroutine.yield() -- directly yield to Copas end) @@ -137,7 +137,7 @@ tests.timerwheel_callbacks_call_the_default_error_handler = function() copas.setErrorHandler(function() call_count = call_count - 10 end, true) copas.loop(function() copas.timeout(0.01, function(co) error("hi there!") end) - copas.sleep(1) + copas.pause(1) end) assert(call_count == -10, "expected callcount -10, got: " .. tostring(call_count)) diff --git a/tests/exit.lua b/tests/exit.lua index 53ed54e..ecf7321 100644 --- a/tests/exit.lua +++ b/tests/exit.lua @@ -7,7 +7,7 @@ local done = false copas.addthread(function() for i = 1, 5 do - copas.sleep(0) + copas.pause() print(i) end @@ -43,7 +43,7 @@ copas.addserver(server, function(skt) end) copas.addthread(function() - copas.sleep(1) + copas.pause(1) local skt = socket.connect("localhost", 20000) print("Sending "..message.."\\n") local bytes = copas.send(skt, message.."\n") diff --git a/tests/exittest.lua b/tests/exittest.lua index 20ebae5..5d4ad67 100644 --- a/tests/exittest.lua +++ b/tests/exittest.lua @@ -19,8 +19,8 @@ print("1) success") print("2) Testing exiting when a task finishes within the loop") copas.addthread(function() - copas.sleep(0.1) -- wait until loop is running - copas.sleep(0.1) -- wait again to make sure its not the initial step in the loop + copas.pause(0.1) -- wait until loop is running + copas.pause(0.1) -- wait again to make sure its not the initial step in the loop print("","2 running...") testran = 2 end) @@ -40,8 +40,8 @@ print("3) success") print("4) Testing exiting when a task fails in the loop") copas.addthread(function() - copas.sleep(0.1) -- wait until loop is running - copas.sleep(0.1) -- wait again to make sure its not the initial step in the loop + copas.pause(0.1) -- wait until loop is running + copas.pause(0.1) -- wait again to make sure its not the initial step in the loop print("","4 running...") testran = 4 error("error on purpose") @@ -54,7 +54,7 @@ print("5) Testing exiting when a task permanently sleeps before the loop") copas.addthread(function() print("","5 running...") testran = 5 - copas.sleep(-1) -- sleep until explicitly woken up + copas.pauseforever() -- sleep until explicitly woken up end) copas.loop() assert(testran == 5, "Test 5 was not executed!") @@ -62,11 +62,11 @@ print("5) success") print("6) Testing exiting when a task permanently sleeps in the loop") copas.addthread(function() - copas.sleep(0.1) -- wait until loop is running - copas.sleep(0.1) -- wait again to make sure its not the initial step in the loop + copas.pause(0.1) -- wait until loop is running + copas.pause(0.1) -- wait again to make sure its not the initial step in the loop print("","6 running...") testran = 6 - copas.sleep(-1) -- sleep until explicitly woken up + copas.pauseforever() -- sleep until explicitly woken up end) copas.loop() assert(testran == 6, "Test 6 was not executed!") diff --git a/tests/http-timeout.lua b/tests/http-timeout.lua index ecfab48..48be63a 100644 --- a/tests/http-timeout.lua +++ b/tests/http-timeout.lua @@ -64,7 +64,7 @@ local function runtest() os.exit(1) end -- we timeout on the request, so sleep here and exit - copas.sleep(timeout + 1) -- sleep 1 second more than the requester timeout, to force a timeout on the request + copas.pause(timeout + 1) -- sleep 1 second more than the requester timeout, to force a timeout on the request print("Server reading port 49500: request-timeout complete") return skt:close() end @@ -105,7 +105,7 @@ local function runtest() os.exit(1) end - copas.sleep(timeout + 1) -- sleep 1 second more than the requester timeout, to force a timeout on the response + copas.pause(timeout + 1) -- sleep 1 second more than the requester timeout, to force a timeout on the response print("Server reading port 49500: response-timeout complete") return skt:close() end)) @@ -115,7 +115,7 @@ local function runtest() copas.addnamedthread("test request", function() print "Waiting a bit for server to start..." - copas.sleep(1) -- give server some time to start + copas.pause(1) -- give server some time to start do print("first test: succesfull round trip") @@ -152,7 +152,7 @@ local function runtest() -- cleanup; sleep 2 secs to wait for closing server socket -- to ensure any error messages do not get intermixed with the next tests output - copas.sleep(2) + copas.pause(2) print(("="):rep(80)) end @@ -192,7 +192,7 @@ local function runtest() -- cleanup; sleep 2 secs to wait for closing server socket -- to ensure any error messages do not get intermixed with the next tests output - copas.sleep(2) + copas.pause(2) print(("="):rep(80)) end @@ -232,7 +232,7 @@ local function runtest() -- cleanup; sleep 2 secs to wait for closing server socket -- to ensure any error messages do not get intermixed with the next tests output - copas.sleep(2) + copas.pause(2) print(("="):rep(80)) end @@ -272,7 +272,7 @@ local function runtest() -- cleanup; sleep 2 secs to wait for closing server socket -- to ensure any error messages do not get intermixed with the next tests output - copas.sleep(2) + copas.pause(2) print(("="):rep(80)) end @@ -312,7 +312,7 @@ local function runtest() -- cleanup; sleep 2 secs to wait for closing server socket -- to ensure any error messages do not get intermixed with the next tests output - copas.sleep(2) + copas.pause(2) print(("="):rep(80)) end diff --git a/tests/largetransfer.lua b/tests/largetransfer.lua index 02987e2..21045a9 100644 --- a/tests/largetransfer.lua +++ b/tests/largetransfer.lua @@ -76,7 +76,7 @@ local function runtest() copas.addnamedthread("test timeout thread", function() local i = 1 while done ~= 4 do - copas.sleep(1) + copas.pause(1) print(i, "seconds:", socket.gettime()-start) i = i + 1 if i > 60 then diff --git a/tests/lock.lua b/tests/lock.lua index cd170c5..6e167cd 100644 --- a/tests/lock.lua +++ b/tests/lock.lua @@ -54,7 +54,7 @@ copas.loop(function() --print(i, "got it!") success_count = success_count + 1 if i == size/3 then - copas.sleep(3) -- keep it long enough for the next 500 to timeout + copas.pause(3) -- keep it long enough for the next 500 to timeout --print(i, "releasing ") assert(lock1:release()) -- by now the 2nd 500 timed out --print(i, "destroying ") @@ -69,7 +69,7 @@ copas.loop(function() --print(i, "timed out!") timeout_count = timeout_count + 1 --if i == (size*2)/3 then - -- copas.sleep(2) -- to ensure thread 500 finished its sleep above + -- copas.pause(2) -- to ensure thread 500 finished its sleep above --end tracker[i] = nil @@ -88,7 +88,7 @@ copas.loop(function() print("releasing "..size.." threads...", gettime()) assert(lock1:release()) print("waiting to finish...") - while next(tracker) do copas.sleep(0.1) end + while next(tracker) do copas.pause(0.1) end -- check results print("success: ", success_count) print("timeout: ", timeout_count) diff --git a/tests/sleep.lua b/tests/pause.lua similarity index 91% rename from tests/sleep.lua rename to tests/pause.lua index e04cd0f..385dda8 100644 --- a/tests/sleep.lua +++ b/tests/pause.lua @@ -7,7 +7,7 @@ local copas = require("copas") local t1 = copas.addthread( function() - copas.sleep(-1) -- sleep until woken up + copas.pauseforever() -- sleep until woken up end ) diff --git a/tests/queue.lua b/tests/queue.lua index 67f2e41..9329a60 100644 --- a/tests/queue.lua +++ b/tests/queue.lua @@ -19,7 +19,7 @@ copas.loop(function() -- yielding on pop when queue is empty local s = now() copas.addthread(function() - copas.sleep(0.5) + copas.pause(0.5) q:push("delayed") end) assert(q:pop() == "delayed", "expected a delayed result") @@ -53,7 +53,7 @@ copas.loop(function() local coro = q:add_worker(function(item) count = count + 1 end) - copas.sleep(0.1) + copas.pause(0.1) assert(count == 3, "expected all 3 items handled") assert(coroutine.status(coro) == "dead", "expected thread to be gone") -- coro should be GC'able @@ -86,7 +86,7 @@ print("test 1 success!") copas.loop(function() local q = Queue:new() q:add_worker(function() end) - copas.sleep(0.5) -- to activate the worker, which will now be blocked on the q semaphore + copas.pause(0.5) -- to activate the worker, which will now be blocked on the q semaphore q:stop() -- this should exit the idle workers and exit the copas loop end) diff --git a/tests/removeserver.lua b/tests/removeserver.lua index a98cdc0..cf6a280 100644 --- a/tests/removeserver.lua +++ b/tests/removeserver.lua @@ -33,7 +33,7 @@ copas.addthread(function() for i = 1, 3 do wait_for_trigger() trigger_it(i) - copas.sleep(0.1) + copas.pause(0.1) end end) diff --git a/tests/removethread.lua b/tests/removethread.lua index 0e36818..ec1d14a 100644 --- a/tests/removethread.lua +++ b/tests/removethread.lua @@ -13,13 +13,13 @@ local t1 = copas.addthread( while true do n = n + 1 print("endless thread:",n) - copas.sleep(0.5) + copas.pause(0.5) end end) local t2 = copas.addthread(function() for i = 1, 5 do - copas.sleep(0.6) + copas.pause(0.6) end print("stopping endless thread externally") copas.removethread(t1) diff --git a/tests/request.lua b/tests/request.lua index 3fdcfec..3d73135 100644 --- a/tests/request.lua +++ b/tests/request.lua @@ -25,7 +25,7 @@ end copas.addthread(function() while switches < max_switches do - copas.sleep(0) + copas.pause() switches = switches + 1 end diff --git a/tests/semaphore.lua b/tests/semaphore.lua index 4d15ed6..6559a43 100644 --- a/tests/semaphore.lua +++ b/tests/semaphore.lua @@ -52,18 +52,18 @@ copas.loop(function() print("got the next 5!") state = state + 2 end) - copas.sleep(0.1) + 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.sleep(0.1) + 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.sleep(0.1) + copas.pause(0.1) assert(state == 3, "expected 2 from the 2nd thread to be added to state") @@ -92,9 +92,9 @@ copas.loop(function() state = state + 1 end end) - copas.sleep(0.1) + copas.pause(0.1) assert(sema:destroy()) - copas.sleep(0.1) + copas.pause(0.1) assert(state == 2, "expected 2 threads to error with 'destroyed'") -- only returns errors from now on, on all methods @@ -121,9 +121,9 @@ copas.loop(function() state = state + 1 end end) - copas.sleep(0.1) + copas.pause(0.1) assert(sema:destroy()) - copas.sleep(0.1) + copas.pause(0.1) assert(state == 1, "expected 1 thread to error with 'destroyed'") sema = nil @@ -137,7 +137,7 @@ copas.loop(function() collectgarbage() -- collect garbage to force eviction from the semaphore registry collectgarbage() - copas.sleep(0.5) -- wait for the timeout to expire if it is still set + copas.pause(0.5) -- wait for the timeout to expire if it is still set assert(errors == 0, "expected no errors") test_complete = true diff --git a/tests/starve.lua b/tests/starve.lua index 127ca19..6d07d3b 100644 --- a/tests/starve.lua +++ b/tests/starve.lua @@ -63,7 +63,7 @@ local function runtest() local i = 0 local start = socket.gettime() while done ~= 2 do - copas.sleep(1) -- delta sleep, so it slowly diverges if starved + copas.pause(1) -- delta sleep, so it slowly diverges if starved i = i + 1 local time_passed = socket.gettime()-start print("slept "..i.." seconds, time passed: ".. time_passed.." seconds") diff --git a/tests/timeout_errors.lua b/tests/timeout_errors.lua index 70f2b98..d0411bd 100644 --- a/tests/timeout_errors.lua +++ b/tests/timeout_errors.lua @@ -23,7 +23,7 @@ function tests.error_on_timeout() error("oops...") end) print "going to sleep for 1 second" - copas.sleep(1) + copas.pause(1) if not (err_received or ""):find("oops...", 1, true) then print("expected to find the error string 'oops...', but got: " .. tostring(err_received)) diff --git a/tests/tls-sni.lua b/tests/tls-sni.lua index 81a3cf8..94841b3 100644 --- a/tests/tls-sni.lua +++ b/tests/tls-sni.lua @@ -71,7 +71,7 @@ server = assert(socket.bind("*", port)) copas.addserver(server, copas.handler(echoHandler, server_params)) copas.addthread(function() - copas.sleep(0.5) -- allow server socket to be ready + copas.pause(0.5) -- allow server socket to be ready ---------------------- -- Tests start here --