diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index 0aa026c7779..172a4dd96d2 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -20,11 +20,20 @@
 #include "defs.h"
 #include "dwarf2/cooked-index.h"
 #include "dwarf2/read.h"
+#include "dwarf2/index-cache.h"
 #include "cp-support.h"
 #include "c-lang.h"
 #include "ada-lang.h"
 #include "split-name.h"
+#include "observable.h"
+#include "run-on-main-thread.h"
 #include <algorithm>
+#include <unordered_set>
+
+/* We don't want gdb to exit while it is in the process of writing to
+   the index cache.  So, all live cooked index vectors are stored
+   here, and then these are all waited for before exit proceeds.  */
+static std::unordered_set<cooked_index_vector *> active_vectors;
 
 /* Hash function for cooked_index_entry.  */
 
@@ -282,11 +291,46 @@ cooked_index::find (gdb::string_view name, bool completing)
   return range (lower, upper);
 }
 
-cooked_index_vector::cooked_index_vector (vec_type &&vec)
+cooked_index_vector::cooked_index_vector (vec_type &&vec,
+					  dwarf2_per_bfd *per_bfd)
   : m_vector (std::move (vec))
 {
   for (auto &idx : m_vector)
     idx->finalize ();
+
+  /* This must be set after all the finalization tasks have been
+     started, because it may call 'wait'.  */
+  m_write_future
+    = gdb::thread_pool::g_thread_pool->post_task ([this, per_bfd] ()
+      {
+	maybe_write_index (per_bfd);
+      });
+
+  /* ACTIVE_VECTORS is not locked, and this assert ensures that this
+     will be caught if ever moved to the background.  */
+  gdb_assert (is_main_thread ());
+  active_vectors.insert (this);
+}
+
+cooked_index_vector::~cooked_index_vector ()
+{
+  /* The 'finalize' method may be run in a different thread.  If
+     this object is destroyed before this completes, then the method
+     will end up writing to freed memory.  Waiting for this to
+     complete avoids this problem; and the cost seems ignorable
+     because creating and immediately destroying the debug info is a
+     relatively rare thing to do.  */
+  wait ();
+
+  /* Likewise for the index-creating future, though this one must also
+     waited for by the per-BFD object to ensure the required data
+     remains live.  */
+  wait_completely ();
+
+  /* Remove our entry from the global list.  See the assert in the
+     constructor to understand this.  */
+  gdb_assert (is_main_thread ());
+  active_vectors.erase (this);
 }
 
 /* See cooked-index.h.  */
@@ -345,3 +389,30 @@ cooked_index_vector::get_main () const
 
   return result;
 }
+
+void
+cooked_index_vector::maybe_write_index (dwarf2_per_bfd *per_bfd)
+{
+  /* Wait for finalization.  */
+  wait ();
+
+  /* (maybe) store an index in the cache.  */
+  global_index_cache.store (per_bfd);
+}
+
+/* Wait for all the index cache entries to be written before gdb
+   exits.  */
+static void
+wait_for_index_cache (int)
+{
+  gdb_assert (is_main_thread ());
+  for (cooked_index_vector *item : active_vectors)
+    item->wait_completely ();
+}
+
+void _initialize_cooked_index ();
+void
+_initialize_cooked_index ()
+{
+  gdb::observers::gdb_exiting.attach (wait_for_index_cache, "cooked-index");
+}
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 2ea32781be5..fb901c6f87f 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -36,6 +36,7 @@
 #include "gdbsupport/range-chain.h"
 
 struct dwarf2_per_cu_data;
+struct dwarf2_per_bfd;
 
 /* Flags that describe an entry in the index.  */
 enum cooked_index_flag_enum : unsigned char
@@ -307,7 +308,8 @@ class cooked_index_vector : public dwarf_scanner_base
      object.  */
   typedef std::vector<std::unique_ptr<cooked_index>> vec_type;
 
