Skip to content

Commit e61937a

Browse files
committed
auto merge of #11187 : alexcrichton/rust/once, r=brson
Rationale can be found in the first commit, but this is basically the same thing as `pthread_once`
2 parents 02cec05 + c22fed9 commit e61937a

File tree

7 files changed

+176
-56
lines changed

7 files changed

+176
-56
lines changed

src/libnative/io/net.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,19 +205,14 @@ pub fn init() {
205205
}
206206

207207
unsafe {
208-
use std::unstable::mutex::{Mutex, MUTEX_INIT};
209-
static mut LOCK: Mutex = MUTEX_INIT;
210-
static mut INITIALIZED: bool = false;
211-
if INITIALIZED { return }
212-
LOCK.lock();
213-
if !INITIALIZED {
208+
use std::unstable::mutex::{Once, ONCE_INIT};
209+
static mut INIT: Once = ONCE_INIT;
210+
INIT.doit(|| {
214211
let mut data: WSADATA = intrinsics::init();
215212
let ret = WSAStartup(0x202, // version 2.2
216213
&mut data);
217214
assert_eq!(ret, 0);
218-
INITIALIZED = true;
219-
}
220-
LOCK.unlock();
215+
});
221216
}
222217
}
223218

src/librustc/back/link.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,8 @@ pub mod write {
309309
}
310310

311311
unsafe fn configure_llvm(sess: Session) {
312-
use std::unstable::mutex::{MUTEX_INIT, Mutex};
313-
static mut LOCK: Mutex = MUTEX_INIT;
314-
static mut CONFIGURED: bool = false;
312+
use std::unstable::mutex::{Once, ONCE_INIT};
313+
static mut INIT: Once = ONCE_INIT;
315314

316315
// Copy what clan does by turning on loop vectorization at O2 and
317316
// slp vectorization at O3
@@ -340,8 +339,7 @@ pub mod write {
340339
add(*arg);
341340
}
342341

343-
LOCK.lock();
344-
if !CONFIGURED {
342+
INIT.doit(|| {
345343
llvm::LLVMInitializePasses();
346344

347345
// Only initialize the platforms supported by Rust here, because
@@ -368,9 +366,7 @@ pub mod write {
368366

369367
llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
370368
llvm_args.as_ptr());
371-
CONFIGURED = true;
372-
}
373-
LOCK.unlock();
369+
});
374370
}
375371

