Beispiel #1
0
        /// <summary>
        /// Method used as a thread starting point for tasks
        /// </summary>
        private void TaskThread(TaskEntry entry)
        {
            LOG("TaskThread('" + entry + "') - Start");
            ASSERT(entry != null, "Missing parameter 'entry'");

            System.Exception callbackException = null;
            try
            {
                // Copy CultureInfos to this thread
                if (entry.CreatorCultureInfo != null)
                {
                    Thread.CurrentThread.CurrentCulture = entry.CreatorCultureInfo;
                }
                if (entry.CreatorUICultureInfo != null)
                {
                    Thread.CurrentThread.CurrentUICulture = entry.CreatorUICultureInfo;
                }

                entry.Callback(entry);
            }
            catch (System.Exception ex)
            {
                callbackException = ex;
            }

            // The task's thread is terminated. Remove it from this queue.
            // No need to Abort() it => /*abortRunningTask=*/false
            // but 'launchChecksAtEnd=true' will launch CheckRunningTasks() after removing it from 'RunningTasks' (if its status is 'Running' which it still should be...)
            LOG("TaskThread('" + entry + "') - Removing TaskEntry");
            entry.Remove();              // NB: If the task has already been manually Remove()d, this won't have any effect
            // entry.Status should now be 'Removed'

            if (callbackException != null)
            {
                // The task threw an exception. Notify it
                LOG("TaskThread('" + entry + "') - The task threw an exception: " + callbackException);
                entry.SetCallbackException(callbackException);
            }

            LOG("TaskThread('" + entry + "') - Calling entry.TriggerOnTerminated()");
            entry.Terminate();

            LOG("TaskThread('" + entry + "') - Exit");
        }
Beispiel #2
0
        /// <summary>
        /// The one and only method to add a task to this queue.
        /// </summary>
        private TaskEntry CreateTask(DateTime executionDate, Action <TaskEntry> callback)
        {
            LOG("CreateTask('" + executionDate + "') - Start");
            ASSERT(executionDate.Kind == DateTimeKind.Utc, "CreateTimer() called with a non-UTC DateTime");

            TaskEntry entry;

            lock ( LockObject )
            {
                LOG("CreateTask('" + executionDate + "') - Lock acquired");
                if (Disposed)
                {
                    // Cannot create tasks anymore
                    throw new ApplicationException("This TasksQueue instance is disposed");
                }

                entry = new TaskEntry(this, executionDate, TaskEntry.Statuses.Delayed, callback);
                CheckValidity();

                List <Guid> list;
                if (!DelayedTasks.TryGetValue(entry.ExecutionDate, out list))
                {
                    list = new List <Guid>();
                    DelayedTasks.Add(entry.ExecutionDate, list);
                }
                list.Add(entry.ID);

                AllTasks.Add(entry.ID, entry);

                CheckValidity();
            }
            CheckDelayedTasks();

            LOG("CreateTask('" + executionDate + "') - Exit");
            return(entry);
        }
