Wait.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Linq;
36 using System.Linq.Expressions;
37 using System.Reflection;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.ExceptionServices;
40 using System.Runtime.Serialization;
41 using System.Threading;
42 using System.Threading.Tasks;
43 
46 
47 namespace Microsoft.Bot.Builder.Internals.Fibers
48 {
49  public interface IItem<out T> : IAwaitable<T>
50  {
51  }
52 
53  public delegate Task<IWait<C>> Rest<C, in T>(IFiber<C> fiber, C context, IItem<T> item, CancellationToken token);
54 
58  public enum Need
59  {
63  None,
64 
68  Wait,
69 
73  Poll,
74 
78  Call,
79 
83  Done
84  };
85 
86  public interface IWait
87  {
91  Need Need { get; }
92 
96  Type ItemType { get; }
97 
101  Type NeedType { get; }
102 
106  Delegate Rest { get; }
107 
111  void Post<T>(T item);
112 
116  void Fail(Exception error);
117  }
118 
119  public interface IWait<C> : IWait, ICloneable
120  {
121  Task<IWait<C>> PollAsync(IFiber<C> fiber, C context, CancellationToken token);
122  }
123 
127  public sealed class NullWait<C> : IWait<C>
128  {
129  public static readonly IWait<C> Instance = new NullWait<C>();
130  private NullWait()
131  {
132  }
133 
134  Need IWait.Need => Need.None;
135 
136  Type IWait.NeedType => typeof(object);
137 
138  Delegate IWait.Rest
139  {
140  get
141  {
142  throw new InvalidNeedException(this, Need.None);
143  }
144  }
145 
146  Type IWait.ItemType => typeof(object);
147 
148  void IWait.Post<T>(T item)
149  {
150  throw new InvalidNeedException(this, Need.Wait);
151  }
152 
153  void IWait.Fail(Exception error)
154  {
155  throw new InvalidNeedException(this, Need.Wait);
156  }
157 
158  Task<IWait<C>> IWait<C>.PollAsync(IFiber<C> fiber, C context, CancellationToken token)
159  {
160  throw new InvalidNeedException(this, Need.Poll);
161  }
162 
163  object ICloneable.Clone()
164  {
165  return NullWait<C>.Instance;
166  }
167  }
168 
169  public interface IWait<C, out T> : IWait<C>
170  {
171  void Wait(Rest<C, T> rest);
172  }
173 
174  public interface IPost<in T>
175  {
176  void Post(T item);
177  }
178 
179  public sealed class PostStruct<T> : IPost<T>
180  {
181  private readonly IPost<object> postBoxed;
182  public PostStruct(IPost<object> postBoxed)
183  {
184  SetField.NotNull(out this.postBoxed, nameof(postBoxed), postBoxed);
185  }
186  void IPost<T>.Post(T item)
187  {
188  this.postBoxed.Post((object)item);
189  }
190  }
191 
192  [Serializable]
193  public sealed class Wait<C, T> : IItem<T>, IWait<C, T>, IPost<T>, IAwaiter<T>, IEquatable<Wait<C, T>>, ISerializable
194  {
195  private Rest<C, T> rest;
196  private Need need;
197  private T item;
198  private Exception fail;
199 
200  public Wait()
201  {
202  }
203 
204  private Wait(SerializationInfo info, StreamingContext context)
205  {
206  SetField.NotNullFrom(out this.rest, nameof(rest), info);
207  SetField.From(out this.need, nameof(need), info);
208  }
209 
210  void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
211  {
212  info.AddValue(nameof(this.rest), this.rest);
213  info.AddValue(nameof(this.need), this.need);
214  }
215 
216  public override string ToString()
217  {
218  IWait wait = this;
219  return $"Wait: {wait.Need} {wait.NeedType?.Name} for {this.rest?.Target}.{this.rest?.Method.Name} have {wait.ItemType?.Name} {this.item}";
220  }
221 
222  public override int GetHashCode()
223  {
224  return this.rest.GetHashCode();
225  }
226 
227  public override bool Equals(object other)
228  {
229  IEquatable<Wait<C, T>> wait = this;
230  return wait.Equals(other as Wait<C, T>);
231  }
232 
233  bool IEquatable<Wait<C, T>>.Equals(Wait<C, T> other)
234  {
235  return other != null
236  && object.Equals(this.rest, other.rest)
237  && object.Equals(this.need, other.need)
238  && object.Equals(this.item, other.item)
239  && object.Equals(this.fail, other.fail)
240  ;
241  }
242 
243  Need IWait.Need => this.need;
244 
245  Type IWait.NeedType
246  {
247  get
248  {
249  if (this.rest != null)
250  {
251  var method = this.rest.Method;
252  var parameters = method.GetParameters();
253  var itemType = parameters[2].ParameterType;
254  var type = itemType.GenericTypeArguments.Single();
255  return type;
256  }
257  else
258  {
259  return null;
260  }
261  }
262  }
263 
264  Delegate IWait.Rest => this.rest;
265 
266  Type IWait.ItemType => typeof(T);
267 
268  async Task<IWait<C>> IWait<C>.PollAsync(IFiber<C> fiber, C context, CancellationToken token)
269  {
270  this.ValidateNeed(Need.Poll);
271 
272  this.need = Need.Call;
273  try
274  {
275  return await this.rest(fiber, context, this, token);
276  }
277  finally
278  {
279  this.need = Need.Done;
280  }
281  }
282 
283  private static readonly MethodInfo MethodPost = Types.MethodOf(() => ((IWait)null).Post(0)).GetGenericMethodDefinition();
284 
285  void IWait.Post<D>(D item)
286  {
287  this.ValidateNeed(Need.Wait);
288 
289  // try generic type variance first
290  var post = this as IPost<D>;
291  if (post == null)
292  {
293  // then work around lack of generic type variant for value types
294  if (typeof(D).IsValueType)
295  {
296  var postBoxed = this as IPost<object>;
297  if (postBoxed != null)
298  {
299  post = new PostStruct<D>(postBoxed);
300  }
301  }
302  }
303 
304  if (post != null)
305  {
306  post.Post(item);
307  }
308  else
309  {
310  // if we have runtime type information, use reflection and recurse
311  var type = item?.GetType();
312  bool reflection = type != null && !type.IsAssignableFrom(typeof(D));
313  if (reflection)
314  {
315  var generic = MethodPost.MakeGenericMethod(type);
316  generic.Invoke(this, new object[] { item });
317  }
318  else
319  {
320  // otherwise, we cannot satisfy this wait with this item
321  IWait wait = this;
322  wait.Fail(new InvalidTypeException(this, typeof(D)));
323  }
324  }
325  }
326 
327  void IWait.Fail(Exception fail)
328  {
329  this.ValidateNeed(Need.Wait);
330 
331  this.item = default(T);
332  this.fail = fail;
333  this.need = Need.Poll;
334  }
335 
336  void IPost<T>.Post(T item)
337  {
338  this.ValidateNeed(Need.Wait);
339 
340  this.item = item;
341  this.fail = null;
342  this.need = Need.Poll;
343  }
344 
345  void IWait<C, T>.Wait(Rest<C, T> rest)
346  {
347  this.ValidateNeed(Need.None);
348 
349  SetField.NotNull(out this.rest, nameof(rest), rest);
350  this.need = Need.Wait;
351  }
352 
354  {
355  return this;
356  }
357 
359  {
360  get
361  {
362  switch (this.need)
363  {
364  case Need.Call:
365  case Need.Done:
366  return true;
367  default:
368  return false;
369  }
370  }
371  }
372 
374  {
375  if (this.fail != null)
376  {
377  // http://stackoverflow.com/a/17091351
378  ExceptionDispatchInfo.Capture(this.fail).Throw();
379 
380  // just to satisfy compiler - should not reach this line
381  throw new InvalidOperationException();
382  }
383  else
384  {
385  return this.item;
386  }
387  }
388 
389  void INotifyCompletion.OnCompleted(Action continuation)
390  {
391  throw new NotImplementedException();
392  }
393 
394  object ICloneable.Clone()
395  {
396  var clone = new Wait<C, T>();
397  clone.rest = this.rest;
398  clone.need = Need.Wait;
399  clone.item = default(T);
400  clone.fail = null;
401  return clone;
402  }
403  }
404 
405  public interface IWaitFactory<C>
406  {
407  IWait<C, T> Make<T>();
408  }
409 
410  [Serializable]
411  public sealed class WaitFactory<C> : IWaitFactory<C>
412  {
413  IWait<C, T> IWaitFactory<C>.Make<T>()
414  {
415  return new Wait<C, T>();
416  }
417  }
418 }
Definition: Wait.cs:179
Explicit interface to support the compiling of async/await.
Definition: Awaitable.cs:55
The wait needs to be polled for execution after an item has been posted.
Do not show possible choices in the prompt
static MethodInfo MethodOf(Expression< Action > action)
Definition: Types.cs:47
The wait is in the middle of executing the rest delegate.
Need
This is the stage of the wait, showing what the wait needs during its lifecycle.
Definition: Wait.cs:58
static readonly IWait< C > Instance
Definition: Wait.cs:129
PostStruct(IPost< object > postBoxed)
Definition: Wait.cs:182
Definition: Wait.cs:174
override bool Equals(object other)
Definition: Wait.cs:227
The wait has completed executing the rest delegate.
Core namespace for Dialogs and associated infrastructure.
Null object pattern implementation of wait interface.
Definition: Wait.cs:127
Root namespace for the Microsoft Bot Builder SDK.
void Post(T item)
Builder.Internals.Fibers.IAwaiter< T > GetAwaiter()
Get the awaiter for this awaitable item.