diff --git a/core/includes/common.inc b/core/includes/common.inc
old mode 100644
new mode 100755
index 757a0c6..cf028ab
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -161,6 +161,11 @@ const DRUPAL_CACHE_PER_PAGE = 0x0004;
 const DRUPAL_CACHE_GLOBAL = 0x0008;
 
 /**
+ * The default cron rule.
+ */
+const DRUPAL_CRON_RULE = '*/1 * * * *';
+
+/**
  * Adds content to a specified region.
  *
  * @param $region
@@ -5222,42 +5227,62 @@ function drupal_cron_run() {
   $queues = module_invoke_all('cron_queue_info');
   drupal_alter('cron_queue_info', $queues);
 
-  // Try to acquire cron lock.
-  if (!lock_acquire('cron', 240.0)) {
-    // Cron is still running normally.
-    watchdog('cron', 'Attempting to re-run cron while it is already running.', array(), WATCHDOG_WARNING);
+  // Make sure every queue exists. There is no harm in trying to recreate an
+  // existing queue.
+  foreach ($queues as $queue_name => $info) {
+    DrupalQueue::get($queue_name)->createQueue();
   }
-  else {
-    // Make sure every queue exists. There is no harm in trying to recreate an
-    // existing queue.
-    foreach ($queues as $queue_name => $info) {
-      DrupalQueue::get($queue_name)->createQueue();
-    }
-    // Register shutdown callback.
-    drupal_register_shutdown_function('drupal_cron_cleanup');
-
-    // Iterate through the modules calling their cron handlers (if any):
-    foreach (module_implements('cron') as $module) {
-      // Do not let an exception thrown by one module disturb another.
-      try {
-        module_invoke($module, 'cron');
+  $items = drupal_get_cron();
+
+  // Iterate through the modules calling their cron handlers (if any):
+  foreach ($items as $name => $item) {
+    // Do not let an exception thrown by one module disturb another.
+    try {
+      // Lock it!
+      if (!lock_acquire('cron_' . $name, 240)) {
+        watchdog('cron', 'Could not acquire lock for %name, already running', array('%name' => $name), WATCHDOG_WARNING);
+        continue;
       }
-      catch (Exception $e) {
-        watchdog_exception('cron', $e);
+
+      // Skip if not scheduled.
+      $match = FALSE;
+      foreach ($item['rules'] as $rule) {
+        if (drupal_cron_get_last_scheduled($rule) >= variable_get('cron_last_' . $name)) {
+          $match = TRUE;
+          break;
+        }
       }
-    }
+      if (!$match) {
+        watchdog('cron', 'Not running %name', array('%name' => $name), WATCHDOG_INFO);
+        continue;
+      }
+
 
-    // Record cron time.
-    variable_set('cron_last', REQUEST_TIME);
-    watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
+      watchdog('cron', 'Running %name', array('%name' => $name), WATCHDOG_INFO);
 
-    // Release cron lock.
-    lock_release('cron');
+      if (!empty($item['file'])) {
+        require_once($item['file path'] . DIRECTORY_SEPARATOR . $item['file']);
+      }
+      call_user_func_array($item['callback'], $item['arguments']);
+      variable_set('cron_last_' . $name, time());
+      drupal_set_message(t('Cron job %name finished', array('%name' => $name)));
+    }
+    catch (Exception $e) {
+      watchdog_exception('cron', $e);
+    }
 
-    // Return TRUE so other functions can check if it did run successfully
-    $return = TRUE;
+    // Release the log
+    lock_release('cron_' . $name);
   }
 
+  // Record cron time.
+  variable_set('cron_last', REQUEST_TIME);
+  watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
+
+  // Return TRUE so other functions can check if it did run successfully
+  $return = TRUE;
+
+  // Process queues
   foreach ($queues as $queue_name => $info) {
     $function = $info['worker callback'];
     $end = time() + (isset($info['time']) ? $info['time'] : 15);
@@ -5275,6 +5300,60 @@ function drupal_cron_run() {
 }
 
 /**
+ * Get list of defined cron callbacks
+ *
+ * @return array
+ *   List of cron callbacks defined
+ */
+function drupal_get_cron() {
+  $items = &drupal_static(__FUNCTION__);
+  if (isset($items)) {
+    return $items;
+  }
+
+  $items = array();
+  $weight = 0;
+
+  // Retrieve cron hooks and add default values
+  foreach (module_implements('cron') as $module) {
+    $info = module_invoke($module, 'cron');
+    foreach ($info as $name => &$item) {
+      $item += array(
+        'module' => $module,
+        'file path' => drupal_get_path('module', $module),
+        'rules' => array(variable_get('drupal_cron_rule', DRUPAL_CRON_RULE)),
+        'original weight' => $weight++,
+        'weight' => 0,
+        'arguments' => array(),
+        'callback' => $name,
+      );
+    }
+    $items += $info;
+  }
+
+  // Allow other modules to modify the cron definitions
+  drupal_alter('cron', $items);
+
+  // Respect "weight"
+  uasort($items, '_drupal_cron_sort_by_weight');
+
+  return $items;
+}
+
+/**
+ * Custom sort function for cron definitions. Sort by weight, then original weight (module/system weight).
+ */
+function _drupal_cron_sort_by_weight($a, $b) {
+  if ($a['weight'] == $b['weight']) {
+    if ($a['original weight'] == $b['original weight']) {
+      return 0;
+    }
+    return $a['original weight'] < $b ['original weight'] ? -1 : 1;
+  }
+  return $a['weight'] < $b ['weight'] ? -1 : 1;
+}
+
+/**
  * Shutdown function: Performs cron cleanup.
  *
  * @see drupal_cron_run()
@@ -7823,3 +7902,162 @@ function drupal_get_filetransfer_info() {
   }
   return $info;
 }
+
+/**
+ * Expand interval from cronrule part
+ *
+ * @param $matches (e.g. 4-43/5+2)
+ *   array of matches:
+ *     [1] = lower
+ *     [2] = upper
+ *     [5] = step
+ *     [7] = offset
+ *
+ * @return
+ *   (string) comma-separated list of values
+ */
+function _drupal_cron_expand_interval($matches) {
+  $result = array();
+  $matches[5] = isset($matches[5]) ? $matches[5] : 1;
+  $matches[7] = isset($matches[7]) ? $matches[7] : 0;
+  if ($matches[5] <= 0) {
+    return '';
+  }
+  $step = ($matches[5] > 0) ? $matches[5] : 1;
+  for ($i = $matches[1]; $i <= $matches[2]; $i += $step) {
+    $result[] = ($i + $matches[7]) % ($matches[2] + 1);
+  }
+  return implode(',', $result);
+}
+
+/**
+ * Expand range from cronrule part
+ *
+ * @param $rule
+ *   (string) cronrule part, e.g.: 1,2,3,4-43/5
+ * @param $max
+ *   (string) boundaries, e.g.: 0-59
+ * @param $digits
+ *   (int) number of digits of value (leading zeroes)
+ * @return
+ *   (array) array of valid values
+ */
+function _drupal_cron_expand_range($rule, $max) {
+  $rule = str_replace("*", $max, $rule);
+  $rule = preg_replace_callback('/(\d+)-(\d+)((\/(\d+))(\+(\d+))?)?/', '_drupal_cron_expand_interval', $rule);
+  if (!preg_match('/([^0-9\,])/', $rule)) {
+    $rule = explode(',', $rule);
+    rsort($rule);
+  }
+  else {
+    $rule = array();
+  }
+  return $rule;
+}
+
+/**
+ * Generate regex rules
+ *
+ * @param $rule
+ *   (string) cronrule, e.g: 1,2,3,4-43/5 * * * 2,5
+ * @return
+ *   (array) date and time regular expression for mathing rule
+ */
+function _drupal_cron_get_intervals($rule) {
+  $parts = preg_split('/\s+/', $rule);
+  // if ($this->allow_shorthand) $parts += array('*', '*', '*', '*', '*'); // Allow short rules by appending wildcards?
+  if (count($parts) != 5) return FALSE;
+  $intervals = array();
+  $intervals['minutes']  = _drupal_cron_expand_range($parts[0], '0-59');
+  if (empty($intervals['minutes'])) return FALSE;
+  $intervals['hours']    = _drupal_cron_expand_range($parts[1], '0-23');
+  if (empty($intervals['hours'])) return FALSE;
+  $intervals['days']     = _drupal_cron_expand_range($parts[2], '1-31');
+  if (empty($intervals['days'])) return FALSE;
+  $intervals['months']   = _drupal_cron_expand_range($parts[3], '1-12');
+  if (empty($intervals['months'])) return FALSE;
+  $intervals['weekdays'] = _drupal_cron_expand_range($parts[4], '0-6');
+  if (empty($intervals['weekdays'])) return FALSE;
+  $intervals['weekdays'] = array_flip($intervals['weekdays']);
+
+  return $intervals;
+}
+
+/**
+ * Get last execution time of rule in unix timestamp format
+ *
+ * @param $time
+ *   (int) time to use as relative time (default now)
+ * @return
+ *   (int) unix timestamp of last execution time
+ */
+function drupal_cron_get_last_scheduled($rule, $time = NULL) {
+  // Current time round to last minute
+  if (!isset($time)) $time = time();
+  $time = floor($time / 60) * 60;
+
+  // Generate regular expressions from rule
+  $intervals = _drupal_cron_get_intervals($rule);
+  if ($intervals === FALSE) return FALSE;
+
+  // Get starting points
+  $start_year   = date('Y', $time);
+  $end_year     = $start_year - 28; // Go back max 28 years (leapyear * weekdays)
+  $start_month  = date('n', $time);
+  $start_day    = date('j', $time);
+  $start_hour   = date('G', $time);
+  $start_minute = (int)date('i', $time);
+
+  // If both weekday and days are restricted, then use either or
+  // otherwise, use and ... when using or, we have to try out all the days in the month
+  // and not just to the ones restricted
+  $check_both = (count($intervals['days']) != 31 && count($intervals['weekdays']) != 7) ? FALSE : TRUE;
+  $days = $check_both ? $intervals['days'] : range(31, 1);
+
+  // Find last date and time this rule was run
+  for ($year = $start_year; $year > $end_year; $year--) {
+    foreach ($intervals['months'] as $month) {
+      if ($month < 1 || $month > 12) return FALSE;
+      if ($year >= $start_year && $month > $start_month) continue;
+
+      foreach ($days as $day) {
+        if ($day < 1 || $day > 31) return FALSE;
+        if ($year >= $start_year && $month >= $start_month && $day > $start_day) continue;
+        if (!checkdate($month, $day, $year)) continue;
+
+        // Check days and weekdays using and/or logic
+        $date_array = getdate(mktime(0, 0, 0, $month, $day, $year));
+        if ($check_both) {
+          if (!isset($intervals['weekdays'][$date_array['wday']])) continue;
+        }
+        else {
+          if (
+            !in_array($day, $intervals['days']) &&
+            !isset($intervals['weekdays'][$date_array['wday']])
+          ) continue;
+        }
+
+        if ($day != $start_day || $month != $start_month || $year != $start_year) {
+          $start_hour = 23;
+          $start_minute = 59;
+        }
+        foreach ($intervals['hours'] as $hour) {
+          if ($hour < 0 || $hour > 23) return FALSE;
+          if ($hour > $start_hour) continue;
+          if ($hour < $start_hour) $start_minute = 59;
+          foreach ($intervals['minutes'] as $minute) {
+            if ($minute < 0 || $minute > 59) return FALSE;
+            if ($minute > $start_minute) continue;
+            break 5;
+          }
+        }
+      }
+    }
+  }
+
+  // Create unix timestamp from derived date+time
+  $time = mktime($hour, $minute, 0, $month, $day, $year);
+
+  return $time;
+}
+
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
old mode 100644
new mode 100755
index bb66cbe..79b21e1
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -309,10 +309,17 @@ function aggregator_permission() {
 
 /**
  * Implements hook_cron().
- *
- * Queues news feeds for updates once their refresh interval has elapsed.
  */
 function aggregator_cron() {
+  $items = array();
+  $items['aggregator_cron_queue_feeds'] = array();
+  return $items;
+}
+
+/**
+ * Queues news feeds for updates once their refresh interval has elapsed.
+ */
+function aggregator_cron_queue_feeds() {
   $result = db_query('SELECT * FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array(
     ':time' => REQUEST_TIME,
     ':never' => AGGREGATOR_CLEAR_NEVER
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
old mode 100644
new mode 100755
index 496a043..75b9a71
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -95,10 +95,17 @@ function dblog_init() {
 
 /**
  * Implements hook_cron().
- *
- * Remove expired log messages and flood control events.
  */
 function dblog_cron() {
+  $items = array();
+  $items['dblog_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
+ * Remove expired log messages and flood control events.
+ */
+function dblog_cron_cleanup() {
   // Cleanup the watchdog table.
   $row_limit = variable_get('dblog_row_limit', 1000);
 
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
old mode 100644
new mode 100755
index d1e4f73..a1f3a0d
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -364,6 +364,15 @@ function field_theme() {
  * Implements hook_cron().
  */
 function field_cron() {
+  $items = array();
+  $items['field_cron_sync_purge'] = array();
+  return $items;
+}
+
+/**
+ * Sync'n'purge
+ */
+function field_cron_sync_purge() {
   // Refresh the 'active' status of fields.
   field_sync_field_status();
 
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
old mode 100644
new mode 100755
index 5ee9062..13c597a
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -167,6 +167,16 @@ function node_theme() {
  * Implements hook_cron().
  */
 function node_cron() {
+  $items = array();
+  $items['node_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
+ * Cleanup node history.
+ */
+function node_cron_cleanup() {
+  sleep(120);
   db_delete('history')
     ->condition('timestamp', NODE_NEW_LIMIT, '<')
     ->execute();
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
old mode 100644
new mode 100755
index edd73d3..dfe6486
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -1057,11 +1057,18 @@ function openid_verify_assertion_return_url($service, $response) {
 }
 
 /**
- * Remove expired nonces from the database.
- *
- * Implementation of hook_cron().
+ * Implements hook_cron().
  */
 function openid_cron() {
+  $items = array();
+  $items['openid_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
+ * Remove expired nonces from the database.
+ */
+function openid_cron_cleanup() {
   db_delete('openid_nonce')
     ->condition('expires', REQUEST_TIME, '<')
     ->execute();
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
old mode 100644
new mode 100755
index 8862308..2073677
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -151,10 +151,17 @@ function poll_block_view($delta = '') {
 
 /**
  * Implements hook_cron().
- *
- * Closes polls that have exceeded their allowed runtime.
  */
 function poll_cron() {
+  $items = array();
+  $items['poll_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
+ * Closes polls that have exceeded their allowed runtime.
+ */
+function poll_cron_cleanup() {
   $nids = db_query('SELECT p.nid FROM {poll} p INNER JOIN {node} n ON p.nid = n.nid WHERE (n.created + p.runtime) < :request_time AND p.active = :active AND p.runtime <> :runtime', array(':request_time' => REQUEST_TIME, ':active' => 1, ':runtime' => 0))->fetchCol();
   if (!empty($nids)) {
     db_update('poll')
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
old mode 100644
new mode 100755
index 14ffc80..a82c3b2
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -355,12 +355,19 @@ function search_dirty($word = NULL) {
 
 /**
  * Implements hook_cron().
- *
+ */
+function search_cron() {
+  $items = array();
+  $items['search_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
  * Fires hook_update_index() in all modules and cleans up dirty words.
  *
  * @see search_dirty()
  */
-function search_cron() {
+function search_cron_cleanup() {
   // We register a shutdown function to ensure that search_total is always up
   // to date.
   drupal_register_shutdown_function('search_update_totals');
diff --git a/core/modules/simpletest/tests/common_test.module b/core/modules/simpletest/tests/common_test.module
old mode 100644
new mode 100755
index e1cfdf2..aa9c737
--- a/core/modules/simpletest/tests/common_test.module
+++ b/core/modules/simpletest/tests/common_test.module
@@ -278,13 +278,20 @@ function common_test_js_and_css_querystring() {
 
 /**
  * Implements hook_cron().
- *
+ */
+function common_test_cron() {
+  $items = array();
+  $items['common_test_cron_exception'] = array();
+  return $items;
+}
+
+/**
  * System module should handle if a module does not catch an exception and keep
  * cron going.
  *
  * @see common_test_cron_helper()
  *
  */
-function common_test_cron() {
+function common_test_cron_exception() {
   throw new Exception(t('Uncaught exception'));
 }
diff --git a/core/modules/simpletest/tests/common_test_cron_helper.module b/core/modules/simpletest/tests/common_test_cron_helper.module
old mode 100644
new mode 100755
index 94a2b2c..a06d3f5
--- a/core/modules/simpletest/tests/common_test_cron_helper.module
+++ b/core/modules/simpletest/tests/common_test_cron_helper.module
@@ -6,12 +6,19 @@
 
 /**
  * Implements hook_cron().
- *
- * common_test_cron() throws an exception, but the execution should reach this
+ */
+function common_test_cron_helper_cron() {
+  $items = array();
+  $items['common_test_cron_helper_cron_test'] = array();
+  return $items;
+}
+
+/**
+ * common_test_cron_exception() throws an exception, but the execution should reach this
  * function as well.
  *
- * @see common_test_cron()
+ * @see common_test_cron_exception()
  */
-function common_test_cron_helper_cron() {
+function common_test_cron_helper_cron_test() {
   variable_set('common_test_cron', 'success');
 }
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
old mode 100644
new mode 100755
index eda808f..7c8666a
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -230,6 +230,15 @@ function statistics_user_predelete($account) {
  * Implements hook_cron().
  */
 function statistics_cron() {
+  $items = array();
+  $items['statistics_cron_update'] = array();
+  return $items;
+}
+
+/**
+ * Update statistics.
+ */
+function statistics_cron_update() {
   $statistics_timestamp = variable_get('statistics_day_timestamp', '');
 
   if ((REQUEST_TIME - $statistics_timestamp) >= 86400) {
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index d28ecfb..4dfd95a 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1590,10 +1590,31 @@ function system_cron_settings() {
   );
   $form['cron']['cron_safe_threshold'] = array(
     '#type' => 'select',
-    '#title' => t('Run cron every'),
+    '#title' => t('Run poormans cron every'),
     '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
-    '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
+    '#options' => array(0 => t('Never')) + drupal_map_assoc(array(30, 60, 300, 600, 900, 1800, 3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
   );
+  $form['cron']['cron_rule'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default rule for cron'),
+    '#value' => variable_get('cron_rule', DRUPAL_CRON_RULE),
+  );
+  $rows = array();
+  foreach (drupal_get_cron() as $name => $item) {
+    if (lock_may_be_available('cron_' . $name)) {
+      $last_run = variable_get('cron_last_' . $name, NULL);
+      $last_run = $last_run ? format_date($last_run, 'custom', 'Y-m-d H:i:s') : t('Never');
+    }
+    else {
+      $last_run = t('Running');
+    }
+    $rows[] = array($name, $item['module'], implode('; ', $item['rules']), $last_run);
+  }
+  $form['cron']['jobs'] = array(
+    '#markup' => theme('table', array(
+    'header' => array(t('Name'), t('Module'), t('Rules'), t('Last run')),
+    'rows' => $rows,
+  )));
 
   return system_settings_form($form);
 }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
old mode 100644
new mode 100755
index 38c3f55..09a64b8
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2998,10 +2998,17 @@ function system_get_module_admin_tasks($module, $info) {
 
 /**
  * Implements hook_cron().
- *
- * Remove older rows from flood and batch table. Remove old temporary files.
  */
 function system_cron() {
+  $items = array();
+  $items['system_cron_cleanup'] = array();
+  return $items;
+}
+
+/**
+ * Remove older rows from flood and batch table. Remove old temporary files.
+ */
+function system_cron_cleanup() {
   // Cleanup the flood.
   db_delete('flood')
     ->condition('expiration', REQUEST_TIME, '<')
@@ -4010,3 +4017,8 @@ function system_admin_paths() {
   );
   return $paths;
 }
+
+function system_cron_alter(&$items) {
+  $items['node_cron_cleanup']['weight'] = -1;
+}
+
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
old mode 100644
new mode 100755
index 22c8a57..68bb260
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -77,6 +77,15 @@ function tracker_menu() {
  * process, 'tracker_index_nid' will be 0.
  */
 function tracker_cron() {
+  $items = array();
+  $items['tracker_cron_calculate'] = array();
+  return $items;
+}
+
+/**
+ * Calculates tracking.
+ */
+function tracker_cron_calculate() {
   $max_nid = variable_get('tracker_index_nid', 0);
   $batch_size = variable_get('tracker_batch_size', 1000);
   if ($max_nid > 0) {
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
old mode 100644
new mode 100755
index 6577af7..2bed23c
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -286,6 +286,15 @@ function update_theme() {
  * Implements hook_cron().
  */
 function update_cron() {
+  $items = array();
+  $items['update_cron_refresh'] = array();
+  return $items;
+}
+
+/**
+ * Refresh update status.
+ */
+function update_cron_refresh() {
   $frequency = variable_get('update_check_frequency', 1);
   $interval = 60 * 60 * 24 * $frequency;
   if ((REQUEST_TIME - variable_get('update_last_check', 0)) > $interval) {
