Skip to content

Commit

Permalink
Scope thread-locking of the global context to a thread local
Browse files Browse the repository at this point in the history
  • Loading branch information
TheDan64 committed Nov 16, 2019
1 parent 56d858f commit 808c2a1
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 12 deletions.
23 changes: 19 additions & 4 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use llvm_sys::prelude::{LLVMContextRef, LLVMTypeRef, LLVMValueRef, LLVMDiagnosti
use llvm_sys::ir_reader::LLVMParseIRInContext;
use libc::c_void;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use parking_lot::{Mutex, MutexGuard};

#[llvm_versions(4.0..=latest)]
use crate::attributes::Attribute;
Expand All @@ -24,7 +24,15 @@ use std::marker::PhantomData;
use std::mem::forget;
use std::ops::Deref;
use std::ptr;

use std::thread_local;

// The idea of using a Mutex<Context> here and a thread local'd MutexGuard<Context> in
// GLOBAL_CTX_LOCK is to ensure two things:
// 1) Only one thread has access to the global context at a time.
// 2) The thread has shared access across different points in the thread.
// This is still technically unsafe because another program in the same process
// could also be accessing the global context via the C API. `get_context` has been
// marked unsafe for this reason. Iff this isn't the case then this should be fully safe.
static GLOBAL_CTX: Lazy<Mutex<Context>> = Lazy::new(|| {
let ctx = unsafe {
LLVMGetGlobalContext()
Expand All @@ -33,6 +41,10 @@ static GLOBAL_CTX: Lazy<Mutex<Context>> = Lazy::new(|| {
Mutex::new(Context::new(ctx))
});

thread_local! {
pub(crate) static GLOBAL_CTX_LOCK: Lazy<MutexGuard<'static, Context>> = Lazy::new(GLOBAL_CTX.lock);
}

/// A `Context` is a container for all LLVM entities including `Module`s.
///
/// A `Context` is not thread safe and cannot be shared across threads. Multiple `Context`s
Expand Down Expand Up @@ -93,8 +105,11 @@ impl Context {
/// };
/// ```
// FIXME: Types auto-assigned global ctx might be able to get_context w/o locking mutex
pub unsafe fn get_global() -> &'static Mutex<Context> {
&GLOBAL_CTX
pub unsafe fn get_global<F, R>(func: F) -> R
where
F: FnOnce(&MutexGuard<'static, Context>) -> R,
{
GLOBAL_CTX_LOCK.with(|lazy| func(&*lazy))
}

/// Creates a new `Builder` for a `Context`.
Expand Down
6 changes: 2 additions & 4 deletions src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,10 +1406,8 @@ impl<'ctx> Module<'ctx> {
{
match self.non_global_context {
Some(ctx) => f(ctx),
None => {
let ctx = unsafe { Context::get_global().lock() };

f(&ctx)
None => unsafe {
Context::get_global(|ctx_lock| f(&*ctx_lock))
},
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/values/metadata_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,25 @@ impl<'ctx> MetadataValue<'ctx> {
}
}

/// This function creates a `MetadataValue` node in the global `Context`.
pub fn create_node(values: &[&dyn BasicValue<'ctx>]) -> Self {
let mut tuple_values: Vec<LLVMValueRef> = values.iter()
.map(|val| val.as_value_ref())
.collect();
let metadata_value = unsafe {
let metadata_value = Context::get_global(|_ctx| unsafe {
LLVMMDNode(tuple_values.as_mut_ptr(), tuple_values.len() as u32)
};
});

MetadataValue::new(metadata_value)
}

/// This function creates a `MetadataValue` string in the global `Context`.
pub fn create_string(string: &str) -> Self {
let c_string = CString::new(string).expect("Conversion to CString failed unexpectedly");

let metadata_value = unsafe {
let metadata_value = Context::get_global(|_ctx| unsafe {
LLVMMDString(c_string.as_ptr(), string.len() as u32)
};
});

MetadataValue::new(metadata_value)
}
Expand Down

0 comments on commit 808c2a1

Please sign in to comment.