-  explicit cooked_index_vector (vec_type &&vec);
+  cooked_index_vector (vec_type &&vec, dwarf2_per_bfd *per_bfd);
+  ~cooked_index_vector () override;
   DISABLE_COPY_AND_ASSIGN (cooked_index_vector);
 
   /* Wait until the finalization of the entire cooked_index_vector is
@@ -318,17 +320,6 @@ class cooked_index_vector : public dwarf_scanner_base
       item->wait ();
   }
 
-  ~cooked_index_vector ()
-  {
-    /* The 'finalize' methods may be run in a different thread.  If
-       this object is destroyed before these complete, then one will
-       end up writing to freed memory.  Waiting for finalization to
-       complete avoids this problem; and the cost seems ignorable
-       because creating and immediately destroying the debug info is a
-       relatively rare thing to do.  */
-    wait ();
-  }
-
   /* A range over a vector of subranges.  */
   typedef range_chain<cooked_index::range> range;
 
@@ -367,11 +358,27 @@ class cooked_index_vector : public dwarf_scanner_base
 
   quick_symbol_functions_up make_quick_functions () const override;
 
+  /* Wait for the index to be completely finished.  For ordinary uses,
+     the index code ensures this itself -- e.g., 'all_entries' will
+     wait on the 'finalize' future.  However, on destruction, if an
+     index is being written, it's also necessary to wait for that to
+     complete.  */
+  void wait_completely () override
+  {
+    m_write_future.wait ();
+  }
+
 private:
 
+  /* Maybe write the index to the index cache.  */
+  void maybe_write_index (dwarf2_per_bfd *per_bfd);
+
   /* The vector of cooked_index objects.  This is stored because the
      entries are stored on the obstacks in those objects.  */
   vec_type m_vector;
+
+  /* A future that tracks when the 'index_write' method is done.  */
+  std::future<void> m_write_future;
 };
 
 #endif /* GDB_DWARF2_COOKED_INDEX_H */
diff --git a/gdb/dwarf2/mapped-index.h b/gdb/dwarf2/mapped-index.h
index 7d71347f9f4..e0c3a447991 100644
--- a/gdb/dwarf2/mapped-index.h
+++ b/gdb/dwarf2/mapped-index.h
@@ -73,6 +73,15 @@ struct dwarf_scanner_base
      will return 'this' as a cooked index.  For other forms, it will
      throw an exception with an appropriate error message.  */
   virtual cooked_index_vector *index_for_writing () = 0;
+
+  /* Wait for reading of the debuginfo to be completely finished.
+     This normally has a trivial implementation, but if a subclass
+     does any background reading, it's needed to ensure that the
+     reading is completed before destroying the containing per-BFD
+     object.  */
+  virtual void wait_completely ()
+  {
+  }
 };
 
 /* Base class containing bits shared by both .gdb_index and
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index fc8239b4261..6d27fbf1d75 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1467,6 +1467,11 @@ dwarf2_per_bfd::dwarf2_per_bfd (bfd *obfd, const dwarf2_debug_sections *names,
 
 dwarf2_per_bfd::~dwarf2_per_bfd ()
 {
+  /* Data from the per-BFD may be needed when finalizing the cooked
+     index table, so wait here while this happens.  */
+  if (index_table != nullptr)
+    index_table->wait_completely ();
+
   for (auto &per_cu : all_units)
     {
       per_cu->imported_symtabs_free ();
@@ -5432,9 +5437,6 @@ dwarf2_build_psymtabs (struct objfile *objfile)
   try
     {
       dwarf2_build_psymtabs_hard (per_objfile);
-
-      /* (maybe) store an index in the cache.  */
-      global_index_cache.store (per_objfile->per_bfd);
     }
   catch (const gdb_exception_error &except)
     {
@@ -7182,7 +7184,8 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
      indexes.end ());
   indexes.shrink_to_fit ();
 
-  cooked_index_vector *vec = new cooked_index_vector (std::move (indexes));
+  cooked_index_vector *vec = new cooked_index_vector (std::move (indexes),
+						      per_bfd);
   per_bfd->index_table.reset (vec);
 
   const cooked_index_entry *main_entry = vec->get_main ();
