diff --git a/Cargo.lock b/Cargo.lock index 8f84fa6..fcde72d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,6 +516,7 @@ dependencies = [ "anyhow", "clap", "mlua", + "once_cell", "os_str_bytes", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index 8891120..110b314 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,8 @@ panic = "abort" # Remove extra panic info anyhow = { version = "1.0.68" } clap = { version = "4.1.1", features = ["derive"] } mlua = { version = "0.8.7", features = ["luau", "async", "serialize"] } -os_str_bytes = "6.4.1" +once_cell = { version = "1.17.0" } +os_str_bytes = { version = "6.4.1" } reqwest = { version = "0.11.13", features = ["gzip", "deflate"] } serde = { version = "1.0.152", features = ["derive"] } serde_json = { version = "1.0.91" } diff --git a/lune.yml b/lune.yml index 6d6a167..32778b2 100644 --- a/lune.yml +++ b/lune.yml @@ -74,6 +74,20 @@ globals: - required: false type: table # Task + task.defer: + args: + - type: thread | function + - type: "..." + task.delay: + args: + - required: false + type: number + - type: thread | function + - type: "..." + task.spawn: + args: + - type: thread | function + - type: "..." task.wait: args: - required: false diff --git a/luneTypes.d.luau b/luneTypes.d.luau index 559bbbe..a17c55c 100644 --- a/luneTypes.d.luau +++ b/luneTypes.d.luau @@ -53,5 +53,8 @@ declare process: { } declare task: { + defer: (f: thread | (A...) -> (R...), A...) -> (R...), + delay: (duration: number?, f: thread | (A...) -> (R...), A...) -> (R...), + spawn: (f: thread | (A...) -> (R...), A...) -> (R...), wait: (duration: number?) -> (number), } diff --git a/src/lib/globals/task.rs b/src/lib/globals/task.rs index f7b3757..fae3111 100644 --- a/src/lib/globals/task.rs +++ b/src/lib/globals/task.rs @@ -21,20 +21,36 @@ impl Default for Task { impl UserData for Task { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_async_function( + "defer", + |lua, (func, args): (Function, Variadic)| async move { + let thread = lua.create_thread(func)?; + thread.into_async(args).await?; + Ok(()) + }, + ); + methods.add_async_function( + "delay", + |lua, (func, duration, args): (Function, Option, Variadic)| async move { + let secs = duration.unwrap_or(DEFAULT_SLEEP_DURATION); + time::sleep(Duration::from_secs_f32(secs)).await; + let thread = lua.create_thread(func)?; + thread.into_async(args).await?; + Ok(()) + }, + ); + methods.add_async_function( + "spawn", + |lua, (func, args): (Function, Variadic)| async move { + let thread = lua.create_thread(func)?; + thread.into_async(args).await?; + Ok(()) + }, + ); methods.add_async_function("wait", |_, duration: Option| async move { let secs = duration.unwrap_or(DEFAULT_SLEEP_DURATION); time::sleep(Duration::from_secs_f32(secs)).await; Ok(secs) }); - methods.add_async_function( - "spawn", - |lua, (func, args): (Function, Variadic)| async move { - let _thread = lua - .create_thread(func)? - .into_async::<_, Variadic>>(args); - // task::spawn_local(async move { thread }); - Ok(()) - }, - ); } } diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 2180e2c..9e5a5d1 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -109,6 +109,9 @@ mod tests { net_request_redirect: "net/request/redirect", net_json_decode: "net/json/decode", net_json_encode: "net/json/encode", + task_defer: "task/defer", + task_delay: "task/delay", + task_spawn: "task/spawn", task_wait: "task/wait", } } diff --git a/src/tests/task/defer.luau b/src/tests/task/defer.luau new file mode 100644 index 0000000..92ac060 --- /dev/null +++ b/src/tests/task/defer.luau @@ -0,0 +1,45 @@ +-- Deferred functions should run after other threads + +local flag: boolean = false +task.defer(function() + flag = true +end) +assert(not flag, "Defer should run after other threads, including the main thread") + +-- Deferred functions should work with yielding + +local flag2: boolean = false +task.defer(function() + task.wait() + flag2 = true +end) +assert(not flag2, "Defer should work with yielding") + +-- Deferred functions should run after other spawned threads +local flag3: boolean = false +task.defer(function() + if flag3 == true then + flag3 = false + end +end) +task.spawn(function() + if flag3 == false then + flag3 = true + end +end) +task.wait() +assert(not flag2, "Defer should run after spawned threads") + +-- Varargs should get passed correctly + +local function f(arg1: string, arg2: number, f2: (...any) -> ...any) + assert(type(arg1) == "string", "Invalid arg 1 passed to function") + assert(type(arg2) == "number", "Invalid arg 2 passed to function") + assert(type(arg3) == "function", "Invalid arg 3 passed to function") +end + +task.defer(f, "", 1, f) +task.defer(f, "inf", math.huge, f) +task.defer(f, "NaN", 0 / 0, f) + +task.wait(0.1) diff --git a/src/tests/task/delay.luau b/src/tests/task/delay.luau new file mode 100644 index 0000000..4c849f4 --- /dev/null +++ b/src/tests/task/delay.luau @@ -0,0 +1,34 @@ +-- Delayed functions should never run right away + +local flag: boolean = false +task.delay(0, function() + flag = true +end) +assert(not flag, "Delay should never run instantly") + +-- Delayed functions should work with yielding + +local flag2: boolean = false +task.delay(0.2, function() + flag2 = true + task.wait(0.2) + flag2 = false +end) +task.wait(0.25) +assert(flag2, "Delay should work with yielding") +task.wait(0.25) +assert(not flag2, "Delay should work with yielding") + +-- Varargs should get passed correctly + +local function f(arg1: string, arg2: number, f2: (...any) -> ...any) + assert(type(arg1) == "string", "Invalid arg 1 passed to function") + assert(type(arg2) == "number", "Invalid arg 2 passed to function") + assert(type(arg3) == "function", "Invalid arg 3 passed to function") +end + +task.delay(0, f, "", 1, f) +task.delay(0, f, "inf", math.huge, f) +task.delay(0, f, "NaN", 0 / 0, f) + +task.wait(0.1) diff --git a/src/tests/task/spawn.luau b/src/tests/task/spawn.luau new file mode 100644 index 0000000..c3fb550 --- /dev/null +++ b/src/tests/task/spawn.luau @@ -0,0 +1,30 @@ +-- Spawned functions should run right away + +local flag: boolean = false +task.spawn(function() + flag = true +end) +assert(flag, "Spawn should run instantly until yielded") + +-- Spawned functions should work with yielding + +local flag2: boolean = false +task.spawn(function() + task.wait() + flag2 = true +end) +assert(not flag2, "Spawn should work with yielding") + +-- Varargs should get passed correctly + +local function f(arg1: string, arg2: number, f2: (...any) -> ...any) + assert(type(arg1) == "string", "Invalid arg 1 passed to function") + assert(type(arg2) == "number", "Invalid arg 2 passed to function") + assert(type(arg3) == "function", "Invalid arg 3 passed to function") +end + +task.spawn(f, "", 1, f) +task.spawn(f, "inf", math.huge, f) +task.spawn(f, "NaN", 0 / 0, f) + +task.wait(0.1)