├── .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 | --------------------------------------------------------------------------------