mirror of
https://github.com/bertptrs/beul.git
synced 2025-12-25 12:40:31 +01:00
Initial import
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/Cargo.lock
|
||||||
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "beul"
|
||||||
|
version = "0.1.0"
|
||||||
|
# Edition 2021 is not available in MSRV
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "It executes futures"
|
||||||
|
repository = "https://github.com/bertptrs/beul/"
|
||||||
|
authors = [
|
||||||
|
"Bert Peters",
|
||||||
|
]
|
||||||
|
categories = [
|
||||||
|
"asynchronous",
|
||||||
|
"development-tools",
|
||||||
|
]
|
||||||
|
keywords = [
|
||||||
|
"futures",
|
||||||
|
"async",
|
||||||
|
"executor",
|
||||||
|
"runtime",
|
||||||
|
]
|
||||||
13
LICENSE-APACHE
Normal file
13
LICENSE-APACHE
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2022 Bert Peters
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
21
LICENSE-MIT
Normal file
21
LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Bert Peters
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Beul
|
||||||
|
|
||||||
|
Beul is a minimalistic futures executor. No dependencies, no unsafe rust.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Simply call `execute` with your future:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
beul::execute(async {});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backwards compatibility
|
||||||
|
|
||||||
|
This crate requires at least Rust 1.51, due to its reliance on [Wake]. Increases in this version
|
||||||
|
will be considered breaking changes. This crate follows semantic versioning.
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
Beul is a single-threaded executor and will not provide anything but execution. Futures that depend
|
||||||
|
on runtime features, as present for example in [Tokio], will not work.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0 ([LICENSE-APACHE](./LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the
|
||||||
|
work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
|
||||||
|
additional terms or conditions.
|
||||||
|
|
||||||
|
[Tokio]: https://tokio.rs/
|
||||||
|
[Wake]: https://doc.rust-lang.org/std/task/trait.Wake.html
|
||||||
61
src/lib.rs
Normal file
61
src/lib.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
//! Beul - It executes futures.
|
||||||
|
//!
|
||||||
|
//! This crate offers a single function: [`execute`], which will run a single future on the current
|
||||||
|
//! thread. No fancy executor, no future primitives, just a simple executor. No dependencies, no
|
||||||
|
//! unsafe rust.
|
||||||
|
//!
|
||||||
|
//! The design is based on the example `ThreadWaker` from the [`Wake`] documentation, which the
|
||||||
|
//! reentrancy issues resolved by using a [`Condvar`].
|
||||||
|
//!
|
||||||
|
//! Beul is Dutch for executioner.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! beul::execute(async {});
|
||||||
|
//! ```
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
use std::future::Future;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Condvar;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::PoisonError;
|
||||||
|
use std::task::Context;
|
||||||
|
use std::task::Poll;
|
||||||
|
use std::task::Wake;
|
||||||
|
use std::task::Waker;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CondvarWake(Condvar);
|
||||||
|
|
||||||
|
impl Wake for CondvarWake {
|
||||||
|
fn wake(self: Arc<Self>) {
|
||||||
|
self.0.notify_one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wake_by_ref(self: &Arc<Self>) {
|
||||||
|
self.0.notify_one()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block on specified [`Future`].
|
||||||
|
pub fn execute<T>(f: impl Future<Output = T>) -> T {
|
||||||
|
// TODO: replace with std::pin::pin once it gets stabilized
|
||||||
|
let mut pinned = Box::pin(f);
|
||||||
|
|
||||||
|
let wake = Arc::new(CondvarWake::default());
|
||||||
|
let waker = Waker::from(Arc::clone(&wake));
|
||||||
|
|
||||||
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
let mutex = Mutex::new(());
|
||||||
|
// Cannot panic but avoids generating the unwrap code
|
||||||
|
let mut guard = mutex.lock().unwrap_or_else(PoisonError::into_inner);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match pinned.as_mut().poll(&mut context) {
|
||||||
|
Poll::Ready(value) => return value,
|
||||||
|
Poll::Pending => guard = wake.0.wait(guard).unwrap_or_else(PoisonError::into_inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
tests/futures.rs
Normal file
54
tests/futures.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::task::Context;
|
||||||
|
use std::task::Poll;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple() {
|
||||||
|
// Sanity check
|
||||||
|
assert_eq!(42, beul::execute(async { 42 }));
|
||||||
|
|
||||||
|
// Some more esoteric futures
|
||||||
|
beul::execute(async { async {}.await });
|
||||||
|
beul::execute(async { beul::execute(async {}) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_threaded_future() {
|
||||||
|
/// Dummy future that sleeps until a separate thread wakes it
|
||||||
|
///
|
||||||
|
/// It returns the number of times it has been awoken
|
||||||
|
struct ThreadedFuture(Arc<AtomicBool>, usize);
|
||||||
|
|
||||||
|
impl Future for ThreadedFuture {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
// Note down that we've been polled
|
||||||
|
self.1 += 1;
|
||||||
|
|
||||||
|
if self.0.load(Ordering::Acquire) {
|
||||||
|
Poll::Ready(self.1)
|
||||||
|
} else {
|
||||||
|
if self.1 == 1 {
|
||||||
|
let completer = Arc::clone(&self.0);
|
||||||
|
let waker = cx.waker().clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
completer.store(true, Ordering::Release);
|
||||||
|
waker.wake();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let future = ThreadedFuture(Arc::new(AtomicBool::new(false)), 0);
|
||||||
|
|
||||||
|
// Future should be polled twice, once initially and once after the wake-up
|
||||||
|
assert_eq!(beul::execute(future), 2);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user