diff --git a/wp-cron.php b/wp-cron.php index efd66c68b6..f7b3fa16b3 100644 --- a/wp-cron.php +++ b/wp-cron.php @@ -23,31 +23,41 @@ require_once('./wp-load.php'); if ( $_GET['check'] != wp_hash('187425') ) exit; -if ( get_option('doing_cron') > time() ) - exit; - -update_option('doing_cron', time() + 30); +$local_time = time(); $crons = _get_cron_array(); -$keys = array_keys($crons); -if (!is_array($crons) || $keys[0] > time()) - return; +$keys = array_keys( $crons ); + +if (!is_array($crons) || $keys[0] > $local_time) { + update_option('doing_cron', 0); + return; +} + +foreach ($crons as $timestamp => $cronhooks) { + + if ( $timestamp > $local_time ) + break; -foreach ($crons as $timestamp => $cronhooks) { - if ($timestamp > time()) break; foreach ($cronhooks as $hook => $keys) { - foreach ($keys as $key => $args) { - $schedule = $args['schedule']; + + foreach ($keys as $k => $v) { + + $schedule = $v['schedule']; + if ($schedule != false) { - $new_args = array($timestamp, $schedule, $hook, $args['args']); + $new_args = array($timestamp, $schedule, $hook, $v['args']); call_user_func_array('wp_reschedule_event', $new_args); } - wp_unschedule_event($timestamp, $hook, $args['args']); - do_action_ref_array($hook, $args['args']); + + wp_unschedule_event($timestamp, $hook, $v['args']); + + do_action_ref_array($hook, $v['args']); } } } update_option('doing_cron', 0); -?> \ No newline at end of file +die(); + +?> diff --git a/wp-includes/cron.php b/wp-includes/cron.php index 50a95fde55..cdd11ea172 100644 --- a/wp-includes/cron.php +++ b/wp-includes/cron.php @@ -152,17 +152,47 @@ function wp_next_scheduled( $hook, $args = array() ) { * * @return null Cron could not be spawned, because it is not needed to run. */ -function spawn_cron() { - $crons = _get_cron_array(); +function spawn_cron( $local_time ) { + global $current_blog; + /* + * do not even start the cron if local server timer has drifted + * such as due to power failure, or misconfiguration + */ + $timer_accurate = check_server_timer( $local_time ); + if ( !$timer_accurate ) + return; + + //sanity check + $crons = _get_cron_array(); if ( !is_array($crons) ) return; $keys = array_keys( $crons ); - if ( array_shift( $keys ) > time() ) + $timestamp = $keys[0]; + if ( $timestamp > $local_time ) return; $cron_url = get_option( 'siteurl' ) . '/wp-cron.php?check=' . wp_hash('187425'); + /* + * multiple processes on multiple web servers can run this code concurrently + * try to make this as atomic as possible by setting doing_cron switch + */ + $flag = get_option('doing_cron'); + + // clean up potential invalid value resulted from various system chaos + if ( $flag != 0 ) { + if ( $flag > $local_time + 10*60 || $flag < $local_time - 10*60 ) { + update_option('doing_cron', 0); + $flag = 0; + } + } + + //don't run if another process is currently running it + if ( $flag > $local_time ) + return; + + update_option( 'doing_cron', $local_time + 30 ); wp_remote_post($cron_url, array('timeout' => 0.01, 'blocking' => false)); } @@ -175,6 +205,7 @@ function spawn_cron() { * @return null When doesn't need to run Cron. */ function wp_cron() { + // Prevent infinite loops caused by lack of wp-cron.php if ( strpos($_SERVER['REQUEST_URI'], '/wp-cron.php') !== false ) return; @@ -188,13 +219,14 @@ function wp_cron() { if ( isset($keys[0]) && $keys[0] > time() ) return; + $local_time = time(); $schedules = wp_get_schedules(); foreach ( $crons as $timestamp => $cronhooks ) { - if ( $timestamp > time() ) break; + if ( $timestamp > $local_time ) break; foreach ( (array) $cronhooks as $hook => $args ) { if ( isset($schedules[$hook]['callback']) && !call_user_func( $schedules[$hook]['callback'] ) ) continue; - spawn_cron(); + spawn_cron( $local_time ); break 2; } } @@ -327,4 +359,9 @@ function _upgrade_cron_array($cron) { return $new_cron; } +// stub for checking server timer accuracy, using outside standard time sources +function check_server_timer( $local_time ) { + return true; +} + ?>