├── .gitignore
├── ActorGui
├── ActorGui.csproj
├── GuiConstants.cs
├── GuiMachines.cs
├── GuiMachines.drn
├── IMainWindow.cs
├── MainWindow.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── PumpAnimation.cs
└── TextWindow.cs
├── ActorGuiTest
├── ActorGuiTest.csproj
├── PrimesTest.cs
└── Properties
│ └── AssemblyInfo.cs
├── ActorHttp.sln
├── ActorHttp.userprefs
├── ActorHttp
├── ActorHttp.csproj
├── ActorHttp.csproj.user
├── HttpActors.cs
├── HttpActors.drn
├── MiniServer.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── ServerConstants.cs
└── packages.config
├── ActorHttpTest
├── ActorHttpTest.csproj
├── Properties
│ └── AssemblyInfo.cs
├── StreamPumpTest.cs
└── packages.config
├── Actors
├── Actors.csproj
├── CallResult.cs
├── Codes.cs
├── ConsoleLogger.cs
├── DebugLogger.cs
├── DedicatedThread.cs
├── DefaultErrorHandler.cs
├── ExternalThread.cs
├── IActor.cs
├── IActorLogger.cs
├── IErrorHandler.cs
├── IRuntime.cs
├── IThread.cs
├── IoBuffer.cs
├── Message.cs
├── Parcel.cs
├── Properties
│ └── AssemblyInfo.cs
├── Runtime.cs
├── RuntimeData.cs
└── RuntimeThread.cs
├── ActorsTest
├── ActorsTest.csproj
├── RuntimeTest.cs
└── packages.config
├── Packages.dgml
├── README.md
└── packages
├── FakeItEasy.1.25.1
├── FakeItEasy.1.25.1.nupkg
└── lib
│ ├── net35
│ ├── FakeItEasy.dll
│ └── FakeItEasy.xml
│ ├── net40
│ ├── FakeItEasy.dll
│ └── FakeItEasy.xml
│ ├── sl50
│ ├── FakeItEasy.dll
│ └── FakeItEasy.xml
│ └── win8
│ ├── FakeItEasy.dll
│ └── FakeItEasy.xml
├── NUnit.2.6.3
├── NUnit.2.6.3.nupkg
├── lib
│ ├── nunit.framework.dll
│ └── nunit.framework.xml
└── license.txt
├── NUnit.2.6.4
├── NUnit.2.6.4.nupkg
├── lib
│ ├── nunit.framework.dll
│ └── nunit.framework.xml
└── license.txt
└── repositories.config
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | .vs
4 |
--------------------------------------------------------------------------------
/ActorGui/ActorGui.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}
8 | WinExe
9 | Properties
10 | ActorGui
11 | ActorGui
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | false
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 4.0
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Code
59 |
60 |
61 | True
62 | True
63 | Resources.resx
64 |
65 |
66 | True
67 | Settings.settings
68 | True
69 |
70 |
71 |
72 |
73 | ResXFileCodeGenerator
74 | Resources.Designer.cs
75 |
76 |
77 | SettingsSingleFileGenerator
78 | Settings.Designer.cs
79 |
80 |
81 |
82 |
83 |
84 | {bd0aeeda-9523-4cdf-b4c9-ad7d723b1f3b}
85 | Actors
86 |
87 |
88 |
89 |
96 |
--------------------------------------------------------------------------------
/ActorGui/GuiConstants.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ActorGui
8 | {
9 | public static class GuiConstants
10 | {
11 | public const int Download = 840;
12 | public const int Prime = 841;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ActorGui/GuiMachines.cs:
--------------------------------------------------------------------------------
1 | // Autogenerated with DRAKON Editor 1.31
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Net;
7 | using System.IO;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using System.Diagnostics;
11 |
12 | using Actors;
13 |
14 | namespace ActorGui {
15 |
16 | public class GuiMachines {
17 |
18 |
19 |
20 | public partial class MainWindowLogic
21 | : IActor
22 | {
23 | // Arguments
24 | internal IMainWindow Window;
25 | // State
26 | internal int Worker;
27 | internal WebClient Web;
28 |
29 | public enum StateNames {
30 | Destroyed,
31 | Ready,
32 | Calculating,
33 | Downloading
34 | }
35 |
36 | private StateNames _state = StateNames.Ready;
37 |
38 | public StateNames State {
39 | get { return _state; }
40 | internal set { _state = value; }
41 | }
42 |
43 | public const int CompletedMessage = CallResult.Completed;
44 | public const int ErrorMessage = CallResult.Error;
45 | public const int CancelMessage = Codes.Cancel;
46 | public const int DownloadMessage = GuiConstants.Download;
47 | public const int PrimeMessage = GuiConstants.Prime;
48 |
49 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
50 | switch (messageType) {
51 | case CompletedMessage:
52 | return Completed(runtime, myId, message);
53 | case ErrorMessage:
54 | return Error(runtime, myId, message);
55 | case CancelMessage:
56 | return Cancel(runtime, myId, message);
57 | case DownloadMessage:
58 | return Download(runtime, myId, message);
59 | case PrimeMessage:
60 | return Prime(runtime, myId, message);
61 | default:
62 | return null;
63 | }
64 | }
65 |
66 | public object Completed(IRuntime runtime, int myId, Message message) {
67 | switch (State) {
68 | case StateNames.Ready:
69 | case StateNames.Calculating:
70 | return Calculating_Completed(runtime, myId, message);
71 | case StateNames.Downloading:
72 | return Downloading_Completed(runtime, myId, message);
73 | default:
74 | return null;
75 | }
76 | }
77 |
78 | public object Error(IRuntime runtime, int myId, Message message) {
79 | switch (State) {
80 | case StateNames.Ready:
81 | case StateNames.Calculating:
82 | return Calculating_Error(runtime, myId, message);
83 | case StateNames.Downloading:
84 | return Downloading_Error(runtime, myId, message);
85 | default:
86 | return null;
87 | }
88 | }
89 |
90 | public object Cancel(IRuntime runtime, int myId, Message message) {
91 | switch (State) {
92 | case StateNames.Ready:
93 | case StateNames.Calculating:
94 | return Calculating_Cancel(runtime, myId, message);
95 | case StateNames.Downloading:
96 | return Downloading_Cancel(runtime, myId, message);
97 | default:
98 | return null;
99 | }
100 | }
101 |
102 | public object Download(IRuntime runtime, int myId, Message message) {
103 | switch (State) {
104 | case StateNames.Ready:
105 | return Ready_Download(runtime, myId, message);
106 | case StateNames.Calculating:
107 | case StateNames.Downloading:
108 | default:
109 | return null;
110 | }
111 | }
112 |
113 | public object Prime(IRuntime runtime, int myId, Message message) {
114 | switch (State) {
115 | case StateNames.Ready:
116 | return Ready_Prime(runtime, myId, message);
117 | case StateNames.Calculating:
118 | case StateNames.Downloading:
119 | default:
120 | return null;
121 | }
122 | }
123 |
124 | private object Ready_Download(IRuntime runtime, int myId, Message message) {
125 | // item 88
126 | string url = (string)message.Payload;
127 | // item 93
128 | Debug.WriteLine(url);
129 | // item 244
130 | Web = StartDownload(
131 | runtime,
132 | url,
133 | myId
134 | );
135 | // item 90
136 | Window.SwitchToWorking();
137 | // item 92
138 | State = StateNames.Downloading;
139 | return null;
140 | }
141 |
142 | private object Ready_Prime(IRuntime runtime, int myId, Message message) {
143 | // item 89
144 | int n = (int)message.Payload;
145 | // item 94
146 | Debug.WriteLine(n);
147 | // item 232
148 | var calculator = new PrimeCalculator();
149 | calculator.Client = myId;
150 | calculator.N = n;
151 |
152 |
153 | Worker = runtime.AddDedicatedActor(
154 | calculator
155 | );
156 | // item 91
157 | Window.SwitchToWorking();
158 | // item 73
159 | State = StateNames.Calculating;
160 | return null;
161 | }
162 |
163 | private object Calculating_Completed(IRuntime runtime, int myId, Message message) {
164 | // item 116
165 | Window.SwitchToReady();
166 | // item 231
167 | Window.ReportResult(
168 | (string)message.Payload
169 | );
170 | // item 82
171 | State = StateNames.Ready;
172 | return null;
173 | }
174 |
175 | private object Calculating_Error(IRuntime runtime, int myId, Message message) {
176 | // item 233
177 | Window.SwitchToReady();
178 | // item 113
179 | ReportError(
180 | Window,
181 | "Calculation failed",
182 | message
183 | );
184 | // item 82
185 | State = StateNames.Ready;
186 | return null;
187 | }
188 |
189 | private object Calculating_Cancel(IRuntime runtime, int myId, Message message) {
190 | // item 235
191 | Window.SwitchToReady();
192 | // item 234
193 | runtime.RemoveActor(Worker);
194 | // item 82
195 | State = StateNames.Ready;
196 | return null;
197 | }
198 |
199 | private object Downloading_Completed(IRuntime runtime, int myId, Message message) {
200 | // item 236
201 | Window.SwitchToReady();
202 | // item 246
203 | byte[] data = (byte[])message.Payload;
204 | // item 245
205 | string result = String.Format(
206 | "Download completed. Size: {0} bytes.",
207 | data.Length
208 | );
209 | // item 247
210 | Window.ReportResult(
211 | result
212 | );
213 | // item 76
214 | State = StateNames.Ready;
215 | return null;
216 | }
217 |
218 | private object Downloading_Error(IRuntime runtime, int myId, Message message) {
219 | // item 119
220 | Window.SwitchToReady();
221 | // item 120
222 | ReportError(
223 | Window,
224 | "Download failed",
225 | message
226 | );
227 | // item 76
228 | State = StateNames.Ready;
229 | return null;
230 | }
231 |
232 | private object Downloading_Cancel(IRuntime runtime, int myId, Message message) {
233 | // item 237
234 | Window.SwitchToReady();
235 | // item 266
236 | if (Web == null) {
237 |
238 | } else {
239 | // item 265
240 | Web.CancelAsync();
241 | Web = null;
242 | }
243 | // item 76
244 | State = StateNames.Ready;
245 | return null;
246 | }
247 |
248 | public void Shutdown() {
249 | if (State == StateNames.Destroyed) {
250 | return;
251 | }
252 | State = StateNames.Destroyed;
253 |
254 | }
255 | }
256 |
257 | public partial class PrimeCalculator
258 | : IActor
259 | {
260 | const int ChunkSize = 1000;
261 | // Arguments.
262 | internal int N;
263 | internal int Client; // The actor that ordered the computation.
264 | // State
265 | internal readonly List Primes = new List();
266 | internal int Current; // The current candidate.
267 | internal int J;
268 |
269 | public enum StateNames {
270 | Destroyed,
271 | EasyChecks,
272 | Calculation
273 | }
274 |
275 | private StateNames _state = StateNames.EasyChecks;
276 |
277 | public StateNames State {
278 | get { return _state; }
279 | internal set { _state = value; }
280 | }
281 |
282 | public const int PulseMessage = Codes.Pulse;
283 |
284 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
285 | switch (messageType) {
286 | case PulseMessage:
287 | return Pulse(runtime, myId, message);
288 | default:
289 | return null;
290 | }
291 | }
292 |
293 | public object Pulse(IRuntime runtime, int myId, Message message) {
294 | switch (State) {
295 | case StateNames.EasyChecks:
296 | return EasyChecks_Pulse(runtime, myId, message);
297 | case StateNames.Calculation:
298 | return Calculation_Pulse(runtime, myId, message);
299 | default:
300 | return null;
301 | }
302 | }
303 |
304 | private object EasyChecks_Pulse(IRuntime runtime, int myId, Message message) {
305 | // item 1980001
306 | if (N == 0) {
307 | // item 215
308 | ReportPrimes(
309 | runtime,
310 | Primes,
311 | N,
312 | Client
313 | );
314 | // item 156
315 | Shutdown();
316 | return null;
317 | } else {
318 | // item 1980002
319 | if (N == 1) {
320 | // item 211
321 | Primes.Add(1);
322 | // item 215
323 | ReportPrimes(
324 | runtime,
325 | Primes,
326 | N,
327 | Client
328 | );
329 | // item 156
330 | Shutdown();
331 | return null;
332 | } else {
333 | // item 1980003
334 | if (N == 2) {
335 | // item 209
336 | Primes.Add(1);
337 | Primes.Add(2);
338 | // item 215
339 | ReportPrimes(
340 | runtime,
341 | Primes,
342 | N,
343 | Client
344 | );
345 | // item 156
346 | Shutdown();
347 | return null;
348 | } else {
349 | // item 1980004
350 | if (N < 0) {
351 | // item 220
352 | runtime.SendMessage(
353 | Client,
354 | CallResult.Error,
355 | new ArgumentException(
356 | "N",
357 | "Negative argument"),
358 | myId
359 | );
360 | // item 156
361 | Shutdown();
362 | return null;
363 | } else {
364 | // item 269
365 | Primes.Add(1);
366 | Primes.Add(2);
367 | Primes.Add(3);
368 | // item 216
369 | Current = 5;
370 | J = 3;
371 | // item 214
372 | State = StateNames.Calculation;
373 | return null;
374 | }
375 | }
376 | }
377 | }
378 | }
379 |
380 | private object EasyChecks_default(IRuntime runtime, int myId, Message message) {
381 | // item 170
382 | State = StateNames.EasyChecks;
383 | return null;
384 | }
385 |
386 | private object Calculation_Pulse(IRuntime runtime, int myId, Message message) {
387 | // item 1790001
388 | int i = 0;
389 | while (true) {
390 | // item 1790002
391 | if (i < ChunkSize) {
392 |
393 | } else {
394 | // item 159
395 | State = StateNames.Calculation;
396 | return null;
397 | }
398 | // item 193
399 | if (Current > N) {
400 | // item 230
401 | ReportPrimes(
402 | runtime,
403 | Primes,
404 | N,
405 | Client
406 | );
407 | // item 195
408 | Shutdown();
409 | return null;
410 | } else {
411 |
412 | }
413 | // item 181
414 | if (J * J > Current) {
415 | // item 192
416 | Primes.Add(Current);
417 | // item 190
418 | J = 3;
419 | Current += 2;
420 | } else {
421 | // item 185
422 | if (Current % J == 0) {
423 | // item 190
424 | J = 3;
425 | Current += 2;
426 | } else {
427 | // item 184
428 | J += 2;
429 | }
430 | }
431 | // item 1790003
432 | i++;
433 | }
434 | }
435 |
436 | private object Calculation_default(IRuntime runtime, int myId, Message message) {
437 | // item 178
438 | State = StateNames.Calculation;
439 | return null;
440 | }
441 |
442 | public void Shutdown() {
443 | if (State == StateNames.Destroyed) {
444 | return;
445 | }
446 | State = StateNames.Destroyed;
447 |
448 | }
449 | }
450 |
451 | private static void DownloadCompleted(IRuntime runtime, int caller, DownloadDataCompletedEventArgs e) {
452 | // item 255
453 | if (e.Cancelled) {
454 | // item 258
455 | runtime.SendMessage(
456 | caller,
457 | Codes.Cancel,
458 | null,
459 | 0
460 | );
461 | } else {
462 | // item 259
463 | if (e.Error == null) {
464 | // item 262
465 | runtime.SendMessage(
466 | caller,
467 | CallResult.Completed,
468 | e.Result,
469 | 0
470 | );
471 | } else {
472 | // item 261
473 | runtime.SendMessage(
474 | caller,
475 | CallResult.Error,
476 | e.Error,
477 | 0
478 | );
479 | }
480 | }
481 | }
482 |
483 | private static void ReportError(IMainWindow Window, string start, Message message) {
484 | // item 126
485 | string text;
486 | Exception ex = message.Payload as Exception;
487 | // item 128
488 | if (ex == null) {
489 | // item 131
490 | text = start;
491 | } else {
492 | // item 127
493 | text = start + "\n" + ex.Message;
494 | }
495 | // item 132
496 | Window.ReportError(text);
497 | }
498 |
499 | private static void ReportPrimes(IRuntime runtime, List primes, int n, int client) {
500 | // item 229
501 | string[] primesAsText = primes
502 | .Select(p => p.ToString())
503 | .ToArray();
504 | // item 226
505 | string primeString = String.Join(
506 | "\n",
507 | primesAsText
508 | );
509 | // item 227
510 | string result = String.Format(
511 | "Prime numbers up to {0}: {1} found:\n{2}",
512 | n,
513 | primes.Count,
514 | primeString
515 | );
516 | // item 228
517 | runtime.SendMessage(
518 | client,
519 | CallResult.Completed,
520 | result,
521 | 0
522 | );
523 | }
524 |
525 | private static WebClient StartDownload(IRuntime runtime, string url, int receiverId) {
526 | // item 263
527 | WebClient web = new WebClient();
528 | web.DownloadDataCompleted += (sender, e) =>
529 | {
530 | DownloadCompleted(runtime, receiverId, e);
531 | };
532 | // item 264
533 | try {
534 | web.DownloadDataAsync(new Uri(url));
535 | } catch (Exception ex) {
536 | runtime.SendMessage(
537 | receiverId,
538 | CallResult.Error,
539 | ex,
540 | 0
541 | );
542 | }
543 | // item 249
544 | return web;
545 | }
546 | }
547 | }
548 |
--------------------------------------------------------------------------------
/ActorGui/GuiMachines.drn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/ActorGui/GuiMachines.drn
--------------------------------------------------------------------------------
/ActorGui/IMainWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ActorGui
8 | {
9 | public interface IMainWindow
10 | {
11 | void ReportError(string message);
12 | void ReportResult(string result);
13 | void SwitchToWorking();
14 | void SwitchToReady();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ActorGui/MainWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Media;
9 | using System.Windows.Media.Animation;
10 | using System.Windows.Media.Imaging;
11 | using Actors;
12 | using System.Threading;
13 |
14 | namespace ActorGui
15 | {
16 | public class MainWindow : Window, IMainWindow
17 | {
18 | private readonly TextBox _urlEdit;
19 | private readonly TextBox _primesEdit;
20 | private readonly Button _startIoButton;
21 | private readonly Button _startCpuButton;
22 | private readonly Button _cancelButton;
23 | private readonly PumpAnimation _animationControl;
24 | private const string UiThread = "UI Thread";
25 | private const string CalculationThread = "Calculation Thread";
26 |
27 | private readonly int Logic;
28 |
29 | private readonly Runtime _runtime;
30 |
31 | public MainWindow(Runtime runtime)
32 | {
33 | _runtime = runtime;
34 | _runtime.RegisterExternalThread(UiThread, this.PostWorkItem);
35 | var logicActor = new GuiMachines.MainWindowLogic();
36 | logicActor.Window = this;
37 | Logic = _runtime.AddActorToThread(UiThread, logicActor);
38 |
39 | Width = 600;
40 | FontSize = 16;
41 | SizeToContent = System.Windows.SizeToContent.Height;
42 | Title = "Actor-based GUI";
43 |
44 | StackPanel rootPanel = new StackPanel();
45 | rootPanel.Orientation = Orientation.Vertical;
46 | AddChild(rootPanel);
47 |
48 | GroupBox ioBox = AddGroupBox(rootPanel, "IO-bound task");
49 | Button startIoButton = CreateButton("Download", StartIo);
50 | _urlEdit = CreateTextBox("http://www.vg.no/");
51 | FillGroupBox(ioBox, _urlEdit, startIoButton);
52 | _startIoButton = startIoButton;
53 |
54 | GroupBox cpuBox = AddGroupBox(rootPanel, "CPU-bound task");
55 | Button startCpuButton = CreateButton("Calculate primes", StartCpu);
56 | _primesEdit = CreateTextBox("10000000");
57 | FillGroupBox(cpuBox, _primesEdit, startCpuButton);
58 | _startCpuButton = startCpuButton;
59 |
60 | GroupBox aniBox = AddGroupBox(rootPanel, "Progress");
61 | _animationControl = new PumpAnimation();
62 | _animationControl.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
63 | Button cancelButton = CreateButton("Cancel", Cancel);
64 | cancelButton.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
65 | _cancelButton = cancelButton;
66 | FillGroupBox(aniBox, _animationControl, cancelButton);
67 |
68 | _animationControl.Visibility = System.Windows.Visibility.Hidden;
69 | }
70 |
71 | private void PostWorkItem(Action item)
72 | {
73 | Dispatcher.BeginInvoke(item);
74 | }
75 |
76 | private static TextBox CreateTextBox(string text)
77 | {
78 | TextBox textBox = new TextBox();
79 | textBox.Text = text;
80 | textBox.Margin = new Thickness(5);
81 | return textBox;
82 | }
83 |
84 | private static Button CreateButton(string text, Action callback)
85 | {
86 | Button button = new Button();
87 | button.Content = text;
88 | button.Margin = new Thickness(5);
89 | button.Click += (sender, e) => callback();
90 | return button;
91 | }
92 |
93 | private void StartCpu()
94 | {
95 | string nText = _primesEdit.Text.Trim();
96 | if (nText == String.Empty)
97 | {
98 | ReportError("Please input the maximum number.");
99 | return;
100 | }
101 |
102 | int n;
103 | if (!Int32.TryParse(nText, out n))
104 | {
105 | ReportError("Incorrect maximum number.");
106 | return;
107 | }
108 |
109 | if (n <= 0)
110 | {
111 | ReportError("The maximum number must be posiive.");
112 | return;
113 | }
114 |
115 | _runtime.SendMessage(Logic, GuiConstants.Prime, n, 0);
116 | }
117 |
118 | private void StartIo()
119 | {
120 | string url = this._urlEdit.Text.Trim();
121 | _runtime.SendMessage(Logic, GuiConstants.Download, url, 0);
122 | }
123 |
124 | private void Cancel()
125 | {
126 | _runtime.SendMessage(Logic, Codes.Cancel, null, 0);
127 | }
128 |
129 | public void SwitchToWorking()
130 | {
131 | _animationControl.Visibility = System.Windows.Visibility.Visible;
132 | _cancelButton.IsEnabled = true;
133 | _startCpuButton.IsEnabled = false;
134 | _startIoButton.IsEnabled = false;
135 | _primesEdit.IsEnabled = false;
136 | _urlEdit.IsEnabled = false;
137 | _animationControl.Start();
138 | }
139 |
140 | public void SwitchToReady()
141 | {
142 | _animationControl.Visibility = System.Windows.Visibility.Hidden;
143 | _cancelButton.IsEnabled = false;
144 | _startCpuButton.IsEnabled = true;
145 | _startIoButton.IsEnabled = true;
146 | _primesEdit.IsEnabled = true;
147 | _urlEdit.IsEnabled = true;
148 | _animationControl.Stop();
149 | }
150 |
151 | private GroupBox AddGroupBox(StackPanel panel, string text)
152 | {
153 | Label label = new Label();
154 | label.Content = text;
155 | GroupBox group = new GroupBox();
156 | group.Margin = new Thickness(5);
157 | group.Header = label;
158 | panel.Children.Add(group);
159 | return group;
160 | }
161 |
162 | private void FillGroupBox(GroupBox groupBox, UIElement left, Button right)
163 | {
164 | Grid grid = new Grid();
165 | groupBox.Content = grid;
166 | grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.7, GridUnitType.Star) });
167 | grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.3, GridUnitType.Star) });
168 | Grid.SetColumn(left, 0);
169 | Grid.SetColumn(right, 1);
170 | grid.Children.Add(left);
171 | grid.Children.Add(right);
172 | }
173 |
174 | [STAThread]
175 | public static void Main()
176 | {
177 | Application app = new Application();
178 | DefaultErrorHandler errorHandler = new DefaultErrorHandler();
179 | DebugLogger logger = new DebugLogger();
180 | using (Runtime runtime = new Runtime(logger, errorHandler))
181 | {
182 | MainWindow window = new MainWindow(runtime);
183 | app.Run(window);
184 | }
185 | }
186 |
187 | public void ReportError(string message)
188 | {
189 | MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
190 | }
191 |
192 | public void ReportResult(string result)
193 | {
194 | TextWindow dialog = new TextWindow(result);
195 | dialog.ShowDialog();
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/ActorGui/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("ActorGui")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("ActorGui")]
15 | [assembly: AssemblyCopyright("Copyright © 2014")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 | [assembly: InternalsVisibleTo("ActorGuiTest")]
--------------------------------------------------------------------------------
/ActorGui/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ActorGui.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ActorGui.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/ActorGui/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/ActorGui/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ActorGui.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ActorGui/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ActorGui/PumpAnimation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Threading.Tasks;
7 | using System.Windows.Controls;
8 | using System.Windows.Media;
9 | using System.Windows.Threading;
10 | using System.Windows.Media.Animation;
11 | using System.Diagnostics;
12 |
13 | namespace ActorGui
14 | {
15 | public class PumpAnimation : Control
16 | {
17 | private class Cinematics
18 | {
19 | public double BeamAngle;
20 |
21 | public double ConrodEndX;
22 | public double ConrodEndY;
23 |
24 | public double BeamEndX;
25 | public double BeamEndY;
26 |
27 | public static Cinematics Calculate(
28 | double motorAngle,
29 | double beamX,
30 | double beamY,
31 | double beamLength,
32 | double conrodX,
33 | double conrodY,
34 | double conrodLength,
35 | double rodLength)
36 | {
37 | Cinematics result = new Cinematics();
38 | result.ConrodEndX = conrodX + Math.Cos(motorAngle) * conrodLength;
39 | result.ConrodEndY = conrodY - Math.Sin(motorAngle) * conrodLength;
40 |
41 | double cx1 = result.ConrodEndX;
42 | double cy1 = result.ConrodEndY;
43 |
44 | double cx2 = beamX;
45 | double cy2 = beamY;
46 |
47 | double l1 = rodLength;
48 | double l2 = beamLength;
49 |
50 | double dx = cx2 - cx1;
51 | double dy = cy2 - cy1;
52 | double d = Math.Sqrt(dx * dx + dy * dy);
53 |
54 | double beta = CalculateAngleInTriangle(l2, l1, d);
55 | double alfa = Math.Acos(dx / d);
56 |
57 | double gamma = alfa + beta;
58 |
59 | result.BeamEndX = cx1 + Math.Cos(gamma) * l1;
60 | result.BeamEndY = cy1 - Math.Sin(gamma) * l1;
61 |
62 | double bDy = result.BeamEndY - cy2;
63 |
64 | double sinBeanAngle = bDy / l2;
65 | result.BeamAngle = Math.Asin(sinBeanAngle);
66 |
67 | return result;
68 | }
69 |
70 |
71 | }
72 |
73 | private readonly DoubleAnimation _animation;
74 | private double _angleRadians = 0;
75 |
76 | public double Angle
77 | {
78 | get { return (double)this.GetValue(AngleProperty); }
79 | set
80 | {
81 | this.SetValue(AngleProperty, value);
82 | }
83 | }
84 |
85 | public static readonly DependencyProperty AngleProperty = DependencyProperty.Register(
86 | "Angle", typeof(double), typeof(PumpAnimation), new PropertyMetadata(0.0));
87 |
88 |
89 | public PumpAnimation()
90 | {
91 | Width = 300;
92 | Height = 300;
93 |
94 | _animation = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(4)));
95 | _animation.AutoReverse = false;
96 | _animation.RepeatBehavior = RepeatBehavior.Forever;
97 |
98 | }
99 |
100 |
101 | public void Start()
102 | {
103 | BeginAnimation(PumpAnimation.AngleProperty, _animation);
104 | }
105 |
106 | public void Stop()
107 | {
108 | BeginAnimation(PumpAnimation.AngleProperty, null);
109 |
110 | }
111 |
112 | private static double CalculateAngleInTriangle(double a, double b, double c)
113 | {
114 | double cosA = (b * b + c * c - a * a) / (2.0 * b * c);
115 | return Math.Acos(cosA);
116 | }
117 |
118 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
119 | {
120 | base.OnPropertyChanged(e);
121 | if (e.Property == PumpAnimation.AngleProperty)
122 | {
123 | double value = (double)e.NewValue;
124 | _angleRadians = ToRadians(value);
125 | InvalidateVisual();
126 | }
127 | }
128 |
129 | private static double ToRadians(double value)
130 | {
131 | double norm = value;
132 | if (norm < 0)
133 | {
134 | norm = 0;
135 | }
136 | else if (norm > 1)
137 | {
138 | norm = 1;
139 | }
140 |
141 | return norm * Math.PI * 2;
142 | }
143 |
144 | protected override void OnRender(DrawingContext context)
145 | {
146 | SolidColorBrush fore = new SolidColorBrush(Colors.Maroon);
147 |
148 | Cinematics state = Cinematics.Calculate(_angleRadians,
149 | 150, 130, 100,
150 | 55, 220, 40,
151 | 90);
152 | DrawStationaryParts(context, fore);
153 | DrawСonrod(context, fore, 55, 220, RadiansToDegrees(_angleRadians));
154 | DrawBeam(context, fore, 150, 130, RadiansToDegrees(state.BeamAngle));
155 |
156 | context.DrawLine(
157 | new Pen(fore, 3),
158 | new Point(state.BeamEndX, state.BeamEndY),
159 | new Point(state.ConrodEndX, state.ConrodEndY));
160 |
161 | //DrawAxis(context, 150, 130);
162 | //DrawAxis(context, 55, 250);
163 | //DrawAxis(context, state.ConrodEndX, state.ConrodEndY);
164 | //DrawAxis(context, state.BeamEndX, state.BeamEndY);
165 | }
166 |
167 | private void DrawBeam(DrawingContext context, SolidColorBrush fore, int x, int y, double angle)
168 | {
169 | Geometry beam = CreatePolygon(
170 | new Point(x - 105, y + 5),
171 | new Point(x - 105, y - 5),
172 | new Point(x + 110, y - 5),
173 | new Point(x + 110, y - 60),
174 | new Point(x + 120, y - 60),
175 | new Point(x + 135, y - 25),
176 | new Point(x + 135, y + 25),
177 | new Point(x + 120, y + 60),
178 | new Point(x + 110, y + 60),
179 | new Point(x + 110, y + 5),
180 | new Point(x, y + 5));
181 |
182 | TransformGroup transforms = Rotate(x, y, angle);
183 | beam.Transform = transforms;
184 |
185 | context.DrawGeometry(fore, null, beam);
186 | }
187 |
188 | private static double RadiansToDegrees(double value)
189 | {
190 | return value / Math.PI * 180;
191 | }
192 |
193 | private void DrawСonrod(DrawingContext context, SolidColorBrush fore, double x, double y, double angle)
194 | {
195 | Geometry conrod = CreatePolygon(
196 | new Point(x - 20, y + 5),
197 | new Point(x - 20, y - 5),
198 | new Point(x, y - 5),
199 | new Point(x + 20, y - 20),
200 | new Point(x + 50, y - 20),
201 | new Point(x + 50, y + 20),
202 | new Point(x + 20, y + 20),
203 | new Point(x, y + 5));
204 |
205 | TransformGroup transforms = Rotate(x, y, angle);
206 | conrod.Transform = transforms;
207 |
208 | context.DrawGeometry(fore, null, conrod);
209 | }
210 |
211 | private static TransformGroup Rotate(double x, double y, double angle)
212 | {
213 | TransformGroup transforms = new TransformGroup();
214 | transforms.Children.Add(new TranslateTransform(-x, -y));
215 | transforms.Children.Add(new RotateTransform(-angle));
216 | transforms.Children.Add(new TranslateTransform(x, y));
217 | return transforms;
218 | }
219 |
220 | private void DrawStationaryParts(DrawingContext context, SolidColorBrush fore)
221 | {
222 | //context.DrawRectangle(new SolidColorBrush(C1), null, new Rect(0, 0, Width, Height));
223 | Geometry basis = CreatePolygon(
224 | new Point(100, 290),
225 | new Point(150, 120),
226 | new Point(200, 290),
227 | new Point(190, 290),
228 | new Point(150, 150),
229 | new Point(110, 290)
230 | );
231 | context.DrawGeometry(fore, null, basis);
232 |
233 | Geometry motor = CreatePolygon(
234 | new Point(40, 290),
235 | new Point(50, 220),
236 | new Point(60, 220),
237 | new Point(70, 290)
238 | );
239 | context.DrawGeometry(fore, null, motor);
240 | }
241 |
242 | private static void DrawAxis(DrawingContext context, double x, double y)
243 | {
244 | Pen red = new Pen(new SolidColorBrush(Colors.Red), 1);
245 | Pen blue = new Pen(new SolidColorBrush(Colors.Blue), 1);
246 | double size = 20;
247 | double left = x - size;
248 | double right = x + size;
249 | double top = y - size;
250 | double bottom = y + size;
251 | context.DrawLine(red, new Point(left, y), new Point(right, y));
252 | context.DrawLine(blue, new Point(x, top), new Point(x, bottom));
253 | }
254 |
255 | private static Geometry CreatePolygon(params Point[] points)
256 | {
257 | if (points.Length < 3)
258 | {
259 | throw new ArgumentException("At least 3 points expected.");
260 | }
261 | PathFigure figure = new PathFigure();
262 | figure.StartPoint = points[0];
263 | figure.Segments = new PathSegmentCollection();
264 | for (int i = 1; i < points.Length; i++)
265 | {
266 | Point p = points[i];
267 | figure.Segments.Add(new LineSegment { Point = p });
268 | }
269 | PathGeometry geom = new PathGeometry();
270 | geom.Figures.Add(figure);
271 | return geom;
272 | }
273 |
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/ActorGui/TextWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace ActorGui
10 | {
11 | public class TextWindow : Window
12 | {
13 | public TextWindow(string text)
14 | {
15 | Width = 400;
16 | this.SizeToContent = System.Windows.SizeToContent.Height;
17 | StackPanel rootStack = new StackPanel();
18 | this.Content = rootStack;
19 | rootStack.Orientation = Orientation.Vertical;
20 |
21 | TextBox textControl = new TextBox();
22 | textControl.TextWrapping = TextWrapping.Wrap;
23 | textControl.AcceptsReturn = true;
24 | textControl.Text = text;
25 | textControl.Height = 300;
26 | textControl.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
27 |
28 | rootStack.Children.Add(textControl);
29 |
30 | Button closeButton = new Button();
31 | closeButton.Content = "Close";
32 | closeButton.Margin = new Thickness(5);
33 | closeButton.Padding = new Thickness(5);
34 | closeButton.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
35 | closeButton.Click += new RoutedEventHandler((a, b) => this.Close());
36 | rootStack.Children.Add(closeButton);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ActorGuiTest/ActorGuiTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}
8 | Library
9 | Properties
10 | ActorGuiTest
11 | ActorGuiTest
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\FakeItEasy.1.25.1\lib\net40\FakeItEasy.dll
35 |
36 |
37 | ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | {2f2c208c-f41f-49aa-a5d5-b9416c684a0a}
54 | ActorGui
55 |
56 |
57 | {bd0aeeda-9523-4cdf-b4c9-ad7d723b1f3b}
58 | Actors
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
--------------------------------------------------------------------------------
/ActorGuiTest/PrimesTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 | using System.Linq;
4 | using System.Threading;
5 | using Actors;
6 | using System.Collections.Generic;
7 | using System.Collections.Concurrent;
8 | using System.Threading.Tasks;
9 | using FakeItEasy;
10 | using System.IO;
11 | using ActorGui;
12 |
13 | namespace ActorGuiTest
14 | {
15 | [TestFixture]
16 | public class PrimesTest
17 | {
18 | [Test]
19 | public void NormalOperation()
20 | {
21 | CheckPrimes(1, 1);
22 | CheckPrimes(2, 1, 2);
23 | CheckPrimes(3, 1, 2, 3);
24 | CheckPrimes(4, 1, 2, 3);
25 | CheckPrimes(5, 1, 2, 3, 5);
26 | CheckPrimes(6, 1, 2, 3, 5);
27 | CheckPrimes(7, 1, 2, 3, 5, 7);
28 | CheckPrimes(20, 1, 2, 3, 5, 7, 11, 13, 17, 19);
29 | CheckPrimes(30, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
30 | CheckPrimes(50, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47);
31 | }
32 |
33 | private static void CheckPrimes(int n, params int[] expected)
34 | {
35 | bool done = false;
36 | string result = "";
37 | IRuntime runtime = A.Fake();
38 |
39 | Action send = (actorId, code, payload, sender) =>
40 | {
41 | result = (string)payload;
42 | done = true;
43 | };
44 | A.CallTo(() => runtime.SendMessage(40, CallResult.Completed, A.Ignored, 0))
45 | .Invokes(send);
46 |
47 | var prime = new GuiMachines.PrimeCalculator();
48 | prime.N = n;
49 | prime.Client = 40;
50 | while (!done)
51 | {
52 | prime.OnMessage(Codes.Pulse, runtime, 30, new Message(Codes.Pulse, 0, null, 0));
53 | }
54 | int[] actual = ParseResult(result);
55 | CollectionAssert.AreEqual(expected, actual);
56 | }
57 |
58 | private static int[] ParseResult(string result)
59 | {
60 | string[] parts = result.Split(new char[] { '\n' }, StringSplitOptions.None);
61 | int[] primes = parts
62 | .Skip(1)
63 | .Select(Int32.Parse)
64 | .ToArray();
65 | return primes;
66 | }
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/ActorGuiTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ActorGuiTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ActorGuiTest")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c2a3b015-943c-4d5e-b045-4a924fcd791d")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/ActorHttp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorHttp", "ActorHttp\ActorHttp.csproj", "{5B3ABB4C-97CA-456D-9D94-E87D7609D243}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorsTest", "ActorsTest\ActorsTest.csproj", "{BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Actors", "Actors\Actors.csproj", "{BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorHttpTest", "ActorHttpTest\ActorHttpTest.csproj", "{1D6DC932-2900-4403-849F-3B5AFA1B545F}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorGui", "ActorGui\ActorGui.csproj", "{2F2C208C-F41F-49AA-A5D5-B9416C684A0A}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorGuiTest", "ActorGuiTest\ActorGuiTest.csproj", "{B8F55A4E-6324-4C68-8677-5FDFDE665BC7}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Debug|x86 = Debug|x86
22 | Release|Any CPU = Release|Any CPU
23 | Release|x86 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Debug|x86.ActiveCfg = Debug|x86
29 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Debug|x86.Build.0 = Debug|x86
30 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Release|x86.ActiveCfg = Release|x86
33 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}.Release|x86.Build.0 = Release|x86
34 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Debug|x86.ActiveCfg = Debug|Any CPU
37 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Debug|x86.Build.0 = Debug|Any CPU
38 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Release|x86.ActiveCfg = Release|Any CPU
41 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}.Release|x86.Build.0 = Release|Any CPU
42 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Debug|x86.ActiveCfg = Debug|Any CPU
45 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}.Release|x86.ActiveCfg = Release|Any CPU
48 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Debug|x86.ActiveCfg = Debug|Any CPU
51 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}.Release|x86.ActiveCfg = Release|Any CPU
54 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Debug|x86.ActiveCfg = Debug|Any CPU
57 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {2F2C208C-F41F-49AA-A5D5-B9416C684A0A}.Release|x86.ActiveCfg = Release|Any CPU
60 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
62 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Debug|x86.ActiveCfg = Debug|Any CPU
63 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Release|Any CPU.Build.0 = Release|Any CPU
65 | {B8F55A4E-6324-4C68-8677-5FDFDE665BC7}.Release|x86.ActiveCfg = Release|Any CPU
66 | EndGlobalSection
67 | GlobalSection(SolutionProperties) = preSolution
68 | HideSolutionNode = FALSE
69 | EndGlobalSection
70 | GlobalSection(MonoDevelopProperties) = preSolution
71 | StartupItem = ActorHttp\ActorHttp.csproj
72 | EndGlobalSection
73 | EndGlobal
74 |
--------------------------------------------------------------------------------
/ActorHttp.userprefs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/ActorHttp/ActorHttp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | {5B3ABB4C-97CA-456D-9D94-E87D7609D243}
7 | Exe
8 | ActorHttp
9 | ActorHttp
10 | v4.5
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug
17 | DEBUG;
18 | prompt
19 | 4
20 | true
21 | AnyCPU
22 | 8080 ~/Pictures/20140506
23 |
24 |
25 | full
26 | true
27 | bin\Release
28 | prompt
29 | 4
30 | true
31 | x86
32 |
33 |
34 | true
35 | bin\Debug\
36 | DEBUG;
37 | full
38 | AnyCPU
39 | prompt
40 | MinimumRecommendedRules.ruleset
41 | true
42 |
43 |
44 | bin\Release\
45 | true
46 | full
47 | AnyCPU
48 | prompt
49 | MinimumRecommendedRules.ruleset
50 | false
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {bd0aeeda-9523-4cdf-b4c9-ad7d723b1f3b}
69 | Actors
70 |
71 |
72 |
--------------------------------------------------------------------------------
/ActorHttp/ActorHttp.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8080 C:\Users\1\Pictures\20140517
5 |
6 |
7 | 12345 C:\code\drakon_editor
8 |
9 |
--------------------------------------------------------------------------------
/ActorHttp/HttpActors.cs:
--------------------------------------------------------------------------------
1 | // Autogenerated with DRAKON Editor 1.31
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Net;
7 | using System.IO;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | using Actors;
12 |
13 | namespace ActorHttp {
14 |
15 | public class HttpActors {
16 | internal const int RequestArrived = 840;
17 | internal const int PumpFinished = 841;
18 | internal const int ReadFolder = 842;
19 |
20 |
21 | public partial class StreamPump
22 | : IActor
23 | {
24 | // Framework
25 | public int MyId;
26 | public IRuntime Runtime;
27 | // Parameters
28 | internal int Manager; // The id of the pump manager.
29 | internal Stream InStream; // File stream that reads from disk.
30 | internal Stream OutStream; // HTTP stream that sends to the client.
31 | internal int TotalLength = 0; // The total length of the requested file.
32 | // Internal state
33 | internal IoBuffer InBuffer = new IoBuffer(); // Read data from disk here.
34 | internal IoBuffer OutBuffer = new IoBuffer(); // Send data to client from here.
35 | internal int SoFar = 0; // Number of bytes read so far.
36 | internal int Read; // Disk read read call id
37 | internal int Send; // Network send call id
38 |
39 | public enum StateNames {
40 | Destroyed,
41 | JustRead,
42 | ReadSend,
43 | JustSend,
44 | SendRemaining
45 | }
46 |
47 | private StateNames _state = StateNames.JustRead;
48 |
49 | public StateNames State {
50 | get { return _state; }
51 | internal set { _state = value; }
52 | }
53 |
54 | public const int CancelledMessage = CallResult.Cancelled;
55 | public const int CompletedMessage = CallResult.Completed;
56 | public const int ErrorMessage = CallResult.Error;
57 |
58 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
59 | switch (messageType) {
60 | case CancelledMessage:
61 | return Cancelled(runtime, myId, message);
62 | case CompletedMessage:
63 | return Completed(runtime, myId, message);
64 | case ErrorMessage:
65 | return Error(runtime, myId, message);
66 | default:
67 | return null;
68 | }
69 | }
70 |
71 | public object Cancelled(IRuntime runtime, int myId, Message message) {
72 | switch (State) {
73 | case StateNames.JustRead:
74 | return JustRead_Cancelled(runtime, myId, message);
75 | case StateNames.ReadSend:
76 | return ReadSend_Cancelled(runtime, myId, message);
77 | case StateNames.JustSend:
78 | return JustSend_Cancelled(runtime, myId, message);
79 | case StateNames.SendRemaining:
80 | return SendRemaining_Cancelled(runtime, myId, message);
81 | default:
82 | return null;
83 | }
84 | }
85 |
86 | public object Completed(IRuntime runtime, int myId, Message message) {
87 | switch (State) {
88 | case StateNames.JustRead:
89 | return JustRead_Completed(runtime, myId, message);
90 | case StateNames.ReadSend:
91 | return ReadSend_Completed(runtime, myId, message);
92 | case StateNames.JustSend:
93 | return JustSend_Completed(runtime, myId, message);
94 | case StateNames.SendRemaining:
95 | return SendRemaining_Completed(runtime, myId, message);
96 | default:
97 | return null;
98 | }
99 | }
100 |
101 | public object Error(IRuntime runtime, int myId, Message message) {
102 | switch (State) {
103 | case StateNames.JustRead:
104 | return JustRead_Error(runtime, myId, message);
105 | case StateNames.ReadSend:
106 | return ReadSend_Error(runtime, myId, message);
107 | case StateNames.JustSend:
108 | return JustSend_Error(runtime, myId, message);
109 | case StateNames.SendRemaining:
110 | return SendRemaining_Error(runtime, myId, message);
111 | default:
112 | return null;
113 | }
114 | }
115 |
116 | private object JustRead_Completed(IRuntime runtime, int myId, Message message) {
117 | // item 344
118 | SaveReceivedCount(this, message);
119 | // item 343
120 | bool finished = SwapBuffers(this);
121 | // item 411
122 | StartSend(runtime, this, myId);
123 | // item 346
124 | if (finished) {
125 | // item 348
126 | State = StateNames.SendRemaining;
127 | return null;
128 | } else {
129 | // item 499
130 | StartRead(runtime, this, myId);
131 | // item 327
132 | State = StateNames.ReadSend;
133 | return null;
134 | }
135 | }
136 |
137 | private object JustRead_Error(IRuntime runtime, int myId, Message message) {
138 | // item 345
139 | Shutdown();
140 | return null;
141 | }
142 |
143 | private object JustRead_Cancelled(IRuntime runtime, int myId, Message message) {
144 | // item 345
145 | Shutdown();
146 | return null;
147 | }
148 |
149 | private object ReadSend_Completed(IRuntime runtime, int myId, Message message) {
150 | int _sw5030000_ = 0;
151 | // item 5030000
152 | _sw5030000_ = message.Id;
153 | // item 5030001
154 | if (_sw5030000_ == Read) {
155 | // item 398
156 | SaveReceivedCount(this, message);
157 | // item 333
158 | State = StateNames.JustSend;
159 | return null;
160 | } else {
161 | // item 5030002
162 | if (_sw5030000_ == Send) {
163 |
164 | } else {
165 | // item 5030003
166 | throw new InvalidOperationException("Not expected: " + _sw5030000_.ToString());
167 | }
168 | // item 376
169 | State = StateNames.JustRead;
170 | return null;
171 | }
172 | }
173 |
174 | private object ReadSend_Error(IRuntime runtime, int myId, Message message) {
175 | // item 365
176 | Shutdown();
177 | return null;
178 | }
179 |
180 | private object ReadSend_Cancelled(IRuntime runtime, int myId, Message message) {
181 | // item 365
182 | Shutdown();
183 | return null;
184 | }
185 |
186 | private object JustSend_Completed(IRuntime runtime, int myId, Message message) {
187 | // item 472
188 | bool finished = SwapBuffers(this);
189 | // item 523
190 | StartSend(runtime, this, myId);
191 | // item 403
192 | if (finished) {
193 | // item 405
194 | State = StateNames.SendRemaining;
195 | return null;
196 | } else {
197 | // item 524
198 | StartRead(runtime, this, myId);
199 | // item 408
200 | State = StateNames.ReadSend;
201 | return null;
202 | }
203 | }
204 |
205 | private object JustSend_Error(IRuntime runtime, int myId, Message message) {
206 | // item 385
207 | Shutdown();
208 | return null;
209 | }
210 |
211 | private object JustSend_Cancelled(IRuntime runtime, int myId, Message message) {
212 | // item 385
213 | Shutdown();
214 | return null;
215 | }
216 |
217 | private object SendRemaining_Completed(IRuntime runtime, int myId, Message message) {
218 | // item 470
219 | runtime.Log.Info(String.Format(
220 | "Pump {0} completed request.",
221 | myId
222 | ));
223 | // item 330
224 | Shutdown();
225 | return null;
226 | }
227 |
228 | private object SendRemaining_Error(IRuntime runtime, int myId, Message message) {
229 | // item 397
230 | Shutdown();
231 | return null;
232 | }
233 |
234 | private object SendRemaining_Cancelled(IRuntime runtime, int myId, Message message) {
235 | // item 397
236 | Shutdown();
237 | return null;
238 | }
239 |
240 | public void Shutdown() {
241 | if (State == StateNames.Destroyed) {
242 | return;
243 | }
244 | State = StateNames.Destroyed;
245 | // item 446
246 | Runtime.SendMessage(
247 | Manager,
248 | ServerConstants.PumpFinished,
249 | MyId,
250 | 0
251 | );
252 | // item 349
253 | if (InStream == null) {
254 |
255 | } else {
256 | // item 352
257 | InStream.Dispose();
258 | }
259 | // item 353
260 | if (OutStream == null) {
261 |
262 | } else {
263 | // item 356
264 | OutStream.Dispose();
265 | }
266 | }
267 | }
268 |
269 | public partial class PumpManager
270 | : IActor
271 | {
272 | // Parameters
273 | public string Folder;
274 | public int MaxPumps;
275 | public int FolderReader;
276 | // State
277 | private readonly Dictionary ActivePumps =
278 | new Dictionary();
279 |
280 | public enum StateNames {
281 | Destroyed,
282 | Operational
283 | }
284 |
285 | private StateNames _state = StateNames.Operational;
286 |
287 | public StateNames State {
288 | get { return _state; }
289 | internal set { _state = value; }
290 | }
291 |
292 | public const int PumpFinishedMessage = ServerConstants.PumpFinished;
293 | public const int RequestArrivedMessage = ServerConstants.RequestArrived;
294 |
295 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
296 | switch (messageType) {
297 | case PumpFinishedMessage:
298 | return PumpFinished(runtime, myId, message);
299 | case RequestArrivedMessage:
300 | return RequestArrived(runtime, myId, message);
301 | default:
302 | return null;
303 | }
304 | }
305 |
306 | public object PumpFinished(IRuntime runtime, int myId, Message message) {
307 | switch (State) {
308 | case StateNames.Operational:
309 | return Operational_PumpFinished(runtime, myId, message);
310 | default:
311 | return null;
312 | }
313 | }
314 |
315 | public object RequestArrived(IRuntime runtime, int myId, Message message) {
316 | switch (State) {
317 | case StateNames.Operational:
318 | return Operational_RequestArrived(runtime, myId, message);
319 | default:
320 | return null;
321 | }
322 | }
323 |
324 | private object Operational_RequestArrived(IRuntime runtime, int myId, Message message) {
325 | // item 440
326 | HttpListenerContext context = (HttpListenerContext)message.Payload;
327 | // item 474
328 | string url = context.Request.RawUrl;
329 | // item 441
330 | if (ActivePumps.Count < MaxPumps) {
331 | // item 461
332 | runtime.Log.Info("Serving request for: " + url);
333 | // item 452
334 | int actorId = ProcessRequest(
335 | runtime,
336 | myId,
337 | context,
338 | Folder,
339 | url,
340 | FolderReader
341 | );
342 | // item 453
343 | ActivePumps.Add(actorId, context);
344 | } else {
345 | // item 460
346 | runtime.Log.Info("Limit reached. Aborting.");
347 | // item 443
348 | context.Response.Abort();
349 | }
350 | // item 425
351 | State = StateNames.Operational;
352 | return null;
353 | }
354 |
355 | private object Operational_PumpFinished(IRuntime runtime, int myId, Message message) {
356 | // item 457
357 | int actorId = (int)message.Payload;
358 | // item 454
359 | if (ActivePumps.ContainsKey(actorId)) {
360 | // item 464
361 | runtime.Log.Info("Closing pump: " + actorId);
362 | // item 458
363 | HttpListenerResponse response = ActivePumps[actorId].Response;
364 | ActivePumps.Remove(actorId);
365 | // item 459
366 | response.Close();
367 | } else {
368 |
369 | }
370 | // item 425
371 | State = StateNames.Operational;
372 | return null;
373 | }
374 |
375 | public void Shutdown() {
376 | if (State == StateNames.Destroyed) {
377 | return;
378 | }
379 | State = StateNames.Destroyed;
380 |
381 | }
382 | }
383 |
384 | public partial class IndexBuilder
385 | : IActor
386 | {
387 | // Framework
388 | public int MyId;
389 | public IRuntime Runtime;
390 | // Parameters
391 | internal int Manager; // The id of the pump manager.
392 | internal int FolderReader; // Folder reader id.
393 | internal string Folder; // The folder to enumerate.
394 | internal HttpListenerResponse Response;
395 | // Internal state
396 | internal int PumpId; // The stream pump id
397 |
398 | public enum StateNames {
399 | Destroyed,
400 | Init,
401 | WaitingForFolder,
402 | Sending
403 | }
404 |
405 | private StateNames _state = StateNames.Init;
406 |
407 | public StateNames State {
408 | get { return _state; }
409 | internal set { _state = value; }
410 | }
411 |
412 | public const int CancelledMessage = CallResult.Cancelled;
413 | public const int CompletedMessage = CallResult.Completed;
414 | public const int ErrorMessage = CallResult.Error;
415 | public const int TimeoutMessage = CallResult.Timeout;
416 | public const int StartMessage = Codes.Start;
417 | public const int PumpFinishedMessage = ServerConstants.PumpFinished;
418 |
419 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
420 | switch (messageType) {
421 | case CancelledMessage:
422 | return Cancelled(runtime, myId, message);
423 | case CompletedMessage:
424 | return Completed(runtime, myId, message);
425 | case ErrorMessage:
426 | return Error(runtime, myId, message);
427 | case TimeoutMessage:
428 | return Timeout(runtime, myId, message);
429 | case StartMessage:
430 | return Start(runtime, myId, message);
431 | case PumpFinishedMessage:
432 | return PumpFinished(runtime, myId, message);
433 | default:
434 | return null;
435 | }
436 | }
437 |
438 | public object Cancelled(IRuntime runtime, int myId, Message message) {
439 | switch (State) {
440 | case StateNames.Init:
441 | case StateNames.WaitingForFolder:
442 | return WaitingForFolder_Cancelled(runtime, myId, message);
443 | case StateNames.Sending:
444 | default:
445 | return null;
446 | }
447 | }
448 |
449 | public object Completed(IRuntime runtime, int myId, Message message) {
450 | switch (State) {
451 | case StateNames.Init:
452 | case StateNames.WaitingForFolder:
453 | return WaitingForFolder_Completed(runtime, myId, message);
454 | case StateNames.Sending:
455 | default:
456 | return null;
457 | }
458 | }
459 |
460 | public object Error(IRuntime runtime, int myId, Message message) {
461 | switch (State) {
462 | case StateNames.Init:
463 | case StateNames.WaitingForFolder:
464 | return WaitingForFolder_Error(runtime, myId, message);
465 | case StateNames.Sending:
466 | default:
467 | return null;
468 | }
469 | }
470 |
471 | public object Timeout(IRuntime runtime, int myId, Message message) {
472 | switch (State) {
473 | case StateNames.Init:
474 | case StateNames.WaitingForFolder:
475 | return WaitingForFolder_Timeout(runtime, myId, message);
476 | case StateNames.Sending:
477 | default:
478 | return null;
479 | }
480 | }
481 |
482 | public object Start(IRuntime runtime, int myId, Message message) {
483 | switch (State) {
484 | case StateNames.Init:
485 | return Init_Start(runtime, myId, message);
486 | case StateNames.WaitingForFolder:
487 | case StateNames.Sending:
488 | default:
489 | return null;
490 | }
491 | }
492 |
493 | public object PumpFinished(IRuntime runtime, int myId, Message message) {
494 | switch (State) {
495 | case StateNames.Init:
496 | case StateNames.WaitingForFolder:
497 | case StateNames.Sending:
498 | return Sending_PumpFinished(runtime, myId, message);
499 | default:
500 | return null;
501 | }
502 | }
503 |
504 | private object Init_Start(IRuntime runtime, int myId, Message message) {
505 | // item 840
506 | runtime.SendCall(
507 | FolderReader,
508 | ReadFolder,
509 | Folder,
510 | myId,
511 | TimeSpan.FromSeconds(1)
512 | );
513 | // item 766
514 | State = StateNames.WaitingForFolder;
515 | return null;
516 | }
517 |
518 | private object Init_default(IRuntime runtime, int myId, Message message) {
519 | // item 783
520 | State = StateNames.Init;
521 | return null;
522 | }
523 |
524 | private object WaitingForFolder_Completed(IRuntime runtime, int myId, Message message) {
525 | // item 843
526 | var files = (List)message.Payload;
527 | // item 846
528 | string responseText = BuildFolderIndexPage(files);
529 | byte[] responseData = Encoding.UTF8.GetBytes(
530 | responseText
531 | );
532 | // item 844
533 | PumpId = CreatePumpFromBytes(
534 | runtime,
535 | myId,
536 | Response,
537 | responseData,
538 | 200
539 | );
540 | // item 772
541 | State = StateNames.Sending;
542 | return null;
543 | }
544 |
545 | private object WaitingForFolder_Error(IRuntime runtime, int myId, Message message) {
546 | // item 801
547 | Shutdown();
548 | return null;
549 | }
550 |
551 | private object WaitingForFolder_Timeout(IRuntime runtime, int myId, Message message) {
552 | // item 900
553 | runtime.Log.Info(
554 | "Timeout on ReadFolder"
555 | );
556 | // item 801
557 | Shutdown();
558 | return null;
559 | }
560 |
561 | private object WaitingForFolder_Cancelled(IRuntime runtime, int myId, Message message) {
562 | // item 801
563 | Shutdown();
564 | return null;
565 | }
566 |
567 | private object Sending_PumpFinished(IRuntime runtime, int myId, Message message) {
568 | // item 847
569 | int actorId = (int)message.Payload;
570 | // item 817
571 | if (actorId == PumpId) {
572 | // item 820
573 | Shutdown();
574 | return null;
575 | } else {
576 | // item 819
577 | State = StateNames.Sending;
578 | return null;
579 | }
580 | }
581 |
582 | private object Sending_default(IRuntime runtime, int myId, Message message) {
583 | // item 819
584 | State = StateNames.Sending;
585 | return null;
586 | }
587 |
588 | public void Shutdown() {
589 | if (State == StateNames.Destroyed) {
590 | return;
591 | }
592 | State = StateNames.Destroyed;
593 | // item 822
594 | Runtime.SendMessage(
595 | Manager,
596 | ServerConstants.PumpFinished,
597 | MyId,
598 | 0
599 | );
600 | }
601 | }
602 |
603 | public partial class FolderReader
604 | : IActor
605 | {
606 |
607 |
608 | public enum StateNames {
609 | Destroyed,
610 | Working
611 | }
612 |
613 | private StateNames _state = StateNames.Working;
614 |
615 | public StateNames State {
616 | get { return _state; }
617 | internal set { _state = value; }
618 | }
619 |
620 | public const int ReadFolderMessage = ServerConstants.ReadFolder;
621 |
622 | public object OnMessage(int messageType, IRuntime runtime, int myId, Message message) {
623 | switch (messageType) {
624 | case ReadFolderMessage:
625 | return ReadFolder(runtime, myId, message);
626 | default:
627 | return null;
628 | }
629 | }
630 |
631 | public object ReadFolder(IRuntime runtime, int myId, Message message) {
632 | switch (State) {
633 | case StateNames.Working:
634 | return Working_ReadFolder(runtime, myId, message);
635 | default:
636 | return null;
637 | }
638 | }
639 |
640 | private object Working_ReadFolder(IRuntime runtime, int myId, Message message) {
641 | // item 870
642 | string folder = (string)message.Payload;
643 | // item 871
644 | List files = null;
645 | Exception exception = null;
646 | try {
647 | files = Directory
648 | .EnumerateFiles(folder)
649 | .ToList();
650 | } catch (Exception ex) {
651 | exception = ex;
652 | runtime.Log.Error(
653 | "Error reading folder: " + folder,
654 | ex
655 | );
656 | }
657 | // item 873
658 | if (files == null) {
659 | // item 875
660 | runtime.SendResult(
661 | message.Sender,
662 | message.Id,
663 | CallResult.Error,
664 | exception,
665 | myId
666 | );
667 | } else {
668 | // item 872
669 | runtime.SendResult(
670 | message.Sender,
671 | message.Id,
672 | CallResult.Completed,
673 | files,
674 | myId
675 | );
676 | }
677 | // item 856
678 | State = StateNames.Working;
679 | return null;
680 | }
681 |
682 | private object Working_default(IRuntime runtime, int myId, Message message) {
683 | // item 856
684 | State = StateNames.Working;
685 | return null;
686 | }
687 |
688 | public void Shutdown() {
689 | if (State == StateNames.Destroyed) {
690 | return;
691 | }
692 | State = StateNames.Destroyed;
693 |
694 | }
695 | }
696 |
697 | public static int ProcessRequest(IRuntime runtime, int manager, HttpListenerContext context, string folder, string url, int reader) {
698 | // item 670
699 | int actorId;
700 | string path = folder + "/" + url;
701 | // item 674
702 | if (Directory.Exists(path)) {
703 | // item 901
704 | string indexPath = path + "/" + "index.html";
705 | // item 904
706 | if (File.Exists(indexPath)) {
707 | // item 905
708 | path = indexPath;
709 | // item 707
710 | actorId = CreateFilePump(
711 | runtime,
712 | path,
713 | manager,
714 | context.Response
715 | );
716 | // item 750
717 | if (actorId == 0) {
718 | // item 753
719 | actorId = Create404Pump(
720 | runtime,
721 | manager,
722 | context.Response
723 | );
724 | } else {
725 |
726 | }
727 | } else {
728 | // item 755
729 | actorId = CreateIndexBuilder(
730 | runtime,
731 | manager,
732 | context.Response,
733 | path,
734 | reader
735 | );
736 | }
737 | } else {
738 | // item 707
739 | actorId = CreateFilePump(
740 | runtime,
741 | path,
742 | manager,
743 | context.Response
744 | );
745 | // item 750
746 | if (actorId == 0) {
747 | // item 753
748 | actorId = Create404Pump(
749 | runtime,
750 | manager,
751 | context.Response
752 | );
753 | } else {
754 |
755 | }
756 | }
757 | // item 754
758 | return actorId;
759 | }
760 |
761 | private static string BuildFolderIndexPage(List files) {
762 | // item 892
763 | StringBuilder sb = new StringBuilder();
764 | // item 894
765 | sb.AppendLine("");
766 | sb.AppendLine("");
767 | foreach (string file in files) {
768 | // item 899
769 | string filename = Path.GetFileName(file);
770 | // item 898
771 | sb.AppendFormat("{0}
", filename);
772 | sb.AppendLine();
773 | }
774 | // item 895
775 | sb.AppendLine("");
776 | sb.AppendLine("");
777 | // item 893
778 | return sb.ToString();
779 | }
780 |
781 | private static int Create404Pump(IRuntime runtime, int manager, HttpListenerResponse response) {
782 | // item 731
783 | byte[] responseData = Encoding.UTF8.GetBytes(
784 | "" +
785 | "404 Not Found
" +
786 | "Actor HTTP megaserver
Resource not found.
" +
787 | ""
788 | );
789 | // item 748
790 | return CreatePumpFromBytes(
791 | runtime,
792 | manager,
793 | response,
794 | responseData,
795 | 404
796 | );
797 | }
798 |
799 | private static int CreateFilePump(IRuntime runtime, string path, int manager, HttpListenerResponse response) {
800 | // item 697
801 | if (File.Exists(path)) {
802 | // item 698
803 | Stream fstream = TryOpenFile(path);
804 | // item 699
805 | if (fstream == null) {
806 | // item 703
807 | runtime.Log.Info(
808 | "Could not open file: "
809 | + path
810 | );
811 | // item 701
812 | return 0;
813 | } else {
814 | // item 691
815 | FileInfo about = new FileInfo(path);
816 | // item 696
817 | runtime.Log.Info(
818 | String.Format(
819 | "Found file: {0}. Length: {1}",
820 | path,
821 | (int)about.Length
822 | )
823 | );
824 | // item 689
825 | var pump = new StreamPump();
826 | pump.Manager = manager;
827 | pump.TotalLength = (int)about.Length;
828 | pump.InStream = fstream;
829 | pump.OutStream = response.OutputStream;
830 | // item 690
831 | int id = runtime.AddActor(pump);
832 | pump.MyId = id;
833 | pump.Runtime = runtime;
834 | // item 692
835 | response.StatusCode = 200;
836 | response.ContentLength64 = pump.TotalLength;
837 | // item 694
838 | runtime.StartRead(
839 | pump.InStream,
840 | pump.InBuffer,
841 | id
842 | );
843 | // item 695
844 | return id;
845 | }
846 | } else {
847 | // item 702
848 | runtime.Log.Info(
849 | "File not found: "
850 | + path
851 | );
852 | // item 701
853 | return 0;
854 | }
855 | }
856 |
857 | private static int CreateIndexBuilder(IRuntime runtime, int manager, HttpListenerResponse response, string path, int reader) {
858 | // item 883
859 | var builder = new IndexBuilder();
860 | builder.Manager = manager;
861 | builder.FolderReader = reader;
862 | builder.Folder = path;
863 | builder.Response = response;
864 | // item 884
865 | int actorId = runtime.AddActor(builder);
866 | builder.MyId = actorId;
867 | builder.Runtime = runtime;
868 | // item 886
869 | runtime.SendMessage(
870 | actorId,
871 | Codes.Start,
872 | null,
873 | 0
874 | );
875 | // item 885
876 | return actorId;
877 | }
878 |
879 | private static int CreatePumpFromBytes(IRuntime runtime, int manager, HttpListenerResponse response, byte[] responseData, int responseCode) {
880 | // item 744
881 | response.StatusCode = responseCode;
882 | response.ContentLength64 = responseData.Length;
883 | // item 740
884 | var pump = new StreamPump();
885 | pump.Manager = manager;
886 | pump.OutStream = response.OutputStream;
887 | pump.OutBuffer.Data = responseData;
888 | pump.OutBuffer.Count = responseData.Length;
889 | // item 745
890 | pump.State = StreamPump.StateNames.SendRemaining;
891 | // item 911
892 | int id = runtime.AddActor(pump);
893 | pump.MyId = id;
894 | pump.Runtime = runtime;
895 | // item 746
896 | runtime.StartWrite(
897 | pump.OutStream,
898 | pump.OutBuffer,
899 | id
900 | );
901 | // item 742
902 | return id;
903 | }
904 |
905 | private static bool IsReadingFinished(StreamPump pump) {
906 | // item 266
907 | if ((pump.InBuffer.Count == 0) || (!(pump.SoFar < pump.TotalLength))) {
908 | // item 269
909 | return true;
910 | } else {
911 | // item 270
912 | return false;
913 | }
914 | }
915 |
916 | private static void SaveReceivedCount(StreamPump pump, Message message) {
917 | // item 262
918 | pump.InBuffer.Count = (int)message.Payload;
919 | pump.SoFar += pump.InBuffer.Count;
920 | }
921 |
922 | private static void StartRead(IRuntime runtime, StreamPump pump, int myId) {
923 | // item 491
924 | pump.Read = runtime.StartRead(
925 | pump.InStream,
926 | pump.InBuffer,
927 | myId
928 | );
929 | }
930 |
931 | private static void StartSend(IRuntime runtime, StreamPump pump, int myId) {
932 | // item 498
933 | pump.Send = runtime.StartWrite(
934 | pump.OutStream,
935 | pump.OutBuffer,
936 | myId
937 | );
938 | }
939 |
940 | private static bool SwapBuffers(StreamPump pump) {
941 | // item 471
942 | bool finished = IsReadingFinished(pump);
943 | // item 254
944 | IoBuffer oldIn = pump.InBuffer;
945 | IoBuffer oldOut = pump.OutBuffer;
946 | // item 255
947 | pump.OutBuffer = oldIn;
948 | pump.InBuffer = oldOut;
949 | // item 473
950 | return finished;
951 | }
952 |
953 | private static Stream TryOpenFile(string path) {
954 | // item 248
955 | try {
956 | Stream fstream = new FileStream(path, FileMode.Open, FileAccess.Read);
957 | return fstream;
958 | }
959 | catch {
960 | return null;
961 | }
962 | }
963 | }
964 | }
965 |
--------------------------------------------------------------------------------
/ActorHttp/HttpActors.drn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/ActorHttp/HttpActors.drn
--------------------------------------------------------------------------------
/ActorHttp/MiniServer.cs:
--------------------------------------------------------------------------------
1 | using Actors;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace ActorHttp
11 | {
12 | public class MiniServer : IDisposable
13 | {
14 | private const int MaxPumps = 10000;
15 | private readonly HttpListener _listener = new HttpListener();
16 | private readonly int _port;
17 | private readonly string _folder;
18 | private readonly IActorLogger _logger = new ConsoleLogger();
19 | private readonly IErrorHandler _handler = new DefaultErrorHandler();
20 | private Runtime _runtime;
21 | private readonly CancellationTokenSource _source = new CancellationTokenSource();
22 |
23 |
24 | public MiniServer(int port, string folder)
25 | {
26 | _port = port;
27 | _folder = folder;
28 | _runtime = new Runtime(_logger, _handler);
29 | string prefix1 = String.Format("http://+:{0}/", port);
30 | _listener.Prefixes.Add(prefix1);
31 | }
32 |
33 | public void Start()
34 | {
35 | int logicalProcs = Environment.ProcessorCount;
36 | for (int i = 0; i < logicalProcs; i++)
37 | {
38 | string name = String.Format("T{0}", i + 1);
39 | _runtime.CreateThread(name);
40 | }
41 |
42 | Console.WriteLine("Created {0} runtime threads.", logicalProcs);
43 |
44 | var folderReader = new HttpActors.FolderReader();
45 | int readerId = _runtime.AddActor(folderReader);
46 |
47 | var manager = new HttpActors.PumpManager();
48 | manager.MaxPumps = MaxPumps;
49 | manager.Folder = _folder;
50 | manager.FolderReader = readerId;
51 |
52 | int managerId = _runtime.AddActor(manager);
53 |
54 | Console.WriteLine("Starting web server at port {0} and folder '{1}'...", _port, _folder);
55 | _listener.Start ();
56 | Console.WriteLine("Server started.");
57 |
58 | RunServer(_listener, managerId, _runtime, _source.Token);
59 | }
60 |
61 | public void Stop()
62 | {
63 | if (_runtime != null)
64 | {
65 | _source.Cancel();
66 | _runtime.Dispose();
67 | _listener.Close();
68 | _runtime = null;
69 | }
70 | }
71 |
72 |
73 | public void Dispose()
74 | {
75 | Stop();
76 | }
77 |
78 | private static void RunServer(HttpListener server, int managerId, IRuntime runtime, CancellationToken cancellation)
79 | {
80 | ThreadStart method = () => RunServerThreadProc(server, managerId, runtime, cancellation);
81 | Thread thread = new Thread(method);
82 | thread.Start();
83 | }
84 |
85 | private static void RunServerThreadProc(HttpListener server, int managerId, IRuntime runtime, CancellationToken cancellation)
86 | {
87 | try
88 | {
89 | while (true)
90 | {
91 | cancellation.ThrowIfCancellationRequested();
92 | Task contextTask = server.GetContextAsync();
93 | contextTask.Wait(cancellation);
94 | HttpListenerContext context = contextTask.Result;
95 | runtime.SendMessage(managerId, HttpActors.RequestArrived, context, 0);
96 | }
97 | }
98 | catch (OperationCanceledException)
99 | {
100 | Console.WriteLine("Cancelled by user.");
101 | }
102 | catch (Exception ex)
103 | {
104 | Console.WriteLine(ex);
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/ActorHttp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.IO;
5 | using System.Text;
6 | using System.Linq;
7 | using Actors;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using System.Net.Sockets;
11 |
12 | namespace ActorHttp
13 | {
14 | class MainClass
15 | {
16 | public static void Main(string[] args)
17 | {
18 | int port;
19 | string folder;
20 |
21 | if (!ReadConfiguration(args, out port, out folder))
22 | {
23 | return;
24 | }
25 |
26 | try
27 | {
28 | using (MiniServer server = new MiniServer(port, folder))
29 | {
30 | server.Start();
31 | Console.WriteLine("Press 'Enter' to exit.");
32 | Console.ReadLine();
33 | }
34 | }
35 | catch (Exception ex)
36 | {
37 | Console.WriteLine(ex);
38 | }
39 |
40 | Console.WriteLine("Finished.");
41 | }
42 |
43 | private static bool ReadConfiguration(string[] args, out int port, out string folder)
44 | {
45 | port = 0;
46 | folder = null;
47 | if (args.Length == 0)
48 | {
49 | port = 8080;
50 | folder = ".";
51 | }
52 | else if (args.Length != 2)
53 | {
54 | Console.WriteLine("Actor HTTP megaserver. Usage:");
55 | Console.WriteLine("ActorHttp ");
56 | return false;
57 | }
58 | else
59 | {
60 | if (!Int32.TryParse(args[0], out port))
61 | {
62 | Console.WriteLine("Port is not an integer.");
63 | return false;
64 | }
65 |
66 | if (port <= 0 || port >= UInt16.MaxValue)
67 | {
68 | Console.WriteLine("Bad port.");
69 | return false;
70 | }
71 |
72 | folder = args[1];
73 |
74 | if (!Directory.Exists(folder))
75 | {
76 | Console.WriteLine("Folder '{0}' does not exist.", folder);
77 | return false;
78 | }
79 | }
80 |
81 | return true;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ActorHttp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle ("ActorHttp")]
8 | [assembly: AssemblyDescription ("")]
9 | [assembly: AssemblyConfiguration ("")]
10 | [assembly: AssemblyCompany ("")]
11 | [assembly: AssemblyProduct ("")]
12 | [assembly: AssemblyCopyright ("stipan")]
13 | [assembly: AssemblyTrademark ("")]
14 | [assembly: AssemblyCulture ("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion ("1.0.*")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | //[assembly: AssemblyDelaySign(false)]
26 | //[assembly: AssemblyKeyFile("")]
27 | [assembly: InternalsVisibleTo("ActorHttpTest")]
28 |
29 |
--------------------------------------------------------------------------------
/ActorHttp/ServerConstants.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ActorHttp
8 | {
9 | public static class ServerConstants
10 | {
11 | public const int RequestArrived = 840;
12 | public const int PumpFinished = 841;
13 | public const int ReadFolder = 842;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ActorHttp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ActorHttpTest/ActorHttpTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1D6DC932-2900-4403-849F-3B5AFA1B545F}
8 | Library
9 | Properties
10 | ActorHttpTest
11 | ActorHttpTest
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\FakeItEasy.1.25.1\lib\net40\FakeItEasy.dll
35 |
36 |
37 | ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | {5b3abb4c-97ca-456d-9d94-e87d7609d243}
54 | ActorHttp
55 |
56 |
57 | {bd0aeeda-9523-4cdf-b4c9-ad7d723b1f3b}
58 | Actors
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
75 |
--------------------------------------------------------------------------------
/ActorHttpTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ActorHttpTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ActorHttpTest")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("397c123a-4533-4a16-b76e-d28a0063cbe4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/ActorHttpTest/StreamPumpTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 | using System.Threading;
4 | using Actors;
5 | using System.Collections.Generic;
6 | using System.Collections.Concurrent;
7 | using System.Threading.Tasks;
8 | using FakeItEasy;
9 | using System.IO;
10 | using ActorHttp;
11 |
12 |
13 | namespace ActorsTest
14 | {
15 | [TestFixture]
16 | public class StreamPumpTest
17 | {
18 | private HttpActors.StreamPump _actor;
19 | private IoBuffer _oldInBuffer;
20 | private IoBuffer _oldOutBuffer;
21 | private IRuntime _runtime;
22 |
23 | [SetUp]
24 | public void SetUp()
25 | {
26 | _runtime = A.Fake();
27 | _actor = new HttpActors.StreamPump();
28 | _actor.Runtime = _runtime;
29 | _actor.MyId = 888;
30 | _actor.InStream = new MemoryStream();
31 | _actor.OutStream = new MemoryStream();
32 | _actor.TotalLength = 100;
33 |
34 | _oldInBuffer = _actor.InBuffer;
35 | _oldOutBuffer = _actor.OutBuffer;
36 | _oldInBuffer.Data[0] = 1;
37 | _oldOutBuffer.Data[0] = 2;
38 | }
39 |
40 | [TearDown]
41 | public void TearDown()
42 | {
43 | if (_actor != null)
44 | {
45 | _actor.InStream.Dispose();
46 | _actor.OutStream.Dispose();
47 | }
48 | }
49 |
50 | [Test]
51 | public void JustRead_Completed_NotFinished()
52 | {
53 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 0, 10, 0));
54 |
55 | A.CallTo(() => _runtime.StartWrite(_actor.OutStream, _oldInBuffer, 888)).MustHaveHappened();
56 | A.CallTo(() => _runtime.StartRead(_actor.InStream, _oldOutBuffer, 888)).MustHaveHappened();
57 |
58 | Assert.AreEqual(10, _actor.OutBuffer.Count);
59 | Assert.AreEqual(10, _actor.SoFar);
60 |
61 | Assert.AreEqual(HttpActors.StreamPump.StateNames.ReadSend, _actor.State);
62 | }
63 |
64 | [Test]
65 | public void JustRead_Completed_Finished()
66 | {
67 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 0, 100, 0));
68 |
69 | A.CallTo(() => _runtime.StartWrite(_actor.OutStream, _oldInBuffer, 888)).MustHaveHappened();
70 | A.CallTo(() => _runtime.StartRead(_actor.InStream, _oldOutBuffer, 888)).MustNotHaveHappened();
71 |
72 | Assert.AreEqual(100, _actor.OutBuffer.Count);
73 | Assert.AreEqual(100, _actor.SoFar);
74 |
75 | Assert.AreEqual(HttpActors.StreamPump.StateNames.SendRemaining, _actor.State);
76 | }
77 |
78 | [Test]
79 | public void JustRead_Failure_Removed()
80 | {
81 | _actor.OnMessage(CallResult.Error, _runtime, 888, new Message(CallResult.Error, 0, null, 0));
82 |
83 | A.CallTo(() => _runtime.SendMessage(_actor.Manager, ServerConstants.PumpFinished, 888, 0)).MustHaveHappened();
84 |
85 |
86 | Assert.AreEqual(HttpActors.StreamPump.StateNames.Destroyed, _actor.State);
87 | }
88 |
89 | [Test]
90 | public void JustRead_Cancelled_Removed()
91 | {
92 | _actor.OnMessage(CallResult.Cancelled, _runtime, 888, new Message(CallResult.Cancelled, 0, null, 0));
93 |
94 | A.CallTo(() => _runtime.SendMessage(_actor.Manager, ServerConstants.PumpFinished, 888, 0)).MustHaveHappened();
95 |
96 | Assert.AreEqual(HttpActors.StreamPump.StateNames.Destroyed, _actor.State);
97 | }
98 |
99 | [Test]
100 | public void ReadSend_CompletedRead_JustSend()
101 | {
102 | _actor.Read = 777;
103 | _actor.Send = 999;
104 | _actor.SoFar = 20;
105 | _actor.State = HttpActors.StreamPump.StateNames.ReadSend;
106 |
107 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 777, 10, 0));
108 |
109 |
110 | Assert.AreEqual(10, _actor.InBuffer.Count);
111 | Assert.AreEqual(30, _actor.SoFar);
112 |
113 | Assert.AreEqual(HttpActors.StreamPump.StateNames.JustSend, _actor.State);
114 | }
115 |
116 | [Test]
117 | public void ReadSend_CompletedWrite_JustRead()
118 | {
119 | _actor.Read = 777;
120 | _actor.Send = 999;
121 | _actor.SoFar = 20;
122 | _actor.State = HttpActors.StreamPump.StateNames.ReadSend;
123 |
124 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 999, 10, 0));
125 |
126 | Assert.AreEqual(HttpActors.StreamPump.StateNames.JustRead, _actor.State);
127 | }
128 |
129 | [Test]
130 | public void ReadSend_Failure_Removed()
131 | {
132 | _actor.State = HttpActors.StreamPump.StateNames.ReadSend;
133 | _actor.OnMessage(CallResult.Error, _runtime, 888, new Message(CallResult.Error, 0, null, 0));
134 |
135 | A.CallTo(() => _runtime.SendMessage(_actor.Manager, ServerConstants.PumpFinished, 888, 0)).MustHaveHappened();
136 |
137 |
138 | Assert.AreEqual(HttpActors.StreamPump.StateNames.Destroyed, _actor.State);
139 | }
140 |
141 | [Test]
142 | public void ReadSend_Cancelled_Removed()
143 | {
144 | _actor.State = HttpActors.StreamPump.StateNames.ReadSend;
145 | _actor.OnMessage(CallResult.Cancelled, _runtime, 888, new Message(CallResult.Cancelled, 0, null, 0));
146 |
147 | A.CallTo(() => _runtime.SendMessage(_actor.Manager, ServerConstants.PumpFinished, 888, 0)).MustHaveHappened();
148 |
149 |
150 | Assert.AreEqual(HttpActors.StreamPump.StateNames.Destroyed, _actor.State);
151 | }
152 |
153 | [Test]
154 | public void JustSend_Completed_NotFinished()
155 | {
156 | _actor.SoFar = 20;
157 | _actor.InBuffer.Count = 20;
158 | _actor.State = HttpActors.StreamPump.StateNames.JustSend;
159 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 0, 0, 0));
160 |
161 | A.CallTo(() => _runtime.StartWrite(_actor.OutStream, _oldInBuffer, 888)).MustHaveHappened();
162 | A.CallTo(() => _runtime.StartRead(_actor.InStream, _oldOutBuffer, 888)).MustHaveHappened();
163 |
164 | Assert.AreEqual(HttpActors.StreamPump.StateNames.ReadSend, _actor.State);
165 | }
166 |
167 | [Test]
168 | public void JustSend_Completed_Finished()
169 | {
170 | _actor.SoFar = 100;
171 | _actor.InBuffer.Count = 20;
172 | _actor.State = HttpActors.StreamPump.StateNames.JustSend;
173 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 0, 0, 0));
174 |
175 | A.CallTo(() => _runtime.StartWrite(_actor.OutStream, _oldInBuffer, 888)).MustHaveHappened();
176 | A.CallTo(() => _runtime.StartRead(_actor.InStream, _oldOutBuffer, 888)).MustNotHaveHappened();
177 |
178 | Assert.AreEqual(HttpActors.StreamPump.StateNames.SendRemaining, _actor.State);
179 | }
180 |
181 | [Test]
182 | public void SendRemaining_Completed_Removed()
183 | {
184 | A.CallTo(() => _runtime.Log).Returns(new ConsoleLogger());
185 | _actor.State = HttpActors.StreamPump.StateNames.SendRemaining;
186 | _actor.OnMessage(CallResult.Completed, _runtime, 888, new Message(CallResult.Completed, 0, null, 0));
187 |
188 | A.CallTo(() => _runtime.SendMessage(_actor.Manager, ServerConstants.PumpFinished, 888, 0)).MustHaveHappened();
189 |
190 |
191 | Assert.AreEqual(HttpActors.StreamPump.StateNames.Destroyed, _actor.State);
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/ActorHttpTest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Actors/Actors.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BD0AEEDA-9523-4CDF-B4C9-AD7D723B1F3B}
8 | Library
9 | Properties
10 | Actors
11 | Actors
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
70 |
--------------------------------------------------------------------------------
/Actors/CallResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Actors
5 | {
6 | ///
7 | /// Codes for results of runtime function calls.
8 | ///
9 | public static class CallResult
10 | {
11 | public const int Completed = 1;
12 | public const int Error = 2;
13 | public const int Cancelled = 3;
14 | public const int Timeout = 4;
15 |
16 | public static void Check(int code)
17 | {
18 | if (code != Completed && code != Error && code != Cancelled && code != Timeout)
19 | {
20 | throw new InvalidOperationException("Bad call result.");
21 | }
22 | }
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Actors/Codes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | ///
10 | /// Widely used generic message codes.
11 | ///
12 | public static class Codes
13 | {
14 | public const int Invalid = 0;
15 | public const int Start = 150;
16 | public const int Shutdown = 200;
17 | public const int Cancel = 300;
18 | public const int Pulse = 1010;
19 |
20 | public const int Max = 10000;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Actors/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Actors
4 | {
5 | ///
6 | /// Logs to the console.
7 | ///
8 | public class ConsoleLogger : IActorLogger
9 | {
10 | public void Error(string message, Exception ex)
11 | {
12 | Console.WriteLine("Error: {0}", message);
13 | if (ex != null)
14 | {
15 | Console.WriteLine(ex);
16 | }
17 | }
18 | public void Info(string message)
19 | {
20 | Console.WriteLine("{0}", message);
21 | }
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/Actors/DebugLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Diagnostics;
7 |
8 | namespace Actors
9 | {
10 | ///
11 | /// Logs to debug output.
12 | ///
13 | public class DebugLogger : IActorLogger
14 | {
15 | public void Error(string message, Exception ex)
16 | {
17 | Debug.WriteLine("Error: {0}", message);
18 | if (ex != null)
19 | {
20 | Debug.WriteLine(ex);
21 | }
22 | }
23 | public void Info(string message)
24 | {
25 | Debug.WriteLine("{0}", message);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Actors/DedicatedThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Actors
9 | {
10 | internal class DedicatedThread : IThread
11 | {
12 | #region Own items
13 | private readonly string _name;
14 | private readonly int _actorId;
15 | private IActor _actor;
16 | #endregion
17 |
18 | #region Injected dependencies
19 | private readonly IRuntime _runtime;
20 | private readonly IActorLogger _logger;
21 | private readonly IErrorHandler _errorHandler;
22 | #endregion
23 |
24 | private readonly ConcurrentQueue _messages = new ConcurrentQueue();
25 |
26 | public DedicatedThread(string name, IRuntime runtime, IActorLogger logger, IErrorHandler errorHandler, int actorId, IActor actor)
27 | {
28 | _actor = actor;
29 | _actorId = actorId;
30 | _name = name;
31 | _runtime = runtime;
32 | _logger = logger;
33 | _errorHandler = errorHandler;
34 | }
35 |
36 | public string Name
37 | {
38 | get { return _name; }
39 | }
40 |
41 | public bool IsNormal
42 | {
43 | get { return false; }
44 | }
45 |
46 | public void PostMessage(int actorId, Message message)
47 | {
48 | if (message == null)
49 | {
50 | throw new ArgumentNullException("message");
51 | }
52 |
53 | _messages.Enqueue(message);
54 | }
55 |
56 | public void AddActor(int actorId, IActor actor)
57 | {
58 | throw new NotSupportedException();
59 | }
60 |
61 | public void RemoveActor(int actorId)
62 | {
63 | if (actorId == _actorId)
64 | {
65 | Stop();
66 | }
67 | }
68 |
69 | public void Start()
70 | {
71 | Task.Factory.StartNew(ThreadProcedure, TaskCreationOptions.LongRunning);
72 | }
73 |
74 | public void Stop()
75 | {
76 | _messages.Enqueue(null);
77 | }
78 |
79 | private void ThreadProcedure()
80 | {
81 | _logger.Info("Started dedicated thread " + _name);
82 | Message pulse = new Message(Codes.Pulse, 0, null, 0);
83 | while (true)
84 | {
85 | bool mustExist = ProcessMessages();
86 | if (mustExist)
87 | {
88 | break;
89 | }
90 |
91 | RunMessageHandler(pulse);
92 | }
93 |
94 | Runtime runtime = (Runtime)_runtime;
95 | runtime.RemoveThread(_actorId);
96 |
97 | _logger.Info("Finished dedicated thread " + _name);
98 | }
99 |
100 | private bool ProcessMessages()
101 | {
102 | Message message;
103 | while (_messages.TryDequeue(out message))
104 | {
105 | if (message == null)
106 | {
107 | CleanUpActor();
108 | return true;
109 | }
110 |
111 | RunMessageHandler(message);
112 | }
113 | return false;
114 | }
115 |
116 | private void RunMessageHandler(Message message)
117 | {
118 | try
119 | {
120 | _actor.OnMessage(message.Code, _runtime, _actorId, message);
121 | }
122 | catch (Exception ex)
123 | {
124 | _logger.Error("Exception in actor OnMessage", ex);
125 | IActor newActor = _errorHandler.OnError(_runtime, _actorId, _actor, ex);
126 | if (newActor != null)
127 | {
128 | _logger.Info(String.Format("Replacing actor {0}, type: {1}", _actorId, newActor.GetType().Name));
129 | _actor = newActor;
130 | }
131 | }
132 | }
133 |
134 | private void CleanUpActor()
135 | {
136 | try
137 | {
138 | _actor.Shutdown();
139 | }
140 | catch (Exception ex)
141 | {
142 | _logger.Error("Exception during actor cleanup", ex);
143 | }
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Actors/DefaultErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Actors
4 | {
5 | ///
6 | /// Logs the occured exception to the runtime logger.
7 | ///
8 | public class DefaultErrorHandler : IErrorHandler
9 | {
10 | public IActor OnError(IRuntime runtime, int actorId, IActor actor, Exception ex)
11 | {
12 | string message = String.Format("ActorId={0} of type={1}", actorId, actor.GetType().Name);
13 | runtime.Log.Error(message, ex);
14 | return null;
15 | }
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/Actors/ExternalThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | ///
10 | /// An external thread with an own message loop.
11 | /// For example, a GUI thread.
12 | ///
13 | internal class ExternalThread : IThread
14 | {
15 | #region Own items
16 | private readonly string _name;
17 | #endregion
18 |
19 | #region Injected dependencies
20 | private readonly IRuntime _runtime;
21 | private readonly IActorLogger _logger;
22 | private readonly IErrorHandler _errorHandler;
23 | private readonly Action _dispatcher;
24 | #endregion
25 |
26 | private readonly object _lock = new Object();
27 | #region Guarded by _lock
28 | ///
29 | /// The actors that belong to this thread.
30 | ///
31 | private readonly Dictionary _actors = new Dictionary();
32 | #endregion
33 |
34 | public ExternalThread(string name, IRuntime runtime, IActorLogger logger, IErrorHandler errorHandler, Action dispatchMethod)
35 | {
36 | _name = name;
37 | _runtime = runtime;
38 | _logger = logger;
39 | _errorHandler = errorHandler;
40 | _dispatcher = dispatchMethod;
41 | }
42 | public string Name
43 | {
44 | get { return _name; }
45 | }
46 |
47 | public bool IsNormal
48 | {
49 | get { return false; }
50 | }
51 |
52 | public void PostMessage(int actorId, Message message)
53 | {
54 | IActor actor;
55 | lock (_lock)
56 | {
57 | if (!_actors.TryGetValue(actorId, out actor))
58 | {
59 | return;
60 | }
61 | }
62 |
63 | Action toDo = () =>
64 | {
65 | RunMessageHandler(actorId, actor, message);
66 | };
67 |
68 | _dispatcher(toDo);
69 | }
70 |
71 | private void RunMessageHandler(int actorId, IActor actor, Message message)
72 | {
73 | try
74 | {
75 | actor.OnMessage(message.Code, _runtime, actorId, message);
76 | }
77 | catch (Exception ex)
78 | {
79 | _logger.Error("Exception in actor OnMessage", ex);
80 | }
81 | }
82 |
83 | public void AddActor(int actorId, IActor actor)
84 | {
85 | lock (_lock)
86 | {
87 | _actors.Add(actorId, actor);
88 | }
89 | }
90 |
91 | public void RemoveActor(int actorId)
92 | {
93 | IActor actor;
94 | lock (_lock)
95 | {
96 | if (!_actors.TryGetValue(actorId, out actor))
97 | {
98 | return;
99 | }
100 |
101 | _actors.Remove(actorId);
102 | }
103 |
104 | Action toDo = () =>
105 | {
106 | CleanUpActor(actor);
107 | };
108 |
109 | _dispatcher(toDo);
110 | }
111 |
112 | private void CleanUpActor(IActor actor)
113 | {
114 | try
115 | {
116 | actor.Shutdown();
117 | }
118 | catch (Exception ex)
119 | {
120 | _logger.Error("Exception during actor cleanup", ex);
121 | }
122 | }
123 |
124 | public void Start()
125 | {
126 | }
127 |
128 | public void Stop()
129 | {
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Actors/IActor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | ///
10 | /// An autonomous actor that can accept and send asyncronous messages.
11 | /// The runtime guarantees to call all methods of an IActor in the same thread.
12 | ///
13 | public interface IActor
14 | {
15 | ///
16 | /// Reacts to a message sent to the actor.
17 | /// Must not block.
18 | ///
19 | /// The message type code.
20 | /// The runtime this actor is living inside.
21 | /// The id of this actor.
22 | /// The message. Not null.
23 | object OnMessage(int messageType, IRuntime runtime, int myActorId, Message message);
24 |
25 | ///
26 | /// The runtime will call this method before the actor is being destroyed.
27 | /// Should release owned resources.
28 | /// This method will be called only once.
29 | ///
30 | void Shutdown();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Actors/IActorLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | ///
10 | /// A simple logger contract.
11 | ///
12 | public interface IActorLogger
13 | {
14 | void Error(string message, Exception ex);
15 | void Info(string message);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Actors/IErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Actors
4 | {
5 | ///
6 | /// Responds to unhandled exceptions in the actor's OnMessage method.
7 | ///
8 | public interface IErrorHandler
9 | {
10 | ///
11 | /// Handles an exception that was thrown inside IActor.OnMessage.
12 | /// Re-creates the actor if necessary.
13 | ///
14 | /// The runtime.
15 | /// The id of the failed actor.
16 | /// The actor object.
17 | /// The exception.
18 | /// If null, the runtime does nothing. If not null, the runtime
19 | /// will replace the failed actor with the returned object.
20 | IActor OnError(IRuntime runtime, int actorId, IActor actor, Exception ex);
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/Actors/IRuntime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Actors
9 | {
10 | ///
11 | /// A contract for concurrent multi-actor environment.
12 | ///
13 | public interface IRuntime : IDisposable
14 | {
15 | IActorLogger Log { get; }
16 |
17 | ///
18 | /// Adds an actor to the runtime.
19 | /// One actor must belong only to one runtime.
20 | /// An internal thread will be selected to host the actor.
21 | ///
22 | /// The actor to add. Accepts ownership.
23 | /// Returns the actor id.
24 | int AddActor(IActor actor);
25 |
26 | ///
27 | /// Adds an actor to the specified thread of the runtime.
28 | /// One actor must belong only to one runtime.
29 | ///
30 | /// The thread name to add the actor to.
31 | /// The actor to add. Accepts ownership.
32 | /// Returns the actor id.
33 | int AddActorToThread(string threadName, IActor actor);
34 |
35 | ///
36 | /// Creates a dedicated thread and places the actor into it.
37 | /// The thread will send Codes.Pulse messages to the actor busily.
38 | ///
39 | /// The actor to add. Accepts ownership.
40 | /// Returns the actor id.
41 | int AddDedicatedActor(IActor actor);
42 |
43 | ///
44 | /// Removes an existing actor from the runtime.
45 | /// The actors's CleanUp method will be called.
46 | ///
47 | /// The actor id.
48 | void RemoveActor(int actorId);
49 |
50 | ///
51 | /// Asyncronously sends a message to the actor.
52 | /// Returns immediately.
53 | ///
54 | /// The recepient actor id.
55 | /// Message code.
56 | /// Payload. Can be null.
57 | /// The id of the actor that sends the message (optional).
58 | void SendMessage(int actorId, int messageCode, object payload, int sender);
59 |
60 | ///
61 | /// Asyncronously starts a function call against an actor.
62 | /// Returns immediately.
63 | ///
64 | /// The recepient actor id.
65 | /// Message code.
66 | /// Payload. Can be null.
67 | /// The caller that should receive the result of the call.
68 | /// If no result comes from the target actor after this timeout, the caller actor will get CallResult.Timeout
69 | /// Returns a unique call id.
70 | int SendCall(int actorId, int messageCode, object payload, int sender, TimeSpan timeout);
71 |
72 | ///
73 | /// Sends the result of a received function call.
74 | ///
75 | /// The original sender of the function call.
76 | /// The unique call id.
77 | /// The result code. Must be one of CodeResult.
78 | /// Payload. Can be null.
79 | /// The current actor id.
80 | void SendResult(int caller, int callId, int resultCode, object payload, int sender);
81 |
82 | ///
83 | /// Sends an CallResult.Timeout message to the actor after the specified time interval.
84 | /// Returns immediately.
85 | ///
86 | /// The target actor id.
87 | /// The specified time interval.
88 | /// Returns a unique id of the timer object. The timer object
89 | /// will be destroyed automatically after the timeout.
90 | int SendTimeout(int actorId, TimeSpan duration);
91 |
92 | ///
93 | /// Cancels a previously scheduled timeout message and destroys the timer.
94 | ///
95 | /// The unique timer id.
96 | void CancelTimeout(int timerId);
97 |
98 | ///
99 | /// Starts an asyncronous method as a runtime function call.
100 | ///
101 | /// The method to start.
102 | /// The actor id that will return the result.
103 | /// Returns the unique call id.
104 | int StartVoidCall(Func method, int actorId);
105 |
106 | ///
107 | /// Starts an asyncronous void-returning method as a runtime function call.
108 | ///
109 | /// The method to start.
110 | /// The actor id that will return the result.
111 | /// Returns the unique call id.
112 | int StartCall(Func> method, int actorId);
113 |
114 | ///
115 | /// Starts an async stream write operation as a runtime function call.
116 | ///
117 | /// Stream to write to.
118 | /// The source buffer.
119 | /// The actor that will receive the result.
120 | /// Returns the unique call id.
121 | int StartWrite (Stream stream, IoBuffer buffer, int actorId);
122 |
123 | ///
124 | /// Starts an async stream read operation as a runtime function call.
125 | ///
126 | /// Stream to write to.
127 | /// The destination buffer.
128 | /// The actor that will receive the result.
129 | /// Returns the unique call id.
130 | int StartRead (Stream stream, IoBuffer buffer, int actorId);
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Actors/IThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | internal interface IThread
10 | {
11 | string Name { get; }
12 | bool IsNormal { get; }
13 | void PostMessage(int actorId, Message message);
14 | void AddActor(int actorId, IActor actor);
15 | void RemoveActor(int actorId);
16 | void Start();
17 | void Stop();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Actors/IoBuffer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Actors
4 | {
5 | ///
6 | /// Data for IO operations.
7 | ///
8 | public class IoBuffer
9 | {
10 | ///
11 | /// Contains the data to be written or the data that has been read.
12 | ///
13 | public byte[] Data = new byte[4096];
14 |
15 | ///
16 | /// The actual number of meaningful bytes in the Data field.
17 | ///
18 | public int Count = 0;
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Actors/Message.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | ///
10 | /// Objects of this type can be sent from one IActor to another.
11 | ///
12 | public class Message
13 | {
14 | ///
15 | /// Gets the type of the message, understood be the target actor.
16 | ///
17 | public readonly int Code;
18 |
19 | ///
20 | /// Optional call id. Must not be 0 for function calls.
21 | ///
22 | public readonly int Id;
23 |
24 | ///
25 | /// The message payload. Can be null.
26 | /// Should be immutable (ideally).
27 | ///
28 | public readonly object Payload;
29 |
30 | ///
31 | /// The sender of the message.
32 | ///
33 | public readonly int Sender;
34 |
35 | public Message(int code, int id, object payload, int sender)
36 | {
37 | Code = code;
38 | Id = id;
39 | Payload = payload;
40 | Sender = sender;
41 | }
42 |
43 | public override string ToString()
44 | {
45 | return String.Format("Message Code={0} Id={1} Payload={2} Sender={3}", Code, Id, Payload, Sender);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Actors/Parcel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Actors
8 | {
9 | internal struct Parcel
10 | {
11 | public int ActorId;
12 | public IActor Actor;
13 | public Message Message;
14 | public bool Kill;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Actors/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Actors")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Actors")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("23566207-6248-46d3-a3e8-7d1541ca2bef")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Actors/Runtime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Actors
10 | {
11 | ///
12 | /// Concurrent multi-actor environment.
13 | /// It is a container for self-sufficient actors that exchange asyncronous messages.
14 | /// Can have several threads inside. Each thread can run many actors.
15 | /// Benefits:
16 | /// 1. Incapsulates multithreading API. Protects against many low-level multithreading bugs.
17 | /// The rest of the application should not deal with threads.
18 | /// 2. Manages timers and timeouts.
19 | /// See IRuntime for the documentation on the public methods.
20 | /// Runtime guarantees that:
21 | /// 1. All methods of an actor (including CleanUp) are invoked in the same thread.
22 | /// 2. CleanUp is invoked when an actor is removed.
23 | /// 3. CleanUp is called only once.
24 | ///
25 | public class Runtime : IRuntime
26 | {
27 | private readonly IActorLogger _logger;
28 |
29 | private readonly RuntimeData _data;
30 |
31 |
32 | public Runtime(IActorLogger logger, IErrorHandler errorHandler)
33 | {
34 | _logger = logger;
35 | _data = new RuntimeData(logger, errorHandler);
36 | }
37 |
38 | public IActorLogger Log
39 | {
40 | get { return _logger; }
41 | }
42 |
43 | public void Dispose()
44 | {
45 | List threads = _data.Clear();
46 | foreach (IThread thread in threads)
47 | {
48 | thread.Stop();
49 | }
50 | }
51 |
52 | ///
53 | /// Creates a System.Threading.Thread object and starts a message loop for it.
54 | ///
55 | public void CreateThread(string name)
56 | {
57 | IThread thread = _data.CreateThread(this, name, null);
58 | thread.Start();
59 | }
60 |
61 | ///
62 | /// Registers an external thread with an own message loop.
63 | ///
64 | /// Thread name.
65 | /// Posts work items to the external thread's message loop.
66 | public void RegisterExternalThread(string name, Action dispatcherMethod)
67 | {
68 | _data.CreateThread(this, name, dispatcherMethod);
69 | }
70 |
71 | public int AddActor(IActor actor)
72 | {
73 | IThread thread = _data.GetRandomThread();
74 | int actorId = _data.AllocateActor(thread);
75 | thread.AddActor(actorId, actor);
76 | return actorId;
77 | }
78 |
79 | public int AddActorToThread(string threadName, IActor actor)
80 | {
81 | IThread thread = _data.FindThreadByName(threadName);
82 | int actorId = _data.AllocateActor(thread);
83 | thread.AddActor(actorId, actor);
84 | return actorId;
85 | }
86 |
87 | public int AddDedicatedActor(IActor actor)
88 | {
89 | return _data.AllocateDedicatedActor(this, actor);
90 | }
91 |
92 |
93 | public void RemoveActor(int actorId)
94 | {
95 | IThread thread = _data.DeallocateActor(actorId);
96 | if (thread != null)
97 | {
98 | thread.RemoveActor(actorId);
99 | }
100 | }
101 |
102 | public void SendMessage(int recepient, int messageCode, object payload, int sender)
103 | {
104 | SendMessageCore(recepient, 0, messageCode, payload, sender);
105 | }
106 |
107 | public void SendResult(int caller, int callId, int resultCode, object payload, int sender)
108 | {
109 | CallResult.Check(resultCode);
110 |
111 | bool callFound = _data.UnregisterCall(caller, callId);
112 |
113 | if (callFound)
114 | {
115 | SendMessageCore(caller, callId, resultCode, payload, sender);
116 | }
117 | }
118 |
119 |
120 | private void SendMessageCore(int recepient, int callId, int messageCode, object payload, int sender)
121 | {
122 | if (recepient < 1)
123 | {
124 | throw new ArgumentException("Invalid recepient: " + recepient.ToString());
125 | }
126 |
127 | Message message = new Message(messageCode, callId, payload, sender);
128 |
129 | IThread thread = _data.FindThreadByActor(recepient);
130 |
131 | if (thread != null)
132 | {
133 | thread.PostMessage(recepient, message);
134 | }
135 | }
136 |
137 | private void OnTimer(int timerCallId, int actorId)
138 | {
139 | SendResult(actorId, timerCallId, CallResult.Timeout, null, 0);
140 | _data.RemoveTimer(timerCallId);
141 | }
142 |
143 | public int SendTimeout(int actorId, TimeSpan duration)
144 | {
145 | return SendTimeoutCore(actorId, 0, duration);
146 | }
147 |
148 | private int SendTimeoutCore(int actorId, int callId, TimeSpan duration)
149 | {
150 | if (actorId < 1)
151 | {
152 | throw new ArgumentException("Invalid actorId: " + actorId.ToString());
153 | }
154 |
155 | if (callId == 0)
156 | {
157 | callId = _data.RegisterCall(actorId);
158 | }
159 |
160 | TimerCallback callback = (obj) => OnTimer(callId, actorId);
161 |
162 | Timer timer = new Timer(callback, null, (long)duration.TotalMilliseconds, -1);
163 |
164 | _data.AddTimer(timer, callId, actorId);
165 |
166 | return callId;
167 | }
168 |
169 | public void CancelTimeout(int timerCallId)
170 | {
171 | _data.RemoveTimer(timerCallId);
172 | }
173 |
174 | public int StartVoidCall(Func method, int callerId)
175 | {
176 | int callId = _data.RegisterCall(callerId);
177 | Task mainTask = method();
178 | mainTask.ContinueWith((t) =>
179 | {
180 | if (t.IsFaulted)
181 | {
182 | _logger.Error(String.Format("Error. Caller id: {0}, call id: {1}.", callerId, callId), t.Exception);
183 | SendResult(callerId, callId, CallResult.Error, t.Exception, 0);
184 | }
185 | else if (t.IsCanceled)
186 | {
187 | _logger.Info(String.Format("Cancelled. Caller id: {0}, call id: {1}.", callerId, callId));
188 | SendResult(callerId, callId, CallResult.Cancelled, null, 0);
189 | }
190 | else
191 | {
192 | SendResult(callerId, callId, CallResult.Completed, null, 0);
193 | }
194 | }, TaskContinuationOptions.AttachedToParent);
195 | return callId;
196 | }
197 |
198 | public int SendCall(int recepient, int messageCode, object payload, int sender, TimeSpan timeout)
199 | {
200 | int callId = _data.RegisterCall(sender);
201 | SendMessageCore(recepient, callId, messageCode, payload, sender);
202 | SendTimeoutCore(sender, callId, timeout);
203 | return callId;
204 | }
205 |
206 | public int StartCall(Func> method, int callerId)
207 | {
208 | int callId = _data.RegisterCall(callerId);
209 | Task mainTask = method();
210 | mainTask.ContinueWith((t) =>
211 | {
212 | if (t.IsFaulted)
213 | {
214 | _logger.Error(String.Format("Error. Caller id: {0}, call id: {1}.", callerId, callId), t.Exception);
215 | SendResult(callerId, callId, CallResult.Error, t.Exception, 0);
216 | }
217 | else if (t.IsCanceled)
218 | {
219 | _logger.Info(String.Format("Cancelled. Caller id: {0}, call id: {1}.", callerId, callId));
220 | SendResult(callerId, callId, CallResult.Cancelled, null, 0);
221 | }
222 | else
223 | {
224 | object payload = t.Result;
225 | SendResult(callerId, callId, CallResult.Completed, payload, 0);
226 | }
227 | }, TaskContinuationOptions.AttachedToParent);
228 | return callId;
229 | }
230 |
231 | public int StartWrite(Stream stream, IoBuffer buffer, int actorId)
232 | {
233 | if (buffer.Count == 0)
234 | {
235 | int callId = _data.NextCallId();
236 | SendMessageCore(actorId, callId, CallResult.Completed, null, 0);
237 | return callId;
238 | }
239 | else
240 | {
241 | Func writeMethod = () => stream.WriteAsync(buffer.Data, 0, buffer.Count);
242 | return StartVoidCall(writeMethod, actorId);
243 | }
244 | }
245 |
246 | public int StartRead(Stream stream, IoBuffer buffer, int actorId)
247 | {
248 | Func> readMethod = () => stream.ReadAsync(buffer.Data, 0, buffer.Data.Length);
249 | return StartCall(readMethod, actorId);
250 | }
251 |
252 | internal void RemoveThread(int actorId)
253 | {
254 | _data.RemoveThread(actorId);
255 | }
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/Actors/RuntimeData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Actors
10 | {
11 | ///
12 | /// Thread-safe. The data structure for Runtime.
13 | /// All methods MUST be wrapped in a "lock" statement.
14 | /// Mitigates 2 kinds of risk:
15 | /// 1. Race conditions when different threads access the data structure.
16 | /// 2. A deadlock that involves the runtime lock and a thread lock.
17 | /// The only allowed sequence of locking is: 1. thread lock. 2. runtime lock.
18 | /// Therefore calling any IThread methods is not allowed here.
19 | ///
20 | internal class RuntimeData
21 | {
22 |
23 | private class TimerInfo
24 | {
25 | public Timer Timer;
26 | public int CallId;
27 | public int Actor;
28 | }
29 |
30 | ///
31 | /// The runtime lock.
32 | ///
33 | private readonly object _lock = new Object();
34 |
35 | private int _callId = 0;
36 |
37 | /// select * from
38 | /// Actor id to thread mapping.
39 | ///
40 | private readonly Dictionary _actorsToThreads = new Dictionary();
41 |
42 | ///
43 | /// The list of call ids for each actor that have outstanding calls.
44 | /// The point is to protect an actor from getting the result of a call initiated by someone else.
45 | ///
46 | private readonly Dictionary> _callsInProgress = new Dictionary>();
47 |
48 | ///
49 | /// Thread name to thread mapping.
50 | ///
51 | private readonly Dictionary _threadsByName = new Dictionary();
52 |
53 | ///
54 | /// The next actor id.
55 | ///
56 | private int _nextActorId = 1;
57 |
58 | ///
59 | /// The list of currently active timers.
60 | ///
61 | private readonly Dictionary _timersByCallId = new Dictionary();
62 |
63 | private readonly Random _random = new Random();
64 |
65 |
66 | private readonly IActorLogger _logger;
67 | private readonly IErrorHandler _errorHandler;
68 |
69 | public RuntimeData(IActorLogger logger, IErrorHandler errorHandler)
70 | {
71 | _logger = logger;
72 | _errorHandler = errorHandler;
73 | }
74 |
75 | public List Clear()
76 | {
77 | lock (_lock)
78 | {
79 | List threads = _threadsByName.Values.ToList();
80 | _actorsToThreads.Clear();
81 | _callsInProgress.Clear();
82 | _threadsByName.Clear();
83 | _timersByCallId.Clear();
84 | _nextActorId = 1;
85 | return threads;
86 | }
87 | }
88 |
89 | public IThread GetRandomThread()
90 | {
91 | lock (_lock)
92 | {
93 | List threads = _threadsByName
94 | .Values
95 | .Where(t => t.IsNormal)
96 | .ToList();
97 |
98 | if (threads.Count == 0)
99 | {
100 | throw new InvalidOperationException("No internal threads exist. Call CreateThread to create some.");
101 | }
102 |
103 | int index = _random.Next(0, threads.Count);
104 | return threads[index];
105 | }
106 | }
107 |
108 | public IThread CreateThread(IRuntime runtime, string name, Action dispatchMethod)
109 | {
110 | lock (_lock)
111 | {
112 | if (String.IsNullOrWhiteSpace(name))
113 | {
114 | throw new ArgumentNullException(name);
115 | }
116 | if (_threadsByName.ContainsKey(name))
117 | {
118 | throw new ArgumentException("Thread already exists: " + name);
119 | }
120 | IThread thread;
121 | if (dispatchMethod == null)
122 | {
123 | thread = new RuntimeThread(name, runtime, _logger, _errorHandler);
124 | }
125 | else
126 | {
127 | thread = new ExternalThread(name, runtime, _logger, _errorHandler, dispatchMethod);
128 | }
129 |
130 | _threadsByName.Add(name, thread);
131 | return thread;
132 | }
133 | }
134 |
135 | public int AllocateActor(IThread thread)
136 | {
137 | lock (_lock)
138 | {
139 | int actorId = _nextActorId;
140 | _nextActorId++;
141 |
142 | _actorsToThreads.Add(actorId, thread);
143 | return actorId;
144 | }
145 | }
146 |
147 | internal int AllocateDedicatedActor(IRuntime runtime, IActor actor)
148 | {
149 | lock (_lock)
150 | {
151 | int actorId = _nextActorId;
152 | _nextActorId++;
153 | string name = String.Format("Dedicated thread for actor {0}", actorId);
154 | IThread thread = new DedicatedThread(name, runtime, _logger, _errorHandler, actorId, actor);
155 |
156 | _actorsToThreads.Add(actorId, thread);
157 | thread.Start();
158 | return actorId;
159 | }
160 | }
161 |
162 | public IThread DeallocateActor(int actorId)
163 | {
164 | lock (_lock)
165 | {
166 | if (actorId < 0)
167 | {
168 | throw new ArgumentException("Invalid actorId: " + actorId.ToString());
169 | }
170 |
171 | IThread thread;
172 | _actorsToThreads.TryGetValue(actorId, out thread);
173 |
174 | _actorsToThreads.Remove(actorId);
175 | _callsInProgress.Remove(actorId);
176 |
177 | return thread;
178 | }
179 | }
180 |
181 | public bool UnregisterCall(int actor, int call)
182 | {
183 | lock (_lock)
184 | {
185 | List calls;
186 | if (_callsInProgress.TryGetValue(actor, out calls))
187 | {
188 | if (calls.Contains(call))
189 | {
190 | calls.Remove(call);
191 | return true;
192 | }
193 | }
194 | return false;
195 | }
196 | }
197 |
198 | public int RegisterCall(int actor)
199 | {
200 | lock (_lock)
201 | {
202 | _callId++;
203 | int call = _callId;
204 | List calls;
205 | if (!_callsInProgress.TryGetValue(actor, out calls))
206 | {
207 | calls = new List();
208 | _callsInProgress.Add(actor, calls);
209 | }
210 |
211 | calls.Add(call);
212 | return call;
213 | }
214 | }
215 |
216 | public IThread FindThreadByActor(int actorId)
217 | {
218 | lock (_lock)
219 | {
220 | IThread thread;
221 | if (_actorsToThreads.TryGetValue(actorId, out thread))
222 | {
223 | return thread;
224 | }
225 | return null;
226 | }
227 | }
228 |
229 | public IThread FindThreadByName(string threadName)
230 | {
231 | lock (_lock)
232 | {
233 | IThread thread;
234 | if (!_threadsByName.TryGetValue(threadName, out thread))
235 | {
236 | throw new ArgumentException("Thread does not exists: " + threadName);
237 | }
238 | return thread;
239 | }
240 | }
241 |
242 |
243 | public void AddTimer(Timer timer, int callId, int actorId)
244 | {
245 | lock (_lock)
246 | {
247 | TimerInfo record = new TimerInfo { Timer = timer, CallId = callId, Actor = actorId };
248 | _timersByCallId[callId] = record;
249 | }
250 | }
251 |
252 |
253 | public void RemoveTimer(int timerCallId)
254 | {
255 | lock (_lock)
256 | {
257 | TimerInfo timer;
258 | if (_timersByCallId.TryGetValue(timerCallId, out timer))
259 | {
260 | _timersByCallId.Remove(timer.CallId);
261 | UnregisterCall(timer.Actor, timer.CallId);
262 | timer.Timer.Dispose();
263 | }
264 | }
265 | }
266 |
267 |
268 | public int NextCallId()
269 | {
270 | lock (_lock)
271 | {
272 | _callId++;
273 | return _callId;
274 | }
275 | }
276 |
277 | public void RemoveThread(int actorId)
278 | {
279 | lock (_lock)
280 | {
281 | IThread thread;
282 | if (_actorsToThreads.TryGetValue(actorId, out thread))
283 | {
284 | _actorsToThreads.Remove(actorId);
285 | _threadsByName.Remove(thread.Name);
286 | }
287 | }
288 | }
289 | }
290 | }
291 |
292 |
--------------------------------------------------------------------------------
/Actors/RuntimeThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace Actors
9 | {
10 | ///
11 | /// A thread inside a runtime.
12 | /// Owns a System.Threading.Thread object.
13 | /// Thread safe. Public methods can be called from any thread.
14 | ///
15 | internal class RuntimeThread : IThread
16 | {
17 | #region Own items
18 | ///
19 | /// The system thread that is owned by this. Can be null.
20 | ///
21 | private readonly Thread _thread;
22 | private readonly string _name;
23 | #endregion
24 |
25 | #region Injected dependencies
26 | private readonly IRuntime _runtime;
27 | private readonly IActorLogger _logger;
28 | private readonly IErrorHandler _errorHandler;
29 | #endregion
30 |
31 | private readonly object _queueLock = new Object();
32 | #region Guarded by _queueLock
33 | ///
34 | /// The actors that belong to this thread.
35 | ///
36 | private readonly Dictionary _actors = new Dictionary();
37 | ///
38 | /// The incoming message queue.
39 | ///
40 | private List _queue = new List();
41 | ///
42 | /// Exit flag.
43 | ///
44 | private bool _mustExit = false;
45 | #endregion
46 |
47 | #region Internal thread-local
48 | ///
49 | /// The message queue for internal processing.
50 | ///
51 | private List _queue2 = new List();
52 | #endregion
53 |
54 |
55 |
56 | public RuntimeThread(string name, IRuntime runtime, IActorLogger logger, IErrorHandler errorHandler)
57 | {
58 | _name = name;
59 | _runtime = runtime;
60 | _logger = logger;
61 | _errorHandler = errorHandler;
62 | _thread = new Thread(ThreadMessageLoop);
63 | _thread.Name = name;
64 | }
65 |
66 | public string Name
67 | {
68 | get { return _name; }
69 | }
70 |
71 | public bool IsNormal
72 | {
73 | get { return true; }
74 | }
75 |
76 | public void Start()
77 | {
78 | _thread.Start();
79 | }
80 |
81 | public void AddActor(int actorId, IActor actor)
82 | {
83 | if (actor == null)
84 | {
85 | throw new ArgumentNullException("actor");
86 | }
87 |
88 | lock (_queueLock)
89 | {
90 | if (_actors.ContainsKey(actorId))
91 | {
92 | throw new ArgumentException("Actor with this id already exists " + actorId);
93 | }
94 | _actors.Add(actorId, actor);
95 | }
96 | }
97 |
98 | public void RemoveActor(int actorId)
99 | {
100 | PostToQueue(actorId, true, null);
101 | }
102 |
103 | public void PostMessage(int actorId, Message message)
104 | {
105 | PostToQueue(actorId, false, message);
106 | }
107 |
108 | public void Stop()
109 | {
110 | lock (_queueLock)
111 | {
112 | _mustExit = true;
113 | // Notify the internal thread there is an incoming event.
114 | Monitor.Pulse(_queueLock);
115 | }
116 | }
117 |
118 | private void PostToQueue(int actorId, bool kill, Message message)
119 | {
120 | lock (_queueLock)
121 | {
122 | IActor actor;
123 | if (!_actors.TryGetValue(actorId, out actor)) return;
124 | Parcel parcel = new Parcel { ActorId = actorId, Actor = actor, Message = message, Kill = kill };
125 | _queue.Add(parcel);
126 |
127 | // Notify the internal thread there is an incoming event.
128 | Monitor.Pulse(_queueLock);
129 | }
130 | }
131 |
132 | ///
133 | /// Run by the internal thread.
134 | ///
135 | private void ThreadMessageLoop()
136 | {
137 | while (true)
138 | {
139 | // Wait for events.
140 | lock (_queueLock)
141 | {
142 | // This loop is necessary.
143 | while (true)
144 | {
145 | if (_mustExit)
146 | {
147 | CleanUp();
148 | return;
149 | }
150 | if (_queue.Count == 0)
151 | {
152 | // Wait for incoming events.
153 | Monitor.Wait(_queueLock);
154 | }
155 | else
156 | {
157 | // Got one or more events!
158 | break;
159 | }
160 | }
161 | // Could just swap here, actually.
162 | _queue = Interlocked.Exchange(ref _queue2, _queue);
163 | }
164 |
165 | ProcessMessages();
166 | }
167 | }
168 |
169 | private void ProcessMessages()
170 | {
171 | // Process messages.
172 | foreach (Parcel parcel in _queue2)
173 | {
174 | if (parcel.Kill)
175 | {
176 | CleanUpActor(parcel);
177 | RemoveActorCore(parcel.ActorId);
178 | }
179 | else
180 | {
181 | RunMessageHandler(parcel);
182 | }
183 | }
184 | _queue2.Clear();
185 | }
186 |
187 | private void RunMessageHandler(Parcel parcel)
188 | {
189 | try
190 | {
191 | parcel.Actor.OnMessage(parcel.Message.Code, _runtime, parcel.ActorId, parcel.Message);
192 | }
193 | catch (Exception ex)
194 | {
195 | _logger.Error("Exception in actor OnMessage", ex);
196 | IActor newActor = _errorHandler.OnError(_runtime, parcel.ActorId, parcel.Actor, ex);
197 | if (newActor != null)
198 | {
199 | _logger.Info(String.Format("Replacing actor {0}, type: {1}", parcel.ActorId, newActor.GetType().Name));
200 | ReplaceActor(parcel.ActorId, newActor);
201 | }
202 | }
203 | }
204 |
205 | private void CleanUpActor(Parcel parcel)
206 | {
207 | try
208 | {
209 | parcel.Actor.Shutdown();
210 | }
211 | catch (Exception ex)
212 | {
213 | _logger.Error("Exception during actor cleanup", ex);
214 | }
215 | }
216 |
217 | private void RemoveActorCore(int actorId)
218 | {
219 | lock (_queueLock)
220 | {
221 | _actors.Remove(actorId);
222 | }
223 | }
224 |
225 | private void ReplaceActor(int actorId, IActor actor)
226 | {
227 | lock (_queueLock)
228 | {
229 | _actors[actorId] = actor;
230 | }
231 | }
232 |
233 | private void CleanUp()
234 | {
235 | foreach (KeyValuePair record in _actors)
236 | {
237 | record.Value.Shutdown();
238 | }
239 | }
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/ActorsTest/ActorsTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {BBD6F2EC-4E7B-4121-98CD-105FA1EC7DFC}
7 | Library
8 | ActorsTest
9 | ActorsTest
10 | v4.5
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug
17 | DEBUG;
18 | prompt
19 | 4
20 | false
21 |
22 |
23 | full
24 | true
25 | bin\Release
26 | prompt
27 | 4
28 | false
29 |
30 |
31 |
32 | ..\packages\FakeItEasy.1.25.1\lib\net40\FakeItEasy.dll
33 |
34 |
35 |
36 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | {bd0aeeda-9523-4cdf-b4c9-ad7d723b1f3b}
52 | Actors
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ActorsTest/RuntimeTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 | using System.Threading;
4 | using System.Collections.Generic;
5 | using System.Collections.Concurrent;
6 | using System.Threading.Tasks;
7 | using Actors;
8 |
9 | namespace ActorsTest
10 | {
11 | [TestFixture ()]
12 | public class RuntimeTest
13 | {
14 |
15 | private class LogActor : IActor
16 | {
17 | public ConcurrentBag History;
18 | public AutoResetEvent FinishTest;
19 |
20 | #region IActor implementation
21 | public object OnMessage (int messageId, IRuntime runtime, int myActorId, Message message)
22 | {
23 | string thread = GetThread ();
24 | string entry = String.Format("OnMessage {0} {1} {2} {3}", thread, myActorId, message.Code, message.Sender);
25 | History.Add(entry);
26 | SetEvent(FinishTest);
27 | return null;
28 | }
29 | public void Shutdown()
30 | {
31 | string thread = GetThread ();
32 | string entry = String.Format("CleanUp {0}", thread);
33 | History.Add(entry);
34 | SetEvent(FinishTest);
35 | }
36 | #endregion
37 |
38 | private string GetThread()
39 | {
40 | return Thread.CurrentThread.Name;
41 | }
42 | }
43 |
44 | private static void SetEvent(AutoResetEvent evt)
45 | {
46 | try
47 | {
48 | evt.Set();
49 | }
50 | catch (ObjectDisposedException)
51 | {
52 |
53 | }
54 | }
55 |
56 | private class Resender : IActor
57 | {
58 | public int Target;
59 | #region IActor implementation
60 | public object OnMessage (int messageId, IRuntime runtime, int myActorId, Message message)
61 | {
62 | runtime.SendMessage (Target, message.Code, message.Payload, myActorId);
63 | return null;
64 | }
65 |
66 | public void Shutdown ()
67 | {
68 | }
69 | #endregion
70 |
71 | private string GetThread()
72 | {
73 | return Thread.CurrentThread.Name;
74 | }
75 | }
76 |
77 | private AutoResetEvent _finishTest;
78 | private Runtime _runtime;
79 | private ConcurrentBag _history;
80 | private IActorLogger _logger = new ConsoleLogger ();
81 | private IErrorHandler _handler = new DefaultErrorHandler ();
82 |
83 | [SetUp]
84 | public void SetUp() {
85 | _finishTest = new AutoResetEvent (false);
86 | _history = new ConcurrentBag ();
87 | _runtime = new Runtime (_logger, _handler);
88 | _runtime.CreateThread ("T1");
89 | _runtime.CreateThread ("T2");
90 | }
91 |
92 | [TearDown]
93 | public void TearDown() {
94 | if (_runtime != null) {
95 | _runtime.Dispose ();
96 | }
97 | _finishTest.Dispose ();
98 | }
99 |
100 | [Test ()]
101 | public void OnMessage_CleanUp_OnSameThread ()
102 | {
103 | LogActor actor = new LogActor { History = _history, FinishTest = _finishTest };
104 | int actorId = _runtime.AddActorToThread ("T1", actor);
105 | _runtime.SendMessage (actorId, 20, null, 4000);
106 | _finishTest.WaitOne ();
107 | _runtime.RemoveActor (actorId);
108 | _finishTest.WaitOne ();
109 |
110 | CollectionAssert.AreEquivalent (
111 | new string[] { "OnMessage T1 1 20 4000", "CleanUp T1" },
112 | _history
113 | );
114 | }
115 |
116 | [Test]
117 | public void SendMessage_SameThread()
118 | {
119 | LogActor logActor = new LogActor { History = _history, FinishTest = _finishTest };
120 | int actorId = _runtime.AddActorToThread ("T1", logActor);
121 | Resender resender = new Resender { Target = actorId };
122 | int resenderId = _runtime.AddActorToThread("T1", resender);
123 | _runtime.SendMessage(resenderId, 20, null, 4000);
124 | _finishTest.WaitOne();
125 |
126 | CollectionAssert.AreEquivalent (
127 | new string[] { "OnMessage T1 1 20 2" },
128 | _history
129 | );
130 | }
131 |
132 | [Test]
133 | public void SendMessage_DifferentThread()
134 | {
135 | LogActor logActor = new LogActor { History = _history, FinishTest = _finishTest };
136 | int actorId = _runtime.AddActorToThread ("T1", logActor);
137 | Resender resender = new Resender { Target = actorId };
138 | int resenderId = _runtime.AddActorToThread("T2", resender);
139 |
140 | _runtime.SendMessage(resenderId, 20, null, 4000);
141 | _finishTest.WaitOne();
142 |
143 | CollectionAssert.AreEquivalent (
144 | new string[] { "OnMessage T1 1 20 2" },
145 | _history
146 | );
147 |
148 | }
149 |
150 | [Test ()]
151 | public void Shutdown_calls_CleanUp ()
152 | {
153 | LogActor actor = new LogActor { History = _history, FinishTest = _finishTest };
154 | _runtime.AddActorToThread ("T2", actor);
155 | _runtime.Dispose ();
156 | _runtime = null;
157 | _finishTest.WaitOne ();
158 |
159 | CollectionAssert.AreEquivalent (
160 | new string[] { "CleanUp T2" },
161 | _history
162 | );
163 | }
164 |
165 | private const int Start = 88888;
166 |
167 | class Caller : IActor
168 | {
169 | public ConcurrentBag History;
170 | public AutoResetEvent Finished;
171 | public int Target;
172 | #region IActor implementation
173 | public object OnMessage (int messageId, IRuntime runtime, int myActorId, Message message)
174 | {
175 | switch (message.Code) {
176 | case CallResult.Completed:
177 | History.Add (String.Format ("Completed {0} {1}", myActorId, message.Payload));
178 | Finished.Set ();
179 | break;
180 | case CallResult.Error:
181 | History.Add (String.Format ("Failure {0}", myActorId));
182 | Finished.Set ();
183 | break;
184 | case CallResult.Cancelled:
185 | History.Add (String.Format ("Cancelled {0}", myActorId));
186 | Finished.Set ();
187 | break;
188 | case CallResult.Timeout:
189 | History.Add (String.Format ("Timeout {0}", myActorId));
190 | Finished.Set ();
191 | break;
192 | case Start:
193 | runtime.SendCall (Target, 666, null, myActorId, TimeSpan.FromSeconds (0.5));
194 | break;
195 | default:
196 | break;
197 | }
198 | return null;
199 | }
200 | public void Shutdown()
201 | {
202 |
203 | }
204 | #endregion
205 | }
206 |
207 | class Callee : IActor
208 | {
209 | public int Result;
210 | public object OnMessage (int messageType, IRuntime runtime, int myActorId, Message message)
211 | {
212 | switch (Result) {
213 | case CallResult.Completed:
214 | runtime.SendResult (message.Sender, message.Id, CallResult.Completed, "hi", myActorId);
215 | break;
216 | case CallResult.Error:
217 | runtime.SendResult (message.Sender, message.Id, CallResult.Error, null, myActorId);
218 | break;
219 | case CallResult.Cancelled:
220 | runtime.SendResult (message.Sender, message.Id, CallResult.Cancelled, null, myActorId);
221 | break;
222 | case CallResult.Timeout:
223 | break;
224 | default:
225 | break;
226 | }
227 | return null;
228 | }
229 | public void Shutdown ()
230 | {
231 |
232 | }
233 | }
234 |
235 | [Test]
236 | public void SendCall_SendResult_Completed()
237 | {
238 | Callee callee = new Callee { Result = CallResult.Completed };
239 | int target = _runtime.AddActor (callee);
240 | Caller caller = new Caller { History = _history, Finished = _finishTest, Target = target };
241 | int callerId = _runtime.AddActor (caller);
242 | _runtime.SendMessage (callerId, Start, null, 0);
243 | _finishTest.WaitOne ();
244 |
245 | CollectionAssert.AreEquivalent (
246 | new string[] { "Completed 2 hi" },
247 | _history
248 | );
249 | }
250 |
251 | [Test]
252 | public void SendCall_SendResult_Cancelled()
253 | {
254 | Callee callee = new Callee { Result = CallResult.Cancelled };
255 | int target = _runtime.AddActor (callee);
256 | Caller caller = new Caller { History = _history, Finished = _finishTest, Target = target };
257 | int callerId = _runtime.AddActor (caller);
258 | _runtime.SendMessage (callerId, Start, null, 0);
259 | _finishTest.WaitOne ();
260 |
261 | CollectionAssert.AreEquivalent (
262 | new string[] { "Cancelled 2" },
263 | _history
264 | );
265 | }
266 |
267 | [Test]
268 | public void SendCall_SendResult_Failure()
269 | {
270 | Callee callee = new Callee { Result = CallResult.Error };
271 | int target = _runtime.AddActor (callee);
272 | Caller caller = new Caller { History = _history, Finished = _finishTest, Target = target };
273 | int callerId = _runtime.AddActor (caller);
274 | _runtime.SendMessage (callerId, Start, null, 0);
275 | _finishTest.WaitOne ();
276 |
277 | CollectionAssert.AreEquivalent (
278 | new string[] { "Failure 2" },
279 | _history
280 | );
281 | }
282 |
283 | [Test]
284 | public void SendCall_SendResult_Timeout()
285 | {
286 | Callee callee = new Callee { Result = CallResult.Timeout };
287 | int target = _runtime.AddActor (callee);
288 | Caller caller = new Caller { History = _history, Finished = _finishTest, Target = target };
289 | int callerId = _runtime.AddActor (caller);
290 | _runtime.SendMessage (callerId, Start, null, 0);
291 | _finishTest.WaitOne ();
292 |
293 | CollectionAssert.AreEquivalent (
294 | new string[] { "Timeout 2" },
295 | _history
296 | );
297 | }
298 |
299 | [Test]
300 | public void SendTimeout()
301 | {
302 | LogActor actor = new LogActor { History = _history, FinishTest = _finishTest };
303 | int actorId = _runtime.AddActorToThread("T1", actor);
304 | _runtime.SendTimeout(actorId, TimeSpan.FromSeconds(0.2));
305 | _finishTest.WaitOne();
306 |
307 | CollectionAssert.AreEquivalent(
308 | new string[] { "OnMessage T1 1 4 0" },
309 | _history
310 | );
311 | }
312 |
313 | [Test]
314 | public void CancelTimeout()
315 | {
316 | LogActor actor = new LogActor { History = _history, FinishTest = _finishTest };
317 | int actorId = _runtime.AddActorToThread("T1", actor);
318 | int timeout = _runtime.SendTimeout(actorId, TimeSpan.FromSeconds(0.5));
319 | Thread.Sleep(100);
320 | _runtime.CancelTimeout(timeout);
321 | Thread.Sleep(1000);
322 |
323 | CollectionAssert.AreEquivalent(
324 | new string[] {},
325 | _history
326 | );
327 | }
328 |
329 | [Test]
330 | public void StartVoidCall_Completed()
331 | {
332 | Caller actor = new Caller { History = _history, Finished = _finishTest };
333 | int actorId = _runtime.AddActorToThread("T1", actor);
334 | _runtime.StartVoidCall(async () =>
335 | {
336 | await Task.Delay(50);
337 | }, actorId);
338 | _finishTest.WaitOne();
339 |
340 | CollectionAssert.AreEquivalent(
341 | new string[] { "Completed 1 "},
342 | _history
343 | );
344 | }
345 |
346 | [Test]
347 | public void StartVoidCall_Failed()
348 | {
349 | Caller actor = new Caller { History = _history, Finished = _finishTest };
350 | int actorId = _runtime.AddActorToThread("T1", actor);
351 | _runtime.StartVoidCall(async () =>
352 | {
353 | await Task.Delay(50);
354 | throw new Exception("oi");
355 | }, actorId);
356 | _finishTest.WaitOne();
357 |
358 | CollectionAssert.AreEquivalent(
359 | new string[] { "Failure 1" },
360 | _history
361 | );
362 | }
363 |
364 | [Test]
365 | public void StartVoidCall_Cancelled()
366 | {
367 | Caller actor = new Caller { History = _history, Finished = _finishTest };
368 | int actorId = _runtime.AddActorToThread("T1", actor);
369 | _runtime.StartVoidCall(async () =>
370 | {
371 | await Task.Delay(50);
372 | throw new TaskCanceledException("oi");
373 | }, actorId);
374 | _finishTest.WaitOne();
375 |
376 | CollectionAssert.AreEquivalent(
377 | new string[] { "Cancelled 1" },
378 | _history
379 | );
380 | }
381 |
382 | [Test]
383 | public void StartCall_Completed()
384 | {
385 | Caller actor = new Caller { History = _history, Finished = _finishTest };
386 | int actorId = _runtime.AddActorToThread("T1", actor);
387 | _runtime.StartCall(async () =>
388 | {
389 | await Task.Delay(50);
390 | return 77;
391 | }, actorId);
392 | _finishTest.WaitOne();
393 |
394 | CollectionAssert.AreEquivalent(
395 | new string[] { "Completed 1 77" },
396 | _history
397 | );
398 | }
399 |
400 | [Test]
401 | public void StartCall_Failed()
402 | {
403 | Caller actor = new Caller { History = _history, Finished = _finishTest };
404 | int actorId = _runtime.AddActorToThread("T1", actor);
405 | _runtime.StartCall(Fail, actorId);
406 | _finishTest.WaitOne();
407 |
408 | CollectionAssert.AreEquivalent(
409 | new string[] { "Failure 1" },
410 | _history
411 | );
412 | }
413 |
414 | private static Task Fail()
415 | {
416 | TaskCompletionSource source = new TaskCompletionSource();
417 | source.SetException(new Exception("oi"));
418 | return source.Task;
419 | }
420 |
421 | private static Task Cancel()
422 | {
423 | TaskCompletionSource source = new TaskCompletionSource();
424 | source.SetCanceled();
425 | return source.Task;
426 | }
427 |
428 | [Test]
429 | public void StartCall_Cancelled()
430 | {
431 | Caller actor = new Caller { History = _history, Finished = _finishTest };
432 | int actorId = _runtime.AddActorToThread("T1", actor);
433 | _runtime.StartCall(Cancel, actorId);
434 | _finishTest.WaitOne();
435 |
436 | CollectionAssert.AreEquivalent(
437 | new string[] { "Cancelled 1" },
438 | _history
439 | );
440 | }
441 | }
442 | }
443 |
444 |
--------------------------------------------------------------------------------
/ActorsTest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Packages.dgml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About
2 |
3 | A proof of concept for automata-based programming in DRAKON and C#.
4 |
5 | http://drakon-editor.sourceforge.net/actors.html
6 |
7 | The solution contains an actor framework and two sample applications:
8 | - a WPF GUI application
9 | - an HTTP server
10 |
11 | Both applications demonstrate concurrency using actors and message passing.
12 | Actors are designed as state machines in the DRAKON-C# visual language.
13 |
14 | ## System requirements
15 |
16 | - Visual Studio 2017
17 | - DRAKON Editor 1.31
18 |
--------------------------------------------------------------------------------
/packages/FakeItEasy.1.25.1/FakeItEasy.1.25.1.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/FakeItEasy.1.25.1/FakeItEasy.1.25.1.nupkg
--------------------------------------------------------------------------------
/packages/FakeItEasy.1.25.1/lib/net35/FakeItEasy.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/FakeItEasy.1.25.1/lib/net35/FakeItEasy.dll
--------------------------------------------------------------------------------
/packages/FakeItEasy.1.25.1/lib/net40/FakeItEasy.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/FakeItEasy.1.25.1/lib/net40/FakeItEasy.dll
--------------------------------------------------------------------------------
/packages/FakeItEasy.1.25.1/lib/sl50/FakeItEasy.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/FakeItEasy.1.25.1/lib/sl50/FakeItEasy.dll
--------------------------------------------------------------------------------
/packages/FakeItEasy.1.25.1/lib/win8/FakeItEasy.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/FakeItEasy.1.25.1/lib/win8/FakeItEasy.dll
--------------------------------------------------------------------------------
/packages/NUnit.2.6.3/NUnit.2.6.3.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.3/NUnit.2.6.3.nupkg
--------------------------------------------------------------------------------
/packages/NUnit.2.6.3/lib/nunit.framework.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.3/lib/nunit.framework.dll
--------------------------------------------------------------------------------
/packages/NUnit.2.6.3/license.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.3/license.txt
--------------------------------------------------------------------------------
/packages/NUnit.2.6.4/NUnit.2.6.4.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.4/NUnit.2.6.4.nupkg
--------------------------------------------------------------------------------
/packages/NUnit.2.6.4/lib/nunit.framework.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.4/lib/nunit.framework.dll
--------------------------------------------------------------------------------
/packages/NUnit.2.6.4/license.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepan-mitkin/actor-http/3e30618aab208ecc021a4448a757748ca4c5c558/packages/NUnit.2.6.4/license.txt
--------------------------------------------------------------------------------
/packages/repositories.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------