Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / System.Core / System.Linq.Parallel / QueryCheckerVisitor.cs
1 //
2 // QueryCheckerVisitor.cs
3 //
4 // Author:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 //
7 // Copyright (c) 2010 Jérémie "Garuma" Laval
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 #if NET_4_0 || MOBILE
28 using System;
29 using System.Threading;
30 using System.Linq.Parallel.QueryNodes;
31
32 namespace System.Linq.Parallel
33 {
34         using OptionsList = Tuple<ParallelMergeOptions?, ParallelExecutionMode?, CancellationToken?, int, CancellationTokenSource>;
35
36         internal class QueryCheckerVisitor : INodeVisitor
37         {
38                 const int minSequentialThreshold = 20;
39
40                 // Information gathering
41                 ParallelMergeOptions? options = null;
42                 ParallelExecutionMode? mode = null;
43                 CancellationToken? token = null;
44                 int? degreeOfParallelism = null;
45                 CancellationToken implementerToken = CancellationToken.None;
46
47                 int partitionCount;
48                 bool? behindOrderGuard = null;
49
50                 internal QueryCheckerVisitor (int partitionCount)
51                 {
52                         this.partitionCount = partitionCount;
53                 }
54
55                 #region INodeVisitor implementation
56                 public void Visit (QueryBaseNode node)
57                 {
58                         // Nothing to do atm. Later we can check if the node is a
59                         // Take or a Skip and set accordingly UseStrip
60                 }
61
62                 public void Visit (QueryChildNode node)
63                 {
64                         node.Parent.Visit (this);
65                 }
66
67                 public void Visit (QueryOptionNode node)
68                 {
69                         MergeOptions (node.GetOptions ());
70
71                         Visit ((QueryChildNode)node);
72                 }
73
74                 public void Visit (QueryStartNode node)
75                 {
76                         if (behindOrderGuard == null)
77                                 behindOrderGuard = false;
78                         if (degreeOfParallelism != null)
79                                 partitionCount = degreeOfParallelism.Value;
80
81                         int count;
82                         if ((count = node.Count) != -1 && count < minSequentialThreshold)
83                                 ShouldBeSequential = true;
84                 }
85
86                 public void Visit (QueryStreamNode node)
87                 {
88                         if (node.IsIndexed)
89                                 UseStrip = true;
90
91                         Visit ((QueryChildNode)node);
92                 }
93
94                 public void Visit (QueryOrderGuardNode node)
95                 {
96                         if (behindOrderGuard == null) {
97                                 if (node.EnsureOrder) {
98                                         behindOrderGuard = true;
99                                         //UseStrip = true;
100                                 } else {
101                                         behindOrderGuard = false;
102                                 }
103                         }
104
105                         Visit ((QueryStreamNode)node);
106                 }
107
108                 public void Visit (QueryMuxNode node)
109                 {
110                         Visit ((QueryChildNode)node);
111                 }
112
113                 public void Visit (QueryHeadWorkerNode node)
114                 {
115                         // Wouldn't it be better with standard Linq?
116                         if (node.Count.HasValue && node.Count < partitionCount)
117                                 ShouldBeSequential = true;
118
119                         Visit ((QueryStreamNode)node);
120                 }
121                 #endregion
122
123                 internal QueryOptions Options {
124                         get {
125                                 return new QueryOptions (options, mode, token == null ? CancellationToken.None : token.Value,
126                                                          UseStrip, behindOrderGuard, partitionCount, implementerToken, ShouldBeSequential);
127                         }
128                 }
129
130                 internal bool UseStrip {
131                         get;
132                         private set;
133                 }
134
135                 internal bool BehindOrderGuard {
136                         get {
137                                 return behindOrderGuard.Value;
138                         }
139                 }
140
141                 internal bool ShouldBeSequential {
142                         get;
143                         private set;
144                 }
145
146                 void MergeOptions (OptionsList list)
147                 {
148                         if (list.Item1 != null) {
149                                 if (options == null)
150                                         options = list.Item1;
151                                 else
152                                         Throw ("WithMergeOptions");
153                         }
154
155                         if (list.Item2 != null) {
156                                 if (mode == null)
157                                         mode = list.Item2;
158                                 else
159                                         Throw ("WithExecutionMode");
160                         }
161
162                         if (list.Item3 != null) {
163                                 if (token == null)
164                                         token = list.Item3;
165                                 else
166                                         Throw ("WithCancellationToken");
167                         }
168
169                         if (list.Item4 != -1) {
170                                 if (degreeOfParallelism == null)
171                                         degreeOfParallelism = list.Item4;
172                                 else
173                                         Throw ("WithDegreeOfParallelism");
174                         }
175
176                         // That one is treated specially
177                         if (list.Item5 != null) {
178                                 implementerToken = implementerToken.Chain (list.Item5);
179                         }
180                 }
181
182                 void Throw (string methName)
183                 {
184                         throw new InvalidOperationException ("You can't have more than one " + methName + " node in a query");
185                 }
186         }
187 }
188 #endif