Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.735
diff -u -p -r1.735 system.module
--- modules/system/system.module	3 Aug 2009 06:06:23 -0000	1.735
+++ modules/system/system.module	4 Aug 2009 17:59:01 -0000
@@ -1552,14 +1552,26 @@ function system_block_view($delta = '') 
  */
 function system_admin_menu_block($item) {
   $cache = &drupal_static(__FUNCTION__, array());
+
+  // This function is called on every page request if administrative links are
+  // displayed, so we cache by path to improve the performance of
+  // system_admin_menu_block_access(). While path is not guaranteed to be
+  // unique if users configure their administrative menu using the menu
+  // interface, we don't support two top level items with the same path
+  // here since this would require an additional query, or duplicate cache to
+  // be implemented.
+  if (isset($cache[$item['path']])) {
+    return $cache[$item['path']];
+  }
+  global $user;
+  $cid = 'system:admin:links:' . implode('|', array_keys($user->roles)) . $item['path'];
+  if ($cached = cache_get($cid, 'cache_menu')) {
+    $cache[$item['path']] = $cached->data;
+    return $cached->data;
+  }
   if (!isset($item['mlid'])) {
     $item += db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = :path AND module = 'system'", array(':path' => $item['path']))->fetchAssoc();
   }
-
-  if (isset($cache[$item['mlid']])) {
-    return $cache[$item['mlid']];
-  }
-
   $content = array();
   $result = db_query("
     SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
@@ -1581,7 +1593,8 @@ function system_admin_menu_block($item) 
     $content[(50000 + $link['weight']) . ' ' . $link['title'] . ' ' . $link['mlid']] = $link;
   }
   ksort($content);
-  $cache[$item['mlid']] = $content;
+  cache_set($cid, $content, 'cache_menu');
+  $cache[$item['path']] = $content;
   return $content;
 }
 
@@ -2926,6 +2939,21 @@ function system_goto_action($object, $co
 }
 
 /**
+ * Implementation of hook_user_role_update().
+ */
+function system_user_role_update($role) {
+  // Clear the system_admin_menu_block() cache.
+  cache_clear_all('system:admin:links:', 'cache_menu', TRUE);
+}
+
+/**
+ * Implementation of hook_user_role_delete().
+ */
+function system_user_role_delete() {
+  cache_clear_all('system:admin:links:', 'cache_menu', TRUE);
+}
+
+/**
  * Implement a Drupal action.
  * Blocks the user's IP address.
  */
