public TaskExecutionInfo ProcessTaskDefinition(SingleTaskDefinition taskDefinition) { var lockInfo = LockInfo.Empty(taskDefinition.Identifier); var taskTypeInfo = _taskManager.GetTaskInfo(taskDefinition.Identifier); //links this task's cancellation token to the manager's token var ctSource = CancellationTokenSource.CreateLinkedTokenSource(_listenerCtSource.Token); try { //Inter-process lock. Ignores the task if it doesn't acquire lock if (taskTypeInfo == null || !_taskManager.LockManager.TryRenewLock(lockInfo, taskTypeInfo.LockCycle, retryLock: true)) { return(new TaskExecutionInfo(_managedThreadPool.CreateCompletedTask(), ctSource, lockInfo, taskDefinition)); } var executionInfoLocal = new TaskExecutionInfo(null, ctSource, lockInfo, taskDefinition); var taskBody = CreateTaskBody(executionInfoLocal, taskTypeInfo); var task = _managedThreadPool.QueueUserWorkItem((o) => taskBody(), null); executionInfoLocal.SetTask(task); return(executionInfoLocal); } catch (Exception ex) { ex.LogException(); return(new TaskExecutionInfo(_managedThreadPool.CreateCompletedTask(), ctSource, lockInfo, taskDefinition)); } }
private Action CreateTaskBody(TaskExecutionInfo executionInfoLocal, TaskTypeInfo taskTypeInfo) { return(() => { try { executionInfoLocal.SendAliveSignal(); executionInfoLocal.SetTaskThread(Thread.CurrentThread); if (executionInfoLocal.CancellationTokenSource.IsCancellationRequested) { return; } if (!SetRunningStatus(executionInfoLocal, taskTypeInfo)) { return; } executionInfoLocal.SendAliveSignal(); //Flags executionInfoLocal right before invoking the task implementation executionInfoLocal.RealTaskInvokedFlag = true; taskTypeInfo.Invoke(executionInfoLocal.TaskDefinition.JsonParameters, executionInfoLocal); SetCompletedOrCancelledStatus(executionInfoLocal); } catch (ThreadAbortException ex) { //Tries to save information about the task status. //Reverts ThreadAbort Thread.ResetAbort(); SetAbortedStatus(executionInfoLocal); } catch (Exception ex) { ex.LogException(); SetCompletedOrCancelledStatus(executionInfoLocal); } finally { try { //See comment below executionInfoLocal.LockWillBeReleasedFlag = true; _taskManager.LockManager.Release(executionInfoLocal.LockInfo); //**** LockWillBeReleasedFlag avoids having the task being aborted right here when it's about to end, because we just released the lock // and the lifetime manager could try to cancel it forcefully. } catch (Exception ex) { ex.LogException(); } } }); }
internal void Invoke(string jsonParameter, TaskExecutionInfo executionInfo) { object parameter = null; if (jsonParameter != null) { parameter = JsonConvert.DeserializeObject(jsonParameter, this.ParamType); } var instance = Activator.CreateInstance(this.TaskType); TaskTypeInfo.TypedInvoke((dynamic)instance, (dynamic)parameter, executionInfo); }
private bool SetRunningStatus(TaskExecutionInfo executionInfoLocal, TaskTypeInfo taskTypeInfo) { var lockInfo = executionInfoLocal.LockInfo; var taskDefinition = executionInfoLocal.TaskDefinition; //It may be that the task scheduler took too long to start the thread. //Renews the lock. Ignores the task if it doesn't acquire lock if (!_taskManager.LockManager.TryRenewLock(lockInfo, taskTypeInfo.LockCycle, retryLock: true)) { return(false); } using (var tr = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var conn = _taskManager.CreateConnection()) using (var command = DbAccessHelper.CreateDbCommand(conn, SET_RUNNING_STATUS_SQL)) { //milisecond precision var lastRunAt = _taskManager.GetUtcNow().SetFractionalSecondPrecision(3); var lockState = lockInfo.AsImmutable(); command.Parameters.Add(new SqlParameter("Id", taskDefinition.Id)); command.Parameters.Add(new SqlParameter("LastRunAt", System.Data.SqlDbType.DateTime2) { Value = lastRunAt }); command.Parameters.Add(new SqlParameter("LockedUntil", System.Data.SqlDbType.DateTime2) { Value = lockState.LockedUntil }); //Has the task been deleted or already handled? if (command.ExecuteNonQuery() != 1) { return(false); } executionInfoLocal.LastRunAt = lastRunAt; return(true); } }
private void SetCompletedOrCancelledStatus(TaskExecutionInfo executionInfoLocal) { executionInfoLocal.SendAliveSignal(); var lockInfo = executionInfoLocal.LockInfo; var taskDefinition = executionInfoLocal.TaskDefinition; /* We should not renew lock here because the task has already completed */ //If the task didn't run, don't do anything. if (executionInfoLocal.LastRunAt == null) { return; } try { using (var tr = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var conn = _taskManager.CreateConnection()) using (var command = DbAccessHelper.CreateDbCommand(conn, SET_COMPLETED_STATUS_FOR_RUNNING)) { var newStatus = executionInfoLocal.IsCancellationRequested ? Distributed.SingleTaskStatus.CompletedCancelled : Distributed.SingleTaskStatus.Completed; command.Parameters.Add(new SqlParameter("Id", taskDefinition.Id)); command.Parameters.Add(new SqlParameter("LastRunAt", System.Data.SqlDbType.DateTime2) { Value = executionInfoLocal.LastRunAt }); command.Parameters.Add(new SqlParameter("NewStatus", (int)newStatus)); command.ExecuteNonQuery(); } } catch (Exception ex) { ex.LogException(); } }
private void RenewTaskLock(TaskExecutionInfo taskInfo) { //renews lock if (_taskManager.LockManager.TryRenewLock(taskInfo.LockInfo)) { try { using (var tr = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var conn = _taskManager.CreateConnection()) using (var command = DbAccessHelper.CreateDbCommand(conn, SET_LOCKEDUNTIL_SQL)) { var lockedUntil = taskInfo.LockInfo.AsImmutable().LockedUntil; if (lockedUntil == null) { return; } command.Parameters.Add(new SqlParameter("Id", taskInfo.TaskDefinition.Id)); command.Parameters.Add(new SqlParameter("LastRunAt", System.Data.SqlDbType.DateTime2) { Value = taskInfo.LastRunAt }); command.Parameters.Add(new SqlParameter("LockedUntil", System.Data.SqlDbType.DateTime2) { Value = lockedUntil }); command.ExecuteNonQuery(); } } catch (Exception ex) { ex.LogException(); } } }
internal static void TypedInvoke <P>(IDistributedTask <P> instance, P parameter, TaskExecutionInfo executionInfo) { instance.Run(parameter, executionInfo); }