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