Index: include/lld/Core/Parallel.h =================================================================== --- include/lld/Core/Parallel.h +++ include/lld/Core/Parallel.h @@ -68,6 +68,41 @@ } }; +/// \brief An implementation of future. std:future and std::promise in +/// libstdc++ had a threading bug; there is a small chance that a call +/// of future::get throws an exception in the normal use case. We want +/// to use our own future implementation until we drop support of old +/// version of libstdc++. +/// https://gcc.gnu.org/ml/gcc-patches/2014-05/msg01389.html +template class Future { +public: + Future() : _hasValue(false) {} + + void set(T &&val) { + assert(!_hasValue); + { + std::unique_lock lock(_mutex); + _val = val; + _hasValue = true; + } + _cond.notify_all(); + } + + T &get() { + std::unique_lock lock(_mutex); + if (_hasValue) + return _val; + _cond.wait(lock, [&] { return _hasValue; }); + return _val; + } + +private: + T _val; + bool _hasValue; + std::mutex _mutex; + std::condition_variable _cond; +}; + /// \brief An abstract class that takes closures and runs them asynchronously. class Executor { public: Index: lib/Core/Resolver.cpp =================================================================== --- lib/Core/Resolver.cpp +++ lib/Core/Resolver.cpp @@ -31,9 +31,12 @@ bool undefAdded = false; for (const DefinedAtom *atom : file.defined()) doDefinedAtom(*atom); - for (const UndefinedAtom *atom : file.undefined()) - if (doUndefinedAtom(*atom)) + for (const UndefinedAtom *atom : file.undefined()) { + if (doUndefinedAtom(*atom)) { undefAdded = true; + maybePreloadArchiveMember(atom->name()); + } + } for (const SharedLibraryAtom *atom : file.sharedLibrary()) doSharedLibraryAtom(*atom); for (const AbsoluteAtom *atom : file.absolute()) @@ -230,6 +233,17 @@ doDefinedAtom(*newAtom); } +// Instantiate an archive file member if there's a file containing a +// defined symbol for a given symbol name. Instantiation is done in a +// different worker thread and has no visible side effect. +void Resolver::maybePreloadArchiveMember(StringRef sym) { + auto it = _archiveMap.find(sym); + if (it == _archiveMap.end()) + return; + ArchiveLibraryFile *archive = it->second; + archive->preload(_ctx.getTaskGroup(), sym); +} + // Returns true if at least one of N previous files has created an // undefined symbol. bool Resolver::undefinesAdded(int begin, int end) { Index: lib/ReaderWriter/FileArchive.cpp =================================================================== --- lib/ReaderWriter/FileArchive.cpp +++ lib/ReaderWriter/FileArchive.cpp @@ -10,6 +10,7 @@ #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/LLVM.h" #include "lld/Core/LinkingContext.h" +#include "lld/Core/Parallel.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/Archive.h" @@ -17,7 +18,6 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" -#include #include #include #include @@ -63,8 +63,9 @@ std::lock_guard lock(_mutex); auto it = _preloaded.find(memberStart); if (it != _preloaded.end()) { - std::future &future = it->second; - return future.get(); + std::unique_ptr> &p = it->second; + Future *future = p.get(); + return future->get(); } } @@ -93,17 +94,13 @@ return; // Instantiate the member - auto *promise = new std::promise; - _preloaded[memberStart] = promise->get_future(); - _promises.push_back(std::unique_ptr>(promise)); + auto *future = new Future(); + _preloaded[memberStart] = std::unique_ptr>(future); group.spawn([=] { std::unique_ptr result; - if (instantiateMember(ci, result)) { - promise->set_value(nullptr); - return; - } - promise->set_value(result.release()); + std::error_code ec = instantiateMember(ci, result); + future->set(ec ? nullptr : result.release()); }); } @@ -269,8 +266,7 @@ atom_collection_vector _absoluteAtoms; bool _logLoading; mutable std::vector> _memberBuffers; - mutable std::map> _preloaded; - mutable std::vector>> _promises; + mutable std::map>> _preloaded; mutable std::mutex _mutex; };