Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ tasks:
doc:
cmds:
# note: require nightly toolchain in CI
- cargo doc --no-deps --features full,nightly
- cargo doc --no-deps --features full,nightly -p pistonite-cu -p pistonite-cu-proc-macros -p pistonite-pm
- cmd: >
echo "Generating index redirect";
echo "<!DOCTYPE html>" > target/doc/index.html;
Expand Down
11 changes: 8 additions & 3 deletions packages/copper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ terminal_size = { version = "0.4.3", optional = true }
unicode-width = { version = "0.2.2", features = ["cjk"], optional = true }
clap = { version = "4.5.54", features = ["derive"], optional = true }
regex = { version = "1.12.2", optional = true }
ctrlc = { version = "3.5.1", optional = true }

# --- Coroutine ---
num_cpus = { version = "1.17.0", optional = true }
Expand Down Expand Up @@ -74,15 +75,16 @@ full = [
]

# --- Command Line Interface ---
print = ["dep:oneshot", "dep:regex", "dep:env_filter", "dep:terminal_size", "dep:unicode-width"]
print = ["dep:oneshot", "dep:regex", "dep:env_filter", "dep:terminal_size", "dep:unicode-width", "dep:ctrlc"]
cli = ["dep:clap", "print"]
prompt = ["print"]
prompt-password = ["prompt"]

# --- Coroutine ---
coroutine = [
"dep:tokio", "dep:num_cpus",
"tokio/sync", "tokio/io-util", "tokio/io-std"
"tokio/sync", "tokio/io-util", "tokio/io-std",
"tokio/time",
]
coroutine-heavy = ["coroutine"] # enable heavy coroutine drived by multi-threaded tokio runtime

Expand All @@ -91,7 +93,6 @@ process = [ # enable spawning child processes
"coroutine", "fs",
"dep:spin",
"tokio/process",
"tokio/time",
]
fs = [ # enable file system and path util
"dep:which", "dep:pathdiff", "dep:dunce", "dep:filetime", "dep:glob",
Expand Down Expand Up @@ -134,3 +135,7 @@ required-features = ["fs", "cli"]
[[example]]
name = "cargo"
required-features = ["process", "cli", "json"]

[[example]]
name = "ctrlc"
required-features = ["cli", "coroutine"]
100 changes: 100 additions & 0 deletions packages/copper/examples/ctrlc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use pistonite_cu as cu;
use std::time::Duration;

#[cu::cli]
fn main(_: cu::cli::Flags) -> cu::Result<()> {
sync_main()?;
cu::co::run(async move { async_auto_check().await })?;
cu::co::run(async move { async_manual_check().await })?;
Ok(())
}

fn sync_main() -> cu::Result<()> {
let result = cu::cli::ctrlc_frame().execute(move |ctrlc| {
for _ in 0..30 {
cu::print!("(sync) please press Ctrl-C");
std::thread::sleep(Duration::from_millis(100));
ctrlc.check()?;
}
cu::warn!("(sync) about to return!");
cu::Ok(42)
});
match result {
Ok(None) => cu::info!("(sync) was aborted!"),
Ok(Some(n)) => cu::info!("(sync) was finished: {n}"),
Err(e) => cu::error!("(sync) error: {e:?}"),
}
Ok(())
}

async fn async_manual_check() -> cu::Result<()> {
let result = cu::cli::ctrlc_frame()
.abort_threshold(3)
.on_signal(|ctrlc| {
if !ctrlc.should_abort() {
cu::warn!("Ctrl-C 3 times to abort!")
}
})
.co_execute(async |ctrlc| {
for _ in 0..10 {
cu::print!("(async) please press Ctrl-C");
cu::co::sleep(Duration::from_secs(1)).await;
if ctrlc.should_abort() {
cu::info!("just kidding, we never abort");
}
}
cu::warn!("(async) about to return!");
cu::Ok(42)
})
.await;
match result {
Ok(None) => cu::info!("(async) was aborted!"),
Ok(Some(n)) => cu::info!("(async) was finished: {n}"),
Err(e) => cu::error!("(async) error: {e:?}"),
}
Ok(())
}

async fn async_auto_check() -> cu::Result<()> {
let (send, mut recv) = tokio::sync::mpsc::unbounded_channel();
let result = cu::cli::ctrlc_frame()
.on_signal(move |_| {
let _ = send.send(());
})
.co_execute(async move |ctrlc| {
let waiter = async move {
loop {
if recv.recv().await.is_none() {
return;
}
if ctrlc.should_abort() {
return;
}
}
};
tokio::select! {
result = my_long_running_task() => {
result
}
_ = waiter => {
// does not matter what the value is -
// co_execute will ensure None is returned
// when aborted
Ok(0)
}
}
})
.await?;
match result {
Some(x) => cu::info!("valud is: {x}"),
None => cu::error!("aborted!"),
}
Ok(())
}

async fn my_long_running_task() -> cu::Result<i32> {
loop {
cu::print!("will run forever if you don't abort");
cu::co::sleep(Duration::from_secs(1)).await;
}
}
8 changes: 8 additions & 0 deletions packages/copper/src/atomic.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::atomic::{AtomicUsize, Ordering};

/// An atomic wrapper with an underlying atomic storage and conversion to
/// a type T.
///
Expand Down Expand Up @@ -78,3 +80,9 @@ impl_atomic_type! {
isize => AtomicIsize, new_isize,
usize => AtomicUsize, new_usize,
}

#[allow(unused)]
pub(crate) fn next_atomic_usize() -> usize {
static ID: AtomicUsize = AtomicUsize::new(1);
ID.fetch_add(1, Ordering::SeqCst)
}
Loading