Compare commits
8 Commits
f17bf33277
...
e0048be071
Author | SHA1 | Date |
---|---|---|
Taiki Endo | e0048be071 | |
John Nunley | c5a8fab56f | |
Taiki Endo | e86ac7cfc9 | |
Taiki Endo | d40a8747f6 | |
Taiki Endo | 93d1bcf191 | |
Taiki Endo | 148b4f7701 | |
John Nunley | 1ef25770ab | |
John Nunley | 6995e2e77a |
|
@ -1,5 +1,8 @@
|
|||
name: CI
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
@ -10,6 +13,7 @@ on:
|
|||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: 1
|
||||
|
@ -30,7 +34,7 @@ jobs:
|
|||
os: [ubuntu-latest]
|
||||
rust: [nightly, beta, stable]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
|
||||
- run: cargo build --all --all-features --all-targets
|
||||
|
@ -45,9 +49,9 @@ jobs:
|
|||
matrix:
|
||||
# When updating this, the reminder to update the minimum supported
|
||||
# Rust version in Cargo.toml.
|
||||
rust: ['1.35']
|
||||
rust: ['1.63']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
|
||||
- run: cargo build
|
||||
|
@ -55,7 +59,7 @@ jobs:
|
|||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- run: cargo clippy --all-features --all-targets
|
||||
|
@ -63,7 +67,7 @@ jobs:
|
|||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- run: cargo fmt --all --check
|
||||
|
@ -71,7 +75,7 @@ jobs:
|
|||
miri:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup toolchain install nightly --component miri && rustup default nightly
|
||||
- run: cargo miri test
|
||||
|
@ -80,9 +84,13 @@ jobs:
|
|||
RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout
|
||||
|
||||
security_audit:
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
# https://github.com/rustsec/audit-check/issues/2
|
||||
- uses: rustsec/audit-check@master
|
||||
with:
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
name: Release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
|
@ -10,7 +13,7 @@ jobs:
|
|||
if: github.repository_owner == 'smol-rs'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/create-gh-release-action@v1
|
||||
with:
|
||||
changelog: CHANGELOG.md
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# Version 3.3.1
|
||||
|
||||
- Reimplement using 100% safe code. (#11)
|
||||
|
||||
# Version 3.3.0
|
||||
|
||||
- Add `Parallel::collect()` and `Parallel::finish_in()` to allow for generic containers. (#9)
|
||||
|
|
|
@ -3,10 +3,10 @@ name = "easy-parallel"
|
|||
# When publishing a new version:
|
||||
# - Update CHANGELOG.md
|
||||
# - Create "v3.x.y" git tag
|
||||
version = "3.3.0"
|
||||
version = "3.3.1"
|
||||
authors = ["Stjepan Glavina <stjepang@gmail.com>"]
|
||||
edition = "2018"
|
||||
rust-version = "1.35"
|
||||
edition = "2021"
|
||||
rust-version = "1.63"
|
||||
description = "Run closures in parallel"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
repository = "https://github.com/smol-rs/easy-parallel"
|
||||
|
|
102
src/lib.rs
102
src/lib.rs
|
@ -57,12 +57,17 @@
|
|||
//! ```
|
||||
|
||||
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc(
|
||||
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
|
||||
)]
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
|
||||
)]
|
||||
|
||||
use std::fmt;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::mem;
|
||||
use std::iter;
|
||||
use std::panic;
|
||||
use std::process;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
|
@ -256,58 +261,50 @@ impl<'a, T> Parallel<'a, T> {
|
|||
T: Send + 'a,
|
||||
C: FromIterator<T>,
|
||||
{
|
||||
// Set up a guard that aborts on panic.
|
||||
let guard = NoPanic;
|
||||
// Set up a new thread scope.
|
||||
thread::scope(|scope| {
|
||||
// Join handles for spawned threads.
|
||||
let mut handles = Vec::new();
|
||||
|
||||
// Join handles for spawned threads.
|
||||
let mut handles = Vec::new();
|
||||
// Channels to collect results from spawned threads.
|
||||
let mut receivers = Vec::new();
|
||||
|
||||
// Channels to collect results from spawned threads.
|
||||
let mut receivers = Vec::new();
|
||||
for f in self.closures.into_iter() {
|
||||
// Wrap into a closure that sends the result back.
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let f = move || sender.send(f()).unwrap();
|
||||
|
||||
// Spawn a thread for each closure after the first one.
|
||||
for f in self.closures.into_iter() {
|
||||
// Wrap into a closure that sends the result back.
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let f = move || sender.send(f()).unwrap();
|
||||
|
||||
// Erase the `'a` lifetime.
|
||||
let f: Box<dyn FnOnce() + Send + 'a> = Box::new(f);
|
||||
let f: Box<dyn FnOnce() + Send + 'static> = unsafe { mem::transmute(f) };
|
||||
|
||||
// Spawn a thread for the closure.
|
||||
handles.push(thread::spawn(f));
|
||||
receivers.push(receiver);
|
||||
}
|
||||
|
||||
let mut last_err = None;
|
||||
|
||||
// Run the main closure on the main thread.
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(f));
|
||||
|
||||
// Join threads and save the last panic if there was one.
|
||||
for h in handles {
|
||||
if let Err(err) = h.join() {
|
||||
last_err = Some(err);
|
||||
// Spawn it on the scope.
|
||||
handles.push(scope.spawn(f));
|
||||
receivers.push(receiver);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the guard because we may resume a panic now.
|
||||
drop(guard);
|
||||
let mut last_err = None;
|
||||
|
||||
// If a thread has panicked, resume the last collected panic.
|
||||
if let Some(err) = last_err {
|
||||
panic::resume_unwind(err);
|
||||
}
|
||||
// Run the main closure on the main thread.
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(f));
|
||||
|
||||
// Collect the results from threads.
|
||||
let results = receivers.into_iter().map(|r| r.recv().unwrap()).collect();
|
||||
// Join threads and save the last panic if there was one.
|
||||
for h in handles {
|
||||
if let Err(err) = h.join() {
|
||||
last_err = Some(err);
|
||||
}
|
||||
}
|
||||
|
||||
// If the main closure panicked, resume its panic.
|
||||
match res {
|
||||
Ok(r) => (results, r),
|
||||
Err(err) => panic::resume_unwind(err),
|
||||
}
|
||||
// If a thread has panicked, resume the last collected panic.
|
||||
if let Some(err) = last_err {
|
||||
panic::resume_unwind(err);
|
||||
}
|
||||
|
||||
// Collect the results from threads.
|
||||
let results = receivers.into_iter().map(|r| r.recv().unwrap()).collect();
|
||||
|
||||
// If the main closure panicked, resume its panic.
|
||||
match res {
|
||||
Ok(r) => (results, r),
|
||||
Err(err) => panic::resume_unwind(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,14 +321,3 @@ impl<T> Default for Parallel<'_, T> {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Aborts the process if dropped while panicking.
|
||||
struct NoPanic;
|
||||
|
||||
impl Drop for NoPanic {
|
||||
fn drop(&mut self) {
|
||||
if thread::panicking() {
|
||||
process::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use easy_parallel::Parallel;
|
|||
#[test]
|
||||
fn smoke() {
|
||||
let m = Mutex::new(0);
|
||||
let v = vec![2, 3, 5, 7, 11];
|
||||
let v = [2, 3, 5, 7, 11];
|
||||
|
||||
Parallel::new()
|
||||
.add(|| *m.lock().unwrap() += 10)
|
||||
|
@ -18,7 +18,7 @@ fn smoke() {
|
|||
|
||||
#[test]
|
||||
fn squares() {
|
||||
let v = vec![10, 20, 30];
|
||||
let v = [10, 20, 30];
|
||||
|
||||
let squares = Parallel::new().each(0..v.len(), |i| v[i] * v[i]).run();
|
||||
|
||||
|
@ -27,7 +27,7 @@ fn squares() {
|
|||
|
||||
#[test]
|
||||
fn finish() {
|
||||
let v = vec![10, 20, 30];
|
||||
let v = [10, 20, 30];
|
||||
|
||||
let (squares, len) = Parallel::new()
|
||||
.each(0..v.len(), |i| v[i] * v[i])
|
||||
|
|
Loading…
Reference in New Issue