Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module wraps XPCOM threading functions with Rust functions
//! to make it safer and more convenient to call the XPCOM functions.
//! It also provides the Task trait and TaskRunnable struct,
//! which make it easier to dispatch tasks to threads.

mod dispatcher;
pub use dispatcher::{dispatch_background_task, dispatch_local, dispatch_onto, RunnableBuilder};
mod event_loop;
mod executor;
pub use executor::{
    spawn, spawn_blocking, spawn_local, spawn_onto, spawn_onto_blocking, AsyncTask, TaskBuilder,
};

// Expose functions intended to be used only in gtest via this module.
// We don't use a feature gate here to stop the need to compile all crates that
// depend upon `moz_task` twice.
pub mod gtest_only {
    pub use crate::event_loop::spin_event_loop_until;
}

use nserror::nsresult;
use nsstring::{nsACString, nsCString};
use std::{ffi::CStr, marker::PhantomData, mem, ptr};
use xpcom::{
    getter_addrefs,
    interfaces::{nsIEventTarget, nsIRunnable, nsISerialEventTarget, nsISupports, nsIThread},
    AtomicRefcnt, RefCounted, RefPtr, XpCom,
};

extern "C" {
    fn NS_GetCurrentThreadRust(result: *mut *const nsIThread) -> nsresult;
    fn NS_GetMainThreadRust(result: *mut *const nsIThread) -> nsresult;
    fn NS_IsMainThread() -> bool;
    fn NS_NewNamedThreadWithDefaultStackSize(
        name: *const nsACString,
        result: *mut *const nsIThread,
        event: *const nsIRunnable,
    ) -> nsresult;
    fn NS_IsOnCurrentThread(target: *const nsIEventTarget) -> bool;
    fn NS_ProxyReleaseISupports(
        name: *const libc::c_char,
        target: *const nsIEventTarget,
        doomed: *const nsISupports,
        always_proxy: bool,
    );
    fn NS_CreateBackgroundTaskQueue(
        name: *const libc::c_char,
        target: *mut *const nsISerialEventTarget,
    ) -> nsresult;
    fn NS_DispatchBackgroundTask(event: *const nsIRunnable, flags: u32) -> nsresult;
}

pub fn get_current_thread() -> Result<RefPtr<nsIThread>, nsresult> {
    getter_addrefs(|p| unsafe { NS_GetCurrentThreadRust(p) })
}

pub fn get_main_thread() -> Result<RefPtr<nsIThread>, nsresult> {
    getter_addrefs(|p| unsafe { NS_GetMainThreadRust(p) })
}

pub fn is_main_thread() -> bool {
    unsafe { NS_IsMainThread() }
}

// There's no OS requirement that thread names be static, but dynamic thread
// names tend to conceal more than they reveal when processing large numbers of
// crash reports.
pub fn create_thread(name: &'static str) -> Result<RefPtr<nsIThread>, nsresult> {
    getter_addrefs(|p| unsafe {
        NS_NewNamedThreadWithDefaultStackSize(&*nsCString::from(name), p, ptr::null())
    })
}

pub fn is_on_current_thread(target: &nsIEventTarget) -> bool {
    unsafe { NS_IsOnCurrentThread(target) }
}

/// Creates a queue that runs tasks on the background thread pool. The tasks
/// will run in the order they're dispatched, one after the other.
pub fn create_background_task_queue(
    name: &'static CStr,
) -> Result<RefPtr<nsISerialEventTarget>, nsresult> {
    getter_addrefs(|p| unsafe { NS_CreateBackgroundTaskQueue(name.as_ptr(), p) })
}

/// Dispatches a one-shot runnable to an event target, like a thread or a
/// task queue, with the given options.
///
/// This function leaks the runnable if dispatch fails.
///
/// # Safety
///
/// As there is no guarantee that the runnable is actually `Send + Sync`, we
/// can't know that it's safe to dispatch an `nsIRunnable` to any
/// `nsIEventTarget`.
pub unsafe fn dispatch_runnable(
    runnable: &nsIRunnable,
    target: &nsIEventTarget,
    options: DispatchOptions,
) -> Result<(), nsresult> {
    // NOTE: DispatchFromScript performs an AddRef on `runnable` which is
    // why this function leaks on failure.
    target
        .DispatchFromScript(runnable, options.flags())
        .to_result()
}

