diff --git a/core/authorize.php b/core/authorize.php
index fc74bfb..c53475b 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -27,9 +27,8 @@
  * Global flag to identify update.php and authorize.php runs.
  *
  * Identifies update.php and authorize.php runs, avoiding unwanted operations
- * such as hook_init() invocations, css/js preprocessing and
- * translation, and solves some theming issues. The flag is checked in other
- * places in Drupal code (not just authorize.php).
+ * such as css/js preprocessing and translation, and solves some theming issues.
+ * The flag is checked in other places in Drupal code (not just authorize.php).
  */
 const MAINTENANCE_MODE = 'update';
 
diff --git a/core/core.services.yml b/core/core.services.yml
index 55d5b34..856c198 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -427,3 +427,7 @@ services:
   batch.storage:
     class: Drupal\Core\Utility\BatchStorage
     arguments: ['@database']
+  slave_database_ignore__subscriber:
+    class: Drupal\Core\EventSubscriber\SlaveDatabaseIgnoreSubscriber
+    tags:
+      - {name: event_subscriber}
diff --git a/core/includes/common.inc b/core/includes/common.inc
index e4c9505..36896fb 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1888,7 +1888,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  * all typical visitors and most pages of a site. It is critical that all
  * preprocessed files are added unconditionally on every page, even if the
  * files do not happen to be needed on a page. This is normally done by calling
- * drupal_add_css() in a hook_init() implementation.
+ * drupal_add_css() in a hook_page_build() implementation.
  *
  * Non-preprocessed files should only be added to the page when they are
  * actually needed.
@@ -1943,14 +1943,15 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  *     enabled, this should be set to TRUE if the stylesheet is present on every
  *     page of the website for users for whom it is present at all. This
  *     defaults to FALSE. It is set to TRUE for stylesheets added via module and
- *     theme .info.yml files. Modules that add stylesheets within hook_init()
- *     implementations, or from other code that ensures that the stylesheet is
- *     added to all website pages, should also set this flag to TRUE. All
- *     stylesheets within the same group that have the 'every_page' flag set to
- *     TRUE and do not have 'preprocess' set to FALSE are aggregated together
- *     into a single aggregate file, and that aggregate file can be reused
- *     across a user's entire site visit, leading to faster navigation between
- *     pages. However, stylesheets that are only needed on pages less frequently
+ *     theme .info.yml files. Modules that add stylesheets within
+ *     hook_page_build() implementations, or from other code that ensures that
+ *     the stylesheet is added to all website pages, should also set this flag
+ *     to TRUE. All stylesheets within the same group that have the 'every_page'
+ *     flag set to TRUE and do not have 'preprocess' set to FALSE are aggregated
+ *     together into a single aggregate file, and that aggregate file can be
+ *     reused across a user's entire site visit, leading to faster navigation
+ *     between pages.
+ *     However, stylesheets that are only needed on pages less frequently
  *     visited, can be added by code that only runs for those particular pages,
  *     and that code should not set the 'every_page' flag. This minimizes the
  *     size of the aggregate file that the user needs to download when first
