mirror of
https://github.com/lune-org/lune.git
synced 2025-05-04 10:43:57 +01:00
Guarantee that wait/sleep functions always yield in both task library and scheduler examples
This commit is contained in:
parent
8ffbb328f3
commit
c091b05f6c
4 changed files with 56 additions and 2 deletions
|
@ -5,7 +5,10 @@ use std::time::Duration;
|
|||
use mlua::prelude::*;
|
||||
use mlua_luau_scheduler::Functions;
|
||||
|
||||
use tokio::time::{sleep, Instant};
|
||||
use tokio::{
|
||||
task::yield_now,
|
||||
time::{sleep, Instant},
|
||||
};
|
||||
|
||||
use lune_utils::TableBuilder;
|
||||
|
||||
|
@ -49,7 +52,14 @@ return defer(function(...)
|
|||
end, ...)
|
||||
";
|
||||
|
||||
async fn wait(_: Lua, secs: Option<f64>) -> LuaResult<f64> {
|
||||
async fn wait(lua: Lua, secs: Option<f64>) -> LuaResult<f64> {
|
||||
// NOTE: We must guarantee that the task.wait API always yields
|
||||
// from a lua perspective, even if sleep/timer completes instantly
|
||||
yield_now().await;
|
||||
wait_inner(lua, secs).await
|
||||
}
|
||||
|
||||
async fn wait_inner(_: Lua, secs: Option<f64>) -> LuaResult<f64> {
|
||||
let duration = Duration::from_secs_f64(secs.unwrap_or_default());
|
||||
|
||||
let before = Instant::now();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use async_io::{block_on, Timer};
|
||||
use futures_lite::future::yield_now;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use mlua_luau_scheduler::Scheduler;
|
||||
|
@ -22,6 +23,10 @@ pub fn main() -> LuaResult<()> {
|
|||
lua.globals().set(
|
||||
"sleep",
|
||||
lua.create_async_function(|_, duration: f64| async move {
|
||||
// Guarantee that the coroutine that calls this sleep function
|
||||
// always yields, even if the timer completes without doing so
|
||||
yield_now().await;
|
||||
// We may then sleep as normal
|
||||
let before = Instant::now();
|
||||
let after = Timer::after(Duration::from_secs_f64(duration)).await;
|
||||
Ok((after - before).as_secs_f64())
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use async_io::{block_on, Timer};
|
||||
use futures_lite::future::yield_now;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use mlua_luau_scheduler::{Functions, Scheduler};
|
||||
|
@ -28,6 +29,9 @@ pub fn main() -> LuaResult<()> {
|
|||
lua.globals().set(
|
||||
"sleep",
|
||||
lua.create_async_function(|_, ()| async move {
|
||||
// Guarantee that the coroutine that calls this sleep function
|
||||
// always yields, even if the timer completes without doing so
|
||||
yield_now().await;
|
||||
// Obviously we can't sleep for a single nanosecond since
|
||||
// this uses OS scheduling under the hood, but we can try
|
||||
Timer::after(ONE_NANOSECOND).await;
|
||||
|
|
35
test.luau
Normal file
35
test.luau
Normal file
|
@ -0,0 +1,35 @@
|
|||
local task = require("@lune/task")
|
||||
|
||||
local started = os.clock()
|
||||
|
||||
local amount = 400000
|
||||
local batches = 5
|
||||
local per_batch = amount / batches
|
||||
|
||||
for current = 1, batches do
|
||||
local thread = coroutine.running()
|
||||
|
||||
print(`Batch {current} / {batches}`)
|
||||
|
||||
for i = 1, per_batch do
|
||||
--print("Spawning thread #" .. i)
|
||||
task.spawn(function()
|
||||
print("[BEFORE] Thread number", i)
|
||||
task.wait(0.1)
|
||||
--_TEST_ASYNC_WORK(0.1)
|
||||
print("[AFTER] Thread number", i)
|
||||
if i == per_batch then
|
||||
print("Last thread in batch #" .. current)
|
||||
assert(
|
||||
coroutine.status(thread) == "suspended",
|
||||
`Thread {i} has status {coroutine.status(thread)}`
|
||||
)
|
||||
task.spawn(thread)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
coroutine.yield()
|
||||
end
|
||||
local took = os.clock() - started
|
||||
print(`Spawned {amount} sleeping threads in {took}s`)
|
Loading…
Add table
Reference in a new issue