Beispiel #1
0
        private void AddThread(KeyValuePair <DispatchThread, Result <DispatchWorkItem> > keyvalue)
        {
            DispatchThread            thread = keyvalue.Key;
            Result <DispatchWorkItem> result = keyvalue.Value;

            if (_threadVelocity >= 0)
            {
                lock (_syncRoot) {
                    _threadVelocity = 0;

                    // check if an item is available for dispatch
                    Action callback;
                    if (TryRequestItem(null, out callback))
                    {
                        RegisterThread("new thread", thread);

                        // dispatch work-item
                        result.Return(new DispatchWorkItem(callback, this));
                        return;
                    }
                }
            }

            // we have no need for this thread
            RemoveThread("insufficient work for new thread", thread, result);
        }
Beispiel #2
0
        private void RemoveThread(string reason, DispatchThread thread, Result <DispatchWorkItem> result)
        {
            if (thread == null)
            {
                throw new ArgumentNullException("thread");
            }
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }
            if (thread.PendingWorkItemCount != 0)
            {
                throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread");
            }

            // remove thread from list of allocated threads
            lock (_syncRoot) {
                _threadVelocity = 0;
                UnregisterThread(reason, thread);
            }

            // check if we can put thread into the reserved list
            if (_reservedThreads.Count < MinReservedThreads)
            {
                if (!_reservedThreads.TryPush(new KeyValuePair <DispatchThread, Result <DispatchWorkItem> >(thread, result)))
                {
                    throw new NotSupportedException("TryPush failed");
                }
            }
            else
            {
                // return thread to resource manager
                DispatchThreadScheduler.ReleaseThread(thread, result);
            }
        }
Beispiel #3
0
        private bool TryRequestItem(DispatchThread thread, out Action callback)
        {
            // check if we can find a work-item in the shared queue
            if (_inbox.TryDequeue(out callback))
            {
                return(true);
            }

            // try to steal a work-item from another thread; take a snapshot of all allocated threads (needed in case the array is copied for resizing)
            DispatchThread[] threads = _activeThreads;
            foreach (DispatchThread entry in threads)
            {
                // check if we can steal a work-item from this thread
                if ((entry != null) && !ReferenceEquals(entry, thread) && entry.TryStealWorkItem(out callback))
                {
                    return(true);
                }
            }

            // check again if we can find a work-item in the shared queue since trying to steal may have overlapped with the arrival of a new item
            if (_inbox.TryDequeue(out callback))
            {
                return(true);
            }
            return(false);
        }
Beispiel #4
0
        private void DispatchLoop()
        {
            // set thread-local self-reference
            CurrentThread = this;

            // begin thread loop
            try {
                Action callback;
                while (GetNextWorkItem(out callback))
                {
                    // execute work-item
                    if (callback != null)
                    {
                        try {
                            callback();
                        } catch (Exception e) {
                            _log.Warn("an unhandled exception occurred while executing the work-item", e);
                        }
                    }
                }
            } catch (Exception e) {
                // something went wrong that shouldn't have!
                _log.ErrorExceptionMethodCall(e, string.Format("DispatchLoop #{0}: FATAL ERROR", _id));

                // TODO (steveb): tell _host about untimely exit; post items in queue to host inbox (o/w we lose them)
            } finally {
                CurrentThread = null;
            }
        }
Beispiel #5
0
        //--- Class Methods ---
        public static bool TryQueueWorkItem(IDispatchQueue queue, Action callback)
        {
            DispatchThread current = CurrentThread;

            if ((current != null) && ReferenceEquals(AsyncUtil.CurrentDispatchQueue, queue))
            {
                // NOTE (steveb): next call can never fail since we're calling the queue work-item method of the current thread
                current.QueueWorkItem(callback);
                return(true);
            }
            return(false);
        }
Beispiel #6
0
        public void EvictWorkItems()
        {
            // clear CurrentThread to avoid having the evicted items being immediately added back again to the thread (i.e. infinite loop)
            CurrentThread = null;
            try {
                Action item;

                // remove up to 100 items from the current threads work queue
                for (int i = 0; (i < 100) && _inbox.TryPop(out item); ++i)
                {
                    _queue.QueueWorkItem(item);
                }
            } finally {
                // restore CurrentThread before we exit
                CurrentThread = this;
            }
        }
