@@ -495,21 +495,78 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
495
495
Context.getRecoverableErrorHandler ()(std::move (e));
496
496
}
497
497
498
- Error DWARFUnit::tryExtractDIEsIfNeeded (bool CUDieOnly) {
499
- if ((CUDieOnly && !DieArray.empty ()) ||
500
- DieArray.size () > 1 )
501
- return Error::success (); // Already parsed.
498
+ static bool DoubleCheckedRWLocker (llvm::sys::RWMutex &Mutex,
499
+ const std::function<bool ()> &reader,
500
+ const std::function<void()> &writer) {
501
+ {
502
+ llvm::sys::ScopedReader Lock (Mutex);
503
+ if (reader ())
504
+ return true ;
505
+ }
506
+ llvm::sys::ScopedWriter Lock (Mutex);
507
+ if (reader ())
508
+ return true ;
509
+ // If we get here, then the reader function returned false. This means that
510
+ // no one else is currently writing to this data structure and it's safe for
511
+ // us to write to it now. The scoped writer lock guarantees there are no
512
+ // other readers or writers at this point.
513
+ writer ();
514
+ return false ;
515
+ }
502
516
503
- bool HasCUDie = !DieArray.empty ();
504
- extractDIEsToVector (!HasCUDie, !CUDieOnly, DieArray);
517
+ // Helper to safely check if the Compile-Unit DIE has been extracted already.
518
+ // If not, then extract it, and return false, indicating that it was *not*
519
+ // already extracted.
520
+ bool DWARFUnit::extractCUDieIfNeeded (bool CUDieOnly, bool &HasCUDie) {
521
+ return DoubleCheckedRWLocker (
522
+ ExtractCUDieMutex,
523
+ // Calculate if the CU DIE has been extracted already.
524
+ [&]() {
525
+ return ((CUDieOnly && !DieArray.empty ()) || DieArray.size () > 1 );
526
+ },
527
+ // Lambda to extract the CU DIE.
528
+ [&]() {
529
+ HasCUDie = !DieArray.empty ();
530
+ extractDIEsToVector (!HasCUDie, !CUDieOnly, DieArray);
531
+ });
532
+ }
505
533
506
- if (DieArray.empty ())
507
- return Error::success ();
534
+ // Helper to safely check if the non-Compile-Unit DIEs have been parsed
535
+ // already. If they haven't been parsed, go ahead and parse them.
536
+ Error DWARFUnit::extractNonCUDIEsIfNeeded (bool HasCUDie) {
537
+ Error Result = Error::success ();
538
+ DoubleCheckedRWLocker (
539
+ ExtractNonCUDIEsMutex,
540
+ // Lambda to check if all DIEs have been extracted already.
541
+ [=]() { return (DieArray.empty () || HasCUDie); },
542
+ // Lambda to extract all the DIEs using the helper function
543
+ [&]() {
544
+ if (Error E = extractNonCUDIEsHelper ()) {
545
+ // Consume the success placeholder and save the actual error
546
+ consumeError (std::move (Result));
547
+ Result = std::move (E);
548
+ }
549
+ });
550
+ return Result;
551
+ }
508
552
509
- // If CU DIE was just parsed, copy several attribute values from it.
510
- if (HasCUDie)
553
+ Error DWARFUnit::tryExtractDIEsIfNeeded (bool CUDieOnly) {
554
+ // Acquire the FreeDIEsMutex lock (in read-mode) to prevent the Compile Unit
555
+ // DIE from being freed by a thread calling clearDIEs() after the CU DIE was
556
+ // parsed, but before the rest of the DIEs are parsed, as there are no other
557
+ // locks held during that brief period.
558
+ llvm::sys::ScopedReader FreeLock (FreeDIEsMutex);
559
+ bool HasCUDie = false ;
560
+ if (extractCUDieIfNeeded (CUDieOnly, HasCUDie))
511
561
return Error::success ();
562
+ // Right here is where the above-mentioned race condition exists.
563
+ return extractNonCUDIEsIfNeeded (HasCUDie);
564
+ }
512
565
566
+ // Helper used from the tryExtractDIEsIfNeeded function: it must already have
567
+ // acquired the ExtractNonCUDIEsMutex for writing.
568
+ Error DWARFUnit::extractNonCUDIEsHelper () {
569
+ // If CU DIE was just parsed, copy several attribute values from it.
513
570
DWARFDie UnitDie (this , &DieArray[0 ]);
514
571
if (std::optional<uint64_t > DWOId =
515
572
toUnsigned (UnitDie.find (DW_AT_GNU_dwo_id)))
@@ -653,6 +710,10 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
653
710
}
654
711
655
712
void DWARFUnit::clearDIEs (bool KeepCUDie) {
713
+ // We need to acquire the FreeDIEsMutex lock in write-mode, because we are
714
+ // going to free the DIEs, when other threads might be trying to create them.
715
+ llvm::sys::ScopedWriter FreeLock (FreeDIEsMutex);
716
+
656
717
// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
657
718
// shrink_to_fit() is a *non-binding* request to reduce capacity() to size().
658
719
// It depends on the implementation whether the request is fulfilled.
0 commit comments