Bug 1542528 - Add an `xpcom::c_str` macro for creating static C strings. r=froydnj
authorLina Cambridge <lina@yakshaving.ninja>
Mon, 08 Apr 2019 14:43:35 +0000
changeset 468354 4b5b95e9afa09c40aa81c2510e0fb1c0045ef580
parent 468353 d689bb3db16ea6b533e733efd9773cc65d5f1d47
child 468355 88fd8c3036dcd6b45af7689bcb7080cfa94c0373
push id35835
push useraciure@mozilla.com
push dateMon, 08 Apr 2019 19:00:29 +0000
treeherdermozilla-central@40456af7da1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1542528
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1542528 - Add an `xpcom::c_str` macro for creating static C strings. r=froydnj This is based on the `c_str` macro in `js/rust/src/heap.rs`, but returns a `NulTerminatedCStr` type that can be cast to a pointer. This commit also changes `ThreadPtrHolder::new` to take a `NulTerminatedCStr`, and removes an unnecessary `RefPtr` reference in `is_current_thread`. Differential Revision: https://phabricator.services.mozilla.com/D26429
Cargo.lock
toolkit/components/places/bookmark_sync/src/merger.rs
xpcom/rust/moz_task/Cargo.toml
xpcom/rust/moz_task/src/lib.rs
xpcom/rust/xpcom/src/cstr.rs
xpcom/rust/xpcom/src/lib.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1810,17 +1810,16 @@ name = "moz_cbor"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "moz_task"
 version = "0.1.0"
 dependencies = [
  "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "xpcom 0.1.0",
 ]
 
 [[package]]
 name = "mozilla-central-workspace-hack"
 version = "0.1.0"
