Giter Club home page Giter Club logo

parallel-event-emitter's Introduction

Parallel Event Emitter

Implementation of an event emitter that invokes event listener callbacks concurrently in a configurable thread pool, using Futures to notify callers of success or errors.

Because all values must be transferred across thread boundaries, all types T must be Send.

Additionally, all types T must be Any, so T: 'static.

Usage

[dependencies]
futures = "0.1"
parallel-event-emitter = "0.2.4"

Example using a String as the key:

extern crate futures;
extern crate parallel_event_emitter;

use futures::Future;
use parallel_event_emitter::*;

fn main() {
    let mut emitter: ParallelEventEmitter<String> = ParallelEventEmitter::new();

    emitter.add_listener("some event", || {
        println!("Hello, World!");

        Ok(())
    }).unwrap();

    assert_eq!(1, emitter.emit("some event").wait().unwrap());
}

Or using a custom event type:

extern crate futures;
extern crate parallel_event_emitter;

use futures::Future;
use parallel_event_emitter::*;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
enum MyEvents {
    EventA,
    EventB,
}

fn main() {
    let mut emitter: ParallelEventEmitter<MyEvents> = ParallelEventEmitter::new();

    emitter.add_listener(MyEvents::EventA, || {
        println!("Hello, World!");

        Ok(())
    }).unwrap();

    assert_eq!(1, emitter.emit(MyEvents::EventA).wait().unwrap());
}

Trace<E> type

This crate depends on the trace-error crate to have simple and lightweight backtraces on all error Results.

If you choose not to use that, which is fine by me, simply call .into_error() on all Trace<E> values to get the real error.

impl Trait feature

Instead of having all the emit* methods returning a boxed Future (BoxFuture), the Cargo feature conservative_impl_trait can be given to enable impl Future return types on all the emit* methods.

[dependencies.parallel-event-emitter]
version = "0.2.4"
features = ["default", "conservative_impl_trait"] # And maybe integer_atomics

Larger ListenerIds

Although the ListenerId type itself is u64, the atomic counter underneath is restricted to AtomicUsize by default.

To enable true guaranteed 64-bit counters, use the integer_atomics feature for the crate.

[dependencies.parallel-event-emitter]
version = "0.2.4"
features = ["default", "integer_atomics"] # And maybe conservative_impl_trait

parallel-event-emitter's People

Contributors

novacrazy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

kip-app

parallel-event-emitter's Issues

Appears possible to use components that don't implement Send across threads

I'm building a project with GTK-rs and seem to be able to pass GTK widgets (that are not thread safe) in listener callbacks and use them.

as far as I can see, closures passed to add_listener are run in another thread (as long as I'm not mistaken)

This appears possible;

        let label = gtk::Label::new(None);
        events.add_listener_value(Event::Window, clone!(label
            move |event_opt: Option<String>| {
                label.set_text(&text);
                Ok(())
            }
        )).unwrap();

However, this (correctly) throws an error at compile time;

        let label = gtk::Label::new(None);
        thread::spawn(clone!(label move || {
            label.set_text(&text)
        }));

How come I can do the former? gtk::Label doesn't implement send: http://gtk-rs.org/docs/gtk/struct.Label.html

Data race allowed on `K` of `ParallelEventEmitter<K>`

Hello,
we (Rust group @sslab-gatech) found a memory-safety/soundness issue in this crate while scanning Rust code on crates.io for potential vulnerabilities.

Issue Description

unsafe impl<K: EventKey> Sync for Inner<K> {}

Inner<K> implements Sync for all K: EventKey, even when K: !Sync.
It is possible to create a data race to K: !Sync using ParallelEventEmitter::event_names_visitor(), which may lead to undefined behavior.

Reproduction

Below is an example program that exhibits undefined behavior using safe APIs of parallel-event-emitter.

Show Detail

#![forbid(unsafe_code)]
use parallel_event_emitter::ParallelEventEmitter;

use std::cell::Cell;
use std::hash::{Hash, Hasher};
use std::sync::Arc;

// A simple tagged union used to demonstrate problems with data races in Cell.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
enum RefOrInt {
    Ref(&'static u64),
    Int(u64),
}
static SOME_INT: u64 = 123;

#[derive(PartialEq, Eq, Clone)]
struct Foo(Cell<RefOrInt>);

impl Hash for Foo {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.get().hash(state);
    }
}

fn main() {
    let non_sync_key = Foo(Cell::new(RefOrInt::Ref(&SOME_INT)));
    let mut emit0 = ParallelEventEmitter::new();
    emit0.add_listener(
        non_sync_key,
        || Ok(()) // dummy listener
    );
    let emit0 = Arc::new(emit0);

    let emit1 = emit0.clone();
    std::thread::spawn(move || {
        let emit1 = emit1;

        emit1.event_names_visitor(|key: &Foo| {
            loop {
                // Repeatedly write Ref(&addr) and Int(0xdeadbeef) into the cell.
                key.0.set(RefOrInt::Ref(&SOME_INT));
                key.0.set(RefOrInt::Int(0xdeadbeef));
            }
        });
    });

    emit0.event_names_visitor(|key: &Foo| {
        loop {
            if let RefOrInt::Ref(addr) = key.0.get() {
                // Hope that between the time we pattern match the object as a
                // `Ref`, it gets written to by the other thread.
                if addr as *const u64 == &SOME_INT as *const u64 {
                    continue;
                }

                println!("Pointer is now: {:p}", addr);
                println!("Dereferencing addr will now segfault: {}", *addr);
            }
        }
    });
}

Output:

Pointer is now: 0xdeadbeef

Terminated with signal 11 (SIGSEGV)

Tested Environment

  • Crate: parallel-event-emitter
  • Version: 0.2.4
  • OS: Ubuntu 18.04.5 LTS
  • Rustc version: rustc 1.50.0 (cb75ad5db 2021-02-10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.