Beispiel #7
0
        private void UnregisterThread(string reason, DispatchThread thread)
        {
            thread.Host = null;

            // find thread and remove it
            for (int i = 0; i < _activeThreads.Length; ++i)
            {
                if (ReferenceEquals(_activeThreads[i], thread))
                {
                    --_threadCount;
                    _activeThreads[i] = null;
#if EXTRA_DEBUG
                    _log.DebugFormat("RemoveThread: {1} - {0}", this, reason);
#endif
                    break;
                }
            }
        }
        private bool TryQueueWorkItem(int priority, Action callback)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("ElasticThreadPool has already been disposed");
            }

            // check if we can enqueue work-item into current dispatch thread
            IDispatchQueue queue = this[priority];

            if (DispatchThread.TryQueueWorkItem(queue, callback))
            {
                return(true);
            }

            // check if there are available threads to which the work-item can be given to
            KeyValuePair <DispatchThread, Result <DispatchWorkItem> > entry;

            if (_reservedThreads.TryPop(out entry))
            {
                lock (_syncRoot) {
                    RegisterThread("new item", entry.Key);
                }

                // found an available thread, let's resume it with the work-item
                entry.Value.Return(new DispatchWorkItem(callback, queue));
                return(true);
            }

            // no threads available, keep work-item for later
            if (!_inbox.TryEnqueue(priority, callback))
            {
                return(false);
            }

            // check if we need to request a thread to kick things off
            if (ThreadCount == 0)
            {
                ((IDispatchHost)this).IncreaseThreadCount("request first thread");
            }
            return(true);
        }
        public static void ReleaseThread(DispatchThread thread, Result <DispatchWorkItem> result)
        {
            if (thread == null)
            {
                throw new ArgumentNullException("thread");
            }
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            // unbind thread from current host
            thread.Host = null;

            // add thread to list of idle threads
            if (!_threadReserve.TryEnqueue(new KeyValuePair <DispatchThread, Result <DispatchWorkItem> >(thread, result)))
            {
                throw new NotSupportedException("TryEnqueue failed");
            }
        }
        void IDispatchHost.RequestWorkItem(DispatchThread thread, Result <DispatchWorkItem> result)
        {
            if (thread == null)
            {
                throw new ArgumentNullException("thread");
            }
            if (thread.PendingWorkItemCount > 0)
            {
                throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread");
            }
            if (!ReferenceEquals(thread.Host, this))
            {
                throw new InvalidOperationException(string.Format("thread is allocated to another queue: received {0}, expected: {1}", thread.Host, this));
            }
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            // check if we need to decommission threads without causing starvation
            if (_threadVelocity < 0)
            {
                RemoveThread("system saturation", thread, result);
                return;
            }

            // check if we found a work-item
            int    priority;
            Action callback;

            if (TryRequestItem(thread, out priority, out callback))
            {
                // dispatch work-item
                result.Return(new DispatchWorkItem(callback, this[priority]));
            }
            else
            {
                // relinquich thread; it's not required anymore
                RemoveThread("insufficient work", thread, result);
            }
        }
Beispiel #11
0
        private void RegisterThread(string reason, DispatchThread thread)
        {
            ++_threadCount;
            thread.Host = this;

            // find an empty slot in the array of all threads
            int index;

            for (index = 0; index < _activeThreads.Length; ++index)
            {
                // check if we found an empty slot
                if (_activeThreads[index] == null)
                {
                    // assign it to the found slot and stop iterating
                    _activeThreads[index] = thread;
                    break;
                }
            }

            // check if we need to grow the array
            if (index == _activeThreads.Length)
            {
                // make room to add a new thread by doubling the array size and copying over the existing entries
                DispatchThread[] newArray = new DispatchThread[2 * _activeThreads.Length];
                Array.Copy(_activeThreads, newArray, _activeThreads.Length);

                // assign new thread
                newArray[index] = thread;

                // update instance field
                _activeThreads = newArray;
            }
#if EXTRA_DEBUG
            _log.DebugFormat("AddThread: {1} - {0}", this, reason);
#endif
        }