--- a/toolkit/components/places/bookmark_sync/src/merger.rs
+++ b/toolkit/components/places/bookmark_sync/src/merger.rs
@@ -149,29 +149,29 @@ impl MergeTask {
                 mozISyncedBookmarksMirrorLogger::LEVEL_WARN => LevelFilter::Warn,
                 mozISyncedBookmarksMirrorLogger::LEVEL_DEBUG => LevelFilter::Debug,
                 mozISyncedBookmarksMirrorLogger::LEVEL_TRACE => LevelFilter::Trace,
                 _ => LevelFilter::Off,
             })
             .unwrap_or(LevelFilter::Off);
         let logger = match logger {
             Some(logger) => Some(ThreadPtrHolder::new(
-                "mozISyncedBookmarksMirrorLogger",
+                c_str!("mozISyncedBookmarksMirrorLogger"),
                 logger,
             )?),
             None => None,
         };
         Ok(MergeTask {
             db: db.clone(),
             max_log_level,
             logger,
             local_time_millis: local_time_seconds * 1000,
             remote_time_millis: remote_time_seconds * 1000,
             weak_uploads,
-            callback: ThreadPtrHolder::new("mozISyncedBookmarksMirrorCallback", callback)?,
+            callback: ThreadPtrHolder::new(c_str!("mozISyncedBookmarksMirrorCallback"), callback)?,
             result: AtomicRefCell::default(),
         })
     }
 }
 
 impl Task for MergeTask {
     fn run(&self) {
         let mut db = self.db.clone();
--- a/xpcom/rust/moz_task/Cargo.toml
+++ b/xpcom/rust/moz_task/Cargo.toml
@@ -2,12 +2,11 @@
 name = "moz_task"
 version = "0.1.0"
 authors = ["Myk Melez <myk@mykzilla.org>"]
 license = "MPL-2.0"
 description = "Rust wrappers around XPCOM threading functions"
 
 [dependencies]
 libc = "0.2"
-memchr = "2.0"
 nserror = { path = "../nserror" }
 nsstring = { path = "../nsstring" }
 xpcom = { path = "../xpcom" }
--- a/xpcom/rust/moz_task/src/lib.rs
+++ b/xpcom/rust/moz_task/src/lib.rs
@@ -3,32 +3,31 @@
  * 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.
 
 extern crate libc;
-extern crate memchr;
 extern crate nserror;
 extern crate nsstring;
 extern crate xpcom;
 
 use nserror::{nsresult, NS_OK};
 use nsstring::{nsACString, nsCString};
 use std::{
     marker::PhantomData,
     mem, ptr,
     sync::atomic::{AtomicBool, Ordering},
 };
 use xpcom::{
     getter_addrefs,
     interfaces::{nsIEventTarget, nsIRunnable, nsISupports, nsIThread},
-    AtomicRefcnt, RefCounted, RefPtr, XpCom, xpcom, xpcom_method,
+    xpcom, xpcom_method, AtomicRefcnt, NulTerminatedCStr, RefCounted, RefPtr, XpCom,
 };
 
 extern "C" {
     fn NS_GetCurrentThreadEventTarget(result: *mut *const nsIThread) -> nsresult;
     fn NS_GetMainThreadEventTarget(result: *mut *const nsIThread) -> nsresult;
     fn NS_IsMainThread() -> bool;
     fn NS_NewNamedThreadWithDefaultStackSize(
         name: *const nsACString,
@@ -57,17 +56,17 @@ pub fn is_main_thread() -> bool {
 }
 
 pub fn create_thread(name: &str) -> Result<RefPtr<nsIThread>, nsresult> {
     getter_addrefs(|p| unsafe {
         NS_NewNamedThreadWithDefaultStackSize(&*nsCString::from(name), p, ptr::null())
     })
 }
 
-pub fn is_current_thread(thread: &RefPtr<nsIThread>) -> bool {
+pub fn is_current_thread(thread: &nsIThread) -> bool {
     unsafe { NS_IsCurrentThread(thread.coerce()) }
 }
 
 /// A task represents an operation that asynchronously executes on a target
 /// thread, and returns its result to the original thread.
 pub trait Task {
     fn run(&self);
     fn done(&self) -> Result<(), nsresult>;
@@ -137,17 +136,17 @@ impl TaskRunnable {
 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 str,
+    name: NulTerminatedCStr,
     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> {
@@ -163,36 +162,34 @@ unsafe impl<T: XpCom + 'static> RefCount
             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_current_thread(&self.owning_thread) {
                     (*self.ptr).release()
                 } else {
-                    let name = self.name.as_bytes() as *const [u8] as *const libc::c_char;
                     NS_ProxyReleaseISupports(
-                        name,
+                        self.name.as_ptr(),
                         self.owning_thread.coerce(),
                         self.ptr as *const T as *const nsISupports,
                         false,
                     );
                 }
             }
             // ...And deallocate the holder.
             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 str, ptr: RefPtr<T>) -> Result<RefPtr<Self>, nsresult> {
-        assert!(memchr::memchr(0, name.as_bytes()).is_none());
+    pub fn new(name: NulTerminatedCStr, 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 {
new file mode 100644
--- /dev/null
+++ b/xpcom/rust/xpcom/src/cstr.rs
@@ -0,0 +1,28 @@
+/* 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/. */
+
+use libc::c_char;
+
+/// Creates a static C string by adding a nul terminator to `$str`.
+#[macro_export]
+macro_rules! c_str {
+    ($str:expr) => {
+        $crate::NulTerminatedCStr(concat!($str, '\0').as_bytes())
+    };
+}
+
+/// A nul-terminated, static C string. This should only be created via the
+/// `c_str` macro.
+pub struct NulTerminatedCStr(pub &'static [u8]);
+
+impl NulTerminatedCStr {
+    /// Returns a raw pointer to this string, asserting that it's
+    /// nul-terminated. This pointer can be passed to any C function expecting a
+    /// `const char*`, or any XPIDL method expecting an `in string`.
+    #[inline]
+    pub fn as_ptr(&self) -> *const c_char {
+        assert_eq!(self.0.last(), Some(&0), "C strings must be nul-terminated");
+        self.0 as *const [u8] as *const c_char
+    }
+}
--- a/xpcom/rust/xpcom/src/lib.rs
+++ b/xpcom/rust/xpcom/src/lib.rs
@@ -23,16 +23,19 @@ extern crate thin_vec;
 extern crate xpcom_macros;
 #[doc(hidden)]
 pub use xpcom_macros::*;
 
 // Helper functions and data structures are exported in the root of the crate.
 mod base;
 pub use base::*;
 
+mod cstr;
+pub use cstr::*;
+
 // Declarative macro to generate XPCOM method stubs.
 mod method;
 pub use method::*;
 
 mod refptr;
 pub use refptr::*;
 
 mod statics;