/// Dispatches a one-shot task runnable to the background thread pool with the
/// given options. The task may run concurrently with other background tasks.
/// If you need tasks to run in a specific order, please create a background
/// task queue using `create_background_task_queue`, and dispatch tasks to it
/// instead.
///
/// This function leaks the runnable if dispatch fails. This avoids a race where
/// a runnable can be destroyed on either the original or target thread, which
/// is important if the runnable holds thread-unsafe members.
///
/// ### Safety
///
/// As there is no guarantee that the runnable is actually `Send + Sync`, we
/// can't know that it's safe to dispatch an `nsIRunnable` to any
/// `nsIEventTarget`.
pub unsafe fn dispatch_background_task_runnable(
    runnable: &nsIRunnable,
    options: DispatchOptions,
) -> Result<(), nsresult> {
    // This eventually calls the non-`already_AddRefed<nsIRunnable>` overload of
    // `nsIEventTarget::Dispatch` (see xpcom/threads/nsIEventTarget.idl#20-25),
    // which adds an owning reference and leaks if dispatch fails.
    NS_DispatchBackgroundTask(runnable, options.flags()).to_result()
}

/// Options to control how task runnables are dispatched.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct DispatchOptions(u32);

impl Default for DispatchOptions {
    #[inline]
    fn default() -> Self {
        DispatchOptions(nsIEventTarget::DISPATCH_NORMAL)
    }
}

impl DispatchOptions {
    /// Creates a blank set of options. The runnable will be dispatched using
    /// the default mode.
    #[inline]
    pub fn new() -> Self {
        DispatchOptions::default()
    }

    /// Indicates whether or not the dispatched runnable may block its target
    /// thread by waiting on I/O. If `true`, the runnable may be dispatched to a
    /// dedicated thread pool, leaving the main pool free for CPU-bound tasks.
    #[inline]
    pub fn may_block(self, may_block: bool) -> DispatchOptions {
        const FLAG: u32 = nsIEventTarget::DISPATCH_EVENT_MAY_BLOCK;
        if may_block {
            DispatchOptions(self.flags() | FLAG)
        } else {
            DispatchOptions(self.flags() & !FLAG)
        }
    }

    /// Specifies that the dispatch is occurring from a running event that was
    /// dispatched to the same event target, and that event is about to finish.
    ///
    /// A thread pool can use this as an optimization hint to not spin up
    /// another thread, since the current thread is about to become idle.
    ///
    /// Setting this flag is unsafe, as it may only be used from the target
    /// event target when the event is about to finish.
    #[inline]
    pub unsafe fn at_end(self, may_block: bool) -> DispatchOptions {
        const FLAG: u32 = nsIEventTarget::DISPATCH_AT_END;
        if may_block {
            DispatchOptions(self.flags() | FLAG)
        } else {
            DispatchOptions(self.flags() & !FLAG)
        }
    }

    /// Returns the set of bitflags to pass to `DispatchFromScript`.
    #[inline]
    fn flags(self) -> u32 {
        self.0
    }
}

/// A task represents an operation that asynchronously executes on a target
/// thread, and returns its result to the original thread.
///
/// # Alternatives
///
/// This trait is no longer necessary for basic tasks to be dispatched to
/// another thread with a callback on the originating thread. `moz_task` now has
/// a series of more rust-like primitives which can be used instead. For
/// example, it may be preferable to use the async executor over `Task`:
///
/// ```ignore
/// // Spawn a task onto the background task pool, and capture the result of its
/// // execution.
/// let bg_task = moz_task::spawn("Example", async move {
///     do_background_work(captured_state)
/// });
///
/// // Spawn another task on the calling thread which will await on the result
/// // of the async operation, and invoke a non-Send callback. This task won't
/// // be awaited on, so needs to be `detach`-ed.
/// moz_task::spawn_local("Example", async move {
///     callback.completed(bg_task.await);
/// })
/// .detach();
/// ```
///
/// If no result is needed, the task returned from `spawn` may be also detached
/// directly.
pub trait Task {
    // FIXME: These could accept `&mut`.
    fn run(&self);
    fn done(&self) -> Result<(), nsresult>;
}

pub struct TaskRunnable {
    name: &'static str,
    task: Box<dyn Task + Send + Sync>,
}

impl TaskRunnable {
    // XXX: Fixme: clean up this old API. (bug 1744312)
    pub fn new(
        name: &'static str,
        task: Box<dyn Task + Send + Sync>,
    ) -> Result<TaskRunnable, nsresult> {
        Ok(TaskRunnable { name, task })
    }

