|
1 | 1 | // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) |
2 | 2 | // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. |
3 | | -using System; |
4 | | -using System.Collections.Generic; |
5 | | -using System.Threading.Tasks; |
| 3 | +namespace Stride.Core.MicroThreading; |
6 | 4 |
|
7 | | -namespace Stride.Core.MicroThreading |
| 5 | +/// <summary> |
| 6 | +/// Provides a communication mechanism between <see cref="MicroThread"/>. |
| 7 | +/// </summary> |
| 8 | +/// <remarks> |
| 9 | +/// <see cref="MicroThread"/> can send and receive to a <see cref="Channel{T}"/>. Depending on the <see cref="Channel{T}.Preference"/>, |
| 10 | +/// sending or receiving <see cref="MicroThread"/> might be suspended and yield execution to another <see cref="MicroThread"/>. |
| 11 | +/// </remarks> |
| 12 | +/// <typeparam name="T">The type of element handled by this channel.</typeparam> |
| 13 | +// TODO: Thread-safety |
| 14 | +public class Channel<T> |
8 | 15 | { |
| 16 | + private readonly Queue<ChannelMicroThreadAwaiter<T>> receivers = []; |
| 17 | + private readonly Queue<ChannelMicroThreadAwaiter<T>> senders = []; |
| 18 | + |
| 19 | + public Channel() |
| 20 | + { |
| 21 | + Preference = ChannelPreference.PreferReceiver; |
| 22 | + } |
| 23 | + |
| 24 | + public void Reset() |
| 25 | + { |
| 26 | + receivers.Clear(); |
| 27 | + senders.Clear(); |
| 28 | + } |
| 29 | + |
| 30 | + /// <summary> |
| 31 | + /// Gets or sets the preference, allowing you to customize how <see cref="Send"/> and <see cref="Receive"/> behave regarding scheduling. |
| 32 | + /// </summary> |
| 33 | + /// <value> |
| 34 | + /// The preference. |
| 35 | + /// </value> |
| 36 | + public ChannelPreference Preference { get; set; } |
| 37 | + |
| 38 | + /// <summary> |
| 39 | + /// Gets the balance, which is the number of <see cref="MicroThread"/> waiting to send (if greater than 0) or receive (if smaller than 0). |
| 40 | + /// </summary> |
| 41 | + /// <value> |
| 42 | + /// The balance. |
| 43 | + /// </value> |
| 44 | + public int Balance { get { return senders.Count - receivers.Count; } } |
| 45 | + |
9 | 46 | /// <summary> |
10 | | - /// Provides a communication mechanism between <see cref="MicroThread"/>. |
| 47 | + /// Sends a value over the channel. If no other <see cref="MicroThread"/> is waiting for data, the sender will be blocked. |
| 48 | + /// If someone was waiting for data, which of the sender or receiver continues next depends on <see cref="Preference"/>. |
11 | 49 | /// </summary> |
12 | | - /// <remarks> |
13 | | - /// <see cref="MicroThread"/> can send and receive to a <see cref="Channel{T}"/>. Depending on the <see cref="Channel{T}.Preference"/>, |
14 | | - /// sending or receiving <see cref="MicroThread"/> might be suspended and yield execution to another <see cref="MicroThread"/>. |
15 | | - /// </remarks> |
16 | | - /// <typeparam name="T">The type of element handled by this channel.</typeparam> |
17 | | - // TODO: Thread-safety |
18 | | - public class Channel<T> |
| 50 | + /// <param name="data">The data.</param> |
| 51 | + /// <returns>Awaitable data.</returns> |
| 52 | + public ChannelMicroThreadAwaiter<T> Send(T data) |
19 | 53 | { |
20 | | - private readonly Queue<ChannelMicroThreadAwaiter<T>> receivers = new Queue<ChannelMicroThreadAwaiter<T>>(); |
21 | | - private readonly Queue<ChannelMicroThreadAwaiter<T>> senders = new Queue<ChannelMicroThreadAwaiter<T>>(); |
22 | | - |
23 | | - public Channel() |
| 54 | + if (receivers.Count == 0) |
24 | 55 | { |
25 | | - Preference = ChannelPreference.PreferReceiver; |
| 56 | + // Nobody receiving, let's wait until something comes up |
| 57 | + var microThread = MicroThread.Current; |
| 58 | + var waitingMicroThread = ChannelMicroThreadAwaiter<T>.New(microThread); |
| 59 | + waitingMicroThread.Result = data; |
| 60 | + senders.Enqueue(waitingMicroThread); |
| 61 | + return waitingMicroThread; |
26 | 62 | } |
27 | 63 |
|
28 | | - public void Reset() |
| 64 | + var receiver = receivers.Dequeue(); |
| 65 | + receiver.Result = data; |
| 66 | + if (Preference == ChannelPreference.PreferSender) |
29 | 67 | { |
30 | | - receivers.Clear(); |
31 | | - senders.Clear(); |
| 68 | + receiver.MicroThread.ScheduleContinuation(ScheduleMode.Last, receiver.Continuation); |
32 | 69 | } |
33 | | - |
34 | | - /// <summary> |
35 | | - /// Gets or sets the preference, allowing you to customize how <see cref="Send"/> and <see cref="Receive"/> behave regarding scheduling. |
36 | | - /// </summary> |
37 | | - /// <value> |
38 | | - /// The preference. |
39 | | - /// </value> |
40 | | - public ChannelPreference Preference { get; set; } |
41 | | - |
42 | | - /// <summary> |
43 | | - /// Gets the balance, which is the number of <see cref="MicroThread"/> waiting to send (if greater than 0) or receive (if smaller than 0). |
44 | | - /// </summary> |
45 | | - /// <value> |
46 | | - /// The balance. |
47 | | - /// </value> |
48 | | - public int Balance { get { return senders.Count - receivers.Count; } } |
49 | | - |
50 | | - /// <summary> |
51 | | - /// Sends a value over the channel. If no other <see cref="MicroThread"/> is waiting for data, the sender will be blocked. |
52 | | - /// If someone was waiting for data, which of the sender or receiver continues next depends on <see cref="Preference"/>. |
53 | | - /// </summary> |
54 | | - /// <param name="data">The data.</param> |
55 | | - /// <returns>Awaitable data.</returns> |
56 | | - public ChannelMicroThreadAwaiter<T> Send(T data) |
| 70 | + else if (Preference == ChannelPreference.PreferReceiver) |
57 | 71 | { |
58 | | - if (receivers.Count == 0) |
59 | | - { |
60 | | - // Nobody receiving, let's wait until something comes up |
61 | | - var microThread = MicroThread.Current; |
62 | | - var waitingMicroThread = ChannelMicroThreadAwaiter<T>.New(microThread); |
63 | | - waitingMicroThread.Result = data; |
64 | | - senders.Enqueue(waitingMicroThread); |
65 | | - return waitingMicroThread; |
66 | | - } |
67 | | - |
68 | | - var receiver = receivers.Dequeue(); |
69 | | - receiver.Result = data; |
70 | | - if (Preference == ChannelPreference.PreferSender) |
71 | | - { |
72 | | - receiver.MicroThread.ScheduleContinuation(ScheduleMode.Last, receiver.Continuation); |
73 | | - } |
74 | | - else if (Preference == ChannelPreference.PreferReceiver) |
75 | | - { |
76 | | - receiver.MicroThread.ScheduleContinuation(ScheduleMode.First, receiver.Continuation); |
77 | | - throw new NotImplementedException(); |
78 | | - //await Scheduler.Yield(); |
79 | | - } |
80 | | - receiver.IsCompleted = true; |
81 | | - return receiver; |
| 72 | + receiver.MicroThread.ScheduleContinuation(ScheduleMode.First, receiver.Continuation); |
| 73 | + throw new NotImplementedException(); |
| 74 | + //await Scheduler.Yield(); |
82 | 75 | } |
| 76 | + receiver.IsCompleted = true; |
| 77 | + return receiver; |
| 78 | + } |
83 | 79 |
|
84 | | - /// <summary> |
85 | | - /// Receives a value over the channel. If no other <see cref="MicroThread"/> is sending data, the receiver will be blocked. |
86 | | - /// If someone was sending data, which of the sender or receiver continues next depends on <see cref="Preference"/>. |
87 | | - /// </summary> |
88 | | - /// <returns>Awaitable data.</returns> |
89 | | - public ChannelMicroThreadAwaiter<T> Receive() |
| 80 | + /// <summary> |
| 81 | + /// Receives a value over the channel. If no other <see cref="MicroThread"/> is sending data, the receiver will be blocked. |
| 82 | + /// If someone was sending data, which of the sender or receiver continues next depends on <see cref="Preference"/>. |
| 83 | + /// </summary> |
| 84 | + /// <returns>Awaitable data.</returns> |
| 85 | + public ChannelMicroThreadAwaiter<T> Receive() |
| 86 | + { |
| 87 | + if (senders.Count == 0) |
90 | 88 | { |
91 | | - if (senders.Count == 0) |
92 | | - { |
93 | | - var microThread = MicroThread.Current; |
94 | | - if (microThread == null) |
95 | | - throw new Exception("Cannot receive out of micro-thread context."); |
96 | | - |
97 | | - var waitingMicroThread = ChannelMicroThreadAwaiter<T>.New(microThread); |
98 | | - receivers.Enqueue(waitingMicroThread); |
99 | | - return waitingMicroThread; |
100 | | - } |
| 89 | + var microThread = MicroThread.Current ?? throw new Exception("Cannot receive out of micro-thread context."); |
| 90 | + var waitingMicroThread = ChannelMicroThreadAwaiter<T>.New(microThread); |
| 91 | + receivers.Enqueue(waitingMicroThread); |
| 92 | + return waitingMicroThread; |
| 93 | + } |
101 | 94 |
|
102 | | - var sender = senders.Dequeue(); |
103 | | - if (Preference == ChannelPreference.PreferReceiver) |
104 | | - { |
105 | | - sender.MicroThread.ScheduleContinuation(ScheduleMode.Last, sender.Continuation); |
106 | | - } |
107 | | - else if (Preference == ChannelPreference.PreferSender) |
108 | | - { |
109 | | - sender.MicroThread.ScheduleContinuation(ScheduleMode.First, sender.Continuation); |
110 | | - throw new NotImplementedException(); |
111 | | - //await Scheduler.Yield(); |
112 | | - } |
113 | | - sender.IsCompleted = true; |
114 | | - return sender; |
| 95 | + var sender = senders.Dequeue(); |
| 96 | + if (Preference == ChannelPreference.PreferReceiver) |
| 97 | + { |
| 98 | + sender.MicroThread.ScheduleContinuation(ScheduleMode.Last, sender.Continuation); |
| 99 | + } |
| 100 | + else if (Preference == ChannelPreference.PreferSender) |
| 101 | + { |
| 102 | + sender.MicroThread.ScheduleContinuation(ScheduleMode.First, sender.Continuation); |
| 103 | + throw new NotImplementedException(); |
| 104 | + //await Scheduler.Yield(); |
115 | 105 | } |
| 106 | + sender.IsCompleted = true; |
| 107 | + return sender; |
116 | 108 | } |
117 | 109 | } |
0 commit comments