Index: modules/toolbar/toolbar.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.module,v
retrieving revision 1.4
diff -u -p -r1.4 toolbar.module
--- modules/toolbar/toolbar.module	28 Jul 2009 10:35:56 -0000	1.4
+++ modules/toolbar/toolbar.module	4 Aug 2009 17:59:04 -0000
@@ -111,6 +111,17 @@ function toolbar_build() {
  * Get only the top level items below the 'admin' path.
  */
 function toolbar_get_menu_tree() {
+  global $user;
+  // Since generating the key can take 25% of page execution time, cache this
+  // for each combination of user roles. This guarantees a unique set of
+  // permissions for the minimum number of cache entries. We use the
+  // {cache_menu} bin to ensure this cache is cleared when the menu is updated.
+  // Also use the same $cid prefix as system_admin_menu_block() since we want
+  // this to be cleared in the same situations (changes to role permissions).
+  $cid = 'system:admin:links:toolbar:' . implode('|', array_keys($user->roles));
+  if ($cached = cache_get($cid, 'cache_menu')) {
+    return $cached->data;
+  }
   $tree = menu_tree_all_data('management');
   foreach ($tree as $item) {
     if ($item['link']['link_path'] == 'admin' && !empty($item['below'])) {
@@ -124,6 +135,7 @@ function toolbar_get_menu_tree() {
     // Get rid of subitems to have a leaner data structure.
     unset($tree[$key]['below']);
   }
+  cache_set($cid, $tree, 'cache_menu');
   return $tree;
 }
 
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.66
diff -u -p -r1.66 user.admin.inc
--- modules/user/user.admin.inc	31 Jul 2009 19:01:03 -0000	1.66
+++ modules/user/user.admin.inc	4 Aug 2009 17:59:05 -0000
@@ -659,7 +659,8 @@ function user_admin_permissions($form_st
  * @see user_admin_permissions()
  */
 function user_admin_permissions_submit($form, &$form_state) {
-  foreach ($form_state['values']['role_names'] as $rid => $name) {
+  $roles = $form_state['values']['role_names'];
+  foreach ($roles as $rid => $name) {
     $checked = array_filter($form_state['values'][$rid]);
     // Delete existing permissions for the role. This handles "unchecking" checkboxes.
     db_delete('role_permission')
@@ -673,10 +674,14 @@ function user_admin_permissions_submit($
       ));
     }
     $query->execute();
+    // Allow modules to respond to changes to user roles.
+    module_invoke_all('user_role_update', array('rid' => $rid, 'name' => $name));
   }
 
   drupal_set_message(t('The changes have been saved.'));
 
+  // Allow modules to respond to changes to roles.
+
   // Clear the cached pages and blocks.
   cache_clear_all();
 }
@@ -790,11 +795,14 @@ function user_admin_role_validate($form,
 }
 
 function user_admin_role_submit($form, &$form_state) {
+  $name = isset($form_state['values']['name']) ? $form_state['values']['name'] : FALSE;
+  $rid = isset($form_state['values']['name']) ? $form_state['values']['name'] : FALSE;
   if ($form_state['values']['op'] == t('Save role')) {
     db_update('role')
-      ->fields(array('name' => $form_state['values']['name']))
-      ->condition('rid', $form_state['values']['rid'])
+      ->fields(array('name' => $name))
+      ->condition('rid', $rid)
       ->execute();
+    module_invoke_all('user_role_update', array('rid' => $rid, 'name' => $name));
     drupal_set_message(t('The role has been renamed.'));
   }
   elseif ($form_state['values']['op'] == t('Delete role')) {
@@ -809,12 +817,15 @@ function user_admin_role_submit($form, &
       ->condition('rid', $form_state['values']['rid'])
       ->execute();
 
+    module_invoke_all('user_role_delete', $rid);
+
     drupal_set_message(t('The role has been deleted.'));
   }
   elseif ($form_state['values']['op'] == t('Add role')) {
-    db_insert('role')
+    $rid = db_insert('role')
       ->fields(array('name' => $form_state['values']['name']))
       ->execute();
+    module_invoke_all('user_role_insert', array('rid' => $rid, 'name' => $name));
     drupal_set_message(t('The role has been added.'));
   }
   $form_state['redirect'] = 'admin/settings/roles';
Index: modules/user/user.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.api.php,v
retrieving revision 1.8
diff -u -p -r1.8 user.api.php
--- modules/user/user.api.php	31 Jul 2009 19:01:03 -0000	1.8
+++ modules/user/user.api.php	4 Aug 2009 17:59:05 -0000
@@ -263,6 +263,39 @@ function hook_user_categories() {
   ));
 }
 
+/**
+ * Act on user role creation.
+ *
+ * @param $role
+ *   An array containing the role ID and name.
+ */
+function user_role_insert($role) {
+  db_insert('my_table')->fields(array('rid' => $role['rid'], 'my_field' => $role['name']))->execute();
+}
+
+/**
+ * Act on user role updates.
+ *
+ * @param $role
+ *   An array containing the role ID and name.
+ */
+function hook_user_role_update($role) {
+  // Invalidate the system_admin_menu_block() cache, since this 
+  // depends on user permissions.
+  cache_clear_all('system:admin:links:', 'cache_menu', TRUE);
+}
+
+/**
+ * Act on user role deletion.
+ *
+ * @param $rid
+ *   The role ID.
+ */
+function system_user_role_delete($rid) {
+  // Invalidate the system_admin_menu_bock() cache since this
+  // depends on user roles for the cache key.
+  cache_clear_all('system:admin:links:', 'cache_menu', TRUE);
+}
 
 /**
  * @} End of "addtogroup hooks".
