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::prelude::*;
|
||||||
use mlua_luau_scheduler::Functions;
|
use mlua_luau_scheduler::Functions;
|
||||||
|
|
||||||
use tokio::time::{sleep, Instant};
|
use tokio::{
|
||||||
|
task::yield_now,
|
||||||
|
time::{sleep, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
use lune_utils::TableBuilder;
|
||||||
|
|
||||||
|
@ -49,7 +52,14 @@ return defer(function(...)
|
||||||
end, ...)
|
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 duration = Duration::from_secs_f64(secs.unwrap_or_default());
|
||||||
|
|
||||||
let before = Instant::now();
|
let before = Instant::now();
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use async_io::{block_on, Timer};
|
use async_io::{block_on, Timer};
|
||||||
|
use futures_lite::future::yield_now;
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use mlua_luau_scheduler::Scheduler;
|
use mlua_luau_scheduler::Scheduler;
|
||||||
|
@ -22,6 +23,10 @@ pub fn main() -> LuaResult<()> {
|
||||||
lua.globals().set(
|
lua.globals().set(
|
||||||
"sleep",
|
"sleep",
|
||||||
lua.create_async_function(|_, duration: f64| async move {
|
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 before = Instant::now();
|
||||||
let after = Timer::after(Duration::from_secs_f64(duration)).await;
|
let after = Timer::after(Duration::from_secs_f64(duration)).await;
|
||||||
Ok((after - before).as_secs_f64())
|
Ok((after - before).as_secs_f64())
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_io::{block_on, Timer};
|
use async_io::{block_on, Timer};
|
||||||
|
use futures_lite::future::yield_now;
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use mlua_luau_scheduler::{Functions, Scheduler};
|
use mlua_luau_scheduler::{Functions, Scheduler};
|
||||||
|
@ -28,6 +29,9 @@ pub fn main() -> LuaResult<()> {
|
||||||
lua.globals().set(
|
lua.globals().set(
|
||||||
"sleep",
|
"sleep",
|
||||||
lua.create_async_function(|_, ()| async move {
|
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
|
// Obviously we can't sleep for a single nanosecond since
|
||||||
// this uses OS scheduling under the hood, but we can try
|
// this uses OS scheduling under the hood, but we can try
|
||||||
Timer::after(ONE_NANOSECOND).await;
|
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