-
Notifications
You must be signed in to change notification settings - Fork 235
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1515 from bendk/async-refactor
Async refactor
- Loading branch information
Showing
69 changed files
with
2,435 additions
and
1,329 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "uniffi-fixture-foreign-executor" | ||
version = "0.23.0" | ||
edition = "2021" | ||
license = "MPL-2.0" | ||
publish = false | ||
|
||
[lib] | ||
crate-type = ["lib", "cdylib"] | ||
name = "uniffi_fixture_foreign_executor" | ||
|
||
[dependencies] | ||
uniffi = { path = "../../uniffi" } | ||
|
||
[build-dependencies] | ||
uniffi = { path = "../../uniffi", features = ["build"] } | ||
|
||
[dev-dependencies] | ||
uniffi = { path = "../../uniffi", features = ["bindgen-tests"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
fn main() { | ||
uniffi::generate_scaffolding("./src/foreign_executor.udl").unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace fixture_foreign_executor { }; | ||
|
||
interface ForeignExecutorTester { | ||
constructor(ForeignExecutor executor); | ||
[Name=new_from_sequence] | ||
constructor(sequence<ForeignExecutor> executors); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
use std::sync::{Arc, Mutex}; | ||
use std::thread; | ||
use std::time::Instant; | ||
use uniffi::ForeignExecutor; | ||
|
||
pub struct ForeignExecutorTester { | ||
executor: ForeignExecutor, | ||
last_result: Arc<Mutex<Option<TestResult>>>, | ||
} | ||
|
||
// All constructor have to be defined in UDL for now | ||
impl ForeignExecutorTester { | ||
fn new(executor: ForeignExecutor) -> Self { | ||
Self { | ||
executor, | ||
last_result: Arc::new(Mutex::new(None)), | ||
} | ||
} | ||
|
||
// Test inputting the ForeignExecutor from a Vec. This tests that they can be written to a | ||
// `RustBuffer` | ||
fn new_from_sequence(executors: Vec<ForeignExecutor>) -> Self { | ||
assert_eq!(executors.len(), 1); | ||
Self::new(executors.into_iter().next().unwrap()) | ||
} | ||
} | ||
|
||
#[uniffi::export] | ||
impl ForeignExecutorTester { | ||
/// Schedule a fire-and-forget task to run the test | ||
fn schedule_test(&self, delay: u32) { | ||
let last_result = self.last_result.clone(); | ||
*last_result.lock().unwrap() = None; | ||
// Start a thread to schedule the call. This tests if the foreign bindings can handle | ||
// schedule callbacks from a thread that they don't manage. | ||
thread::scope(move |scope| { | ||
scope.spawn(move || { | ||
let start_time = Instant::now(); | ||
let initial_thread_id = thread::current().id(); | ||
// Schedule a call with the foreign executor | ||
self.executor.schedule(delay, move || { | ||
// Return data on when/where the call happened. We check that this matches the | ||
// expectations in the foreign bindings tests | ||
let call_happened_in_different_thread = | ||
thread::current().id() != initial_thread_id; | ||
let delay_ms = start_time.elapsed().as_millis() as u32; | ||
*last_result.lock().unwrap() = Some(TestResult { | ||
call_happened_in_different_thread, | ||
delay_ms, | ||
}); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
fn get_last_result(&self) -> Option<TestResult> { | ||
self.last_result.lock().unwrap().take() | ||
} | ||
} | ||
|
||
#[derive(uniffi::Record)] | ||
pub struct TestResult { | ||
pub call_happened_in_different_thread: bool, | ||
pub delay_ms: u32, | ||
} | ||
|
||
include!(concat!(env!("OUT_DIR"), "/foreign_executor.uniffi.rs")); |
47 changes: 47 additions & 0 deletions
47
fixtures/foreign-executor/tests/bindings/test_foreign_executor.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import uniffi.fixture_foreign_executor.* | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.runBlocking | ||
|
||
|
||
val coroutineScope = CoroutineScope(Dispatchers.IO) | ||
// Test scheduling calls with no delay | ||
runBlocking { | ||
val tester = ForeignExecutorTester(coroutineScope) | ||
launch { | ||
tester.scheduleTest(0U) | ||
} | ||
delay(100L) | ||
val result = tester.getLastResult() ?: throw RuntimeException("ForeignExecutorTester.getLastResult() returned null") | ||
assert(result.callHappenedInDifferentThread) | ||
assert(result.delayMs <= 100U) | ||
tester.close() | ||
} | ||
|
||
// Test scheduling calls with a delay and using the newFromSequence constructor | ||
runBlocking { | ||
val tester = ForeignExecutorTester.newFromSequence(listOf(coroutineScope)) | ||
launch { | ||
tester.scheduleTest(100U) | ||
} | ||
delay(200L) | ||
val result = tester.getLastResult() ?: throw RuntimeException("ForeignExecutorTester.getLastResult() returned null") | ||
assert(result.callHappenedInDifferentThread) | ||
assert(result.delayMs >= 100U) | ||
assert(result.delayMs <= 200U) | ||
tester.close() | ||
} | ||
|
||
// Test that we cleanup when dropping a ForeignExecutor handles | ||
assert(FfiConverterForeignExecutor.handleCount() == 0) | ||
val tester = ForeignExecutorTester(coroutineScope) | ||
val tester2 = ForeignExecutorTester.newFromSequence(listOf(coroutineScope)) | ||
tester.close() | ||
tester2.close() | ||
assert(FfiConverterForeignExecutor.handleCount() == 0) |
50 changes: 50 additions & 0 deletions
50
fixtures/foreign-executor/tests/bindings/test_foreign_executor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import asyncio | ||
import unittest | ||
import weakref | ||
from fixture_foreign_executor import ForeignExecutorTester | ||
|
||
class TestForeignExecutor(unittest.TestCase): | ||
def test_schedule(self): | ||
async def run_test(constructor, delay): | ||
if constructor == "primary": | ||
tester = ForeignExecutorTester(asyncio.get_running_loop()) | ||
elif constructor == "new_from_sequence": | ||
tester = ForeignExecutorTester.new_from_sequence([asyncio.get_running_loop()]) | ||
else: | ||
raise AssertionError(f"Unknown constructor: {constructor}") | ||
tester.schedule_test(delay) | ||
await asyncio.sleep((delay / 1000) + 0.1) | ||
return tester.get_last_result() | ||
|
||
# Test no delay and lifting the foreign executor directly | ||
result = asyncio.run(run_test("primary", 0)) | ||
self.assertTrue(result.call_happened_in_different_thread) | ||
self.assertTrue(result.delay_ms <= 1) | ||
|
||
# Test no delay and reading the foreign executor from a list | ||
result = asyncio.run(run_test("new_from_sequence", 10)) | ||
self.assertTrue(result.call_happened_in_different_thread) | ||
self.assertTrue(9 <= result.delay_ms <= 11) | ||
|
||
def test_reference_counts(self): | ||
# Create an event loop | ||
loop = asyncio.new_event_loop() | ||
loop_ref = weakref.ref(loop) | ||
# Create ForeignExecutorTester that stores the loop | ||
tester = ForeignExecutorTester(loop) | ||
tester2 = ForeignExecutorTester.new_from_sequence([loop]), | ||
# Test that testers hold a reference to the loop. After deleting the loop, the weakref should still be alive | ||
loop.close() | ||
del loop | ||
self.assertNotEqual(loop_ref(), None, "ForeignExecutor didn't take a reference to the event loop") | ||
# Deleting testers should cause the loop to be destroyed | ||
del tester | ||
del tester2 | ||
self.assertEqual(loop_ref(), None, "ForeignExecutor didn't release a reference to the event loop") | ||
|
||
if __name__=='__main__': | ||
unittest.main() |
42 changes: 42 additions & 0 deletions
42
fixtures/foreign-executor/tests/bindings/test_foreign_executor.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import Foundation | ||
import fixture_foreign_executor | ||
|
||
func runTest(tester: ForeignExecutorTester, delay: UInt32) async -> TestResult { | ||
let handle = Task { () -> TestResult in | ||
tester.scheduleTest(delay: delay) | ||
try! await Task.sleep(nanoseconds: numericCast((delay + 10) * 1000000)) | ||
return tester.getLastResult()! | ||
} | ||
return await handle.value | ||
} | ||
|
||
Task { | ||
// Test scheduling with no delay | ||
let result = await runTest( | ||
tester: ForeignExecutorTester( | ||
executor: UniFfiForeignExecutor(priority: TaskPriority.background) | ||
), | ||
delay: 0 | ||
) | ||
assert(result.callHappenedInDifferentThread) | ||
assert(result.delayMs <= 1) | ||
|
||
// Test scheduling with delay and an executor created from a list | ||
let result2 = await runTest( | ||
tester: ForeignExecutorTester.newFromSequence( | ||
executors: [UniFfiForeignExecutor(priority: TaskPriority.background)] | ||
), | ||
delay: 1000 | ||
) | ||
assert(result2.callHappenedInDifferentThread) | ||
assert(result2.delayMs >= 90) | ||
assert(result2.delayMs <= 110) | ||
} | ||
|
||
|
||
|
||
// No need to test reference counting, since `UniFfiForeignExecutor` on Swift is just a value type |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
uniffi::build_foreign_language_testcases!( | ||
"tests/bindings/test_foreign_executor.py", | ||
"tests/bindings/test_foreign_executor.kts", | ||
"tests/bindings/test_foreign_executor.swift", | ||
); |
Oops, something went wrong.