Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
+190
-18
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Task Model
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 5 Aug 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -23,6 +23,79 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Task extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
/**
|
||||
* Run a background queue task
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function runQueue()
|
||||
{
|
||||
$queueData = \IPS\Db::i()->select( '*', 'core_queue', NULL, 'priority ASC, RAND()', 1 )->first();
|
||||
$newOffset = 0;
|
||||
|
||||
$queueData['_originalOffset'] = $queueData['offset'];
|
||||
|
||||
try
|
||||
{
|
||||
$extensions = \IPS\Application::load( $queueData['app'] )->extensions( 'core', 'Queue', FALSE );
|
||||
if ( !isset( $extensions[ $queueData['key'] ] ) )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
$class = new $extensions[ $queueData['key'] ];
|
||||
$json = json_decode( $queueData['data'], TRUE );
|
||||
$newOffset = $class->run( $json, $queueData['offset'] );
|
||||
|
||||
$queueData['offset'] = $newOffset;
|
||||
|
||||
/* This is here for legacy purposes - background queue tasks should throw \IPS\Task\Queue\OutOfRangeException to indicate they are completed, but
|
||||
for now we'll still support this and log it for investigation */
|
||||
if ( is_null( $newOffset ) )
|
||||
{
|
||||
\IPS\Log::log( $queueData['key'] . " returned a NULL offset - tasks should throw \\IPS\\Task\\Queue\\OutOfRangeException when they are finished", 'runQueue_log' );
|
||||
\IPS\Db::i()->delete( 'core_queue', array( 'id=?', $queueData['id'] ) );
|
||||
|
||||
/* Do we have a post-completion callback? */
|
||||
if( method_exists( $class, 'postComplete' ) )
|
||||
{
|
||||
$class->postComplete( $queueData );
|
||||
}
|
||||
}
|
||||
/* Task completed successfully and a new offset was returned - store the value and then return it */
|
||||
else
|
||||
{
|
||||
\IPS\Log::debug( $queueData['key'] . " returned a new offset of " . $newOffset, 'runQueue_log' );
|
||||
|
||||
\IPS\Db::i()->update( 'core_queue', array( 'offset' => $newOffset ), array( 'id=?', $queueData['id'] ) );
|
||||
|
||||
$newData = json_encode( $json );
|
||||
|
||||
/* Did it change?? */
|
||||
if ( $newData !== $json )
|
||||
{
|
||||
$queueData['data'] = $newData;
|
||||
\IPS\Db::i()->update( 'core_queue', array( 'data' => $newData ), array( 'id=?', $queueData['id'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
/* This means the task is done */
|
||||
catch( \IPS\Task\Queue\OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Log::debug( $queueData['key'] . " completed successfully", 'runQueue_log' );
|
||||
|
||||
\IPS\Db::i()->delete( 'core_queue', array( 'id=?', $queueData['id'] ) );
|
||||
|
||||
/* Do we have a post-completion callback? */
|
||||
if( isset( $class ) AND method_exists( $class, 'postComplete' ) )
|
||||
{
|
||||
$class->postComplete( $queueData );
|
||||
}
|
||||
}
|
||||
|
||||
return $queueData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a background task
|
||||
*
|
||||
@@ -94,7 +167,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
'priority' => $priority
|
||||
) );
|
||||
|
||||
\IPS\DB::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'queue' ) );
|
||||
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'queue' ) );
|
||||
}
|
||||
|
||||
/* !Task */
|
||||
@@ -107,21 +180,25 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
public static function queued()
|
||||
{
|
||||
$fifteenMinutesAgo = ( time() - 900 );
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_tasks', array( 'next_run<? AND enabled=1', ( time() + 60 ) ), 'next_run ASC' ) as $task )
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_tasks', array( 'next_run<? AND enabled=1', ( time() + 60 ) ), 'next_run ASC', NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER ) as $task )
|
||||
{
|
||||
$task = static::constructFromData( $task );
|
||||
|
||||
if ( !$task->running or $task->next_run < $fifteenMinutesAgo )
|
||||
try
|
||||
{
|
||||
if ( $task->running )
|
||||
$task = static::constructFromData( $task );
|
||||
|
||||
if ( !$task->running or $task->next_run < $fifteenMinutesAgo )
|
||||
{
|
||||
$task->unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $task;
|
||||
if ( $task->running )
|
||||
{
|
||||
$task->unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \RuntimeException $e ) { }
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -174,6 +251,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
$this->running = 0;
|
||||
$this->lock_count = 0;
|
||||
$this->next_run = \IPS\DateTime::create()->add( new \DateInterval( $this->frequency ) )->getTimestamp();
|
||||
$this->last_run = \IPS\DateTime::create()->getTimestamp();
|
||||
$this->save();
|
||||
|
||||
return $output;
|
||||
@@ -195,6 +273,94 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run until timeout
|
||||
*
|
||||
* @param callback $callback The code to run. Should return TRUE if we're happy to keep going, and FALSE if there's no more work to do
|
||||
* @return void
|
||||
*/
|
||||
public function runUntilTimeout( $callback )
|
||||
{
|
||||
/* Work out the maximum execution time */
|
||||
$timeLeft = 45;
|
||||
if ( $phpMaxExecutionTime = ini_get('max_execution_time') and $phpMaxExecutionTime <= $timeLeft )
|
||||
{
|
||||
$timeLeft = $phpMaxExecutionTime - 2;
|
||||
}
|
||||
|
||||
/* Factor in wait_timeout if possible */
|
||||
try
|
||||
{
|
||||
$mysqlTimeout = \IPS\Db::i()->query( "SHOW SESSION VARIABLES LIKE 'wait_timeout'" )->fetch_assoc();
|
||||
$mysqlTimeout = $mysqlTimeout['Value'];
|
||||
|
||||
if( $mysqlTimeout <= $timeLeft )
|
||||
{
|
||||
$timeLeft = $mysqlTimeout - 2;
|
||||
}
|
||||
}
|
||||
catch( \IPS\Db\Exception $e ){}
|
||||
|
||||
$timeTheLastRunTook = 0;
|
||||
|
||||
/* Work out the memory limit */
|
||||
$memoryLeft = 0;
|
||||
$memoryUnlimited = FALSE;
|
||||
if ( function_exists( 'memory_get_usage' ) )
|
||||
{
|
||||
$memory_limit = ini_get('memory_limit');
|
||||
if ( $memory_limit == -1 )
|
||||
{
|
||||
$memoryUnlimited = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( preg_match('/^(\d+)(.)$/', $memory_limit, $matches ) )
|
||||
{
|
||||
if ( $matches[2] == 'G' )
|
||||
{
|
||||
$memory_limit = $matches[1] * 1024 * 1024 * 1024;
|
||||
}
|
||||
elseif ( $matches[2] == 'M' )
|
||||
{
|
||||
$memory_limit = $matches[1] * 1024 * 1024;
|
||||
}
|
||||
elseif ( $matches[2] == 'K' )
|
||||
{
|
||||
$memory_limit = $matches[1] * 1024;
|
||||
}
|
||||
}
|
||||
$memoryLeft = $memory_limit - memory_get_usage( TRUE );
|
||||
}
|
||||
}
|
||||
$memoryTheLastRunTook = 0;
|
||||
|
||||
/* Run until we run out of time */
|
||||
do
|
||||
{
|
||||
/* Start a timer */
|
||||
$timer = microtime( TRUE );
|
||||
$memoryTimer = function_exists( 'memory_get_usage' ) ? memory_get_usage( TRUE ) : 0;
|
||||
|
||||
/* Execute */
|
||||
if ( call_user_func( $callback ) === FALSE )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Decrease the time left */
|
||||
$timeTheLastRunTook = round( ( microtime( TRUE ) - $timer ), 2 );
|
||||
$timeLeft -= $timeTheLastRunTook;
|
||||
$memoryTheLastRunTook = function_exists( 'memory_get_usage' ) ? ( memory_get_usage( TRUE ) - $memoryTimer ) : 0;
|
||||
if ( !$memoryUnlimited )
|
||||
{
|
||||
$memoryLeft = $memory_limit - memory_get_usage( TRUE );
|
||||
}
|
||||
|
||||
}
|
||||
while ( $timeLeft > $timeTheLastRunTook and ( $memoryUnlimited or $memoryLeft > $memoryTheLastRunTook ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
@@ -239,11 +405,10 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $taskDirectory Directory where PHP files are stored
|
||||
* @param string $subpackage The value to use for the subpackage in the task file's header
|
||||
* @param string $namespace The namespace for the task file
|
||||
* @param int $version The application/plugin's current version
|
||||
* @param int|string $appKeyOrPluginId If taks belongs to an application, it's key, or if a plun, it's ID
|
||||
* @return string
|
||||
*/
|
||||
public static function devTable( $json, $url, $taskDirectory, $subpackage, $namespace, $version, $appKeyOrPluginId )
|
||||
public static function devTable( $json, $url, $taskDirectory, $subpackage, $namespace, $appKeyOrPluginId )
|
||||
{
|
||||
if ( !file_exists( $json ) )
|
||||
{
|
||||
@@ -356,15 +521,12 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
"{subpackage}\n",
|
||||
'{date}',
|
||||
'{namespace}',
|
||||
'{version_long}',
|
||||
),
|
||||
array(
|
||||
$values['dev_task_key'],
|
||||
( $subpackage != 'core' ) ? ( " * @subpackage\t" . $subpackage . "\n" ) : '',
|
||||
date( 'd M Y' ),
|
||||
$namespace,
|
||||
$version,
|
||||
|
||||
),
|
||||
file_get_contents( \IPS\ROOT_PATH . "/applications/core/data/defaults/Task.txt" )
|
||||
) );
|
||||
@@ -491,6 +653,11 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'key' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
|
||||
/**
|
||||
* Construct ActiveRecord from database row
|
||||
*
|
||||
@@ -513,6 +680,11 @@ class _Task extends \IPS\Patterns\ActiveRecord
|
||||
\IPS\IPS::monkeyPatch( 'IPS\pluginTasks', $data['key'] );
|
||||
}
|
||||
|
||||
if ( ! class_exists( $classname ) )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
|
||||
Reference in new issue
Block a user