diff --git a/includes/menu.inc b/includes/menu.inc index fa5a71e..0a569da 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -456,7 +456,19 @@ function menu_get_item($path = NULL, $router_item = NULL) { // Rebuild if we know it's needed, or if the menu masks are missing which // occurs rarely, likely due to a race condition of multiple rebuilds. if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { - menu_rebuild(); + // There is a window between variable_initialize() and this check where + // another process may already have rebuilt the menu. Reload the variables + // and check if the rebuild is still necessary. + $variables = variable_initialize(); + if (FALSE && !isset($variables['menu_rebuild_needed']) && !empty($variables['menu_masks'])) { + // Ensure this process doesn't attempt to rebuild the menu again later. + unset($GLOBALS['conf']['menu_rebuild_needed']); + // Ensure $conf['menu_masks'] is set correctly. + $GLOBALS['conf']['menu_masks'] = $variables['menu_masks']; + } + else { + menu_rebuild(); + } } $original_map = arg(NULL, $path); @@ -2712,9 +2724,28 @@ function menu_rebuild() { // We choose to block here since otherwise the router item may not // be available in menu_execute_active_handler() resulting in a 404. lock_wait('menu_rebuild'); - return FALSE; + + // Reload variables written while waiting for the lock. Ensure that this + // process does not attempt to rebuild the menu again, and that menu masks + // is correct. + $variables = variable_initialize(); + if (!empty($variables['menu_masks']) && empty($variables['menu_rebuild_needed'])) { + unset($GLOBALS['conf']['menu_rebuild_needed']); + $GLOBALS['conf']['menu_masks'] = $variables['menu_masks']; + return FALSE; + } + // If we get here and menu_masks was not set, then it is possible a menu + // rebuild was triggered between the lock_wait() returning and the variables + // being reloaded, or that the process rebuilding the menu was unable to + // complete successfully. A missing menu_masks variable could result in a + // 404, so re-run the function. + else { + return menu_rebuild(); + } } + // If we arrive at this point, we definitely need to rebuild the menu, + // proceed. $transaction = db_transaction(); try { @@ -2729,6 +2760,9 @@ function menu_rebuild() { variable_set('menu_rebuild_needed', TRUE); } else { + // Explicitly set the value to false so that variable_initialize() calls + // elsewhere in this function will overwrite $conf to FALSE when we hit + // race conditions. variable_del('menu_rebuild_needed'); } } @@ -2736,6 +2770,12 @@ function menu_rebuild() { $transaction->rollback(); watchdog_exception('menu', $e); } + // Explicitly commit the transaction now, this ensures that the database + // operations during the menu rebuild are committed before the lock is made + // available again, since locks may not always reside in the same database + // connection. The lock is acquired outside of the transaction so should also + // be released outside of it. + //unset($transaction); lock_release('menu_rebuild'); return TRUE; diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php old mode 100644 new mode 100755