    pub fn dispatch(self, target: &nsIEventTarget) -> Result<(), nsresult> {
        self.dispatch_with_options(target, DispatchOptions::default())
    }

    pub fn dispatch_with_options(
        self,
        target: &nsIEventTarget,
        options: DispatchOptions,
    ) -> Result<(), nsresult> {
        // Perform `task.run()` on a background thread.
        let task = self.task;
        let handle = TaskBuilder::new(self.name, async move {
            task.run();
            task
        })
        .options(options)
        .spawn_onto(target);

        // Run `task.done()` on the starting thread once the background thread
        // is done with the task.
        spawn_local(self.name, async move {
            let task = handle.await;
            let _ = task.done();
        })
        .detach();
        Ok(())
    }

    pub fn dispatch_background_task_with_options(
        self,
        options: DispatchOptions,
    ) -> Result<(), nsresult> {
        // Perform `task.run()` on a background thread.
        let task = self.task;
        let handle = TaskBuilder::new(self.name, async move {
            task.run();
            task
        })
        .options(options)
        .spawn();

        // Run `task.done()` on the starting thread once the background thread
        // is done with the task.
        spawn_local(self.name, async move {
            let task = handle.await;
            let _ = task.done();
        })
        .detach();
        Ok(())
    }
}

pub type ThreadPtrHandle<T> = RefPtr<ThreadPtrHolder<T>>;

/// A Rust analog to `nsMainThreadPtrHolder` that wraps an `nsISupports` object
/// with thread-safe refcounting. The holder keeps one reference to the wrapped
/// object that's released when the holder's refcount reaches zero.
pub struct ThreadPtrHolder<T: XpCom + 'static> {
    ptr: *const T,
    marker: PhantomData<T>,
    name: &'static CStr,
    owning_thread: RefPtr<nsIThread>,
    refcnt: AtomicRefcnt,
}

unsafe impl<T: XpCom + 'static> Send for ThreadPtrHolder<T> {}
unsafe impl<T: XpCom + 'static> Sync for ThreadPtrHolder<T> {}

unsafe impl<T: XpCom + 'static> RefCounted for ThreadPtrHolder<T> {
    unsafe fn addref(&self) {
        self.refcnt.inc();
    }

    unsafe fn release(&self) {
        let rc = self.refcnt.dec();
        if rc == 0 {
            // Once the holder's count reaches zero, release the wrapped
            // object...
            if !self.ptr.is_null() {
                // The holder can be released on any thread. If we're on the
                // owning thread, we can release the object directly. Otherwise,
                // we need to post a proxy release event to release the object
                // on the owning thread.
                if is_on_current_thread(&self.owning_thread) {
                    (*self.ptr).release()
                } else {
                    NS_ProxyReleaseISupports(
                        self.name.as_ptr(),
                        self.owning_thread.coerce(),
                        self.ptr as *const T as *const nsISupports,
                        false,
                    );
                }
            }
            // ...And deallocate the holder.
            mem::drop(Box::from_raw(self as *const Self as *mut Self));
        }
    }
}

impl<T: XpCom + 'static> ThreadPtrHolder<T> {
    /// Creates a new owning thread pointer holder. Returns an error if the
    /// thread manager has shut down. Panics if `name` isn't a valid C string.
    pub fn new(name: &'static CStr, ptr: RefPtr<T>) -> Result<RefPtr<Self>, nsresult> {
        let owning_thread = get_current_thread()?;
        // Take ownership of the `RefPtr`. This does _not_ decrement its
        // refcount, which is what we want. Once we've released all references
        // to the holder, we'll release the wrapped `RefPtr`.
        let raw: *const T = &*ptr;
        mem::forget(ptr);
        unsafe {
            let boxed = Box::new(ThreadPtrHolder {
                name,
                ptr: raw,
                marker: PhantomData,
                owning_thread,
                refcnt: AtomicRefcnt::new(),
            });
            Ok(RefPtr::from_raw(Box::into_raw(boxed)).unwrap())
        }
    }

    /// Returns the wrapped object's owning thread.
    pub fn owning_thread(&self) -> &nsIThread {
        &self.owning_thread
    }

    /// Returns the wrapped object if called from the owning thread, or
    /// `None` if called from any other thread.
    pub fn get(&self) -> Option<&T> {
        if is_on_current_thread(&self.owning_thread) && !self.ptr.is_null() {
            unsafe { Some(&*self.ptr) }
        } else {
            None
        }
    }
}

[ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge