Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental use of constructor functions to call init #334

Merged
merged 4 commits into from
Jun 1, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 59 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
//! There is a large number of releases for libcurl, all with different sets of
//! capabilities. Robust programs may wish to inspect `Version::get()` to test
//! what features are implemented in the linked build of libcurl at runtime.
//!
//! # Initialization
//!
//! The underlying libcurl library must be initialized before use and has
//! certain requirements on how this is done. Check the documentation for
//! [`init`] for more details.

#![deny(missing_docs, missing_debug_implementations)]
#![doc(html_root_url = "https://docs.rs/curl/0.4")]
Expand Down Expand Up @@ -78,34 +84,63 @@ mod panic;

/// Initializes the underlying libcurl library.
///
/// It's not required to call this before the library is used, but it's
/// recommended to do so as soon as the program starts.
/// The underlying libcurl library must be initialized before use, and must be
/// done so on the main thread before any other threads are created by the
/// program. This crate will do this for you automatically in the following
/// scenarios:
///
/// - Creating a new [`Easy`][easy::Easy] or [`Multi`][multi::Multi] handle
/// - At program startup on Windows, macOS, Linux, Android, or FreeBSD systems
///
/// This should be sufficient for most applications and scenarios, but in any
/// other case, it is strongly recommended that you call this function manually
/// as soon as your program starts.
///
/// Calling this function more than once is harmless and has no effect.
#[inline]
pub fn init() {
/// Used to prevent concurrent or duplicate initialization.
static INIT: Once = Once::new();
INIT.call_once(|| {
platform_init();
unsafe {
assert_eq!(curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL), 0);
}

// Note that we explicitly don't schedule a call to
// `curl_global_cleanup`. The documentation for that function says
//
// > You must not call it when any other thread in the program (i.e. a
// > thread sharing the same memory) is running. This doesn't just mean
// > no other thread that is using libcurl.
//
// We can't ever be sure of that, so unfortunately we can't call the
// function.
});

#[cfg(need_openssl_init)]
fn platform_init() {
openssl_sys::init();

/// An exported constructor function. On supported platforms, this will be
/// invoked automatically before the program's `main` is called.
#[cfg_attr(
any(target_os = "linux", target_os = "freebsd", target_os = "android"),
link_section = ".init_array"
)]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
static INIT_CTOR: extern "C" fn() = init_inner;

/// This is the body of our constructor function.
#[cfg_attr(
any(target_os = "linux", target_os = "android"),
link_section = ".text.startup"
)]
extern "C" fn init_inner() {
INIT.call_once(|| {
#[cfg(need_openssl_init)]
openssl_sys::init();

unsafe {
assert_eq!(curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL), 0);
}

// Note that we explicitly don't schedule a call to
// `curl_global_cleanup`. The documentation for that function says
//
// > You must not call it when any other thread in the program (i.e.
// > a thread sharing the same memory) is running. This doesn't just
// > mean no other thread that is using libcurl.
//
// We can't ever be sure of that, so unfortunately we can't call the
// function.
});
}

#[cfg(not(need_openssl_init))]
fn platform_init() {}
// We invoke our init function through our static to ensure the symbol isn't
// optimized away by a bug: https://github.com/rust-lang/rust/issues/47384
INIT_CTOR();
}

unsafe fn opt_str<'a>(ptr: *const libc::c_char) -> Option<&'a str> {
Expand Down