[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Devel.Tests.Ubuntu / nunit.framework / Internal / Execution / ParallelWorkItemDispatcher.cs
1 // ***********************************************************************
2 // Copyright (c) 2012-2014 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 #define NETCF
30 #if PARALLEL
31
32 using System;
33 using System.Collections.Generic;
34 using System.Threading;
35
36 namespace NUnit.Framework.Internal.Execution
37 {
38     /// <summary>
39     /// ParallelWorkItemDispatcher handles execution of work items by
40     /// queuing them for worker threads to process.
41     /// </summary>
42     public class ParallelWorkItemDispatcher : IWorkItemDispatcher
43     {
44         private static readonly Logger log = InternalTrace.GetLogger("WorkItemDispatcher");
45
46         private readonly int _levelOfParallelism;
47         private int _itemsDispatched;
48
49         // Non-STA
50         private readonly WorkShift _parallelShift = new WorkShift("Parallel");
51         private readonly WorkShift _nonParallelShift = new WorkShift("NonParallel");
52         private readonly Lazy<WorkItemQueue> _parallelQueue;
53         private readonly Lazy<WorkItemQueue> _nonParallelQueue;
54
55         // STA
56 #if !NETCF
57         private readonly WorkShift _nonParallelSTAShift = new WorkShift("NonParallelSTA");
58         private readonly Lazy<WorkItemQueue> _parallelSTAQueue;
59         private readonly Lazy<WorkItemQueue> _nonParallelSTAQueue;
60 #endif
61
62         // The first WorkItem to be dispatched, assumed to be top-level item
63         private WorkItem _topLevelWorkItem;
64
65         /// <summary>
66         /// Construct a ParallelWorkItemDispatcher
67         /// </summary>
68         /// <param name="levelOfParallelism">Number of workers to use</param>
69         public ParallelWorkItemDispatcher(int levelOfParallelism)
70         {
71             _levelOfParallelism = levelOfParallelism;
72
73             Shifts = new WorkShift[]
74             {
75                 _parallelShift,
76                 _nonParallelShift,
77 #if !NETCF
78                 _nonParallelSTAShift
79 #endif
80             };
81             foreach (var shift in Shifts)
82                 shift.EndOfShift += OnEndOfShift;
83
84             _parallelQueue = new Lazy<WorkItemQueue>(() =>
85             {
86                 var parallelQueue = new WorkItemQueue("ParallelQueue");
87                 _parallelShift.AddQueue(parallelQueue);
88
89                 for (int i = 1; i <= _levelOfParallelism; i++)
90                 {
91                     string name = string.Format("Worker#" + i.ToString());
92 #if NETCF
93                     _parallelShift.Assign(new TestWorker(parallelQueue, name));
94 #else
95                     _parallelShift.Assign(new TestWorker(parallelQueue, name, ApartmentState.MTA));
96 #endif
97                 }
98
99                 return parallelQueue;
100             });
101
102 #if !NETCF
103             _parallelSTAQueue = new Lazy<WorkItemQueue>(() =>
104             {
105                 var parallelSTAQueue = new WorkItemQueue("ParallelSTAQueue");
106                 _parallelShift.AddQueue(parallelSTAQueue);
107                 _parallelShift.Assign(new TestWorker(parallelSTAQueue, "Worker#STA", ApartmentState.STA));
108
109                 return parallelSTAQueue;
110             });
111 #endif
112
113             _nonParallelQueue = new Lazy<WorkItemQueue>(() =>
114             {
115                 var nonParallelQueue = new WorkItemQueue("NonParallelQueue");
116                 _nonParallelShift.AddQueue(nonParallelQueue);
117 #if NETCF
118                 _nonParallelShift.Assign(new TestWorker(nonParallelQueue, "Worker#NP"));
119 #else
120                 _nonParallelShift.Assign(new TestWorker(nonParallelQueue, "Worker#STA_NP", ApartmentState.MTA));
121 #endif
122
123                 return nonParallelQueue;
124             });
125
126 #if !NETCF
127             _nonParallelSTAQueue = new Lazy<WorkItemQueue>(() =>
128             {
129                 var nonParallelSTAQueue = new WorkItemQueue("NonParallelSTAQueue");
130                 _nonParallelSTAShift.AddQueue(nonParallelSTAQueue);
131                 _nonParallelSTAShift.Assign(new TestWorker(nonParallelSTAQueue, "Worker#NP_STA", ApartmentState.STA));
132
133                 return nonParallelSTAQueue;
134             });
135 #endif
136         }
137
138         /// <summary>
139         /// Enumerates all the shifts supported by the dispatcher
140         /// </summary>
141         public IEnumerable<WorkShift> Shifts { get; private set; }
142
143         #region IWorkItemDispatcher Members
144
145         /// <summary>
146         /// Dispatch a single work item for execution. The first
147         /// work item dispatched is saved as the top-level
148         /// work item and used when stopping the run.
149         /// </summary>
150         /// <param name="work">The item to dispatch</param>
151         public void Dispatch(WorkItem work)
152         {
153             // Special handling of the top-level item
154             if (Interlocked.CompareExchange (ref _topLevelWorkItem, work, null) == null)
155             {
156                 Enqueue(work);
157                 StartNextShift();
158             }
159             // We run child items directly, rather than enqueuing them...
160             // 1. If the context is single threaded.
161             // 2. If there is no fixture, and so nothing to do but dispatch grandchildren.
162             // 3. For now, if this represents a test case. This avoids issues of
163             // tests that access the fixture state and allows handling ApartmentState
164             // preferences set on the fixture.
165             else if (work.Context.IsSingleThreaded
166                   || work.Test.TypeInfo == null
167                   || work is SimpleWorkItem)
168                 Execute(work);
169             else
170                 Enqueue(work);
171
172             Interlocked.Increment(ref _itemsDispatched);
173         }
174
175         private void Execute(WorkItem work)
176         {
177             log.Debug("Directly executing {0}", work.Test.Name);
178             work.Execute();
179         }
180
181         private void Enqueue(WorkItem work)
182         {
183             log.Debug("Enqueuing {0}", work.Test.Name);
184
185             if (work.IsParallelizable)
186             {
187 #if !NETCF
188                 if (work.TargetApartment == ApartmentState.STA)
189                     ParallelSTAQueue.Enqueue(work);
190                 else
191 #endif
192                 ParallelQueue.Enqueue(work);
193             }
194 #if !NETCF
195             else if (work.TargetApartment == ApartmentState.STA)
196                 NonParallelSTAQueue.Enqueue(work);
197 #endif
198             else
199                 NonParallelQueue.Enqueue(work);
200         }
201
202         /// <summary>
203         /// Cancel the ongoing run completely.
204         /// If no run is in process, the call has no effect.
205         /// </summary>
206         public void CancelRun(bool force)
207         {
208             foreach (var shift in Shifts)
209                 shift.Cancel(force);
210         }
211
212         #endregion
213
214         #region Private Queue Properties
215
216         // Queues are not actually created until the first time the property
217         // is referenced by the Dispatch method adding a WorkItem to it.
218
219         private WorkItemQueue ParallelQueue
220         {
221             get
222             {
223                 return _parallelQueue.Value;
224             }
225         }
226
227 #if !NETCF
228         private WorkItemQueue ParallelSTAQueue
229         {
230             get
231             {
232                 return _parallelSTAQueue.Value;
233             }
234         }
235 #endif
236
237         private WorkItemQueue NonParallelQueue
238         {
239             get
240             {
241                 return _nonParallelQueue.Value;
242             }
243         }
244
245 #if !NETCF
246         private WorkItemQueue NonParallelSTAQueue
247         {
248             get
249             {
250                 return _nonParallelSTAQueue.Value;
251             }
252         }
253 #endif
254         #endregion
255
256         #region Helper Methods
257
258         private void OnEndOfShift(object sender, EventArgs ea)
259         {
260             if (!StartNextShift())
261             {
262                 foreach (var shift in Shifts)
263                     shift.ShutDown();
264             }
265         }
266
267         private bool StartNextShift()
268         {
269             foreach (var shift in Shifts)
270             {
271                 if (shift.HasWork)
272                 {
273                     shift.Start();
274                     return true;
275                 }
276             }
277
278             return false;
279         }
280
281         #endregion
282     }
283 }
284
285 #endif