376372
unsafe fn populate_llvm_passes(fpm: lib::llvm::PassManagerRef,

src/librustc/lib/llvm.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,15 +1459,16 @@ pub mod llvm {
14591459
BufferName: *c_char)
14601460
-> MemoryBufferRef;
14611461

1462+
pub fn LLVMIsMultithreaded() -> Bool;
1463+
pub fn LLVMStartMultithreaded() -> Bool;
1464+
14621465
/** Returns a string describing the last error caused by an LLVMRust*
14631466
call. */
14641467
pub fn LLVMRustGetLastError() -> *c_char;
14651468

14661469
/// Print the pass timings since static dtors aren't picking them up.
14671470
pub fn LLVMRustPrintPassTimings();
14681471

1469-
pub fn LLVMRustStartMultithreading() -> bool;
1470-
14711472
pub fn LLVMStructCreateNamed(C: ContextRef, Name: *c_char) -> TypeRef;
14721473

14731474
pub fn LLVMStructSetBody(StructTy: TypeRef,

src/librustc/middle/trans/base.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3189,8 +3189,21 @@ pub fn trans_crate(sess: session::Session,
31893189
analysis: &CrateAnalysis,
31903190
output: &Path) -> CrateTranslation {
31913191
// Before we touch LLVM, make sure that multithreading is enabled.
3192-
if unsafe { !llvm::LLVMRustStartMultithreading() } {
3193-
sess.bug("couldn't enable multi-threaded LLVM");
3192+
unsafe {
3193+
use std::unstable::mutex::{Once, ONCE_INIT};
3194+
static mut INIT: Once = ONCE_INIT;
3195+
static mut POISONED: bool = false;
3196+
INIT.doit(|| {
3197+
if llvm::LLVMStartMultithreaded() != 1 {
3198+
// use an extra bool to make sure that all future usage of LLVM
3199+
// cannot proceed despite the Once not running more than once.
3200+
POISONED = true;
3201+
}
3202+
});
3203+
3204+
if POISONED {
3205+
sess.bug("couldn't enable multi-threaded LLVM");
3206+
}
31943207
}
31953208

31963209
let mut symbol_hasher = Sha256::new();

src/libstd/rt/local_ptr.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,30 +160,20 @@ pub mod native {
160160
use option::{Option, Some, None};
161161
use ptr;
162162
use tls = rt::thread_local_storage;
163-
use unstable::mutex::{Mutex, MUTEX_INIT};
164163

165-
static mut LOCK: Mutex = MUTEX_INIT;
166-
static mut INITIALIZED: bool = false;
167164
static mut RT_TLS_KEY: tls::Key = -1;
168165

169166
/// Initialize the TLS key. Other ops will fail if this isn't executed
170167
/// first.
171168
pub fn init() {
172169
unsafe {
173-
LOCK.lock();
174-
if !INITIALIZED {
175-
tls::create(&mut RT_TLS_KEY);
176-
INITIALIZED = true;
177-
}
178-
LOCK.unlock();
170+
tls::create(&mut RT_TLS_KEY);
179171
}
180172
}
181173

182174
pub unsafe fn cleanup() {
183-
rtassert!(INITIALIZED);
175+
rtassert!(RT_TLS_KEY != -1);
184176
tls::destroy(RT_TLS_KEY);
185-
LOCK.destroy();
186-
INITIALIZED = false;
187177
}
188178

189179
/// Give a pointer to thread-local storage.

src/libstd/unstable/mutex.rs

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
4848
#[allow(non_camel_case_types)];
4949

50+
use int;
5051
use libc::c_void;
5152
use sync::atomics;
5253

@@ -315,10 +316,156 @@ mod imp {
315316
}
316317
}
317318

319+
/// A type which can be used to run a one-time global initialization. This type
320+
/// is *unsafe* to use because it is built on top of the `Mutex` in this module.
321+
/// It does not know whether the currently running task is in a green or native
322+
/// context, and a blocking mutex should *not* be used under normal
323+
/// circumstances on a green task.
324+
///
325+
/// Despite its unsafety, it is often useful to have a one-time initialization
326+
/// routine run for FFI bindings or related external functionality. This type
327+
/// can only be statically constructed with the `ONCE_INIT` value.
328+
///
329+
/// # Example
330+
///
331+
/// ```rust
332+
/// use std::unstable::mutex::{Once, ONCE_INIT};
333+
///
334+
/// static mut START: Once = ONCE_INIT;
335+
/// unsafe {
336+
/// START.doit(|| {
337+
/// // run initialization here
338+
/// });
339+
/// }
340+
/// ```
341+
pub struct Once {
342+
priv mutex: Mutex,
343+
priv cnt: atomics::AtomicInt,
344+
priv lock_cnt: atomics::AtomicInt,
345+
}
346+
347+
/// Initialization value for static `Once` values.
348+
pub static ONCE_INIT: Once = Once {
349+
mutex: MUTEX_INIT,
350+
cnt: atomics::INIT_ATOMIC_INT,
351+
lock_cnt: atomics::INIT_ATOMIC_INT,
352+
};
353+
354+
impl Once {
355+
/// Perform an initialization routine once and only once. The given closure
356+
/// will be executed if this is the first time `doit` has been called, and
357+
/// otherwise the routine will *not* be invoked.
358+
///
359+
/// This method will block the calling *os thread* if another initialization
360+
/// routine is currently running.
361+
///
362+
/// When this function returns, it is guaranteed that some initialization
363+
/// has run and completed (it may not be the closure specified).
364+
pub fn doit(&mut self, f: ||) {
365+
// Implementation-wise, this would seem like a fairly trivial primitive.
366+
// The stickler part is where our mutexes currently require an
367+
// allocation, and usage of a `Once` should't leak this allocation.
368+
//
369+
// This means that there must be a deterministic destroyer of the mutex
370+
// contained within (because it's not needed after the initialization
371+
// has run).
372+
//
373+
// The general scheme here is to gate all future threads once
374+
// initialization has completed with a "very negative" count, and to
375+
// allow through threads to lock the mutex if they see a non negative
376+
// count. For all threads grabbing the mutex, exactly one of them should
377+
// be responsible for unlocking the mutex, and this should only be done
378+
// once everyone else is done with the mutex.
379+
//
380+
// This atomicity is achieved by swapping a very negative value into the
381+
// shared count when the initialization routine has completed. This will
382+
// read the number of threads which will at some point attempt to
383+
// acquire the mutex. This count is then squirreled away in a separate
384+
// variable, and the last person on the way out of the mutex is then
385+
// responsible for destroying the mutex.
386+
//
387+
// It is crucial that the negative value is swapped in *after* the
388+
// initialization routine has completed because otherwise new threads
389+
// calling `doit` will return immediately before the initialization has
390+
// completed.
391+
392+
let prev = self.cnt.fetch_add(1, atomics::SeqCst);
393+
if prev < 0 {
394+
// Make sure we never overflow, we'll never have int::min_value
395+
// simultaneous calls to `doit` to make this value go back to 0
396+
self.cnt.store(int::min_value, atomics::SeqCst);
397+
return
398+
}
399+
400+
// If the count is negative, then someone else finished the job,
401+
// otherwise we run the job and record how many people will try to grab
402+
// this lock
403+
unsafe { self.mutex.lock() }
404+
if self.cnt.load(atomics::SeqCst) > 0 {
405+
f();
406+
let prev = self.cnt.swap(int::min_value, atomics::SeqCst);
407+
self.lock_cnt.store(prev, atomics::SeqCst);
408+
}
409+
unsafe { self.mutex.unlock() }
410+
411+
// Last one out cleans up after everyone else, no leaks!
412+
if self.lock_cnt.fetch_add(-1, atomics::SeqCst) == 1 {
413+
unsafe { self.mutex.destroy() }
414+
}
415+
}
416+
}
417+
318418
#[cfg(test)]
319419
mod test {
320-
use super::{Mutex, MUTEX_INIT};
420+
use prelude::*;
421+
321422
use rt::thread::Thread;
423+
use super::{ONCE_INIT, Once, Mutex, MUTEX_INIT};
424+
use task;
425+
426+
#[test]
427+
fn smoke_once() {
428+
static mut o: Once = ONCE_INIT;
429+
let mut a = 0;
430+
unsafe { o.doit(|| a += 1); }
431+
assert_eq!(a, 1);
432+
unsafe { o.doit(|| a += 1); }
433+
assert_eq!(a, 1);
434+
}
435+
436+
#[test]
437+
fn stampede_once() {
438+
static mut o: Once = ONCE_INIT;
439+
static mut run: bool = false;
440+
441+
let (p, c) = SharedChan::new();
442+
for _ in range(0, 10) {
443+
let c = c.clone();
444+
do spawn {
445+
for _ in range(0, 4) { task::deschedule() }
446+
unsafe {
447+
o.doit(|| {
448+
assert!(!run);
449+
run = true;
450+
});
451+
assert!(run);
452+
}
453+
c.send(());
454+
}
455+
}
456+
457+
unsafe {
458+
o.doit(|| {
459+
assert!(!run);
460+
run = true;
461+
});
462+
assert!(run);
463+
}
464+
465+
for _ in range(0, 10) {
466+
p.recv();
467+
}
468+
}
322469

323470
#[test]
324471
fn somke_lock() {

src/rustllvm/RustWrapper.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -149,28 +149,6 @@ extern "C" LLVMValueRef LLVMInlineAsm(LLVMTypeRef Ty,
149149
IsAlignStack, (InlineAsm::AsmDialect) Dialect));
150150
}
151151

152-
/**
153-
* This function is intended to be a threadsafe interface into enabling a
154-
* multithreaded LLVM. This is invoked at the start of the translation phase of
155-
* compilation to ensure that LLVM is ready.
156-
*
157-
* All of trans properly isolates LLVM with the use of a different
158-
* LLVMContextRef per task, thus allowing parallel compilation of different
159-
* crates in the same process. At the time of this writing, the use case for
160-
* this is unit tests for rusti, but there are possible other applications.
161-
*/
162-
extern "C" bool LLVMRustStartMultithreading() {
163-
static Mutex lock;
164-
bool ret = true;
165-
assert(lock.acquire());
166-
if (!LLVMIsMultithreaded()) {
167-
ret = LLVMStartMultithreaded();
168-
}
169-
assert(lock.release());
170-
return ret;
171-
}
172-
173-
174152
typedef DIBuilder* DIBuilderRef;
175153

176154
template<typename DIT>

0 commit comments

Comments
 (0)