@@ -244,45 +244,127 @@ dbghelp! {
244
244
}
245
245
}
246
246
247
- pub struct Init ;
247
+ pub struct Init {
248
+ lock : HANDLE ,
249
+ }
248
250
249
- /// Unsafe because this requires external synchronization, must be done
250
- /// inside of the same lock as all other backtrace operations .
251
+ /// Initialize all support necessary to access `dbghelp` API functions from this
252
+ /// crate .
251
253
///
252
- /// Note that the `Dbghelp` returned must also be dropped within the same
253
- /// lock.
254
+ /// Note that this function is **safe**, it internally has its own
255
+ /// synchronization. Also note that it is safe to call this function multiple
256
+ /// times recursively.
254
257
#[ cfg( all( windows, feature = "dbghelp" ) ) ]
255
- pub unsafe fn init ( ) -> Result < Init , ( ) > {
256
- // Calling `SymInitializeW` is quite expensive, so we only do so once per
257
- // process.
258
- static mut INITIALIZED : bool = false ;
259
- if INITIALIZED {
260
- return Ok ( Init ) ;
261
- }
258
+ pub fn init ( ) -> Result < Init , ( ) > {
259
+ use core:: sync:: atomic:: { AtomicUsize , Ordering :: SeqCst } ;
262
260
263
- // Actually load `dbghelp.dll` into the process here, returning an error if
264
- // that fails.
265
- DBGHELP . ensure_open ( ) ?;
261
+ unsafe {
262
+ // First thing we need to do is to synchronize this function. This can
263
+ // be called concurrently from other threads or recursively within one
264
+ // thread. Note that it's trickier than that though because what we're
265
+ // using here, `dbghelp`, *also* needs to be synchronized with all other
266
+ // callers to `dbghelp` in this process.
267
+ //
268
+ // Typically there aren't really that many calls to `dbghelp` within the
269
+ // same process and we can probably safely assume that we're the only
270
+ // ones accessing it. There is, however, one primary other user we have
271
+ // to worry about which is ironically ourselves, but in the standard
272
+ // library. The Rust standard library depends on this crate for
273
+ // backtrace support, and this crate also exists on crates.io. This
274
+ // means that if the standard library is printing a panic backtrace it
275
+ // may race with this crate coming from crates.io, causing segfaults.
276
+ //
277
+ // To help solve this synchronization problem we employ a
278
+ // Windows-specific trick here (it is, after all, a Windows-specific
279
+ // restriction about synchronization). We create a *session-local* named
280
+ // mutex to protect this call. The intention here is that the standard
281
+ // library and this crate don't have to share Rust-level APIs to
282
+ // synchronize here but can instead work behind the scenes to make sure
283
+ // they're synchronizing with one another. That way when this function
284
+ // is called through the standard library or through crates.io we can be
285
+ // sure that the same mutex is being acquired.
286
+ //
287
+ // So all of that is to say that the first thing we do here is we
288
+ // atomically create a `HANDLE` which is a named mutex on Windows. We
289
+ // synchronize a bit with other threads sharing this function
290
+ // specifically and ensure that only one handle is created per instance
291
+ // of this function. Note that the handle is never closed once it's
292
+ // stored in the global.
293
+ //
294
+ // After we've actually go the lock we simply acquire it, and our `Init`
295
+ // handle we hand out will be responsible for dropping it eventually.
296
+ static LOCK : AtomicUsize = AtomicUsize :: new ( 0 ) ;
297
+ let mut lock = LOCK . load ( SeqCst ) ;
298
+ if lock == 0 {
299
+ lock = CreateMutexA (
300
+ ptr:: null_mut ( ) ,
301
+ 0 ,
302
+ "Local\\ RustBacktraceMutex\0 " . as_ptr ( ) as _ ,
303
+ ) as usize ;
304
+ if lock == 0 {
305
+ return Err ( ( ) ) ;
306
+ }
307
+ if let Err ( other) = LOCK . compare_exchange ( 0 , lock, SeqCst , SeqCst ) {
308
+ debug_assert ! ( other != 0 ) ;
309
+ CloseHandle ( lock as HANDLE ) ;
310
+ lock = other;
311
+ }
312
+ }
313
+ debug_assert ! ( lock != 0 ) ;
314
+ let lock = lock as HANDLE ;
315
+ let r = WaitForSingleObjectEx ( lock, INFINITE , FALSE ) ;
316
+ debug_assert_eq ! ( r, 0 ) ;
317
+ let ret = Init { lock } ;
318
+
319
+ // Ok, phew! Now that we're all safely synchronized, let's actually
320
+ // start processing everything. First up we need to ensure that
321
+ // `dbghelp.dll` is actually loaded in this process. We do this
322
+ // dynamically to avoid a static dependency. This has historically been
323
+ // done to work around weird linking issues and is intended at making
324
+ // binaries a bit more portable since this is largely just a debugging
325
+ // utility.
326
+ //
327
+ // Once we've opened `dbghelp.dll` we need to call some initialization
328
+ // functions in it, and that's detailed more below. We only do this
329
+ // once, though, so we've got a global boolean indicating whether we're
330
+ // done yet or not.
331
+ DBGHELP . ensure_open ( ) ?;
266
332
267
- let orig = DBGHELP . SymGetOptions ( ) . unwrap ( ) ( ) ;
333
+ static mut INITIALIZED : bool = false ;
334
+ if INITIALIZED {
335
+ return Ok ( ret) ;
336
+ }
268
337
269
- // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
270
- // according to MSVC's own docs about this: "This is the fastest, most
271
- // efficient way to use the symbol handler.", so let's do that!
272
- DBGHELP . SymSetOptions ( ) . unwrap ( ) ( orig | SYMOPT_DEFERRED_LOADS ) ;
338
+ let orig = DBGHELP . SymGetOptions ( ) . unwrap ( ) ( ) ;
273
339
274
- // Actually initialize symbols with MSVC. Note that this can fail, but we
275
- // ignore it. There's not a ton of prior art for this per se, but LLVM
276
- // internally seems to ignore the return value here and one of the
277
- // sanitizer libraries in LLVM prints a scary warning if this fails but
278
- // basically ignores it in the long run.
279
- //
280
- // One case this comes up a lot for Rust is that the standard library and
281
- // this crate on crates.io both want to compete for `SymInitializeW`. The
282
- // standard library historically wanted to initialize then cleanup most of
283
- // the time, but now that it's using this crate it means that someone will
284
- // get to initialization first and the other will pick up that
285
- // initialization.
286
- DBGHELP . SymInitializeW ( ) . unwrap ( ) ( GetCurrentProcess ( ) , ptr:: null_mut ( ) , TRUE ) ;
287
- Ok ( Init )
340
+ // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
341
+ // according to MSVC's own docs about this: "This is the fastest, most
342
+ // efficient way to use the symbol handler.", so let's do that!
343
+ DBGHELP . SymSetOptions ( ) . unwrap ( ) ( orig | SYMOPT_DEFERRED_LOADS ) ;
344
+
345
+ // Actually initialize symbols with MSVC. Note that this can fail, but we
346
+ // ignore it. There's not a ton of prior art for this per se, but LLVM
347
+ // internally seems to ignore the return value here and one of the
348
+ // sanitizer libraries in LLVM prints a scary warning if this fails but
349
+ // basically ignores it in the long run.
350
+ //
351
+ // One case this comes up a lot for Rust is that the standard library and
352
+ // this crate on crates.io both want to compete for `SymInitializeW`. The
353
+ // standard library historically wanted to initialize then cleanup most of
354
+ // the time, but now that it's using this crate it means that someone will
355
+ // get to initialization first and the other will pick up that
356
+ // initialization.
357
+ DBGHELP . SymInitializeW ( ) . unwrap ( ) ( GetCurrentProcess ( ) , ptr:: null_mut ( ) , TRUE ) ;
358
+ INITIALIZED = true ;
359
+ Ok ( ret)
360
+ }
361
+ }
362
+
363
+ impl Drop for Init {
364
+ fn drop ( & mut self ) {
365
+ unsafe {
366
+ let r = ReleaseMutex ( self . lock ) ;
367
+ debug_assert ! ( r != 0 ) ;
368
+ }
369
+ }
288
370
}
0 commit comments