You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: event.rs
    path of file: ./repos/arrayfire-rust/src/core
    
    the code of the file until where you have to start completion: use super::defines::AfError;
use super::error::HANDLE_ERROR;
use super::util::af_event;

use libc::c_int;
use std::default::Default;

extern "C" {
    fn af_create_event(out: *mut af_event) -> c_int;
    fn af_delete_event(out: af_event) -> c_int;
    fn af_mark_event(out: af_event) -> c_int;
    fn af_enqueue_wait_event(out: af_event) -> c_int;
    fn af_block_event(out: af_event) -> c_int;
}

/// RAII construct to manage ArrayFire events
///
/// ## Sharing Across Threads
///
/// While sharing an Event with other threads, just move it across threads.
pub struct Event {
    event_handle: af_event,
}

unsafe impl Send for Event {}
// No borrowed references are to be shared for Events, hence no sync trait

impl Default for Event {
    fn default() -> Self {
        let mut temp: af_event = std::ptr::null_mut();

        let err_val = unsafe { af_create_event(&mut temp as *mut af_event) };
        HANDLE_ERROR(AfError::from(err_val));

        Self { event_handle: temp }
    }
}

impl Event {
    /// Marks the event on the active computation queue.
    ///
    /// If the event is enqueued/waited on later, any operations that are currently
    /// enqueued on the event queue will be completed before any events that are
    /// enqueued after the call to enqueue
    pub fn mark(&self) {
        let err_val = unsafe { af_mark_event(self.event_handle as af_event) };
  
    
    fn event_block() {
        // This code example will try to compute the following expression
        // using data-graph approach using threads, evens for illustration.
        //
        // (a * (b + c))^(d - 2)
        //
        // ANCHOR: event_block

        // Set active GPU/device on main thread on which
        // subsequent Array objects are created
        set_device(0);
        info();

        let a = randu!(10, 10);
        let b = randu!(10, 10);
        let c = randu!(10, 10);
        let d = randu!(10, 10);

        let (tx, rx) = mpsc::channel();

        thread::spawn(move || {
            set_device(0);

            let add_event = Event::default();

            let add_res = b + c;

            add_event.mark();
            tx.send((add_res, add_event)).unwrap();

            thread::sleep(std::time::Duration::new(10, 0));

            let sub_event = Event::default();

            let sub_res = d - 2;

            sub_event.mark();
            tx.send((sub_res, sub_event)).unwrap();
        });

        let (add_res, add_event) = rx.recv().unwrap();

        println!("Got first message, waiting for addition result ...");
        thread::sleep(std::time::Duration::new(5, 0));
        // Perhaps, do some other tasks
        add_event.block();

        println!("Got addition result, now scaling it ... ");
        let scaled = a * add_res;

        let (sub_res, sub_event) = rx.recv().unwrap();

        println!("Got message, waiting for subtraction result ...");
        thread::sleep(std::time::Duration::new(5, 0));
        // Perhaps, do some other tasks
        sub_event.block();

        let fin_res = pow(&scaled, &sub_res, false);

        af_print!(
            "Final result of the expression: ((a * (b + c))^(d - 2))",
            &fin_res
        );

        // ANCHOR_END: event_block
    }