Beispiel #12
0
        private void DispatchLoop()
        {
            // set thread-local self-reference
            CurrentThread = this;

            // begin thread loop
            try {
                while (true)
                {
                    // check if queue has a work-item
                    Action callback;
                    if (!_inbox.TryPop(out callback))
                    {
                        var result = new Result <DispatchWorkItem>(TimeSpan.MaxValue);

                        // reset the dispatch queue for this thread
                        AsyncUtil.CurrentDispatchQueue = null;

                        // check if thread is associated with a host already
                        if (_host == null)
                        {
                            // NOTE (steveb): this is a brand new thread without a host yet

                            // return the thread to the dispatch scheduler
                            DispatchThreadScheduler.ReleaseThread(this, result);
                        }
                        else
                        {
                            // request another work-item
                            _host.RequestWorkItem(this, result);
                        }

                        // block until a work item is available
                        result.Block();

                        // check if we received a work item or an exception to shutdown
                        if (result.HasException && (result.Exception is DispatchThreadShutdownException))
                        {
                            // time to shut down
                            _log.DebugFormat("DispatchThread #{0} destroyed", _id);
                            return;
                        }
                        callback = result.Value.WorkItem;
                        _queue   = result.Value.DispatchQueue;

                        // TODO (steveb): handle the weird case where _queue is null

                        // set the dispatch queue for this thread
                        AsyncUtil.CurrentDispatchQueue = _queue;
                    }

                    // execute work-item
                    if (callback != null)
                    {
                        try {
                            callback();
                        } catch (Exception e) {
                            _log.Warn("an unhandled exception occurred while executing the work-item", e);
                        }
                    }
                }
            } catch (Exception e) {
                // something went wrong that shouldn't have!
                _log.ErrorExceptionMethodCall(e, string.Format("DispatchLoop #{0}: FATAL ERROR", _id));

                // TODO (steveb): tell _host about untimely exit; post items in queue to host inbox (o/w we lose them)
            } finally {
                CurrentThread = null;
            }
        }
Beispiel #13
0
        void IDispatchHost.RequestWorkItem(DispatchThread thread, Result<DispatchWorkItem> result)
        {
            if(thread == null) {
                throw new ArgumentNullException("thread");
            }
            if(thread.PendingWorkItemCount > 0) {
                throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread");
            }
            if(!ReferenceEquals(thread.Host, this)) {
                throw new InvalidOperationException(string.Format("thread is allocated to another queue: received {0}, expected: {1}", thread.Host, this));
            }
            if(result == null) {
                throw new ArgumentNullException("result");
            }
            Action callback;

            // check if we need to decommission threads without causing starvation
            if(_threadVelocity < 0) {
                RemoveThread("system saturation", thread, result);
                return;
            }

            // check if we found a work-item
            if(TryRequestItem(thread, out callback)) {

                // dispatch work-item
                result.Return(new DispatchWorkItem(callback, this));
            } else {

                // relinquich thread; it's not required anymore
                RemoveThread("insufficient work", thread, result);
            }
        }
Beispiel #14
0
        private void DispatchLoop()
        {
            // set thread-local self-reference
            CurrentThread = this;

            // begin thread loop
            try {
                Action callback;
                while(GetNextWorkItem(out callback)) {

                    // execute work-item
                    if(callback != null) {
                        try {
                            callback();
                        } catch(Exception e) {
                            _log.Warn("an unhandled exception occurred while executing the work-item", e);
                        }
                    }
                }
            } catch(Exception e) {

                // something went wrong that shouldn't have!
                _log.ErrorExceptionMethodCall(e, string.Format("DispatchLoop #{0}: FATAL ERROR", _id));

                // TODO (steveb): tell _host about untimely exit; post items in queue to host inbox (o/w we lose them)

            } finally {
                CurrentThread = null;
            }
        }
        public static void ReleaseThread(DispatchThread thread, Result<DispatchWorkItem> result)
        {
            if(thread == null) {
                throw new ArgumentNullException("thread");
            }
            if(result == null) {
                throw new ArgumentNullException("result");
            }

            // unbind thread from current host
            thread.Host = null;

            // add thread to list of idle threads
            if(!_threadReserve.TryEnqueue(new KeyValuePair<DispatchThread, Result<DispatchWorkItem>>(thread, result))) {
                throw new NotSupportedException("TryEnqueue failed");
            }
        }
Beispiel #16
0
        private void RegisterThread(string reason, DispatchThread thread)
        {
            ++_threadCount;
            thread.Host = this;

            // find an empty slot in the array of all threads
            int index;
            for(index = 0; index < _activeThreads.Length; ++index) {

                // check if we found an empty slot
                if(_activeThreads[index] == null) {

                    // assign it to the found slot and stop iterating
                    _activeThreads[index] = thread;
                    break;
                }
            }

            // check if we need to grow the array
            if(index == _activeThreads.Length) {

                // make room to add a new thread by doubling the array size and copying over the existing entries
                DispatchThread[] newArray = new DispatchThread[2 * _activeThreads.Length];
                Array.Copy(_activeThreads, newArray, _activeThreads.Length);

                // assign new thread
                newArray[index] = thread;

                // update instance field
                _activeThreads = newArray;
            }
            #if EXTRA_DEBUG
            _log.DebugFormat("AddThread: {1} - {0}", this, reason);
            #endif
        }