Beispiel #3
0
        /// <summary>
        /// The one and only method that can be used to remove a task from this queue.
        /// </summary>
        /// <remarks>Can only be called from the TastEntry itself</remarks>
        internal void Remove(TaskEntry entry)
        {
            LOG("Remove('" + entry + ") - Start");
            ASSERT(entry != null, "Missing parameter 'entry'");

            bool launchChecksAtEnd;
            bool checkDelayedTasks = false;
            bool checkRunningTasks = false;

            lock ( LockObject )
            {
                LOG("Remove('" + entry + ") - Lock acquired");
                CheckValidity();

                launchChecksAtEnd = Disposed ? false : true;

                entry.GetStatus((status) =>
                {
                    var id = entry.ID;
                    switch (status)
                    {
                    case TaskEntry.Statuses.Removed:
                        // Already removed
                        // ASSERT( !AllTasks.ContainsKey(id), "The task is declared as Removed but is still in AllTasks" );	<= This can happen when 2 threads are removing the same TaskEntry at the same time => log but don't assert
                        LOG("Remove('" + entry + ") - Is already removed");
                        return;

                    case TaskEntry.Statuses.Delayed: {
                        // Remove item from 'DelayedTasks'
                        LOG("Remove('" + entry + ") - Is delayed");
                        var executionDate = entry.ExecutionDate;
                        var list          = DelayedTasks[executionDate];
                        var rc            = list.Remove(id);
                        ASSERT(rc, "Task was not in 'DelayedTasks'");
                        if (list.Count == 0)
                        {
                            // No more task for this DateTime
                            LOG("Remove('" + entry + ") - No more task for " + executionDate);
                            DelayedTasks.Remove(executionDate);
                        }

                        // The list has changed
                        checkDelayedTasks = true;
                        break;
                    }

                    case TaskEntry.Statuses.Queued: {
                        LOG("Remove('" + entry + ") - Is Queued");
                        // Recreate 'QueuedTasks' without this task's ID
                        QueuedTasks = new Queue <Guid>(QueuedTasks.Where((itemId) => (itemId != id)));

                        // The list has changed
                        checkRunningTasks = true;
                        break;
                    }

                    case TaskEntry.Statuses.Running: {
                        LOG("Remove('" + entry + ") - Is Running");
                        var rc = RunningTasks.Remove(id);
                        ASSERT(rc, "Task was not in 'RunningTasks'");

                        checkRunningTasks = true;
                        break;
                    }

                    default:
                        throw new NotImplementedException("Unknown task status '" + status + "'");
                    }

                    {
                        // Remove the task from 'AllTasks' and set its status to 'Removed'
                        LOG("Remove('" + entry + ") - Removing from AllTasks");
                        var rc = AllTasks.Remove(id);
                        ASSERT(rc, "Task was not in 'AllTasks'");
                        entry.SetStatus(TaskEntry.Statuses.Removed);
                    }
                });

                CheckValidity();
            }
            LOG("Remove('" + entry + ") - Lock released");

            if (launchChecksAtEnd)
            {
                if (checkDelayedTasks)
                {
                    // The 'DelayedTasks' has changed
                    CheckDelayedTasks();
                }

                if (checkRunningTasks)
                {
                    // The 'RunningTasks' has changed
                    CheckRunningTasks();
                }

                LOG("Remove('" + entry + ") - Invoking " + onEntryRemoved.Count + " callbacks for 'OnEntryRemoved' event");
                onEntryRemoved.Invoke(entry);
            }

            LOG("Remove('" + entry + ") - Exit");
        }
Beispiel #4
0
        public void ForEach <T>(T[] arguments, Action <TaskEntry, T> action)
        {
            ASSERT(arguments != null, "Missing parameter 'arguments'");
            ASSERT(action != null, "Missing parameter 'action'");

            // Create arguments.Length TaskEntries
            int count              = arguments.Length;
            var resetEvent         = new ManualResetEvent(false);
            var entries            = new TaskEntry[count];
            var exceptionContainer = new VolatileContainer <Exception> {
                Value = null
            };

            for (int i = 0; i < arguments.Length; ++i)
            {
                var argument = arguments[i];
                entries[i] = CreateTask((entry) =>
                {
                    try
                    {
                        action(entry, argument);
                    }
                    catch (System.Exception ex)
                    {
                        lock ( exceptionContainer )
                        {
                            // If this is the first exception we catch ...
                            if (exceptionContainer.Value == null)
                            {
                                // ... save it
                                exceptionContainer.Value = ex;
                            }
                        }
                        // Release the main thread now so it can 'Remove()' all remaining TaskEntries
                        resetEvent.Set();
                    }
                    finally
                    {
                        var newCount = Interlocked.Decrement(ref count);
                        if (newCount == 0)
                        {
                            // The last TaskEntry has finished
                            resetEvent.Set();
                        }
                    }
                });
            }

            // Wait for all TaskEntries to exit
            resetEvent.WaitOne(new TimeSpan(0, 0, 30));

            // if the resetEvent exited because of an exception ...
            if (exceptionContainer.Value != null)
            {
                // ... discard all remaining TaskEntries (it is the responsibility of the caller to check 'entry.IsRemoved()') ...
                foreach (var entry in entries)
                {
                    entry.Remove();
                }
                // ... and rethrow the caught exception
                throw exceptionContainer.Value;
            }
        }
