[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Devel.Tests.Ubuntu / nunit.framework / Internal / Execution / WorkItemQueue.cs
1 // ***********************************************************************
2 // Copyright (c) 2012 Charlie Poole
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
23 #define PORTABLE
24 #define TIZEN
25 #define NUNIT_FRAMEWORK
26 #define NUNITLITE
27 #define NET_4_5
28 #define PARALLEL
29 #if PARALLEL
30 using System;
31 using System.Collections.Concurrent;
32 using System.Collections.Generic;
33 using System.Threading;
34 #if NET_2_0 || NET_3_5 || NETCF
35 using ManualResetEventSlim = System.Threading.ManualResetEvent;
36 #endif
37
38 namespace NUnit.Framework.Internal.Execution
39 {
40     /// <summary>
41     /// WorkItemQueueState indicates the current state of a WorkItemQueue
42     /// </summary>
43     public enum WorkItemQueueState
44     {
45         /// <summary>
46         /// The queue is paused
47         /// </summary>
48         Paused,
49
50         /// <summary>
51         /// The queue is running
52         /// </summary>
53         Running,
54
55         /// <summary>
56         /// The queue is stopped
57         /// </summary>
58         Stopped
59     }
60
61     /// <summary>
62     /// A WorkItemQueue holds work items that are ready to
63     /// be run, either initially or after some dependency
64     /// has been satisfied.
65     /// </summary>
66     public class WorkItemQueue
67     {
68         private const int spinCount = 5;
69
70         private Logger log = InternalTrace.GetLogger("WorkItemQueue");
71
72         private readonly ConcurrentQueue<WorkItem> _innerQueue = new ConcurrentQueue<WorkItem>();
73
74         /* This event is used solely for the purpose of having an optimized sleep cycle when
75          * we have to wait on an external event (Add or Remove for instance)
76          */
77         private readonly ManualResetEventSlim _mreAdd = new ManualResetEventSlim(false);
78
79         /* The whole idea is to use these two values in a transactional
80          * way to track and manage the actual data inside the underlying lock-free collection
81          * instead of directly working with it or using external locking.
82          *
83          * They are manipulated with CAS and are guaranteed to increase over time and use
84          * of the instance thus preventing ABA problems.
85          */
86         private int _addId = int.MinValue;
87         private int _removeId = int.MinValue;
88
89         /// <summary>
90         /// Initializes a new instance of the <see cref="WorkItemQueue"/> class.
91         /// </summary>
92         /// <param name="name">The name of the queue.</param>
93         public WorkItemQueue(string name)
94         {
95             Name = name;
96             State = WorkItemQueueState.Paused;
97             MaxCount = 0;
98             ItemsProcessed = 0;
99         }
100
101         #region Properties
102
103         /// <summary>
104         /// Gets the name of the work item queue.
105         /// </summary>
106         public string Name { get; private set; }
107
108         private int _itemsProcessed;
109         /// <summary>
110         /// Gets the total number of items processed so far
111         /// </summary>
112         public int ItemsProcessed
113         {
114             get { return _itemsProcessed; }
115             private set { _itemsProcessed = value; }
116         }
117
118         private int _maxCount;
119
120         /// <summary>
121         /// Gets the maximum number of work items.
122         /// </summary>
123         public int MaxCount
124         {
125             get { return _maxCount; }
126             private set { _maxCount = value; }
127         }
128
129         private int _state;
130         /// <summary>
131         /// Gets the current state of the queue
132         /// </summary>
133         public WorkItemQueueState State
134         {
135             get { return (WorkItemQueueState)_state; }
136             private set { _state = (int)value; }
137         }
138
139         /// <summary>
140         /// Get a bool indicating whether the queue is empty.
141         /// </summary>
142         public bool IsEmpty
143         {
144             get { return _innerQueue.IsEmpty; }
145         }
146
147         #endregion
148
149         #region Public Methods
150
151         /// <summary>
152         /// Enqueue a WorkItem to be processed
153         /// </summary>
154         /// <param name="work">The WorkItem to process</param>
155         public void Enqueue(WorkItem work)
156         {
157             do
158             {
159                 int cachedAddId = _addId;
160
161                 // Validate that we have are the current enqueuer
162                 if (Interlocked.CompareExchange(ref _addId, cachedAddId + 1, cachedAddId) != cachedAddId)
163                     continue;
164
165                 // Add to the collection
166                 _innerQueue.Enqueue(work);
167
168                 // Set MaxCount using CAS
169                 int i, j = _maxCount;
170                 do
171                 {
172                     i = j;
173                     j = Interlocked.CompareExchange(ref _maxCount, Math.Max(i, _innerQueue.Count), i);
174                 }
175                 while (i != j);
176
177                 // Wake up threads that may have been sleeping
178                 _mreAdd.Set();
179
180                 return;
181             } while (true);
182         }
183
184         /// <summary>
185         /// Dequeue a WorkItem for processing
186         /// </summary>
187         /// <returns>A WorkItem or null if the queue has stopped</returns>
188         public WorkItem Dequeue()
189         {
190             SpinWait sw = new SpinWait();
191
192             do
193             {
194                 WorkItemQueueState cachedState = State;
195
196                 if (cachedState == WorkItemQueueState.Stopped)
197                     return null; // Tell worker to terminate
198
199                 int cachedRemoveId = _removeId;
200                 int cachedAddId = _addId;
201
202                 // Empty case (or paused)
203                 if (cachedRemoveId == cachedAddId || cachedState == WorkItemQueueState.Paused)
204                 {
205                     // Spin a few times to see if something changes
206                     if (sw.Count <= spinCount)
207                     {
208                         sw.SpinOnce();
209                     }
210                     else
211                     {
212                         // Reset to wait for an enqueue
213                         _mreAdd.Reset();
214
215                         // Recheck for an enqueue to avoid a Wait
216                         if ((cachedRemoveId != _removeId || cachedAddId != _addId) && cachedState != WorkItemQueueState.Paused)
217                         {
218                             // Queue is not empty, set the event
219                             _mreAdd.Set();
220                             continue;
221                         }
222
223                         // Wait for something to happen
224                         _mreAdd.Wait(500);
225                     }
226
227                     continue;
228                 }
229
230                 // Validate that we are the current dequeuer
231                 if (Interlocked.CompareExchange(ref _removeId, cachedRemoveId + 1, cachedRemoveId) != cachedRemoveId)
232                     continue;
233
234
235                 // Dequeue our work item
236                 WorkItem work;
237                 while (!_innerQueue.TryDequeue(out work)) { };
238
239                 // Add to items processed using CAS
240                 Interlocked.Increment(ref _itemsProcessed);
241
242                 return work;
243             } while (true);
244         }
245
246         /// <summary>
247         ///  Start or restart processing of items from the queue
248         /// </summary>
249         public void Start()
250         {
251             log.Info("{0} starting", Name);
252
253             if (Interlocked.CompareExchange(ref _state, (int)WorkItemQueueState.Running, (int)WorkItemQueueState.Paused) == (int)WorkItemQueueState.Paused)
254                 _mreAdd.Set();
255         }
256
257         /// <summary>
258         /// Signal the queue to stop
259         /// </summary>
260         public void Stop()
261         {
262             log.Info("{0} stopping - {1} WorkItems processed, max size {2}", Name, ItemsProcessed, MaxCount);
263
264             if (Interlocked.Exchange(ref _state, (int)WorkItemQueueState.Stopped) != (int)WorkItemQueueState.Stopped)
265                 _mreAdd.Set();
266         }
267
268         /// <summary>
269         /// Pause the queue for restarting later
270         /// </summary>
271         public void Pause()
272         {
273             log.Info("{0} pausing", Name);
274
275             Interlocked.CompareExchange(ref _state, (int)WorkItemQueueState.Paused, (int)WorkItemQueueState.Running);
276         }
277
278         #endregion
279     }
280
281 #if NET_2_0 || NET_3_5 || NETCF
282     internal static class ManualResetEventExtensions
283     {
284         public static bool Wait (this ManualResetEvent mre, int millisecondsTimeout)
285         {
286             return mre.WaitOne(millisecondsTimeout, false);
287         }
288     }
289 #endif
290
291 }
292 #endif