@@ -3044,7 +3045,7 @@ function drupal_region_class($region) {
  * all typical visitors and most pages of a site. It is critical that all
  * preprocessed files are added unconditionally on every page, even if the
  * files are not needed on a page. This is normally done by calling
- * drupal_add_js() in a hook_init() implementation.
+ * drupal_add_js() in a hook_page_build() implementation.
  *
  * Non-preprocessed files should only be added to the page when they are
  * actually needed.
@@ -3087,9 +3088,9 @@ function drupal_region_class($region) {
  *     page of the website for users for whom it is present at all. This
  *     defaults to FALSE. It is set to TRUE for JavaScript files that are added
  *     via module and theme .info.yml files. Modules that add JavaScript within
- *     hook_init() implementations, or from other code that ensures that the
- *     JavaScript is added to all website pages, should also set this flag to
- *     TRUE. All JavaScript files within the same group and that have the
+ *     hook_page_build() implementations, or from other code that ensures that
+ *     the JavaScript is added to all website pages, should also set this flag
+ *     to TRUE. All JavaScript files within the same group and that have the
  *     'every_page' flag set to TRUE and do not have 'preprocess' set to FALSE
  *     are aggregated together into a single aggregate file, and that aggregate
  *     file can be reused across a user's entire site visit, leading to faster
@@ -4373,16 +4374,8 @@ function _drupal_bootstrap_full($skip = FALSE) {
   // Let all modules take action before the menu system handles the request.
   // We do not want this while running update.php.
   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
-    // Prior to invoking hook_init(), initialize the theme (potentially a custom
-    // one for this page), so that:
-    // - Modules with hook_init() implementations that call theme() or
-    //   theme_get_registry() don't initialize the incorrect theme.
-    // - The theme can have hook_*_alter() implementations affect page building
-    //   (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()),
-    //   ahead of when rendering starts.
     menu_set_custom_theme();
     drupal_theme_initialize();
-    module_invoke_all('init');
   }
 }
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
index 242f935..39828ef 100644
--- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
@@ -27,16 +27,8 @@ class LegacyRequestSubscriber implements EventSubscriberInterface {
    */
   public function onKernelRequestLegacy(GetResponseEvent $event) {
     if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
-      // Prior to invoking hook_init(), initialize the theme (potentially a
-      // custom one for this page), so that:
-      // - Modules with hook_init() implementations that call theme() or
-      //   theme_get_registry() don't initialize the incorrect theme.
-      // - The theme can have hook_*_alter() implementations affect page
-      //   building (e.g., hook_form_alter(), hook_node_view_alter(),
-      //   hook_page_alter()), ahead of when rendering starts.
       menu_set_custom_theme();
       drupal_theme_initialize();
-      module_invoke_all('init');
 
       // Tell Drupal it is now fully bootstrapped (for the benefit of code that
       // calls drupal_get_bootstrap_phase()), but without having
diff --git a/core/lib/Drupal/Core/EventSubscriber/SlaveDatabaseIgnoreSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/SlaveDatabaseIgnoreSubscriber.php
new file mode 100644
index 0000000..dc800b5
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/SlaveDatabaseIgnoreSubscriber.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\SlaveDatabaseIgnoreSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Database\Database;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * System subscriber for controller requests.
+ */
+class SlaveDatabaseIgnoreSubscriber implements EventSubscriberInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function checkSlaveServer(GetResponseEvent $event) {
+    // Ignore slave database servers for this request.
+    //
+    // In Drupal's distributed database structure, new data is written to the
+    // master and then propagated to the slave servers.  This means there is a
+    // lag between when data is written to the master and when it is available
+    // on the slave. At these times, we will want to avoid using a slave server
+    // temporarily. For example, if a user posts a new node then we want to
+    // disable the slave server for that user temporarily to allow the slave
+    // server to catch up.
+    // That way, that user will see their changes immediately while for other
+    // users we still get the benefits of having a slave server, just with
+    // slightly stale data.  Code that wants to disable the slave server should
+    // use the db_set_ignore_slave() function to set
+    // $_SESSION['ignore_slave_server'] to the timestamp after which the slave
+    // can be re-enabled.
+    if (isset($_SESSION['ignore_slave_server'])) {
+      if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) {
+        Database::ignoreTarget('default', 'slave');
+      }
+      else {
+        unset($_SESSION['ignore_slave_server']);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('checkSlaveServer');
+    return $events;
+  }
+
+}
diff --git a/core/modules/action/tests/action_loop_test/action_loop_test.module b/core/modules/action/tests/action_loop_test/action_loop_test.module
index 36f6d9b..97dd8d9 100644
--- a/core/modules/action/tests/action_loop_test/action_loop_test.module
+++ b/core/modules/action/tests/action_loop_test/action_loop_test.module
@@ -20,9 +20,12 @@ function action_loop_test_watchdog(array $log_entry) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_custom_theme().
+ *
+ * We need to check wheter a loop should be triggered and we do this as early
+ * possible, in the first hook that fires.
  */
-function action_loop_test_init() {
+function action_loop_test_custom_theme() {
   if (!empty($_GET['trigger_action_on_watchdog'])) {
     watchdog_skip_semaphore('action_loop_test', 'Triggering action loop');
   }
diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module
index b83373a..4877606 100644
--- a/core/modules/language/tests/language_test/language_test.module
+++ b/core/modules/language/tests/language_test/language_test.module
@@ -11,9 +11,9 @@
 use Drupal\Core\Language\Language;
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function language_test_init() {
+function language_test_page_build() {
   language_test_store_language_negotiation();
   if (isset(language(Language::TYPE_INTERFACE)->langcode) && isset(language(Language::TYPE_INTERFACE)->method_id)) {
     drupal_set_message(t('Language negotiation method: @name', array('@name' => language(Language::TYPE_INTERFACE)->method_id)));
diff --git a/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php b/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
index e2e02e9..f8e0aea 100644
--- a/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
+++ b/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
@@ -2,13 +2,17 @@
 
 /**
  * @file
- * Contains Drupal\overlay\EventSubscriber\OverlaySubscriber.
+ * Contains \Drupal\overlay\EventSubscriber\OverlaySubscriber.
  */
 
 namespace Drupal\overlay\EventSubscriber;
 
+use Drupal\Core\ContentNegotiation;
+use Drupal\user\UserData;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
@@ -17,6 +21,93 @@
 class OverlaySubscriber implements EventSubscriberInterface {
 
   /**
+   * The content negotiation service.
+   *
+   * @var \Drupal\Core\ContentNegotiation
+   */
+  protected $negotiation;
+
+  /**
+   * The user.data service.
+   *
+   * @var \Drupal\user\UserData
+   */
+  protected $userData;
+
+  /**
+   * Constructs an OverlaySubscriber object.
+   *
+   * @param \Drupal\Core\ContentNegotiation $negotiation
+   *   The content negotiation service.
+   * @param \Drupal\user\UserData $user_data
+   *   The user.data service.
+   */
+  public function __construct(ContentNegotiation $negotiation, UserData $user_data) {
+    $this->negotiation = $negotiation;
+    $this->userData = $user_data;
+  }
+
+  /**
+   * Performs check on the beginning of a request.
+   *
+   * Determine whether the current page request is destined to appear in the
+   * parent window or in the overlay window, and format the page accordingly.
+   *
+   * @see overlay_set_mode()
+   */
+  public function onRequest(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    if ($this->negotiation->getContentType($request) != 'html') {
+      // Only act on html pages.
+      return;
+    }
+    global $user;
+
+    $mode = overlay_get_mode();
+
+    // Only act if the user has access to the overlay and a mode was not already
+    // set. Other modules can also enable the overlay directly for other uses.
+    $user_data = $this->userData->get('overlay', $user->uid, 'enabled');
+    $use_overlay = !isset($user_data) || $user_data;
+    if (empty($mode) && user_access('access overlay') && $use_overlay) {
+      $current_path = $request->attributes->get('system_path');
+      // After overlay is enabled on the modules page, redirect to
+      // <front>#overlay=admin/modules to actually enable the overlay.
+      if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
+        unset($_SESSION['overlay_enable_redirect']);
+        $response = new RedirectResponse(url('<front>', array('fragment' => 'overlay=' . $current_path, 'absolute' => TRUE)));
+        $event->setResponse($response);
+      }
+
+      if ($request->query->get('render') == 'overlay') {
+        // If a previous page requested that we close the overlay, close it and
+        // redirect to the final destination.
+        if (isset($_SESSION['overlay_close_dialog'])) {
+          call_user_func_array('overlay_close_dialog', $_SESSION['overlay_close_dialog']);
+          unset($_SESSION['overlay_close_dialog']);
+        }
+        // If this page shouldn't be rendered inside the overlay, redirect to
+        // the parent.
+        elseif (!path_is_admin($current_path)) {
+          overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('render'))));
+        }
+
+        // Indicate that we are viewing an overlay child page.
+        overlay_set_mode('child');
+
+        // Unset the render parameter to avoid it being included in URLs on the
+        // page.
+        unset($_GET['render']);
+      }
+      // Do not enable the overlay if we already are on an admin page.
+      elseif (!path_is_admin($current_path)) {
+        // Otherwise add overlay parent code and our behavior.
+        overlay_set_mode('parent');
+      }
+    }
+  }
+
+  /**
    * Performs end of request tasks.
    *
    * When viewing an overlay child page, check if we need to trigger a refresh of
@@ -48,6 +139,7 @@ public function onResponse(FilterResponseEvent $event) {
    * {@inheritdoc}
    */
   static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onRequest');
     $events[KernelEvents::RESPONSE][] = array('onResponse');
 
     return $events;
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index d400a32..d34f175 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -126,59 +126,6 @@ function overlay_user_update($account) {
 }
 
 /**
- * Implements hook_init().
- *
- * Determine whether the current page request is destined to appear in the
- * parent window or in the overlay window, and format the page accordingly.
- *
- * @see overlay_set_mode()
- */
-function overlay_init() {
-  global $user;
-
-  $mode = overlay_get_mode();
-
-  // Only act if the user has access to the overlay and a mode was not already
-  // set. Other modules can also enable the overlay directly for other uses.
-  $user_data = drupal_container()->get('user.data')->get('overlay', $user->uid, 'enabled');
-  $use_overlay = !isset($user_data) || $user_data;
-  if (empty($mode) && user_access('access overlay') && $use_overlay) {
-    $current_path = current_path();
-    // After overlay is enabled on the modules page, redirect to
-    // <front>#overlay=admin/modules to actually enable the overlay.
-    if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
-      unset($_SESSION['overlay_enable_redirect']);
-      drupal_goto('<front>', array('fragment' => 'overlay=' . $current_path));
-    }
-
-    if (isset($_GET['render']) && $_GET['render'] == 'overlay') {
-      // If a previous page requested that we close the overlay, close it and
-      // redirect to the final destination.
-      if (isset($_SESSION['overlay_close_dialog'])) {
-        call_user_func_array('overlay_close_dialog', $_SESSION['overlay_close_dialog']);
-        unset($_SESSION['overlay_close_dialog']);
-      }
-      // If this page shouldn't be rendered inside the overlay, redirect to the
-      // parent.
-      elseif (!path_is_admin($current_path)) {
-        overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('render'))));
-      }
-
-      // Indicate that we are viewing an overlay child page.
-      overlay_set_mode('child');
-
-      // Unset the render parameter to avoid it being included in URLs on the page.
-      unset($_GET['render']);
-    }
-    // Do not enable the overlay if we already are on an admin page.
-    elseif (!path_is_admin($current_path)) {
-      // Otherwise add overlay parent code and our behavior.
-      overlay_set_mode('parent');
-    }
-  }
-}
-
-/**
  * Implements hook_library_info().
  */
 function overlay_library_info() {
@@ -602,10 +549,10 @@ function overlay_get_mode() {
  *   - 'none': This is used to avoid adding any overlay-related code to the
  *     page at all. Modules can set this to explicitly prevent the overlay from
  *     being used. For example, since the overlay module itself sets the mode
- *     to 'parent' or 'child' in overlay_init() when certain conditions are
- *     met, other modules which want to override that behavior can do so by
- *     setting the mode to 'none' earlier in the page request - e.g., in their
- *     own hook_init() implementations, if they have a lower weight.
+ *     to 'parent' or 'child' in the overlay event subscriber when certain
+ *     conditions are met, other modules which want to override that behavior
+ *     can do so by setting the mode to 'none' earlier in the page request -
+ *     e.g., in their own event subscribers, if they have a higher priority.
  *   This parameter is optional, and if omitted, the current mode will be
  *   returned with no action taken.
  *
@@ -613,7 +560,7 @@ function overlay_get_mode() {
  *   The current mode, if any has been set, or NULL if no mode has been set.
  *
  * @ingroup overlay_api
- * @see overlay_init()
+ * @see \Drupal\overlay\EventSubscriber\OverlaySubscriber::onRequest()
  */
 function overlay_set_mode($mode = NULL) {
   global $base_path;
diff --git a/core/modules/overlay/overlay.services.yml b/core/modules/overlay/overlay.services.yml
index c64df81..7ab943f 100644
--- a/core/modules/overlay/overlay.services.yml
+++ b/core/modules/overlay/overlay.services.yml
@@ -3,3 +3,4 @@ services:
     class: Drupal\overlay\EventSubscriber\OverlaySubscriber
     tags:
       - { name: event_subscriber }
+    arguments: ['@content_negotiation', '@user.data']
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/IgnoreSlaveSubscriberTest.php b/core/modules/system/lib/Drupal/system/Tests/System/IgnoreSlaveSubscriberTest.php
new file mode 100644
index 0000000..bf004f9
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/System/IgnoreSlaveSubscriberTest.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Contains \Drupal\system\Tests\System\IgnoreSlaveSubscriberTest.
+ */
+
+namespace Drupal\system\Tests\System;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\EventSubscriber\SlaveDatabaseIgnoreSubscriber;
+use Drupal\Core\DrupalKernel;
+use Drupal\simpletest\UnitTestBase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+
+/**
+ * Tests the event subscriber that disables the slave database.
+ */
+class IgnoreSlaveSubscriberTest extends UnitTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Slave database ignoring event listener',
+      'description' => 'Tests that SlaveDatabaseIgnoreSubscriber functions correctly.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Tests \Drupal\Core\EventSubscriber\SlaveDatabaseIgnoreSubscriber::checkSlaveServer().
+   */
+  function testSystemInitIgnoresSlaves() {
+    // Clone the master credentials to a slave connection.
+    // Note this will result in two independent connection objects that happen
+    // to point to the same place.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+    db_ignore_slave();
+    $kernel = new DrupalKernel('testing', FALSE, drupal_classloader(), FALSE);
+    $event = new GetResponseEvent($kernel, Request::create('http://example.com'), HttpKernelInterface::MASTER_REQUEST);
+    $subscriber = new SlaveDatabaseIgnoreSubscriber();
+    $subscriber->checkSlaveServer($event);
+
+    $db1 = Database::getConnection('default', 'default');
+    $db2 = Database::getConnection('slave', 'default');
+
+    $this->assertIdentical($db1, $db2, 'System Init ignores slaves when requested.');
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/SystemInitTest.php b/core/modules/system/lib/Drupal/system/Tests/System/SystemInitTest.php
deleted file mode 100644
index 7157647..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/System/SystemInitTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Definition of Drupal\system\Tests\System\SystemInitTest.
- */
-
-namespace Drupal\system\Tests\System;
-
-use Drupal\Core\Database\Database;
-use \Drupal\simpletest\UnitTestBase;
-use \Symfony\Component\HttpFoundation\Request;
-
-/**
- * Tests system_init().
- */
-class SystemInitTest extends UnitTestBase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'System Init',
-      'description' => 'Tests the system_init function in system.module.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Tests that system_init properly ignores slaves when requested.
-   */
-  function testSystemInitIgnoresSlaves() {
-    // Clone the master credentials to a slave connection.
-    // Note this will result in two independent connection objects that happen
-    // to point to the same place.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
-
-    db_ignore_slave();
-    system_init();
-
-    $db1 = Database::getConnection('default', 'default');
-    $db2 = Database::getConnection('slave', 'default');
-
-    $this->assertIdentical($db1, $db2, 'System Init ignores slaves when requested.');
-  }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php
deleted file mode 100644
index 3863d22..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Theme\HookInitTest.
- */
-
-namespace Drupal\system\Tests\Theme;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Functional test for initialization of the theme system in hook_init().
- */
-class HookInitTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('theme_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme initialization in hook_init()',
-      'description' => 'Tests that the theme system can be correctly initialized in hook_init().',
-      'group' => 'Theme',
-    );
-  }
-
-  /**
-   * Test that the theme system can generate output when called by hook_init().
-   */
-  function testThemeInitializationHookInit() {
-    $this->drupalGet('theme-test/hook-init');
-    // Verify that themed output generated in hook_init() appears.
-    $this->assertRaw('Themed output generated in hook_init()');
-    // Verify that the default theme's CSS still appears when the theme system
-    // is initialized in hook_init().
-    $this->assertRaw('stark/css/layout.css');
-  }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeEarlyInitializationTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeEarlyInitializationTest.php
new file mode 100644
index 0000000..b2d1b45
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeEarlyInitializationTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Theme\ThemeEarlyInitializationTest.
+ */
+
+namespace Drupal\system\Tests\Theme;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Functional test for initialization of the theme system early in the request.
+ */
+class ThemeEarlyInitializationTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('theme_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Early theme initialization',
+      'description' => 'Tests that the theme system can be correctly initialized early in the page request.',
+      'group' => 'Theme',
+    );
+  }
+
+  /**
+   * Test that the theme system can generate output in a request listener.
+   */
+  function testRequestListener() {
+    $this->drupalGet('theme-test/request-listener');
+    // Verify that themed output generated in the request listener appears.
+    $this->assertRaw('Themed output generated in a KernelEvents::REQUEST listener');
+    // Verify that the default theme's CSS still appears even though the theme
+    // system was initialized early.
+    $this->assertRaw('stark/css/layout.css');
+  }
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 18bcac3..32e4efc 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1348,23 +1348,6 @@ function hook_forms($form_id, $args) {
 }
 
 /**
- * Perform setup tasks for non-cached page requests.
- *
- * This hook is run at the beginning of the page request. It is typically
- * used to set up global parameters that are needed later in the request.
- * When this hook is called, the theme and all modules are already loaded in
- * memory.
- *
- * This hook is not run on cached pages.
- *
- * Do not use this hook to add CSS/JS to pages, use hook_page_build() instead.
- *
- * @see hook_page_build()
- */
-function hook_init() {
-}
-
-/**
  * Alter an email message created with the drupal_mail() function.
  *
  * hook_mail_alter() allows modification of email messages created and sent
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 54ae84b..489d013 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -8,7 +8,6 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Database\Database;
 use Drupal\Core\Utility\ModuleInfo;
 use Drupal\Core\TypedData\Primitive;
 use Drupal\system\Plugin\Block\SystemMenuBlock;
@@ -2504,34 +2503,6 @@ function system_filetransfer_info() {
 }
 
 /**
- * Implements hook_init().
- */
-function system_init() {
-  // Ignore slave database servers for this request.
-  //
-  // In Drupal's distributed database structure, new data is written to the
-  // master and then propagated to the slave servers.  This means there is a
-  // lag between when data is written to the master and when it is available on
-  // the slave. At these times, we will want to avoid using a slave server
-  // temporarily. For example, if a user posts a new node then we want to
-  // disable the slave server for that user temporarily to allow the slave
-  // server to catch up. That way, that user will see their changes immediately
-  // while for other users we still get the benefits of having a slave server,
-  // just with slightly stale data.  Code that wants to disable the slave
-  // server should use the db_ignore_slave() function to set
-  // $_SESSION['ignore_slave_server'] to the timestamp after which the slave
-  // can be re-enabled.
-  if (isset($_SESSION['ignore_slave_server'])) {
-    if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) {
-      Database::ignoreTarget('default', 'slave');
-    }
-    else {
-      unset($_SESSION['ignore_slave_server']);
-    }
-  }
-}
-
-/**
  * Implements hook_page_build().
  */
 function system_page_build(&$page) {
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index ce68fe9..aeb75da 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -532,18 +532,6 @@ function menu_test_menu_trail_callback() {
 }
 
 /**
- * Implements hook_init().
- */
-function menu_test_init() {
-  // When requested by one of the MenuTrailTestCase tests, record the initial
-  // active trail during Drupal's bootstrap (before the user is redirected to a
-  // custom 403 or 404 page). See menu_test_custom_403_404_callback().
-  if (state()->get('menu_test.record_active_trail') ?: FALSE) {
-    state()->set('menu_test.active_trail_initial', menu_get_active_trail());
-  }
-}
-
-/**
  * Callback for our custom 403 and 404 pages.
  */
 function menu_test_custom_403_404_callback() {
@@ -614,6 +602,12 @@ function menu_test_theme_callback($argument) {
  *   The name of the custom theme to use for the current page.
  */
 function menu_test_custom_theme() {
+  // When requested by one of the MenuTrailTestCase tests, record the initial
+  // active trail during Drupal's bootstrap (before the user is redirected to a
+  // custom 403 or 404 page). See menu_test_custom_403_404_callback().
+  if (state()->get('menu_test.record_active_trail') ?: FALSE) {
+    state()->set('menu_test.active_trail_initial', menu_get_active_trail());
+  }
   // If an appropriate variable has been set in the database, request the theme
   // that is stored there. Otherwise, do not attempt to dynamically set the
   // theme.
diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module
index 31352d6..6afb8ac 100644
--- a/core/modules/system/tests/modules/system_test/system_test.module
+++ b/core/modules/system/tests/modules/system_test/system_test.module
@@ -129,17 +129,6 @@ function system_test_modules_uninstalled($modules) {
 }
 
 /**
- * Implements hook_init().
- */
-function system_test_init() {
-  // Used by FrontPageTestCase to get the results of drupal_is_front_page().
-  $frontpage = state()->get('system_test.front_page_output') ?: 0;
-  if ($frontpage && drupal_is_front_page()) {
-    drupal_set_message(t('On front page.'));
-  }
-}
-
-/**
  * Implements hook_system_info_alter().
  */
 function system_test_system_info_alter(&$info, $file, $type) {
@@ -220,6 +209,11 @@ function system_test_page_build(&$page) {
   elseif ($menu_item['path'] == 'system-test/main-content-duplication') {
     drupal_set_page_content();
   }
+  // Used by FrontPageTestCase to get the results of drupal_is_front_page().
+  $frontpage = state()->get('system_test.front_page_output') ?: 0;
+  if ($frontpage && drupal_is_front_page()) {
+    drupal_set_message(t('On front page.'));
+  }
 }
 
 /**
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php
new file mode 100644
index 0000000..d45bf64
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\theme_test\EventSubscriber\ThemeTestSubscriber.
+ */
+
+namespace Drupal\theme_test\EventSubscriber;
+
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Theme test subscriber for controller requests.
+ */
+class ThemeTestSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Generates themed output early in a page request.
+   *
+   * @see Drupal\system\Tests\Theme\ThemeEarlyInitializationTest::testRequestListener()
+   */
+  public function onRequest(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    $current_path = $request->attributes->get('system_path');
+    if ($current_path == 'theme-test/request-listener') {
+      // First, force the theme registry to be rebuilt on this page request.
+      // This allows us to test a full initialization of the theme system in
+      // the code below.
+      drupal_theme_rebuild();
+      // Next, initialize the theme system by storing themed text in a global
+      // variable. We will use this later in
+      // theme_test_request_listener_page_callback() to test that even when the
+      // theme system is initialized this early, it is still capable of
+      // returning output and theming the page as a whole.
+      $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in a KernelEvents::REQUEST listener'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onRequest');
+    return $events;
+  }
+
+}
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index 4204077..e2cf6bd 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -52,8 +52,8 @@ function theme_test_menu() {
     'theme callback' => '_theme_custom_theme',
     'type' => MENU_CALLBACK,
   );
-  $items['theme-test/hook-init'] = array(
-    'page callback' => 'theme_test_hook_init_page_callback',
+  $items['theme-test/request-listener'] = array(
+    'page callback' => 'theme_test_request_listener_page_callback',
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
@@ -71,28 +71,6 @@ function theme_test_menu() {
 }
 
 /**
- * Implements hook_init().
- */
-function theme_test_init() {
-  if (arg(0) == 'theme-test' && arg(1) == 'hook-init') {
-    // First, force the theme registry to be rebuilt on this page request. This
-    // allows us to test a full initialization of the theme system in the code
-    // below.
-    drupal_theme_rebuild();
-    // Next, initialize the theme system by storing themed text in a global
-    // variable. We will use this later in theme_test_hook_init_page_callback()
-    // to test that even when the theme system is initialized this early, it is
-    // still capable of returning output and theming the page as a whole.
-    $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()'));
-  }
-  if (arg(0) == 'user' && arg(1) == 'autocomplete') {
-    // Register a fake registry loading callback. If it gets called by
-    // theme_get_registry(), the registry has not been initialized yet.
-    _theme_registry_callback('_theme_test_load_registry', array());
-  }
-}
-
-/**
  * Fake registry loading callback.
  */
 function _theme_test_load_registry() {
@@ -101,9 +79,9 @@ function _theme_test_load_registry() {
 }
 
 /**
- * Menu callback for testing themed output generated in hook_init().
+ * Menu callback for testing themed output generated in a request listener.
  */
-function theme_test_hook_init_page_callback() {
+function theme_test_request_listener_page_callback() {
   return $GLOBALS['theme_test_output'];
 }
 
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.services.yml b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
new file mode 100644
index 0000000..8e442df
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
@@ -0,0 +1,5 @@
+services:
+  theme_test.subscriber:
+    class: Drupal\theme_test\EventSubscriber\ThemeTestSubscriber
+    tags:
+      - { name: event_subscriber }
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index ddbc398..7ab2249 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -96,9 +96,9 @@ function update_help($path, $arg) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function update_init() {
+function update_page_build() {
   if (arg(0) == 'admin' && user_access('administer site configuration')) {
     switch (current_path()) {
       // These pages don't need additional nagging.
diff --git a/core/update.php b/core/update.php
index cee23c4..1ced56b 100644
--- a/core/update.php
+++ b/core/update.php
@@ -33,8 +33,8 @@
 /**
  * Global flag indicating that update.php is being run.
  *
- * When this flag is set, various operations do not take place, such as invoking
- * hook_init(), css/js preprocessing, and translation.
+ * When this flag is set, various operations do not take place, such as css/js
+ * preprocessing and translation.
  *
  * This constant is defined using define() instead of const so that PHP
  * versions older than 5.3 can display the proper PHP requirements instead of