Beispiel #5
0
 public void Dispose()
 {
     if( Disposed )
         // Already disposed
         return;
     Disposed = true;
     if( Connection != null )
     {
         try { Connection.SendResponseMessage(RootMessage.CreateResetRootMessage()); }
         catch( System.Exception ex ) { ConnectionList.FatalExceptionHandler( "Call to Connection.SendReset() threw an exception", ex ); }
     }
     if( DisconnectionTimeout != null )
     {
         try { DisconnectionTimeout.Remove(); }
         catch( System.Exception ex ) { ConnectionList.FatalExceptionHandler( "Call to DisconnectionTimeout.Remove() threw an exception", ex ); }
         DisconnectionTimeout = null;
     }
     if( StaleTimeout != null )
     {
         try { StaleTimeout.Remove(); }
         catch( System.Exception ex ) { ConnectionList.FatalExceptionHandler( "Call to StaleTimeout.Remove() threw an exception", ex ); }
         StaleTimeout = null;
     }
 }
Beispiel #6
0
        private void HandleMessageThread(MessageContext messageContext, TaskEntry taskEntry)
        {
            ASSERT( messageContext != null, "Missing parameter 'messageContext'" );
            // ASSERT( taskEntry != null, "Missing parameter 'taskEntry'" ); => 'null' means "CallbackItem.CallbackThreaded == null"

            var message = messageContext.Message;
            var callbackItem = messageContext.CallbackItem;

            LOG( "HandleMessageThread(" + taskEntry + "," + message + ") - Start" );

            bool contextRestored = false;
            try
            {
                if(! callbackItem.IsThreaded )
                {
                    // Inline message handler => This thread is still the HTTP handler's thread => No need to restore thread's context

                    ASSERT( callbackItem.CallbackDirect != null, "If not 'IsThreaded' then 'callbackItem.CallbackDirect' is supposed to be set" );
                    ASSERT( callbackItem.CallbackThreaded == null, "If not 'IsThreaded' then 'callbackItem.CallbackThreaded' is supposed to be null" );

                    callbackItem.CallbackDirect( message );
                }
                else  // Threaded message handler => Must restore some of the HTTP handler's thread context
                {
                    ASSERT( callbackItem.CallbackDirect == null, "If 'IsThreaded' then 'callbackItem.CallbackDirect' is supposed to be null" );
                    ASSERT( callbackItem.CallbackThreaded != null, "If 'IsThreaded' then 'callbackItem.CallbackThreaded' is supposed to be set" );

                    // Restore message's context
                    messageContext.RestoreContext();
                    contextRestored = true;

                    callbackItem.CallbackThreaded( taskEntry, message );
                }
                LOG( "HandleMessageThread(" + taskEntry + "," + message + ") - Exit" );
            }
            catch( System.Reflection.TargetInvocationException ex )
            {
                LOG( "HandleMessageThread(" + taskEntry + "," + message + ") - TargetInvocationException" );
                SendMessageToConnection( message.SenderConnectionID, Message.CreateExceptionMessage(exception:ex.InnerException, sourceMessage:message) );
            }
            catch( System.Exception ex )
            {
                LOG( "HandleMessageThread(" + taskEntry + "," + message + ") - Exception" );
                SendMessageToConnection( message.SenderConnectionID, Message.CreateExceptionMessage(exception:ex, sourceMessage:message) );
            }
            finally
            {
                if( contextRestored && (ClearMessageContextObject != null) )
                    // Clear the restored message context
                    try { ClearMessageContextObject(); }
                    catch( System.Exception ex )  { FAIL( "'ClearMessageContextObject()' threw an exception (" + ex.GetType().FullName + "): " + ex.Message ); }
            }
        }