Beispiel #17
0
        private void DispatchLoop()
        {
            // set thread-local self-reference
            CurrentThread = this;

            // begin thread loop
            try {
                while(true) {

                    // check if queue has a work-item
                    Action callback;
                    if(!_inbox.TryPop(out callback)) {
                        var result = new Result<DispatchWorkItem>(TimeSpan.MaxValue);

                        // reset the dispatch queue for this thread
                        Async.CurrentDispatchQueue = null;

                        // check if thread is associated with a host already
                        if(_host == null) {

                            // NOTE (steveb): this is a brand new thread without a host yet

                            // return the thread to the dispatch scheduler
                            DispatchThreadScheduler.ReleaseThread(this, result);
                        } else {

                            // request another work-item
                            _host.RequestWorkItem(this, result);
                        }

                        // block until a work item is available
                        result.Block();

                        // check if we received a work item or an exception to shutdown
                        if(result.HasException && (result.Exception is DispatchThreadShutdownException)) {

                            // time to shut down
                            _log.DebugFormat("DispatchThread #{0} destroyed", _id);
                            return;
                        }
                        callback = result.Value.WorkItem;
                        _queue = result.Value.DispatchQueue;

                        // TODO (steveb): handle the weird case where _queue is null

                        // set the dispatch queue for this thread
                        Async.CurrentDispatchQueue = _queue;
                    }

                    // execute work-item
                    if(callback != null) {
                        try {
                            callback();
                        } catch(Exception e) {
                            _log.Warn("an unhandled exception occurred while executing the work-item", e);
                        }
                    }
                }
            } catch(Exception e) {

                // something went wrong that shouldn't have!
                _log.ErrorExceptionMethodCall(e, string.Format("DispatchLoop #{0}: FATAL ERROR", _id));

                // TODO (steveb): tell _host about untimely exit; post items in queue to host inbox (o/w we lose them)

            } finally {
                CurrentThread = null;
            }
        }
Beispiel #18
0
        public void EvictWorkItems()
        {
            // clear CurrentThread to avoid having the evicted items being immediately added back again to the thread (i.e. infinite loop)
            CurrentThread = null;
            try {
                Action item;

                // remove up to 100 items from the current threads work queue
                for(int i = 0; (i < 100) && _inbox.TryPop(out item); ++i) {
                    _queue.QueueWorkItem(item);
                }
            } finally {

                // restore CurrentThread before we exit
                CurrentThread = this;
            }
        }
Beispiel #19
0
        private void RemoveThread(string reason, DispatchThread thread, Result<DispatchWorkItem> result)
        {
            if(thread == null) {
                throw new ArgumentNullException("thread");
            }
            if(result == null) {
                throw new ArgumentNullException("result");
            }
            if(thread.PendingWorkItemCount != 0) {
                throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread");
            }

            // remove thread from list of allocated threads
            lock(_syncRoot) {
                _threadVelocity = 0;
                UnregisterThread(reason, thread);
            }

            // check if we can put thread into the reserved list
            if(_reservedThreads.Count < MinReservedThreads) {
                if(!_reservedThreads.TryPush(new KeyValuePair<DispatchThread, Result<DispatchWorkItem>>(thread, result))) {
                    throw new NotSupportedException("TryPush failed");
                }
            } else {

                // return thread to resource manager
                DispatchThreadScheduler.ReleaseThread(thread, result);
            }
        }
Beispiel #20
0
        private bool TryRequestItem(DispatchThread thread, out Action callback)
        {
            // check if we can find a work-item in the shared queue
            if(_inbox.TryDequeue(out callback)) {
                return true;
            }

            // try to steal a work-item from another thread; take a snapshot of all allocated threads (needed in case the array is copied for resizing)
            DispatchThread[] threads = _activeThreads;
            foreach(DispatchThread entry in threads) {

                // check if we can steal a work-item from this thread
                if((entry != null) && !ReferenceEquals(entry, thread) && entry.TryStealWorkItem(out callback)) {
                    return true;
                }
            }

            // check again if we can find a work-item in the shared queue since trying to steal may have overlapped with the arrival of a new item
            if(_inbox.TryDequeue(out callback)) {
                return true;
            }
            return false;
        }
Beispiel #21
0
        private void UnregisterThread(string reason, DispatchThread thread)
        {
            thread.Host = null;

            // find thread and remove it
            for(int i = 0; i < _activeThreads.Length; ++i) {
                if(ReferenceEquals(_activeThreads[i], thread)) {
                    --_threadCount;
                    _activeThreads[i] = null;
            #if EXTRA_DEBUG
                    _log.DebugFormat("RemoveThread: {1} - {0}", this, reason);
            #endif
                    break;
                }
            }
        }