| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Threading; |
| |
| namespace Apache.NMS.Stomp.Threads |
| { |
| /// <summary> |
| /// This class provides a wrapper around the ThreadPool mechanism in .NET |
| /// to allow for serial execution of jobs in the ThreadPool and provide |
| /// a means of shutting down the execution of jobs in a deterministic |
| /// way. |
| /// </summary> |
| public class ThreadPoolExecutor |
| { |
| private Queue<Future> workQueue = new Queue<Future>(); |
| private Mutex syncRoot = new Mutex(); |
| private bool running = false; |
| private bool closing = false; |
| private bool closed = false; |
| private ManualResetEvent executionComplete = new ManualResetEvent(true); |
| |
| /// <summary> |
| /// Represents an asynchronous task that is executed on the ThreadPool |
| /// at some point in the future. |
| /// </summary> |
| internal class Future |
| { |
| private WaitCallback callback; |
| private object callbackArg; |
| |
| public Future(WaitCallback callback, object arg) |
| { |
| this.callback = callback; |
| this.callbackArg = arg; |
| } |
| |
| public void Run() |
| { |
| if(this.callback == null) |
| { |
| throw new Exception("Future executed with null WaitCallback"); |
| } |
| |
| try |
| { |
| this.callback(callbackArg); |
| } |
| catch |
| { |
| } |
| } |
| } |
| |
| public void QueueUserWorkItem(WaitCallback worker) |
| { |
| this.QueueUserWorkItem(worker, null); |
| } |
| |
| public void QueueUserWorkItem(WaitCallback worker, object arg) |
| { |
| if(worker == null) |
| { |
| throw new ArgumentNullException("Invalid WaitCallback passed"); |
| } |
| |
| if(!this.closed) |
| { |
| lock(syncRoot) |
| { |
| if(!this.closed || !this.closing) |
| { |
| this.workQueue.Enqueue(new Future(worker, arg)); |
| |
| if(!this.running) |
| { |
| this.executionComplete.Reset(); |
| this.running = true; |
| ThreadPool.QueueUserWorkItem(new WaitCallback(QueueProcessor), null); |
| } |
| } |
| } |
| } |
| } |
| |
| public bool IsShutdown |
| { |
| get { return this.closed; } |
| } |
| |
| public void Shutdown() |
| { |
| if(!this.closed) |
| { |
| syncRoot.WaitOne(); |
| |
| if(!this.closed) |
| { |
| this.closing = true; |
| this.workQueue.Clear(); |
| |
| if(this.running) |
| { |
| syncRoot.ReleaseMutex(); |
| this.executionComplete.WaitOne(); |
| syncRoot.WaitOne(); |
| } |
| |
| this.closed = true; |
| } |
| |
| syncRoot.ReleaseMutex(); |
| } |
| } |
| |
| private void QueueProcessor(object unused) |
| { |
| Future theTask = null; |
| |
| lock(syncRoot) |
| { |
| if(this.workQueue.Count == 0 || this.closing) |
| { |
| this.running = false; |
| this.executionComplete.Set(); |
| return; |
| } |
| |
| theTask = this.workQueue.Dequeue(); |
| } |
| |
| try |
| { |
| theTask.Run(); |
| } |
| finally |
| { |
| if(this.closing) |
| { |
| this.running = false; |
| this.executionComplete.Set(); |
| } |
| else |
| { |
| ThreadPool.QueueUserWorkItem(new WaitCallback(QueueProcessor), null); |
| } |
| } |
| } |
| } |
| } |
| |