diff --git a/includes/module.inc b/includes/module.inc
index 494924f..064c0ec 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -729,29 +729,47 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
     }
   }
 
+  // Early in the bootstrap, the full list of modules is not yet available. If
+  // this function is called then, writing to the static or persistent caches
+  // would result in an incomplete list of hook implementations being stored
+  // for later use. Prevent that from ever happening.
+  static $cache_write_allowed = FALSE;
+  if (!$cache_write_allowed) {
+    // Once all modules have been loaded, cache writing is always allowed. This
+    // is safe to store in a static variable because it's impossible to unload
+    // a module.
+    $cache_write_allowed = module_load_all(NULL);
+  }
+
+  // Track whether the cache needs to be updated.
+  $write_cache = FALSE;
+
   if (!isset($implementations[$hook])) {
-    // The hook is not cached, so ensure that whether or not it has
-    // implementations, that the cache is updated at the end of the request.
-    $implementations['#write_cache'] = TRUE;
+    $hook_implementations = array();
+    // The hook is not yet cached, so update the cache to include it if
+    // allowed.
+    $write_cache = $cache_write_allowed;
     $hook_info = module_hook_info();
-    $implementations[$hook] = array();
     $list = module_list(FALSE, FALSE, $sort);
     foreach ($list as $module) {
       $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
       // Since module_hook() may needlessly try to load the include file again,
       // function_exists() is used directly here.
       if (function_exists($module . '_' . $hook)) {
-        $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
+        $hook_implementations[$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
       }
     }
     // Allow modules to change the weight of specific implementations but avoid
     // an infinite loop.
     if ($hook != 'module_implements_alter') {
-      drupal_alter('module_implements', $implementations[$hook], $hook);
+      drupal_alter('module_implements', $hook_implementations, $hook);
     }
   }
   else {
-    foreach ($implementations[$hook] as $module => $group) {
+    // This hook is already cached, so get the initial list of implementations
+    // from the cache.
+    $hook_implementations = $implementations[$hook];
+    foreach ($hook_implementations as $module => $group) {
       // If this hook implementation is stored in a lazy-loaded file, so include
       // that file first.
       if ($group) {
@@ -763,15 +781,23 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
       // Since module_hook() may needlessly try to load the include file again,
       // function_exists() is used directly here.
       if (!function_exists($module . '_' . $hook)) {
-        // Clear out the stale implementation from the cache and force a cache
-        // refresh to forget about no longer existing hook implementations.
-        unset($implementations[$hook][$module]);
-        $implementations['#write_cache'] = TRUE;
+        unset($hook_implementations[$module]);
+        // Update the cache if allowed, to remove the record of the
+        // implementation that no longer exists.
+        $write_cache = $cache_write_allowed;
       }
     }
   }
 
-  return array_keys($implementations[$hook]);
+  if ($write_cache) {
+    // Write the list of hook implementations to the static cache, and mark the
+    // persistent cache to be updated at the end of the request. See
+    // module_implements_write_cache().
+    $implementations[$hook] = $hook_implementations;
+    $implementations['#write_cache'] = TRUE;
+  }
+
+  return array_keys($hook_implementations);
 }
 
 /**
@@ -1015,8 +1041,11 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$con
   // Some alter hooks are invoked many times per page request, so statically
   // cache the list of functions to call, and on subsequent calls, iterate
   // through them quickly.
-  if (!isset($functions[$cid])) {
-    $functions[$cid] = array();
+  if (isset($functions[$cid])) {
+    $type_functions = $functions[$cid];
+  }
+  else {
+    $type_functions = array();
     $hook = $type . '_alter';
     $modules = module_implements($hook);
     if (!isset($extra_types)) {
@@ -1024,7 +1053,7 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$con
       // function_exists(), since module_implements() returns only modules with
       // implementations.
       foreach ($modules as $module) {
-        $functions[$cid][] = $module . '_' . $hook;
+        $type_functions[] = $module . '_' . $hook;
       }
     }
     else {
@@ -1061,12 +1090,12 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$con
         // function_exists().
         $function = $module . '_' . $hook;
         if (function_exists($function)) {
-          $functions[$cid][] = $function;
+          $type_functions[] = $function;
         }
         foreach ($extra_types as $extra_type) {
           $function = $module . '_' . $extra_type . '_alter';
           if (function_exists($function)) {
-            $functions[$cid][] = $function;
+            $type_functions[] = $function;
           }
         }
       }
@@ -1083,21 +1112,36 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$con
       foreach ($theme_keys as $theme_key) {
         $function = $theme_key . '_' . $hook;
         if (function_exists($function)) {
-          $functions[$cid][] = $function;
+          $type_functions[] = $function;
         }
         if (isset($extra_types)) {
           foreach ($extra_types as $extra_type) {
             $function = $theme_key . '_' . $extra_type . '_alter';
             if (function_exists($function)) {
-              $functions[$cid][] = $function;
+              $type_functions[] = $function;
             }
           }
         }
       }
     }
+
+    // Early in the bootstrap, the full list of modules is not yet available.
+    // If this function is called then, writing to the static cache would
+    // result in an incomplete list of hook implementations being stored for
+    // later use. Prevent that from ever happening.
+    static $cache_write_allowed = FALSE;
+    if (!$cache_write_allowed) {
+      // Once all modules have been loaded, cache writing is always allowed.
+      // This is safe to store in a static variable because it's impossible to
+      // unload a module.
+      $cache_write_allowed = module_load_all(NULL);
+    }
+    if ($cache_write_allowed) {
+      $functions[$cid] = $type_functions;
+    }
   }
 
-  foreach ($functions[$cid] as $function) {
+  foreach ($type_functions as $function) {
     $function($data, $context1, $context2, $context3);
   }
 }
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index 7b139ba..1aec619 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -11,6 +11,7 @@ configure = admin/config/development/testing/settings
 files[] = tests/actions.test
 files[] = tests/ajax.test
 files[] = tests/batch.test
+files[] = tests/boot.test
 files[] = tests/bootstrap.test
 files[] = tests/cache.test
 files[] = tests/common.test
diff --git a/modules/simpletest/tests/boot.test b/modules/simpletest/tests/boot.test
new file mode 100644
index 0000000..562b082
--- /dev/null
+++ b/modules/simpletest/tests/boot.test
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Perform early bootstrap tests.
+ */
+class EarlyBootstrapTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name'  => 'Early bootstrap test',
+      'description'  => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('boot_test_1', 'boot_test_2');
+  }
+
+  /**
+   * Test hook_boot() on both regular and "early exit" pages.
+   */
+  public function testHookBoot() {
+    $paths = array('', 'early_exit');
+    foreach ($paths as $path) {
+      // Empty the module_implements() caches.
+      module_implements(NULL, FALSE, TRUE);
+      // Do a request to the front page, which will call module_implements()
+      // during hook_boot().
+      $this->drupalGet($path);
+      // Reset the static cache so we get implementation data from the persistent
+      // cache.
+      drupal_static_reset();
+      // Make sure we get a full list of all modules implementing hook_help().
+      $modules = module_implements('help');
+      $this->assertTrue(in_array('boot_test_2', $modules));
+    }
+  }
+}
diff --git a/modules/simpletest/tests/boot_test_1.info b/modules/simpletest/tests/boot_test_1.info
new file mode 100644
index 0000000..a6f2ffb
--- /dev/null
+++ b/modules/simpletest/tests/boot_test_1.info
@@ -0,0 +1,6 @@
+name = Early bootstrap tests
+description = A support module for hook_boot testing.
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
diff --git a/modules/simpletest/tests/boot_test_1.module b/modules/simpletest/tests/boot_test_1.module
new file mode 100644
index 0000000..a452e28
--- /dev/null
+++ b/modules/simpletest/tests/boot_test_1.module
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Tests calling module_implements() during hook_boot() invocation.
+ */
+
+/**
+ * Implements hook_boot().
+ */
+function boot_test_1_boot() {
+  // Calling module_implements during hook_boot() will return "vital" modules
+  // only, and this list of modules will be statically cached.
+  module_implements('help');
+  // Define a special path to test that the static cache isn't written away
+  // if we exit before having completed the bootstrap.
+  if ($_GET['q'] == 'early_exit') {
+    module_implements_write_cache();
+    exit();
+  }
+}
diff --git a/modules/simpletest/tests/boot_test_2.info b/modules/simpletest/tests/boot_test_2.info
new file mode 100644
index 0000000..f421997
--- /dev/null
+++ b/modules/simpletest/tests/boot_test_2.info
@@ -0,0 +1,6 @@
+name = Early bootstrap tests
+description = A support module for hook_boot hook testing.
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
diff --git a/modules/simpletest/tests/boot_test_2.module b/modules/simpletest/tests/boot_test_2.module
new file mode 100644
index 0000000..c3ab8d6
--- /dev/null
+++ b/modules/simpletest/tests/boot_test_2.module
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Defines a hook_help() implementation in a non-"bootstrap" module.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function boot_test_2_help($path, $arg) {
+  // Empty hook.
+}
