├── .gitattributes ├── .gitignore ├── Build.fsx ├── Create-Mono.fsx ├── Examples ├── ActorFSharpSample1 │ ├── ActorFSharpSample1.fsproj │ ├── App.config │ ├── Program.fs │ └── packages.config ├── ActorFSharpSample2 │ ├── ActorFSharpSample2.fsproj │ ├── App.config │ ├── Program.fs │ └── packages.config ├── ActorFSharpSample3 │ ├── ActorFSharpSample3.fsproj │ ├── App.config │ ├── Program.fs │ └── packages.config ├── ActorSample1 │ ├── ActorSample1-Mono.csproj │ ├── ActorSample1.csproj │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── ActorSample2 │ ├── ActorSample2-Mono.csproj │ ├── ActorSample2.csproj │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── PingPong │ ├── App.config │ ├── PingPong-Mono.csproj │ ├── PingPong.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── ProtobufSample │ ├── App.config │ ├── Client.cs │ ├── Data.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ProtobufSample-Mono.csproj │ ├── ProtobufSample.csproj │ ├── Server.cs │ └── packages.config ├── ReactiveSample │ ├── App.config │ ├── Client.cs │ ├── Data.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ReactiveSample-Mono.csproj │ ├── ReactiveSample.csproj │ ├── Server.cs │ └── packages.config └── RemoteActorsSample │ ├── App.config │ ├── Client.cs │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── RemoteActorsSample.csproj │ ├── Server.cs │ └── packages.config ├── LICENSE ├── PerfTests ├── Actors │ ├── Actors-Mono.csproj │ ├── Actors.csproj │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── Executors │ ├── App.config │ ├── Benchmark.cs │ ├── Executors-Mono.csproj │ ├── Executors.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── SingleExecutor.cs │ └── packages.config ├── PingPong │ ├── App.config │ ├── PingPong.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config └── SimpleThroughput │ ├── FramedPerfTest │ ├── App.config │ ├── FramedPerfTest-Mono.csproj │ ├── FramedPerfTest.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config │ ├── RawStreamManyClients │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── RawStreamManyClients.csproj │ └── packages.config │ └── RawStreamPerfTest │ ├── App.config │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── RawStreamPerfTest-Mono.csproj │ ├── RawStreamPerfTest.csproj │ └── packages.config ├── README.md ├── SharedAssemblyInfo.cs ├── SharedAssemblyInfo.fs ├── Stacks-Mono.sln ├── Stacks.Actors.DI.Windsor ├── Properties │ └── AssemblyInfo.cs ├── Stacks.Actors.DI.Windsor.csproj ├── Stacks.Actors.DI.Windsor.nuspec ├── WindsorDependencyResolver.cs └── packages.config ├── Stacks.Actors.Tests.DI.Windsor ├── ContainerTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Stacks.Actors.Tests.DI.Windsor.csproj └── packages.config ├── Stacks.Actors.Tests ├── ActorSystemTests │ ├── ActorCodeGeneration.cs │ ├── ActorSystemCreate.cs │ ├── ActorTests.cs │ ├── HierarchyTests.cs │ └── TestActors.cs ├── Properties │ └── AssemblyInfo.cs ├── Remote │ ├── ActorSessionTests.cs │ ├── ConnectionTests.cs │ ├── CustomSerializerTests.cs │ ├── MessageTests.cs │ ├── ObservableTests.cs │ ├── TimeoutTests.cs │ └── Utils.cs ├── Stacks.Actors.Tests.csproj └── packages.config ├── Stacks.Actors ├── Actor.cs ├── ActorContext.cs ├── ActorContextSettings.cs ├── ActorCtorGuardian.cs ├── ActorSettings.cs ├── ActorSystem.cs ├── Bit.cs ├── CodeGen │ ├── ActorTypeGenerator.cs │ ├── ActorWrapperBase.cs │ ├── FormattingExtensions.cs │ ├── Helpers.cs │ ├── IActorCompilerStrategy.cs │ ├── ObservableMethodCompiler.cs │ ├── ObservablePropertiesCompiler.cs │ ├── StandardMethodCompiler.cs │ ├── StandardPropertiesCompiler.cs │ └── TaskMethodsCompiler.cs ├── DI │ ├── Args.cs │ ├── DependencyInjectionHelper.cs │ ├── IDependencyInjectionHelper.cs │ └── IDependencyResolver.cs ├── Exceptions.cs ├── IActor.cs ├── IActorContext.cs ├── NoExceptionHandlerAttribute.cs ├── PathUtils.cs ├── Properties │ └── AssemblyInfo.cs ├── Remote │ ├── ActorClientProxy.cs │ ├── ActorClientProxyOptions.cs │ ├── ActorClientProxyTemplate.cs │ ├── ActorPacketSerializer.cs │ ├── ActorProtocol.cs │ ├── ActorRemoteMessageClient.cs │ ├── ActorServerProxy.cs │ ├── ActorServerProxyOptions.cs │ ├── ActorServerProxyTemplate.cs │ ├── ClientActorDisconnectedData.cs │ ├── CodeGen │ │ ├── ActorTypeBuilder.cs │ │ ├── ClientActorTypeBuilder.cs │ │ └── ServerActorTypeBuilder.cs │ ├── IActorClientProxy.cs │ ├── IActorServerProxy.cs │ ├── IActorSession.cs │ └── IReplyMessage.cs ├── RootActor.cs ├── Stacks.Actors.csproj ├── Stacks.Actors.nuspec └── packages.config ├── Stacks.FSharp ├── Actors │ ├── ActorContext.fs │ └── Executor.fs ├── AssemblyInfo.fs ├── Stacks.FSharp.fsproj ├── Stacks.FSharp.nuspec └── packages.config ├── Stacks.MessagePack ├── Properties │ └── AssemblyInfo.cs ├── Serializers │ └── MessagePackStacksSerializer.cs ├── Stacks.MessagePack-Mono.csproj ├── Stacks.MessagePack.csproj ├── Stacks.MessagePack.nuspec └── packages.config ├── Stacks.Tests ├── Client │ ├── FramedClientTests.cs │ ├── MessageClientTests.cs │ ├── RawBytesClientTests.cs │ ├── ReactiveMessageClientTests.cs │ └── SslClientTests.cs ├── DataHelpers.cs ├── ExecutorTests.cs ├── IPHelpersTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Serialization │ ├── MessagePackSerializerTests.cs │ ├── ProtoBufSerializerTests.cs │ └── SerializationHandlerTests.cs ├── SocketServerTests.cs ├── Stacks.Tests-Mono.csproj ├── Stacks.Tests.csproj ├── StacksTest.pfx ├── Utils.cs └── packages.config ├── Stacks.sln ├── Stacks ├── AddressHelpers.cs ├── Client │ ├── FramedClientBuffer.cs │ ├── IFramedClient.cs │ ├── IMessageClient.cs │ ├── IRawByteClient.cs │ ├── ISocketClient.cs │ ├── RawByteClientStream.cs │ └── Tcp │ │ ├── FramedClient.cs │ │ ├── MessageClient.cs │ │ ├── MessageClientBase.cs │ │ ├── ReactiveClientModelBuilder.cs │ │ ├── ReactiveMessageClient.cs │ │ ├── SocketClient.cs │ │ └── SslClient.cs ├── Ensure.cs ├── Executors │ ├── ActionBlockExecutor.Mono.cs │ ├── ActionBlockExecutor.cs │ ├── ActionBlockExecutorSettings.cs │ ├── CapturedContextExecutor.cs │ ├── Executor.cs │ ├── ExecutorHelper.cs │ └── IExecutor.cs ├── MessageClient │ ├── IMessageIdCache.cs │ ├── ImperativeMessageIdCache.cs │ ├── MessageIdCache.cs │ ├── MessageIdRegistration.cs │ ├── ReactiveMessageReceiverCreator.cs │ └── StacksMessageAttribute.cs ├── Properties │ └── AssemblyInfo.cs ├── ResizableCyclicBuffer.cs ├── Serializers │ ├── IMessageHandler.cs │ ├── IStacksSerializer.cs │ ├── ProtobufStacksSerializer.cs │ └── StacksSerializationHandler.cs ├── Server │ └── Tcp │ │ └── SocketServer.cs ├── Stacks-Mono.csproj ├── Stacks.csproj ├── Stacks.nuspec └── packages.config ├── build.cmd └── tools └── NuGet └── nuget.exe /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | .fake/ 5 | tools/FAKE/* 6 | !tools/NuGet/nuget.exe 7 | 8 | /*.nupkg 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.sln.docstates 14 | 15 | # Build results 16 | 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | build/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 25 | !packages/*/build/ 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | *_i.c 32 | *_p.c 33 | *.ilk 34 | *.meta 35 | *.obj 36 | *.pch 37 | *.pdb 38 | *.pgc 39 | *.pgd 40 | *.rsp 41 | *.sbr 42 | *.tlb 43 | *.tli 44 | *.tlh 45 | *.tmp 46 | *.tmp_proj 47 | *.log 48 | *.vspscc 49 | *.vssscc 50 | .builds 51 | *.pidb 52 | *.log 53 | *.scc 54 | 55 | # Visual C++ cache files 56 | ipch/ 57 | *.aps 58 | *.ncb 59 | *.opensdf 60 | *.sdf 61 | *.cachefile 62 | 63 | # Visual Studio profiler 64 | *.psess 65 | *.vsp 66 | *.vspx 67 | 68 | # Guidance Automation Toolkit 69 | *.gpState 70 | 71 | # ReSharper is a .NET coding add-in 72 | _ReSharper*/ 73 | *.[Rr]e[Ss]harper 74 | 75 | # TeamCity is a build add-in 76 | _TeamCity* 77 | 78 | # DotCover is a Code Coverage Tool 79 | *.dotCover 80 | 81 | # NCrunch 82 | *.ncrunch* 83 | .*crunch*.local.xml 84 | _NCrunch_Stacks/ 85 | 86 | # Installshield output folder 87 | [Ee]xpress/ 88 | 89 | # DocProject is a documentation generator add-in 90 | DocProject/buildhelp/ 91 | DocProject/Help/*.HxT 92 | DocProject/Help/*.HxC 93 | DocProject/Help/*.hhc 94 | DocProject/Help/*.hhk 95 | DocProject/Help/*.hhp 96 | DocProject/Help/Html2 97 | DocProject/Help/html 98 | 99 | # Click-Once directory 100 | publish/ 101 | 102 | # Publish Web Output 103 | *.Publish.xml 104 | 105 | # NuGet Packages Directory 106 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 107 | packages/ 108 | 109 | # Windows Azure Build Output 110 | csx 111 | *.build.csdef 112 | 113 | # Windows Store app package directory 114 | AppPackages/ 115 | 116 | # Others 117 | sql/ 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | 128 | # RIA/Silverlight projects 129 | Generated_Code/ 130 | 131 | # Backup & report files from converting an old project file to a newer 132 | # Visual Studio version. Backup files are not needed, because we have git ;-) 133 | _UpgradeReport_Files/ 134 | Backup*/ 135 | UpgradeLog*.XML 136 | UpgradeLog*.htm 137 | 138 | # SQL Server files 139 | App_Data/*.mdf 140 | App_Data/*.ldf 141 | 142 | 143 | #LightSwitch generated files 144 | GeneratedArtifacts/ 145 | _Pvt_Extensions/ 146 | ModelManifest.xml 147 | 148 | # ========================= 149 | # Windows detritus 150 | # ========================= 151 | 152 | # Windows image file caches 153 | Thumbs.db 154 | ehthumbs.db 155 | 156 | # Folder config file 157 | Desktop.ini 158 | 159 | # Recycle Bin used on file shares 160 | $RECYCLE.BIN/ 161 | 162 | # Mac desktop service store files 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /Create-Mono.fsx: -------------------------------------------------------------------------------- 1 | #r "System.Xml.Linq.dll" 2 | 3 | open System 4 | open System.IO 5 | open System.Xml.Linq 6 | open System.Text.RegularExpressions 7 | 8 | let xn (ns: XNamespace) x = XName.Get(x, ns.ToString()) 9 | let (@@) x y = Path.Combine(x, y) 10 | 11 | let PatchSolution solutionFile = 12 | let slnFile = FileInfo(solutionFile) 13 | let slnMonoFile = Path.GetDirectoryName(slnFile.FullName) @@ 14 | (Path.GetFileNameWithoutExtension(slnFile.Name) + "-Mono.sln") 15 | if File.Exists(slnMonoFile) then File.Delete(slnMonoFile) 16 | 17 | let projRegex = @"Project\(""\{[^}]+\}""\) = ""[^""]+"", ""[^""]+"", ""\{[^}]+\}" 18 | 19 | let outFile = File.CreateText(slnMonoFile) 20 | 21 | for line in File.ReadAllLines(slnFile.FullName) do 22 | match Regex.IsMatch(line, projRegex) with 23 | | true -> outFile.WriteLine(line.Replace(".csproj", "-Mono.csproj")) 24 | | false -> outFile.WriteLine(line) 25 | outFile.Close(); 26 | 27 | let PatchProject projectFile = 28 | let projFileInfo = FileInfo(projectFile) 29 | let projMonoFile = Path.GetDirectoryName(projFileInfo.FullName) @@ 30 | (Path.GetFileNameWithoutExtension(projFileInfo.Name) + "-Mono.csproj") 31 | if File.Exists(projMonoFile) then 32 | File.Delete(projMonoFile) 33 | 34 | let xml = XDocument.Load(projFileInfo.FullName) 35 | let xn = xn (xml.Root.GetDefaultNamespace()) 36 | let axn x = XName.Get(x) 37 | 38 | //Changed target framework version to 4.5 39 | xml.Element(xn"Project").Elements(xn"PropertyGroup") 40 | |> Seq.iter(fun p -> 41 | match p.Element(xn"TargetFrameworkVersion") with 42 | | null -> () 43 | | t -> t.Value <- "v4.5" 44 | ) 45 | 46 | //Patch constants to add MONO (compilation requires it) 47 | xml.Element(xn"Project").Elements(xn"PropertyGroup") 48 | |> Seq.iter(fun p -> 49 | match p.Element(xn"DefineConstants") with 50 | | null -> () 51 | | t -> match t.Value.Contains("MONO") with 52 | | true -> () 53 | | false -> t.Value <- t.Value + ";MONO" 54 | ) 55 | 56 | //Patch tools version to 4.0 (old MsBuild versioning) 57 | xml.Element(xn"Project").Attribute(axn"ToolsVersion").Value <- "4.0" 58 | 59 | xml.Save(projMonoFile) 60 | 61 | let rec PatchAllProjects dir = 62 | Directory.EnumerateDirectories(dir) 63 | |> Seq.iter(fun d -> PatchAllProjects(d)) 64 | 65 | Directory.EnumerateFiles(dir, "*.csproj") 66 | |> Seq.iter(fun f -> if not (f.Contains("-Mono")) then PatchProject(f)) 67 | 68 | PatchSolution "Stacks.sln" 69 | PatchAllProjects "./" 70 | 71 | 72 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample1/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample1/Program.fs: -------------------------------------------------------------------------------- 1 | open Stacks 2 | open Stacks.Actors 3 | 4 | type IFormatter = 5 | abstract member SayHello: string -> Async 6 | 7 | type Formatter() = 8 | inherit Actor() 9 | 10 | member this.GetContext() = 11 | base.GetActorSynchronizationContext() 12 | 13 | interface IFormatter with 14 | member this.SayHello(name: string) = 15 | async { 16 | do! Async.SwitchToContext(this.GetContext()) 17 | return sprintf "Hello %s!" name 18 | } 19 | 20 | type Hello() = 21 | 22 | let formatter = ActorSystem.Default.CreateActor() :?> IFormatter 23 | 24 | member this.SayHelloToFriends(names: seq) = 25 | names 26 | |> Seq.iter (fun t -> printfn "%s" (Async.RunSynchronously <| formatter.SayHello(t))) 27 | 28 | 29 | [] 30 | let main argv = 31 | 32 | let helloPrinter = Hello() 33 | 34 | helloPrinter.SayHelloToFriends( [ "Stan"; "Scott"; "John" ]) 35 | 36 | 0 37 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample1/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample2/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample2/Program.fs: -------------------------------------------------------------------------------- 1 |  2 | open System.Net.Http 3 | open Stacks.Actors 4 | open System.Xml.Linq 5 | open Stacks 6 | 7 | 8 | type IWeather = 9 | abstract member GetTemperature: string -> Async 10 | 11 | type Weather() = 12 | inherit Actor() 13 | let httpClient = new HttpClient() 14 | 15 | let xn s = XName.Get(s) 16 | 17 | interface IWeather with 18 | member __.GetTemperature(city: string) = 19 | base.Context.MakeAsync(async { 20 | let! response = sprintf "http://api.openweathermap.org/data/2.5/\ 21 | weather?q=%s&mode=xml&units=metric" city 22 | |> httpClient.GetStringAsync 23 | |> Async.AwaitTask 24 | 25 | return double (XDocument.Parse(response) 26 | .Element(xn"current") 27 | .Element(xn"temperature") 28 | .Attribute(xn"value")) 29 | }) 30 | 31 | 32 | [] 33 | let main _ = 34 | 35 | let weather = ActorSystem.Default.CreateActor() :?> IWeather 36 | 37 | try 38 | let temp = weather.GetTemperature("Warsaw") |> Async.RunSynchronously 39 | printfn "Temperature in Warsaw, Poland: %.2f\u00B0C" temp 40 | with 41 | | _ -> printfn "Could not get temperature for Warsaw, Poland" 42 | 43 | 0 44 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample2/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample3/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample3/Program.fs: -------------------------------------------------------------------------------- 1 | open RestSharp 2 | open Stacks 3 | open Newtonsoft.Json 4 | open Newtonsoft.Json.Linq 5 | 6 | let GetRepositories(user: string) = async { 7 | let c = new RestClient("https://api.github.com/") 8 | 9 | let! resp = c.ExecuteGetTaskAsync(RestRequest("users/" + user + "/repos")) 10 | |> Async.AwaitTask 11 | 12 | return JsonConvert.DeserializeObject(resp.Content) :?> JArray 13 | |> Seq.map(fun r -> r.["name"].Value()) 14 | |> Seq.toArray 15 | } 16 | 17 | let GetStargazers user repo = async { 18 | let c = new RestClient("https://api.github.com/") 19 | 20 | let! resp = c.ExecuteGetTaskAsync(RestRequest(sprintf "repos/%s/%s/stargazers" user repo)) 21 | |> Async.AwaitTask 22 | 23 | return JsonConvert.DeserializeObject(resp.Content) :?> JArray 24 | |> Seq.map(fun r -> r.["login"].Value()) 25 | |> Seq.toArray 26 | } 27 | 28 | // This sample shows how to use executor to run an async block 29 | // All features, like Async.Parallel can be used, and user code 30 | // will run on executor's context, which can be in this context 31 | // assumed to be an actor. This also means, that external tasks 32 | // can be awaited inside async block on exector. 33 | // 34 | // Sample rules apply to actor contexts and actors, although 35 | // using Actor class is discouraged in F#, because of caveeats 36 | // with inheritance. 37 | // Instead, composition with ActorContext is a way to go 38 | 39 | [] 40 | let main _ = 41 | 42 | let exec = ActionBlockExecutor("main") 43 | 44 | //RunAsync creates new async block, which will run given one 45 | //on executor's context. Whol block can be awaited synchronously 46 | //with Async.RunSynchronously or asynchronously, with Async.StartImmediate. 47 | exec.MakeAsync(async { 48 | printfn "Running on executor: %s" (Executor.GetCurrentName()) 49 | 50 | let! repos = GetRepositories "Ravadre" 51 | 52 | printfn "Still running on executor: %s" (Executor.GetCurrentName()) 53 | printfn "" 54 | printfn "Repositories:" 55 | printfn "%s" (System.String.Join(", ", repos)) 56 | 57 | let! stargazers = repos 58 | |> Seq.map(fun r -> GetStargazers "Ravadre" r) 59 | |> Async.Parallel 60 | 61 | let stargazers = stargazers 62 | |> Array.collect id 63 | |> Set.ofArray 64 | 65 | printfn "" 66 | printfn "Still running on executor: %s" (Executor.GetCurrentName()) 67 | printfn "" 68 | printfn "Stargazers:" 69 | printfn "%s" (System.String.Join(", ", stargazers)) 70 | }) 71 | |> Async.RunSynchronously 72 | 73 | 0 74 | -------------------------------------------------------------------------------- /Examples/ActorFSharpSample3/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Examples/ActorSample1/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/ActorSample1/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks; 7 | using Stacks.Actors; 8 | 9 | namespace ActorSample1 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var formatter = ActorSystem.Default.CreateActor(); 16 | var helloPrinter = new Hello(formatter); 17 | 18 | helloPrinter.SayHelloToFriends(new[] { "Stan", "Scott", "John" }); 19 | } 20 | } 21 | 22 | class Hello 23 | { 24 | private readonly IFormatter formatter; 25 | 26 | public Hello(IFormatter formatter) 27 | { 28 | this.formatter = formatter; 29 | } 30 | 31 | public void SayHelloToFriends(IEnumerable names) 32 | { 33 | foreach (var name in names) 34 | { 35 | Console.WriteLine(formatter.SayHello(name).Result); 36 | } 37 | } 38 | } 39 | 40 | public interface IFormatter 41 | { 42 | Task SayHello(string name); 43 | } 44 | 45 | //One of the ways of defining an actor is to 46 | //inherit from Actor class 47 | class Formatter : Actor, IFormatter 48 | { 49 | //Because every call has to be scheduled on 50 | //actor's context, answer will not be ready instantly, 51 | //so Task is returned. 52 | public async Task SayHello(string name) 53 | { 54 | //Code after this await will be run 55 | //on actor's context. 56 | await Context; 57 | 58 | //When an actor wants to reply to request, it just 59 | //has to return the response. 60 | return "Hello " + name + "!"; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Examples/ActorSample1/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("ActorSample1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ActorSample1")] 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("a0f5a804-763c-4dc9-8efb-da0750f547b8")] 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 | -------------------------------------------------------------------------------- /Examples/ActorSample1/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/ActorSample2/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/ActorSample2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks.Actors; 7 | using System.Net.Http; 8 | using System.Xml.Linq; 9 | 10 | namespace ActorSample2 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | var weather = ActorSystem.Default.CreateActor(); 17 | 18 | try 19 | { 20 | var temp = weather.GetTemperature("Warsaw").Result; 21 | Console.WriteLine("Temperature in Warsaw, Poland: " + 22 | temp.ToString("F2") + 23 | "\u00B0C"); 24 | } 25 | catch (Exception) 26 | { 27 | Console.WriteLine("Could not get temperature for Warsaw, Poland"); 28 | } 29 | } 30 | } 31 | 32 | interface IWeather 33 | { 34 | Task GetTemperature(string city); 35 | } 36 | 37 | class Weather : Actor, IWeather 38 | { 39 | //Actor can also be implemented by defining context 40 | //private ActorContext actor = new ActorContext(); 41 | private HttpClient httpClient = new HttpClient(); 42 | 43 | public async Task GetTemperature(string city) 44 | { 45 | await Context; 46 | 47 | //Other actors or services can be awaited, execution will be resumed 48 | //on actor context automatically. 49 | var data = await httpClient.GetStringAsync( 50 | string.Format("http://api.openweathermap.org/data/2.5/" + 51 | "weather?q={0}&mode=xml&units=metric", 52 | city)); 53 | 54 | return (double)XDocument.Parse(data) 55 | .Element("current") 56 | .Element("temperature") 57 | .Attribute("value"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Examples/ActorSample2/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("ActorSample2")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ActorSample2")] 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("cda5c838-94ea-4479-8238-8c5e501e1b58")] 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 | -------------------------------------------------------------------------------- /Examples/ActorSample2/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/PingPong/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/PingPong/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Stacks; 8 | using System.Reactive.Linq; 9 | using Stacks.Tcp; 10 | 11 | namespace PingPong 12 | { 13 | class Program 14 | { 15 | static IFramedClient client; 16 | static IFramedClient serverClient; 17 | static Encoding encoding; 18 | 19 | static void Main(string[] args) 20 | { 21 | encoding = Encoding.ASCII; 22 | SocketServer server; 23 | server = new SocketServer(new IPEndPoint(IPAddress.Loopback, 0)); 24 | 25 | server.Connected.Subscribe(c => 26 | { 27 | serverClient = new FramedClient(c); 28 | 29 | // When received is called, bs will contain no more and no less 30 | // data than whole packet as sent from client. 31 | serverClient.Received.Subscribe(bs => 32 | { 33 | var msg = encoding.GetString(bs.Array, bs.Offset, bs.Count); 34 | msg = "Hello, " + msg + "!"; 35 | serverClient.SendPacket(encoding.GetBytes(msg)); 36 | }); 37 | }); 38 | 39 | server.Start(); 40 | HandleClient(server.BindEndPoint.Port); 41 | 42 | Console.WriteLine("Press any key to stop..."); 43 | Console.ReadKey(); 44 | 45 | server.Stop(); 46 | client.Close(); 47 | serverClient.Close(); 48 | } 49 | 50 | static async void HandleClient(int serverPort) 51 | { 52 | client = new FramedClient( 53 | new SocketClient()); 54 | 55 | client.Received.Subscribe(bs => 56 | { 57 | Console.WriteLine("Received: " + 58 | encoding.GetString(bs.Array, bs.Offset, bs.Count)); 59 | }); 60 | 61 | await client.Connect(new IPEndPoint(IPAddress.Loopback, serverPort)); 62 | client.SendPacket(encoding.GetBytes("Steve")); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Examples/PingPong/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("PingPong")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PingPong")] 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("2834c464-bd71-418a-8d81-2144daa5f105")] 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 | -------------------------------------------------------------------------------- /Examples/PingPong/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks.Tcp; 7 | using Stacks; 8 | using System.Net; 9 | 10 | namespace ProtobufSample 11 | { 12 | public class Client 13 | { 14 | MessageClient client; 15 | 16 | public Client() 17 | { 18 | 19 | } 20 | 21 | public void Run(int serverPort) 22 | { 23 | client = new MessageClient( 24 | new FramedClient(new SocketClient(useIPv6: true)), 25 | new ProtoBufStacksSerializer(), 26 | new ClientMessageHandler()); 27 | 28 | client.PreLoadTypesFromAssemblyOfType(); 29 | 30 | client.Connect(new IPEndPoint(IPAddress.IPv6Loopback, serverPort)) 31 | .Subscribe(_ => 32 | { 33 | Console.WriteLine("Querying for temperature in London, Warsaw, Madrid"); 34 | client.Send(new TemperatureRequest { City = "London" }); 35 | client.Send(new TemperatureRequest { City = "Warsaw" }); 36 | client.Send(new TemperatureRequest { City = "Madrid" }); 37 | }); 38 | } 39 | } 40 | 41 | public class ClientMessageHandler : IMessageHandler 42 | { 43 | public void HandleTemperatureResponse(IMessageClient client, TemperatureResponse response) 44 | { 45 | Console.WriteLine("Received temperature response: " + 46 | response.City + " = " + response.Temperature.ToString("F2") + "\u00b0C"); 47 | } 48 | 49 | public void HandleTemperatureChanged(IMessageClient client, TemperatureChanged tempChanged) 50 | { 51 | Console.WriteLine("Temperature changed: " + 52 | tempChanged.City + " = " + tempChanged.Temperature.ToString("F2") + "\u00b0C"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/Data.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ProtoBuf; 7 | using Stacks; 8 | 9 | namespace ProtobufSample 10 | { 11 | [ProtoContract] 12 | [StacksMessage(3)] 13 | public class TemperatureChanged 14 | { 15 | [ProtoMember(1)] 16 | public string City { get; set; } 17 | [ProtoMember(2)] 18 | public double Temperature { get; set; } 19 | } 20 | 21 | [ProtoContract] 22 | [StacksMessage(1)] 23 | public class TemperatureRequest 24 | { 25 | [ProtoMember(1)] 26 | public string City { get; set; } 27 | } 28 | 29 | [ProtoContract] 30 | [StacksMessage(2)] 31 | public class TemperatureResponse 32 | { 33 | [ProtoMember(1)] 34 | public string City { get; set; } 35 | [ProtoMember(2)] 36 | public double Temperature { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/Program.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 ProtobufSample 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | Server s = new Server(); 14 | Client c = new Client(); 15 | s.Run(); 16 | c.Run(s.ServerPort); 17 | 18 | Console.WriteLine("Press any key to exit"); 19 | Console.ReadKey(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/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("ProtobufSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ProtobufSample")] 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("0be6e15d-3d02-4c25-bba6-bbe57bea01db")] 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 | -------------------------------------------------------------------------------- /Examples/ProtobufSample/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Reactive.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Stacks; 9 | using Stacks.Tcp; 10 | 11 | namespace ReactiveSample 12 | { 13 | public class Client 14 | { 15 | ReactiveMessageClient client; 16 | IDisposable priceObserver; 17 | 18 | public Client() 19 | { 20 | 21 | } 22 | 23 | public void Run(int serverPort) 24 | { 25 | client = new ReactiveMessageClient( 26 | new FramedClient(new SocketClient()), 27 | new ProtoBufStacksSerializer()); 28 | 29 | client.PreLoadTypesFromAssemblyOfType(); 30 | 31 | priceObserver = client.Packets.Price.Subscribe(p => 32 | { 33 | Console.WriteLine("Price received: {0} - {1:F5} / {2:F5}", 34 | p.Symbol, p.Bid, p.Offer); 35 | }); 36 | 37 | client.Connect(new IPEndPoint(IPAddress.Loopback, serverPort)) 38 | .Subscribe(async _ => 39 | { 40 | await Task.Delay(2000); 41 | client.Send(new RegisterSymbolRequest() { Symbol = "EURUSD", Register = true }); 42 | client.Send(new RegisterSymbolRequest() { Symbol = "GBPLN", Register = true }); 43 | }); 44 | 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/Data.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ProtoBuf; 7 | using Stacks; 8 | 9 | namespace ReactiveSample 10 | { 11 | [ProtoContract] 12 | [StacksMessage(1)] 13 | public class RegisterSymbolRequest 14 | { 15 | [ProtoMember(1)] 16 | public string Symbol { get; set; } 17 | [ProtoMember(2)] 18 | public bool Register { get; set; } 19 | } 20 | 21 | [ProtoContract] 22 | [StacksMessage(2)] 23 | public class Price 24 | { 25 | [ProtoMember(1)] 26 | public string Symbol { get; set; } 27 | [ProtoMember(2)] 28 | public double Bid { get; set; } 29 | [ProtoMember(3)] 30 | public double Offer { get; set; } 31 | } 32 | 33 | public interface IServerPacketHandler 34 | { 35 | IObservable RegisterSymbol { get; } 36 | } 37 | 38 | public interface IClientPacketHandler 39 | { 40 | IObservable Price { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/Program.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 ReactiveSample 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | Server s = new Server(); 14 | Client c = new Client(); 15 | s.Run(); 16 | c.Run(s.ServerPort); 17 | 18 | Console.WriteLine("Press any key to exit"); 19 | Console.ReadKey(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/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("ReactiveSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ReactiveSample")] 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("ee378fa7-9fc7-4d5b-9ba2-9927ceed87e7")] 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 | -------------------------------------------------------------------------------- /Examples/ReactiveSample/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/RemoteActorsSample/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/RemoteActorsSample/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ProtoBuf; 7 | using Stacks.Actors; 8 | 9 | namespace RemoteActorsSample 10 | { 11 | [ProtoContract] 12 | public class Rectangle 13 | { 14 | [ProtoMember(1)] 15 | public double A { get; set; } 16 | [ProtoMember(2)] 17 | public double B { get; set; } 18 | } 19 | 20 | [ProtoContract] 21 | public class RectangleInfo 22 | { 23 | [ProtoMember(1)] 24 | public double Field { get; set; } 25 | [ProtoMember(2)] 26 | public double Perimeter { get; set; } 27 | } 28 | 29 | [ProtoContract] 30 | public class Message 31 | { 32 | [ProtoMember(1)] 33 | public long X { get; set; } 34 | [ProtoMember(2)] 35 | public long Y { get; set; } 36 | [ProtoMember(3)] 37 | public RectangleInfo Info { get; set; } 38 | } 39 | 40 | public interface ICalculatorActor 41 | { 42 | Task Add(double x, double y); 43 | Task Subtract(double x, double y); 44 | Task Increment(int x); 45 | 46 | Task CalculateRectangle(Rectangle rect); 47 | 48 | Task PushNumber(double x); 49 | Task PopNumber(); 50 | Task Ping(); 51 | 52 | Task Mean(double[] xs); 53 | Task MeanEnum(IEnumerable xs); 54 | 55 | Task PingAsync(); 56 | 57 | IObservable Rng { get; } 58 | IObservable Messages { get; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Examples/RemoteActorsSample/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("RemoteActorsSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RemoteActorsSample")] 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("34b947e5-e731-4f9e-9714-02525dfeef78")] 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 | -------------------------------------------------------------------------------- /Examples/RemoteActorsSample/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marcin Deptuła 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PerfTests/Actors/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PerfTests/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("17318eaa-3a5d-4c13-a4ca-fa7078db9607")] 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 | -------------------------------------------------------------------------------- /PerfTests/Actors/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PerfTests/Executors/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PerfTests/Executors/Benchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Executors 9 | { 10 | public class Benchmark 11 | { 12 | public static BenchmarkStats Measure(Action action, int timesToRepeat) 13 | { 14 | var times = new List(); 15 | var sw = new Stopwatch(); 16 | 17 | for (int i = 0; i < timesToRepeat; ++i) 18 | { 19 | GC.Collect(); GC.Collect(); 20 | 21 | sw.Restart(); 22 | action(); 23 | sw.Stop(); 24 | 25 | if (i > 0) 26 | times.Add(sw.Elapsed); 27 | } 28 | 29 | return new BenchmarkStats(times); 30 | } 31 | } 32 | 33 | public class BenchmarkStats 34 | { 35 | public IReadOnlyList Times { get; private set; } 36 | public TimeSpan TotalAverageTime { get; private set; } 37 | 38 | public BenchmarkStats(IEnumerable times) 39 | { 40 | Times = times.ToList(); 41 | TotalAverageTime = TimeSpan.FromSeconds(Times.Average(t => t.TotalSeconds)); 42 | } 43 | 44 | public long GetAverateTimePerActionNs(int actionCount) 45 | { 46 | return TotalAverageTime.Ticks * 1000000L / TimeSpan.TicksPerMillisecond / actionCount; 47 | } 48 | 49 | public override string ToString() 50 | { 51 | return TotalAverageTime.TotalSeconds.ToString("F4") + "s"; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PerfTests/Executors/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks; 7 | 8 | namespace Executors 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | RunSingleExecutor(); 15 | 16 | Console.ReadLine(); 17 | } 18 | 19 | static void RunSingleExecutor() 20 | { 21 | var test = new SingleExecutor(1000000, () => new ActionBlockExecutor()); 22 | var stats = Benchmark.Measure(test.Run, 5); 23 | 24 | Console.WriteLine("Action block (ctx) executor: " + stats); 25 | Console.WriteLine("Action block (ctx) time per msg: " + stats.GetAverateTimePerActionNs(1000000).ToString() + "ns"); 26 | 27 | test = new SingleExecutor(1000000, () => new ActionBlockExecutor(new ActionBlockExecutorSettings() { SupportSynchronizationContext = false })); 28 | stats = Benchmark.Measure(test.Run, 5); 29 | 30 | Console.WriteLine("Action block (no ctx) executor: " + stats); 31 | Console.WriteLine("Action block (no ctx) time per msg: " + stats.GetAverateTimePerActionNs(1000000).ToString() + "ns"); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PerfTests/Executors/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("Executors")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Executors")] 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("19836c5f-f70e-47fa-bed6-b5b617558cd4")] 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 | -------------------------------------------------------------------------------- /PerfTests/Executors/SingleExecutor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks; 7 | 8 | namespace Executors 9 | { 10 | class SingleExecutor 11 | { 12 | private int messageCount; 13 | private Func execFactory; 14 | 15 | public SingleExecutor(int messageCount, Func execFactory) 16 | { 17 | this.messageCount = messageCount; 18 | this.execFactory = execFactory; 19 | } 20 | 21 | public void Run() 22 | { 23 | var ex1 = execFactory(); 24 | 25 | var counter = 0L; 26 | 27 | for (int i = 0; i < messageCount; ++i) 28 | ex1.Enqueue(() => ++counter); 29 | 30 | ex1.Stop().Wait(); 31 | 32 | if (counter != messageCount) 33 | throw new Exception("Invalid result"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /PerfTests/Executors/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PerfTests/PingPong/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PerfTests/PingPong/PingPong.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E46A1856-8BC9-4EB2-8B04-5E290872B211} 8 | Exe 9 | Properties 10 | PingPong 11 | PingPong 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll 41 | 42 | 43 | ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll 44 | 45 | 46 | ..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll 47 | 48 | 49 | ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {733e7987-f63e-4057-8454-f601c87e934b} 68 | Stacks.Actors 69 | 70 | 71 | {9e0d5c52-ad78-4a5e-82ad-289d272a111d} 72 | Stacks 73 | 74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /PerfTests/PingPong/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("PingPong")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PingPong")] 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("b559385e-a43f-4f3a-885e-18dea127559b")] 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 | -------------------------------------------------------------------------------- /PerfTests/PingPong/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/FramedPerfTest/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/FramedPerfTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reactive.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Stacks; 10 | using Stacks.Tcp; 11 | using Stacks.Tests; 12 | 13 | namespace RawStreamPerfTest 14 | { 15 | class Program 16 | { 17 | static SocketServer s; 18 | static SocketClient c1, c2; 19 | static IFramedClient fc1, fc2; 20 | 21 | static void Main(string[] args) 22 | { 23 | ServerHelpers.CreateServerAndConnectedClient(out s, out c1, out c2); 24 | 25 | c1.Disconnected.Subscribe(exn => { Console.WriteLine("C1 d/c " + exn); }); 26 | c2.Disconnected.Subscribe(exn => { Console.WriteLine("C2 d/c " + exn); }); 27 | 28 | fc1 = new FramedClient(c1); 29 | fc2 = new FramedClient(c2); 30 | 31 | Measure(8192, 8192); 32 | Console.ReadLine(); 33 | Measure(8192, 8192 * 64); 34 | 35 | Console.ReadLine(); 36 | } 37 | 38 | private static void Measure(int bufSize, int packets) 39 | { 40 | var buffer = DataHelpers.CreateRandomBuffer(bufSize); 41 | long l = packets; 42 | long totalRecv = 0; 43 | long totalPacketsRecv = 0; 44 | var received = new ManualResetEventSlim(); 45 | 46 | GC.Collect(); 47 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 48 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 49 | GC.CollectionCount(2)); 50 | var sw = Stopwatch.StartNew(); 51 | 52 | Action> recv = bs => 53 | { 54 | totalRecv += bs.Count; 55 | ++totalPacketsRecv; 56 | if (totalPacketsRecv == packets) received.Set(); 57 | }; 58 | 59 | var recvSub = fc2.Received.Subscribe(recv); 60 | 61 | for (int i = 0; i < l; ++i) 62 | { 63 | fc1.SendPacket(buffer); 64 | } 65 | 66 | received.Wait(); 67 | 68 | recvSub.Dispose(); 69 | 70 | var elapsed = sw.Elapsed.TotalSeconds; 71 | GC.Collect(); 72 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 73 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 74 | GC.CollectionCount(2)); 75 | 76 | Console.WriteLine("Elapsed s: " + elapsed); 77 | Console.WriteLine("Rate: " + (double)totalRecv * 8 / elapsed / 1024 / 1024 + " Mb/sec"); 78 | Console.WriteLine("Sent {0} packets. Received: {1}", packets, totalPacketsRecv); 79 | Console.WriteLine($"Rate: {(int)(packets / elapsed)} packets/sec"); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/FramedPerfTest/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("FramedPerfTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FramedPerfTest")] 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("170382ca-62be-4e7a-9a8f-a47be27b483c")] 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 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/FramedPerfTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamManyClients/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamManyClients/Program.cs: -------------------------------------------------------------------------------- 1 | using Stacks; 2 | using Stacks.Tests; 3 | using System; 4 | using System.CodeDom; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Reactive.Linq; 11 | using System.Threading.Tasks; 12 | using Stacks.Tcp; 13 | 14 | namespace RawStreamPerfTest 15 | { 16 | class Program 17 | { 18 | static SocketServer s; 19 | private static SocketClient[] clients; 20 | private static SocketClient[] sClients; 21 | 22 | static void Main(string[] args) 23 | { 24 | var ex = new ActionBlockExecutor(); 25 | clients = new SocketClient[1000]; 26 | sClients = new SocketClient[1000]; 27 | int sIdx = 0; 28 | var server = ServerHelpers.CreateServer(); 29 | server.Start(); 30 | server.Started.Wait(); 31 | 32 | server.Connected.Subscribe(c => 33 | { 34 | sClients[Interlocked.Increment(ref sIdx) - 1] = c; 35 | }); 36 | 37 | var allConnected = new ManualResetEventSlim(); 38 | var connectedCount = 0; 39 | for (var i = 0; i < clients.Length; ++i) 40 | { 41 | clients[i] = new SocketClient(ex); 42 | clients[i].Connected.Subscribe(_ => 43 | { 44 | if (Interlocked.Increment(ref connectedCount) == 1000) 45 | allConnected.Set(); 46 | }); 47 | clients[i].Disconnected.Subscribe(exn => 48 | { 49 | Console.WriteLine("Could not connect: " + exn); 50 | }); 51 | clients[i].Connect("tcp://localhost:" + server.BindEndPoint.Port); 52 | } 53 | 54 | allConnected.Wait(); 55 | 56 | Measure(8192, 10); 57 | Console.ReadLine(); 58 | Measure(8192, 10 * 16); 59 | 60 | Console.ReadLine(); 61 | } 62 | 63 | private static void Measure(int bufSize, int packets) 64 | { 65 | var buffer = DataHelpers.CreateRandomBuffer(bufSize); 66 | long l = packets; 67 | long totalRecv = 0; 68 | var received = new ManualResetEventSlim(); 69 | 70 | GC.Collect(); 71 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 72 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 73 | GC.CollectionCount(2)); 74 | 75 | Action> recv = bs => 76 | { 77 | Interlocked.Add(ref totalRecv, bs.Count); 78 | //totalRecv += bs.Count; 79 | if (totalRecv == sClients.Length * l * bufSize) received.Set(); 80 | }; 81 | 82 | for (var i = 0; i < sClients.Length; ++i) 83 | { 84 | sClients[i].Received.Subscribe(recv); 85 | } 86 | 87 | var sw = Stopwatch.StartNew(); 88 | for (int i = 0; i < clients.Length; ++i) 89 | { 90 | for (int p = 0; p < packets; ++p) 91 | { 92 | clients[i].Send(buffer); 93 | } 94 | } 95 | 96 | received.Wait(); 97 | 98 | var elapsed = sw.Elapsed.TotalSeconds; 99 | GC.Collect(); 100 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 101 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 102 | GC.CollectionCount(2)); 103 | 104 | Console.WriteLine("Elapsed s: " + elapsed); 105 | Console.WriteLine("Rate: " + (double)totalRecv * 8 / elapsed / 1024 / 1024 + " Mb/sec"); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamManyClients/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("RawStreamManyClients")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RawStreamManyClients")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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("3582fb47-6715-44c6-9bdb-6bd863056a9f")] 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 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamManyClients/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamPerfTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamPerfTest/Program.cs: -------------------------------------------------------------------------------- 1 | using Stacks; 2 | using Stacks.Tests; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Reactive.Linq; 10 | using System.Threading.Tasks; 11 | using Stacks.Tcp; 12 | 13 | namespace RawStreamPerfTest 14 | { 15 | class Program 16 | { 17 | static SocketServer s; 18 | static SocketClient c1, c2; 19 | 20 | static void Main(string[] args) 21 | { 22 | 23 | ServerHelpers.CreateServerAndConnectedClient(out s, out c1, out c2); 24 | 25 | c1.Disconnected.Subscribe(exn => { Console.WriteLine("C1 d/c " + exn); }); 26 | c2.Disconnected.Subscribe(exn => { Console.WriteLine("C2 d/c " + exn); }); 27 | 28 | Measure(8192, 81920); 29 | Console.ReadLine(); 30 | Measure(8192, 81920 * 16); 31 | 32 | Console.ReadLine(); 33 | } 34 | 35 | private static void Measure(int bufSize, int packets) 36 | { 37 | var buffer = DataHelpers.CreateRandomBuffer(bufSize); 38 | long l = packets; 39 | long totalRecv = 0; 40 | var received = new ManualResetEventSlim(); 41 | 42 | GC.Collect(); 43 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 44 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 45 | GC.CollectionCount(2)); 46 | var sw = Stopwatch.StartNew(); 47 | 48 | Action> recv = bs => 49 | { 50 | totalRecv += bs.Count; 51 | if (totalRecv == l * bufSize) received.Set(); 52 | }; 53 | 54 | var recvHandle = c2.Received.Subscribe(recv); 55 | 56 | 57 | for (int i = 0; i < l; ++i) 58 | { 59 | c1.Send(buffer); 60 | } 61 | 62 | received.Wait(); 63 | 64 | recvHandle.Dispose(); 65 | 66 | var elapsed = sw.Elapsed.TotalSeconds; 67 | GC.Collect(); 68 | Console.WriteLine("Gen 0: " + GC.CollectionCount(0) + 69 | ", Gen 1: " + GC.CollectionCount(1) + ", Gen 2: " + 70 | GC.CollectionCount(2)); 71 | 72 | Console.WriteLine("Elapsed s: " + elapsed); 73 | Console.WriteLine("Rate: " + (double)totalRecv * 8 / elapsed / 1024 / 1024 + " Mb/sec"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamPerfTest/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("RawStreamPerfTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RawStreamPerfTest")] 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("9d0677a5-f933-4f46-b5b8-09f8764cb85a")] 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 | -------------------------------------------------------------------------------- /PerfTests/SimpleThroughput/RawStreamPerfTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SharedAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyCompany("Marcin Deptuła")] 6 | [assembly: AssemblyCopyright("Copyright © Marcin Deptuła 2014-2016")] 7 | [assembly: AssemblyTrademark("")] 8 | [assembly: AssemblyCulture("")] 9 | 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: AssemblyVersion("2.1.1")] 13 | [assembly: AssemblyFileVersion("2.1.1")] 14 | -------------------------------------------------------------------------------- /SharedAssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Stacks.FSharp 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | [] 8 | [] 9 | [] 10 | [] 11 | 12 | [] 13 | 14 | [] 15 | [] 16 | do() 17 | -------------------------------------------------------------------------------- /Stacks.Actors.DI.Windsor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Stacks.DI.Windsor")] 6 | [assembly: AssemblyDescription("Castle Windsor support for Stacks")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyProduct("Stacks.DI.Windsor")] 9 | 10 | 11 | [assembly: Guid("D27D7D04-A865-4D3E-B605-15AEE777E373")] 12 | -------------------------------------------------------------------------------- /Stacks.Actors.DI.Windsor/Stacks.Actors.DI.Windsor.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stacks.Actors.DI.Windsor 5 | Stacks Actors DI Windsor 6 | @build.number@ 7 | Marcin Deptuła 8 | Marcin Deptuła 9 | Castle Windsor DI support for Stacks 10 | https://raw.githubusercontent.com/Ravadre/Stacks/master/LICENSE 11 | https://github.com/Ravadre/Stacks 12 | false 13 | Castle Windsor DI support for Stacks 14 | Copyright Marcin Deptuła 15 | C# Socket Network Actor Reactive CastleWindsor 16 | @dependencies@ 17 | 18 | -------------------------------------------------------------------------------- /Stacks.Actors.DI.Windsor/WindsorDependencyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.Remoting.Messaging; 5 | using Castle.MicroKernel.ModelBuilder.Descriptors; 6 | using Castle.MicroKernel.Registration; 7 | using Castle.Windsor; 8 | 9 | namespace Stacks.Actors.DI.Windsor 10 | { 11 | public class WindsorDependencyResolver : IDependencyResolver 12 | { 13 | private readonly ActorSystem actorSystem; 14 | private readonly IWindsorContainer container; 15 | 16 | public WindsorDependencyResolver(ActorSystem actorSystem, IWindsorContainer container) 17 | { 18 | if (container == null) 19 | throw new ArgumentNullException(nameof(container)); 20 | 21 | this.actorSystem = actorSystem; 22 | this.container = container; 23 | } 24 | 25 | IDictionary GetArgs(IDictionary arguments) 26 | { 27 | if (arguments == null) 28 | return null; 29 | 30 | var args = arguments as IDictionary; 31 | 32 | if (args != null) 33 | { 34 | return args; 35 | } 36 | else 37 | { 38 | var dic = new Dictionary(arguments); 39 | return dic; 40 | } 41 | } 42 | 43 | public void Register() 44 | where I: class 45 | where TImpl: I 46 | { 47 | container.Register( 48 | Component.For().UsingFactoryMethod( 49 | (kernel, model, ctx) => 50 | { 51 | return actorSystem.CreateActor(() => kernel.Resolve("$Stacks$Internal$Registration$" + typeof(TImpl).FullName, ctx.AdditionalArguments), null, null); 52 | }), 53 | Component.For().ImplementedBy().Named("$Stacks$Internal$Registration$" + typeof(TImpl).FullName) 54 | ); 55 | } 56 | 57 | public void RegisterTransient() 58 | where I : class 59 | where TImpl : I 60 | { 61 | container.Register( 62 | Component.For().UsingFactoryMethod( 63 | (kernel, model, ctx) => 64 | { 65 | return actorSystem.CreateActor(() => kernel.Resolve("$Stacks$Internal$Registration$" + typeof(TImpl).FullName, ctx.AdditionalArguments), null, null); 66 | }).LifestyleTransient(), 67 | Component.For().ImplementedBy().Named("$Stacks$Internal$Registration$" + typeof(TImpl).FullName).LifestyleTransient() 68 | ); 69 | } 70 | 71 | public T Resolve(string resolverKey, IDictionary arguments) 72 | { 73 | if (resolverKey == null) 74 | { 75 | return container.Resolve(GetArgs(arguments)); 76 | } 77 | else 78 | { 79 | return container.Resolve(resolverKey, GetArgs(arguments)); 80 | } 81 | } 82 | 83 | public void Release(T actor) 84 | { 85 | container.Release(actor); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Stacks.Actors.DI.Windsor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests.DI.Windsor/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("Stacks.Tests.DI.Windsor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Stacks.Tests.DI.Windsor")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 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("f5ba1de4-d376-4fdd-8dab-d7c3a8673307")] 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 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests.DI.Windsor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests/ActorSystemTests/TestActors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reactive.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Stacks.Actors; 7 | 8 | namespace Stacks.Tests.ActorSystemTests 9 | { 10 | 11 | public interface ICalculatorActor : IActor 12 | { 13 | Task Div(double x, double y); 14 | } 15 | 16 | public interface ICalculatorExActor : IActor 17 | { 18 | Task Div(double x, double y); 19 | Task AddThenStop(double x, double y); 20 | Task Throw(string msg); 21 | Task Complicated(double x, double y); 22 | Task ComplicatedThenThrow(double x, double y); 23 | Task NoOp(); 24 | } 25 | 26 | public interface ISingleMethodActor 27 | { 28 | Task Test(); 29 | } 30 | 31 | public interface IExplicitInterfaceActor 32 | { 33 | Task Sum(double[] xs); 34 | IObservable Counter { get; } 35 | } 36 | 37 | public class ExplicitInterfaceActor : Actor, IExplicitInterfaceActor 38 | { 39 | IObservable IExplicitInterfaceActor.Counter 40 | { 41 | get { return Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(0.1)).Select(l => (double)l); } 42 | } 43 | 44 | async Task IExplicitInterfaceActor.Sum(double[] xs) 45 | { 46 | await Context; 47 | return xs.Sum(); 48 | } 49 | } 50 | 51 | public class SingleMethodActor : Actor, ISingleMethodActor 52 | { 53 | public async Task Test() 54 | { 55 | await Context; 56 | 57 | return 5; 58 | } 59 | } 60 | 61 | public class CalculatorActor : Actor, ICalculatorActor 62 | { 63 | public async Task Div(double x, double y) 64 | { 65 | await Context; 66 | 67 | await Task.Delay(50); 68 | 69 | if (y == 0.0) 70 | throw new DivideByZeroException(); 71 | 72 | return x / y; 73 | } 74 | } 75 | 76 | public class ActorWithExtraMethod : Actor, ICalculatorActor 77 | { 78 | public async Task Div(double x, double y) 79 | { 80 | await Context; 81 | return 5.0 + TestMethod(); 82 | } 83 | 84 | public int TestMethod() 85 | { 86 | return 10; 87 | } 88 | } 89 | 90 | public class LongStopActor : Actor, ICalculatorActor 91 | { 92 | public async Task Div(double x, double y) 93 | { 94 | await Context; 95 | return 5; 96 | } 97 | 98 | protected override void OnStopped() 99 | { 100 | Thread.Sleep(1000); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Stacks.Actors.Tests/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("Stacks.Actors.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Stacks.Actors.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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("d07f335b-433e-4fbd-81ae-b98baae7144d")] 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 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests/Remote/ActorSessionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks.Actors; 7 | using Xunit; 8 | 9 | namespace Stacks.Tests.Remote 10 | { 11 | public class ActorSessionTests 12 | { 13 | private IActorServerProxy server; 14 | private IMessageActor client; 15 | 16 | 17 | [Fact] 18 | public async void If_Enabled_IActorSession_should_get_accessible_from_ActorSession_Current() 19 | { 20 | var opts = new ActorServerProxyOptions(actorSessionInjectionEnabled: true); 21 | Utils.CreateServerAndClient(opts, out server, out client); 22 | 23 | var client2 = ActorClientProxy.CreateActor("tcp://localhost:" + server.BindEndPoint.Port).Result; 24 | 25 | await client.PassDataForContext(1); 26 | await client.PassDataForContext(1); 27 | await client2.PassDataForContext(2); 28 | await client.PassDataForContext(1); 29 | await client2.PassDataForContext(2); 30 | 31 | } 32 | 33 | [Fact] 34 | public void If_Enabled_StressTest_IActorSession_should_get_accessible_from_ActorSession_Current() 35 | { 36 | var opts = new ActorServerProxyOptions(actorSessionInjectionEnabled: true); 37 | IMessageActor[] clients = new IMessageActor[20]; 38 | 39 | Utils.CreateServerAndClient(opts, out server, out clients[0]); 40 | 41 | for (int i = 1; i < 20; ++i) 42 | clients[i] = ActorClientProxy.CreateActor("tcp://localhost:" + server.BindEndPoint.Port).Result; 43 | 44 | var tasks = new List(); 45 | 46 | for (int i = 0; i < 100; ++i) 47 | { 48 | int idx = i % 20; 49 | tasks.Add(clients[idx].StressTestSession(idx)); 50 | } 51 | 52 | Task.WaitAll(tasks.ToArray()); 53 | } 54 | 55 | [Fact] 56 | public async void If_Not_Enabled_ActorSession_Current_should_be_null() 57 | { 58 | Utils.CreateServerAndClient(out server, out client); 59 | 60 | await client.AssertActorSessionIsNull(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests/Remote/ConnectionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Stacks.Actors; 9 | using Xunit; 10 | 11 | namespace Stacks.Tests.Remote 12 | { 13 | public class ConnectionTests 14 | { 15 | private IActorServerProxy server; 16 | private ITestActor client; 17 | private IActorClientProxy clientProxy; 18 | 19 | [Fact] 20 | public void Client_should_be_able_to_connect_to_server() 21 | { 22 | Utils.CreateServerAndClient(out server, out client); 23 | } 24 | 25 | [Fact] 26 | public void Client_and_server_should_be_able_to_close_without_errors() 27 | { 28 | Utils.CreateServerAndClient(out server, out client); 29 | 30 | server.Stop(); 31 | // ReSharper disable once SuspiciousTypeConversion.Global 32 | ((IActorClientProxy)client).Close(); 33 | } 34 | 35 | [Fact] 36 | public void Client_should_return_task_which_fails_if_it_could_not_connect_to_server() 37 | { 38 | var clientTask = ActorClientProxy.CreateProxy("tcp://localhost:" + Utils.FindFreePort()); 39 | 40 | Assert.Throws(typeof(SocketException), () => 41 | { 42 | try 43 | { 44 | clientTask.Wait(); 45 | } catch (AggregateException exc) 46 | { 47 | throw exc.InnerException; 48 | } 49 | }); 50 | } 51 | 52 | [Fact] 53 | public void Client_should_signal_disconnection_when_server_is_closed() 54 | { 55 | var disconnected = new ManualResetEventSlim(); 56 | Utils.CreateServerAndClientProxy(out server, out clientProxy); 57 | 58 | clientProxy.Disconnected.Subscribe(exn => { disconnected.Set(); }); 59 | 60 | server.Stop(); 61 | 62 | disconnected.AssertWaitFor(); 63 | } 64 | } 65 | 66 | public interface ITestActor 67 | { 68 | Task Ping(); 69 | } 70 | 71 | public class TestActor : Actor, ITestActor 72 | { 73 | public async Task Ping() 74 | { 75 | await Context; 76 | } 77 | 78 | public void Close() 79 | { 80 | throw new NotImplementedException(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests/Remote/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Stacks.Actors; 9 | using Stacks.Actors.Remote; 10 | 11 | namespace Stacks.Tests.Remote 12 | { 13 | public static class Utils 14 | { 15 | public static void CreateServerAndClient(I impl, out IActorServerProxy server, out I client) 16 | { 17 | server = ActorServerProxy.Create("tcp://*:0", impl); 18 | int port = server.BindEndPoint.Port; 19 | 20 | client = ActorClientProxy.CreateActor("tcp://localhost:" + port).Result; 21 | } 22 | 23 | public static void CreateServerAndClient(out IActorServerProxy server, out I client) 24 | where T : Actor, I, new() 25 | { 26 | CreateServerAndClient(ActorServerProxyOptions.Default, out server, out client); 27 | } 28 | 29 | public static void CreateServerAndClient(ActorServerProxyOptions options, out IActorServerProxy server, out I client) 30 | where T: Actor, I, new() 31 | { 32 | server = ActorServerProxy.Create("tcp://*:0", options); 33 | int port = server.BindEndPoint.Port; 34 | 35 | client = ActorClientProxy.CreateActor("tcp://localhost:" + port).Result; 36 | } 37 | 38 | public static void CreateServerAndClient(ActorServerProxyOptions options, ActorClientProxyOptions cOptions, 39 | out IActorServerProxy server, out I client) 40 | where T : Actor, I, new() 41 | { 42 | server = ActorServerProxy.Create("tcp://*:0", options); 43 | int port = server.BindEndPoint.Port; 44 | 45 | client = ActorClientProxy.CreateActor("tcp://localhost:" + port, cOptions).Result; 46 | } 47 | 48 | 49 | public static void CreateServerAndClient(I impl, out IActorServerProxy server, out IActorClientProxy client) 50 | { 51 | server = ActorServerProxy.Create("tcp://*:0", impl); 52 | int port = server.BindEndPoint.Port; 53 | 54 | client = ActorClientProxy.CreateProxy("tcp://localhost:" + port).Result; 55 | } 56 | 57 | public static void CreateServerAndClientProxy(out IActorServerProxy server, out IActorClientProxy client) 58 | where T : Actor, I, new() 59 | { 60 | server = ActorServerProxy.Create("tcp://*:0"); 61 | int port = server.BindEndPoint.Port; 62 | 63 | client = ActorClientProxy.CreateProxy("tcp://localhost:" + port).Result; 64 | } 65 | 66 | public static int FindFreePort() 67 | { 68 | TcpListener l = new TcpListener(new IPEndPoint(IPAddress.Any, 0)); 69 | l.Start(); 70 | int port = ((IPEndPoint)l.LocalEndpoint).Port; 71 | l.Stop(); 72 | return port; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Stacks.Actors.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Stacks.Actors/ActorContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | using System.Reactive.Concurrency; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Stacks.Actors 8 | { 9 | internal class ActorContext : IActorContext 10 | { 11 | private readonly IExecutor executor; 12 | private string name; 13 | 14 | public ActorContext() 15 | : this(new ActionBlockExecutor(ActionBlockExecutorSettings.Default)) 16 | { 17 | } 18 | 19 | public ActorContext(ActorContextSettings settings) 20 | : this(new ActionBlockExecutor(ActionBlockExecutorSettings.DefaultWith(settings.SupportSynchronizationContext)) 21 | ) 22 | { 23 | } 24 | 25 | public ActorContext(IExecutor executor) 26 | { 27 | this.executor = executor; 28 | } 29 | 30 | public Task Completion => executor.Completion; 31 | 32 | public Task Stop() 33 | { 34 | return executor.Stop(); 35 | } 36 | 37 | public void Post(Action action) 38 | { 39 | executor.Enqueue(action); 40 | } 41 | 42 | public Task PostTask(Action action) 43 | { 44 | return executor.PostTask(action); 45 | } 46 | 47 | public Task PostTask(Func func) 48 | { 49 | return executor.PostTask(func); 50 | } 51 | 52 | public IActorContext GetAwaiter() 53 | { 54 | return this; 55 | } 56 | 57 | public bool IsCompleted => executor.Completion.IsCompleted; 58 | 59 | public void OnCompleted(Action continuation) 60 | { 61 | executor.Enqueue(continuation); 62 | } 63 | 64 | public void GetResult() 65 | { 66 | if (executor.Completion.IsCompleted) 67 | throw new ActorStoppedException("Actor context is stopped. New tasks cannot be queued. Further awaits will result in exception being thrown."); 68 | } 69 | 70 | public SynchronizationContext SynchronizationContext => executor.Context; 71 | DateTimeOffset IScheduler.Now => DateTimeOffset.UtcNow; 72 | 73 | public IDisposable Schedule(TState state, DateTimeOffset dueTime, 74 | Func action) 75 | => executor.Schedule(state, dueTime, action); 76 | 77 | public IDisposable Schedule(TState state, TimeSpan dueTime, 78 | Func action) 79 | => executor.Schedule(state, dueTime, action); 80 | 81 | public IDisposable Schedule(TState state, 82 | Func action) 83 | => executor.Schedule(state, action); 84 | 85 | internal void SetName(string newName) 86 | { 87 | name = newName; 88 | } 89 | 90 | public override string ToString() 91 | { 92 | return name == null 93 | ? "Dispatcher context" 94 | : $"Dispatcher context ({name})"; 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /Stacks.Actors/ActorContextSettings.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 Stacks.Actors 8 | { 9 | internal class ActorContextSettings 10 | { 11 | public bool SupportSynchronizationContext { get; set; } 12 | 13 | public ActorContextSettings() 14 | { 15 | SupportSynchronizationContext = true; 16 | } 17 | 18 | public static ActorContextSettings Default 19 | { 20 | get { return new ActorContextSettings(); } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Stacks.Actors/ActorCtorGuardian.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 Stacks.Actors 8 | { 9 | internal class ActorCtorGuardian 10 | { 11 | [ThreadStatic] 12 | private static ActorCtorGuardian current; 13 | 14 | public static void SetGuard() 15 | { 16 | current = new ActorCtorGuardian(); 17 | } 18 | 19 | public static bool IsGuarded() 20 | { 21 | return current != null; 22 | } 23 | 24 | public static void ClearGuard() 25 | { 26 | current = null; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Stacks.Actors/ActorSettings.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 Stacks.Actors 8 | { 9 | public class ActorSettings 10 | { 11 | public bool SupportSynchronizationContext { get; set; } 12 | 13 | public ActorSettings() 14 | { 15 | SupportSynchronizationContext = true; 16 | } 17 | 18 | public static ActorSettings Default 19 | { 20 | get { return new ActorSettings(); } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Stacks.Actors/Bit.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 Stacks.Actors 8 | { 9 | static class Bit 10 | { 11 | public static bool IsSet(int flags, int bit) 12 | { 13 | return (flags & bit) != 0; 14 | } 15 | 16 | public static int Set(int flags, int bit) 17 | { 18 | return flags | bit; 19 | } 20 | 21 | public static int Clear(int flags, int bit) 22 | { 23 | return flags & (~bit); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/ActorWrapperBase.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 Stacks.Actors.CodeGen 8 | { 9 | public class ActorWrapperBase : IActor 10 | { 11 | protected readonly Actor actorImplementation; 12 | public string Name => actorImplementation.Name; 13 | public string Path => actorImplementation.Path; 14 | public IActor Parent => actorImplementation.Parent; 15 | public IEnumerable Children => actorImplementation.Children; 16 | public IObservable ExceptionThrown => actorImplementation.ExceptionThrown; 17 | public Task Stop() => actorImplementation.Stop(); 18 | public bool Stopped => actorImplementation.Stopped; 19 | 20 | 21 | public ActorWrapperBase(Actor actorImplementation) 22 | { 23 | this.actorImplementation = actorImplementation; 24 | } 25 | 26 | protected void StopActorAndNotifySystem(string methodName, Exception exception) 27 | { 28 | // actorImplementation.StopBecauseOfError(methodName, exception); 29 | actorImplementation.OnExceptionThrown(exception); 30 | } 31 | 32 | protected Task HandleException(string methodName, Task task) 33 | { 34 | return task.ContinueWith(t => 35 | { 36 | if (t.Exception != null) 37 | { 38 | StopActorAndNotifySystem(methodName, t.Exception.InnerException); 39 | throw t.Exception.InnerException; 40 | } 41 | return t.Result; 42 | }); 43 | } 44 | 45 | protected Task HandleException(string methodName, Task task) 46 | { 47 | return task.ContinueWith(t => 48 | { 49 | if (t.Exception != null) 50 | { 51 | StopActorAndNotifySystem(methodName, t.Exception.InnerException); 52 | throw t.Exception.InnerException; 53 | } 54 | }); 55 | } 56 | 57 | internal Actor ActorImplementation => actorImplementation; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/FormattingExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks.Actors.CodeGen 9 | { 10 | public static class FormattingExtensions 11 | { 12 | public static string FormatDeclaration(this MethodInfo method) 13 | { 14 | return method.Name; 15 | } 16 | 17 | public static string FormatDeclaration(this PropertyInfo property) 18 | { 19 | return property.Name; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/IActorCompilerStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Stacks.Actors.CodeGen 10 | { 11 | public interface IActorCompilerStrategy 12 | { 13 | bool CanCompile(MethodInfoMapping method); 14 | bool CanCompile(PropertyInfoMapping property); 15 | void Implement(MethodInfoMapping method, Type actorInterface, TypeBuilder wrapperBuilder); 16 | void Implement(PropertyInfoMapping property, Type actorInterface, TypeBuilder wrapperBuilder); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/ObservableMethodCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Stacks.Actors.CodeGen 11 | { 12 | public class ObservableMethodCompiler : IActorCompilerStrategy 13 | { 14 | public bool CanCompile(MethodInfoMapping method) 15 | { 16 | var retType = method.InterfaceInfo.ReturnType; 17 | return retType.IsGenericType && retType.GetGenericTypeDefinition() == typeof(IObservable<>); 18 | } 19 | 20 | public bool CanCompile(PropertyInfoMapping property) 21 | { 22 | return false; 23 | } 24 | 25 | public void Implement(MethodInfoMapping method, Type actorInterface, TypeBuilder wrapperBuilder) 26 | { 27 | var mi = method.InterfaceInfo; 28 | 29 | var mBuilder = wrapperBuilder.DefineMethod(method.PublicName, 30 | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, 31 | CallingConventions.HasThis, mi.ReturnType, 32 | mi.GetParameters().Select(p => p.ParameterType).ToArray()); 33 | var mParams = mi.GetParameters(); 34 | 35 | for (var i = 1; i <= mParams.Length; ++i) 36 | { 37 | mBuilder.DefineParameter(i, ParameterAttributes.None, mParams[i - 1].Name); 38 | } 39 | 40 | var il = mBuilder.GetILGenerator(); 41 | // ((actorInterface)base.actorImplementation).method(params...) 42 | { 43 | il.Emit(OpCodes.Ldarg_0); 44 | il.Emit(OpCodes.Ldfld, 45 | typeof (ActorWrapperBase).GetField("actorImplementation", 46 | BindingFlags.Instance | BindingFlags.NonPublic)); 47 | il.Emit(OpCodes.Castclass, actorInterface); 48 | for (var i = 1; i <= mi.GetParameters().Length; ++i) 49 | { 50 | il.Emit(OpCodes.Ldarg, i); 51 | } 52 | il.EmitCall(OpCodes.Call, mi, null); 53 | } 54 | 55 | //il.EmitCall(OpCodes.Call); 56 | 57 | il.Emit(OpCodes.Ret); 58 | } 59 | 60 | public void Implement(PropertyInfoMapping property, Type actorInterface, TypeBuilder wrapperBuilder) 61 | { 62 | throw new NotSupportedException(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/ObservablePropertiesCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Stacks.Actors.CodeGen 10 | { 11 | public class ObservablePropertiesCompiler : IActorCompilerStrategy 12 | { 13 | public bool CanCompile(MethodInfoMapping method) 14 | { 15 | return false; 16 | } 17 | 18 | public bool CanCompile(PropertyInfoMapping property) 19 | { 20 | var propType = property.InterfaceInfo.PropertyType; 21 | return propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof (IObservable<>); 22 | } 23 | 24 | public void Implement(MethodInfoMapping method, Type actorInterface, TypeBuilder wrapperBuilder) 25 | { 26 | throw new NotSupportedException(); 27 | } 28 | 29 | public void Implement(PropertyInfoMapping property, Type actorInterface, TypeBuilder wrapperBuilder) 30 | { 31 | var prop = wrapperBuilder.DefineProperty(property.PublicName, PropertyAttributes.None, CallingConventions.HasThis, 32 | property.InterfaceInfo.PropertyType, null); 33 | 34 | var getMethod = wrapperBuilder.DefineMethod("get_" + property.PublicName, 35 | MethodAttributes.Public | 36 | MethodAttributes.HideBySig | 37 | MethodAttributes.SpecialName | 38 | MethodAttributes.Virtual, 39 | property.InterfaceInfo.PropertyType, Type.EmptyTypes); 40 | 41 | var il = getMethod.GetILGenerator(); 42 | 43 | il.Emit(OpCodes.Ldarg_0); 44 | il.Emit(OpCodes.Ldfld, typeof(ActorWrapperBase).GetField("actorImplementation", BindingFlags.Instance | BindingFlags.NonPublic)); 45 | il.Emit(OpCodes.Castclass, actorInterface); 46 | 47 | il.EmitCall(OpCodes.Callvirt, property.InterfaceInfo.GetGetMethod(true), null); 48 | il.Emit(OpCodes.Ret); 49 | 50 | prop.SetGetMethod(getMethod); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Stacks.Actors/CodeGen/TaskMethodsCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Stacks.Actors.CodeGen 10 | { 11 | public class TaskMethodsCompiler : IActorCompilerStrategy 12 | { 13 | public bool CanCompile(MethodInfoMapping method) 14 | { 15 | var retType = method.InterfaceInfo.ReturnType; 16 | return typeof (Task).IsAssignableFrom(retType) || 17 | typeof (Task<>).IsAssignableFrom(retType); 18 | } 19 | 20 | public bool CanCompile(PropertyInfoMapping property) 21 | { 22 | return false; 23 | } 24 | 25 | public void Implement(MethodInfoMapping method, Type actorInterface, TypeBuilder wrapperBuilder) 26 | { 27 | var mi = method.InterfaceInfo; 28 | 29 | var mBuilder = wrapperBuilder.DefineMethod(method.PublicName, 30 | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, 31 | CallingConventions.HasThis, mi.ReturnType, 32 | mi.GetParameters().Select(p => p.ParameterType).ToArray()); 33 | var mParams = mi.GetParameters(); 34 | 35 | for (var i = 1; i <= mParams.Length; ++i) 36 | { 37 | mBuilder.DefineParameter(i, ParameterAttributes.None, mParams[i - 1].Name); 38 | } 39 | 40 | 41 | var il = mBuilder.GetILGenerator(); 42 | 43 | il.Emit(OpCodes.Ldarg_0); 44 | il.Emit(OpCodes.Ldstr, method.PublicName); 45 | 46 | // ((actorInterface)base.actorImplementation).method(params...) 47 | { 48 | il.Emit(OpCodes.Ldarg_0); 49 | il.Emit(OpCodes.Ldfld, 50 | typeof (ActorWrapperBase).GetField("actorImplementation", 51 | BindingFlags.Instance | BindingFlags.NonPublic)); 52 | il.Emit(OpCodes.Castclass, actorInterface); 53 | for (var i = 1; i <= mi.GetParameters().Length; ++i) 54 | { 55 | il.Emit(OpCodes.Ldarg, i); 56 | } 57 | 58 | il.EmitCall(OpCodes.Call, mi, null); 59 | } 60 | 61 | il.EmitCall(OpCodes.Call, GetHandleExceptionMethod(mi.ReturnType), null); 62 | 63 | il.Emit(OpCodes.Ret); 64 | } 65 | 66 | private MethodInfo GetHandleExceptionMethod(Type returnType) 67 | { 68 | var g = returnType.IsGenericType; 69 | var method = typeof (ActorWrapperBase) 70 | .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic) 71 | .Where(m => m.Name == "HandleException") 72 | .First(m => m.GetGenericArguments().Length == (g ? 1 : 0)); 73 | return g ? method.MakeGenericMethod(returnType.GenericTypeArguments[0]) : method; 74 | } 75 | 76 | public void Implement(PropertyInfoMapping property, Type actorInterface, TypeBuilder wrapperBuilder) 77 | { 78 | throw new NotSupportedException(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Stacks.Actors/DI/Args.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 Stacks.Actors.DI 8 | { 9 | public class Args : Dictionary 10 | { 11 | public Args(params object[] args) 12 | { 13 | if (args == null) 14 | return; 15 | 16 | for (var i = 0; i < args.Length; ++i) 17 | { 18 | this[i.ToString()] = args[i]; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Stacks.Actors/DI/DependencyInjectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Stacks.Actors.DI 5 | { 6 | class DependencyInjectionHelper : IDependencyInjectionHelper 7 | { 8 | private readonly ActorSystem actorSystem; 9 | 10 | public DependencyInjectionHelper(ActorSystem actorSystem) 11 | { 12 | this.actorSystem = actorSystem; 13 | } 14 | 15 | private IDependencyResolver GetResolverOrFail() 16 | { 17 | var resolver = actorSystem.DependencyResolver; 18 | 19 | if (resolver == null) 20 | throw new Exception($"Cannot use dependency injection ({nameof(ActorSystem)}.{nameof(ActorSystem.DI)}) before setting " + 21 | $"dependency resolver through {nameof(ActorSystem.SetDependencyResolver)} method"); 22 | 23 | return resolver; 24 | } 25 | 26 | public T Resolve() 27 | { 28 | return Resolve(null, null); 29 | } 30 | 31 | public T Resolve(IDictionary args) 32 | { 33 | return Resolve(null, args); 34 | } 35 | 36 | public T Resolve(string resolveName, IDictionary args = null) 37 | { 38 | var resolver = GetResolverOrFail(); 39 | return resolver.Resolve(resolveName, args); 40 | } 41 | 42 | public void Release(T obj) 43 | { 44 | var resolver = GetResolverOrFail(); 45 | resolver.Release(obj); 46 | } 47 | 48 | public void Register() 49 | where I : class 50 | where TImpl : Actor, I 51 | { 52 | var resolver = GetResolverOrFail(); 53 | resolver.Register(); 54 | } 55 | 56 | public void RegisterTransient() 57 | where I : class 58 | where TImpl : Actor, I 59 | { 60 | var resolver = GetResolverOrFail(); 61 | resolver.RegisterTransient(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Stacks.Actors/DI/IDependencyInjectionHelper.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 Stacks.Actors.DI 8 | { 9 | public interface IDependencyInjectionHelper 10 | { 11 | T Resolve(); 12 | T Resolve(IDictionary args); 13 | T Resolve(string resolveName, IDictionary args = null); 14 | void Release(T obj); 15 | void Register() 16 | where I : class 17 | where TImpl : Actor, I; 18 | 19 | void RegisterTransient() 20 | where I : class 21 | where TImpl : Actor, I; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Stacks.Actors/DI/IDependencyResolver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Stacks.Actors.DI 4 | { 5 | public interface IDependencyResolver 6 | { 7 | void Register() 8 | where I : class 9 | where TImpl : I; 10 | 11 | void RegisterTransient() 12 | where I : class 13 | where TImpl : I; 14 | 15 | T Resolve(string resolverKey, IDictionary arguments); 16 | void Release(T actor); 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /Stacks.Actors/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks.Actors 9 | { 10 | [Serializable] 11 | public class ActorStoppedException : Exception 12 | { 13 | public ActorStoppedException() 14 | { 15 | } 16 | 17 | public ActorStoppedException(string message) : base(message) 18 | { 19 | } 20 | 21 | public ActorStoppedException(string message, Exception inner) : base(message, inner) 22 | { 23 | } 24 | 25 | protected ActorStoppedException( 26 | SerializationInfo info, 27 | StreamingContext context) : base(info, context) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Stacks.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 Stacks.Actors 8 | { 9 | public interface IActor 10 | { 11 | bool Stopped { get; } 12 | string Name { get; } 13 | string Path { get; } 14 | IActor Parent { get; } 15 | IEnumerable Children { get; } 16 | 17 | Task Stop(); 18 | 19 | IObservable ExceptionThrown { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Stacks.Actors/IActorContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive.Concurrency; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Stacks.Actors 11 | { 12 | public interface IActorContext : INotifyCompletion, IScheduler 13 | { 14 | bool IsCompleted { get; } 15 | 16 | void Post(Action action); 17 | Task PostTask(Action action); 18 | Task PostTask(Func func); 19 | 20 | IActorContext GetAwaiter(); 21 | void GetResult(); 22 | 23 | Task Stop(); 24 | 25 | SynchronizationContext SynchronizationContext { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Stacks.Actors/NoExceptionHandlerAttribute.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 Stacks.Actors 8 | { 9 | public class NoExceptionHandlerAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Stacks.Actors/PathUtils.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 Stacks.Actors 8 | { 9 | static class PathUtils 10 | { 11 | public static string FixQueryPath(string path) 12 | { 13 | if (path == null) 14 | return null; 15 | if (path.Length == 0) 16 | return path; 17 | 18 | var pb = new StringBuilder(path); 19 | 20 | if (pb[0] != '/') 21 | pb.Insert(0, '/'); 22 | if (!pb.ToString().StartsWith("/root")) 23 | pb.Insert(0, "/root"); 24 | if (pb[pb.Length - 1] != '/') 25 | pb.Append('/'); 26 | return pb.ToString(); 27 | } 28 | 29 | 30 | public static void AssertNameForInvalidCharacters(string name) 31 | { 32 | if (name == null) 33 | return; 34 | 35 | if (name.Length == 0) 36 | throw new Exception("Name can't be empty whitespace"); 37 | 38 | var invalidChars = new[] { '$', ' ', '\t', '/', '\\' }; 39 | 40 | foreach (var ch in invalidChars.Where(ch => name.IndexOf(ch) != -1)) 41 | { 42 | throw new Exception("Actor name cannot contain symbol '" + ch + "'"); 43 | } 44 | } 45 | 46 | public static string GetActorPath(IActor parent, string name) 47 | { 48 | if (parent == null) 49 | return "/" + name + "/"; 50 | return parent.Path + name + "/"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Stacks.Actors/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Stacks Actors")] 5 | [assembly: AssemblyDescription("Stacks actor and network library")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyProduct("Stacks Actors")] 8 | 9 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ActorClientProxyOptions.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 Stacks.Actors.Remote 8 | { 9 | public class ActorClientProxyOptions 10 | { 11 | public Func SerializerProvider { get; private set; } 12 | 13 | public ActorClientProxyOptions(Func serializerProvider = null) 14 | { 15 | SerializerProvider = serializerProvider; 16 | } 17 | 18 | 19 | public static readonly ActorClientProxyOptions Default = 20 | new ActorClientProxyOptions(serializerProvider: null); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ActorPacketSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Stacks.Actors.Proto; 8 | 9 | namespace Stacks.Actors.Remote 10 | { 11 | public class ActorPacketSerializer 12 | { 13 | private readonly IStacksSerializer defaultSerializer; 14 | 15 | public ActorPacketSerializer(IStacksSerializer defaultSerializer) 16 | { 17 | this.defaultSerializer = defaultSerializer; 18 | } 19 | 20 | public virtual void Serialize(ActorProtocolFlags packetFlags, string requestName, T packet, MemoryStream ms) 21 | { 22 | defaultSerializer.Serialize(packet, ms); 23 | } 24 | 25 | public virtual T Deserialize(ActorProtocolFlags packetFlags, string requestName, MemoryStream ms) 26 | { 27 | return defaultSerializer.Deserialize(ms); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ActorProtocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ProtoBuf; 7 | 8 | namespace Stacks.Actors.Proto 9 | { 10 | [Flags] 11 | public enum ActorProtocolFlags : int 12 | { 13 | RequestReponse = 0x01, 14 | Observable = 0x02, 15 | StacksProtocol = 0x04, 16 | } 17 | 18 | public static class ActorProtocol 19 | { 20 | public static readonly int Version = 1; 21 | 22 | public static readonly int HandshakeId = 1; 23 | public static readonly int PingId = 2; 24 | } 25 | 26 | [ProtoContract] 27 | public class HandshakeRequest 28 | { 29 | [ProtoMember(1)] 30 | public int ClientProtocolVersion { get; set; } 31 | } 32 | 33 | [ProtoContract] 34 | public class HandshakeResponse 35 | { 36 | [ProtoMember(1)] 37 | public int RequestedProtocolVersion { get; set; } 38 | [ProtoMember(2)] 39 | public int ServerProtocolVersion { get; set; } 40 | [ProtoMember(3)] 41 | public bool ProtocolMatch { get; set; } 42 | } 43 | 44 | [ProtoContract] 45 | public class Ping 46 | { 47 | [ProtoMember(1)] 48 | public long Timestamp { get; set; } 49 | } 50 | } 51 | 52 | namespace Stacks.Actors 53 | { 54 | [Serializable] 55 | public class InvalidProtocolException : Exception 56 | { 57 | public InvalidProtocolException() { } 58 | public InvalidProtocolException(string message) : base(message) { } 59 | public InvalidProtocolException(string message, Exception inner) : base(message, inner) { } 60 | protected InvalidProtocolException( 61 | System.Runtime.Serialization.SerializationInfo info, 62 | System.Runtime.Serialization.StreamingContext context) 63 | : base(info, context) { } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ActorServerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using Stacks.Actors.Remote.CodeGen; 5 | 6 | // ReSharper disable InconsistentNaming 7 | 8 | namespace Stacks.Actors 9 | { 10 | public class ActorServerProxy 11 | { 12 | private static ServerActorTypeBuilder tBuilder; 13 | 14 | public static IActorServerProxy Create(IPEndPoint bindEndPoint) 15 | where T : Actor, I, new() 16 | { 17 | return Create(ActorSystem.Default.CreateActor(), bindEndPoint, 18 | ActorServerProxyOptions.Default); 19 | } 20 | 21 | public static IActorServerProxy Create(string bindEndPoint) 22 | where T : Actor, I, new() 23 | { 24 | return Create(ActorSystem.Default.CreateActor(), IPHelpers.Parse(bindEndPoint).Result, 25 | ActorServerProxyOptions.Default); 26 | } 27 | 28 | public static IActorServerProxy Create(IPEndPoint bindEndPoint, ActorServerProxyOptions options) 29 | where T : Actor, I, new() 30 | { 31 | return Create(ActorSystem.Default.CreateActor(), bindEndPoint, options); 32 | } 33 | 34 | public static IActorServerProxy Create(string bindEndPoint, ActorServerProxyOptions options) 35 | where T : Actor, I, new() 36 | { 37 | return Create(ActorSystem.Default.CreateActor(), IPHelpers.Parse(bindEndPoint).Result, options); 38 | } 39 | 40 | public static IActorServerProxy Create(IPEndPoint bindEndPoint, I actorImpl) 41 | { 42 | return Create(actorImpl, bindEndPoint, ActorServerProxyOptions.Default); 43 | } 44 | 45 | public static IActorServerProxy Create(string bindEndPoint, I actorImpl) 46 | { 47 | return Create(actorImpl, IPHelpers.Parse(bindEndPoint).Result, ActorServerProxyOptions.Default); 48 | } 49 | 50 | public static IActorServerProxy Create(IPEndPoint bindEndPoint, I actorImpl, ActorServerProxyOptions options) 51 | { 52 | return Create(actorImpl, bindEndPoint, options); 53 | } 54 | 55 | public static IActorServerProxy Create(string bindEndPoint, I actorImpl, ActorServerProxyOptions options) 56 | { 57 | return Create(actorImpl, IPHelpers.Parse(bindEndPoint).Result, options); 58 | } 59 | 60 | private static IActorServerProxy Create(I actorImplementation, IPEndPoint bindEndPoint, 61 | ActorServerProxyOptions options) 62 | { 63 | var aType = actorImplementation.GetType(); 64 | tBuilder = new ServerActorTypeBuilder("ActorServerProxy_ " + aType.FullName); 65 | 66 | tBuilder.DefineMessagesFromInterfaceType(aType); 67 | 68 | var actorImplType = tBuilder.CreateActorType(aType); 69 | tBuilder.SaveToFile(); 70 | 71 | var actor = Activator.CreateInstance(actorImplType, actorImplementation, bindEndPoint, options); 72 | return (IActorServerProxy)actor; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ActorServerProxyOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks.Actors.Remote; 7 | 8 | namespace Stacks.Actors 9 | { 10 | public class ActorServerProxyOptions 11 | { 12 | public bool ActorSessionInjectionEnabled { get; private set; } 13 | public Func SerializerProvider { get; private set; } 14 | 15 | public ActorServerProxyOptions(bool actorSessionInjectionEnabled, Func serializerProvider = null) 16 | { 17 | SerializerProvider = serializerProvider; 18 | ActorSessionInjectionEnabled = actorSessionInjectionEnabled; 19 | } 20 | 21 | public static readonly ActorServerProxyOptions Default = 22 | new ActorServerProxyOptions(actorSessionInjectionEnabled: false, 23 | serializerProvider: null); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/ClientActorDisconnectedData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stacks.Actors 4 | { 5 | public class ClientActorDisconnectedData 6 | { 7 | public ClientActorDisconnectedData(IActorSession session, Exception error) 8 | { 9 | Session = session; 10 | Error = error; 11 | } 12 | 13 | public IActorSession Session { get; private set; } 14 | public Exception Error { get; private set; } 15 | } 16 | } -------------------------------------------------------------------------------- /Stacks.Actors/Remote/IActorClientProxy.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 Stacks.Actors 8 | { 9 | public interface IActorClientProxy : IActorClientProxy 10 | { 11 | T Actor { get; } 12 | } 13 | 14 | public interface IActorClientProxy 15 | { 16 | void Close(); 17 | IObservable Disconnected { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/IActorServerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks.Actors 9 | { 10 | public interface IActorServerProxy 11 | { 12 | IPEndPoint BindEndPoint { get; } 13 | 14 | IObservable ActorClientConnected { get; } 15 | IObservable ActorClientDisconnected { get; } 16 | 17 | Task GetCurrentClientSessions(); 18 | 19 | void Stop(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Stacks.Actors/Remote/IActorSession.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Remoting.Messaging; 2 | 3 | namespace Stacks.Actors 4 | { 5 | public interface IActorSession 6 | { 7 | IFramedClient Client { get; } 8 | void Close(); 9 | } 10 | 11 | public class ActorSession : IActorSession 12 | { 13 | public ActorSession(IFramedClient client) 14 | { 15 | Client = client; 16 | } 17 | 18 | public static IActorSession Current => CallContext.LogicalGetData(ActorSessionCallContextKey) as IActorSession; 19 | 20 | internal static readonly string ActorSessionCallContextKey = "__stacks.actor.session0xc0de"; 21 | 22 | public IFramedClient Client { get; } 23 | 24 | public void Close() 25 | { 26 | Client.Close(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Stacks.Actors/Remote/IReplyMessage.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 Stacks.Actors 8 | { 9 | public interface IReplyMessage 10 | { 11 | T GetResult(); 12 | void SetResult(T result); 13 | void SetError(string error); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Stacks.Actors/RootActor.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 Stacks.Actors 8 | { 9 | public class RootActor : Actor, IRootActor 10 | { 11 | public RootActor() 12 | { 13 | 14 | } 15 | } 16 | 17 | public interface IRootActor : IActor 18 | { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Stacks.Actors/Stacks.Actors.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stacks.Actors 5 | Stacks Actors 6 | @build.number@ 7 | Marcin Deptuła 8 | Marcin Deptuła 9 | Simple to use, async/await aware actor library 10 | https://raw.githubusercontent.com/Ravadre/Stacks/master/LICENSE 11 | https://github.com/Ravadre/Stacks 12 | false 13 | Stacks Actors- reactive actor/network library 14 | Copyright Marcin Deptuła 15 | C# Socket Network Actor Reactive 16 | @dependencies@ 17 | 18 | -------------------------------------------------------------------------------- /Stacks.Actors/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Stacks.FSharp/Actors/ActorContext.fs: -------------------------------------------------------------------------------- 1 | namespace Stacks.Actors 2 | 3 | 4 | [] 5 | module Actors = 6 | type Stacks.Actors.IActorContext with 7 | 8 | /// 9 | /// Creates a wrapper async expression which will execute 10 | /// given one on executor's context. Returned expression 11 | /// is not started automatically, however, it will be rescheduled 12 | /// almost immediately after start, so Async.RunSynchronously or 13 | /// Async.StartImmediate are recommended to start it. 14 | /// 15 | member this.MakeAsync<'T>(wf: Async<'T>) = async { 16 | do! Async.SwitchToContext(this.SynchronizationContext) 17 | 18 | return! wf 19 | } 20 | -------------------------------------------------------------------------------- /Stacks.FSharp/Actors/Executor.fs: -------------------------------------------------------------------------------- 1 | namespace Stacks 2 | 3 | open Stacks 4 | 5 | [] 6 | module Executors = 7 | 8 | type IExecutor with 9 | /// 10 | /// Creates a wrapper async expression which will execute 11 | /// given one on executor's context. Returned expression 12 | /// is not started automatically, however, it will be rescheduled 13 | /// almost immediately after start, so Async.RunSynchronously or 14 | /// Async.StartImmediate are recommended to start it. 15 | /// 16 | member this.MakeAsync<'a>(wf: Async<'a>) = async { 17 | do! Async.SwitchToContext(this.Context) 18 | return! wf 19 | } 20 | 21 | /// 22 | /// Runs given code on executor's context immediately. Result can be awaited with 23 | /// Async.RunSynchronously 24 | /// 25 | member this.RunAsync<'a>(code: unit -> 'a) = 26 | this.PostTask(code) 27 | |> Async.AwaitTask 28 | 29 | /// 30 | /// Runs given code on executor's context immediately. 31 | /// Async.RunSynchronously can be used to await completion of code. 32 | /// 33 | member this.RunAsync(code: unit -> unit) = 34 | this.PostTask(code) 35 | |> Async.AwaitTask 36 | -------------------------------------------------------------------------------- /Stacks.FSharp/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Stacks 2 | 3 | open System.Reflection 4 | 5 | [] 6 | [] 7 | [] 8 | [] 9 | do() 10 | 11 | 12 | -------------------------------------------------------------------------------- /Stacks.FSharp/Stacks.FSharp.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stacks.FSharp 5 | Stacks FSharp 6 | @build.number@ 7 | Marcin Deptuła 8 | Marcin Deptuła 9 | Simple to use actor/network library 10 | https://raw.githubusercontent.com/Ravadre/Stacks/master/LICENSE 11 | https://github.com/Ravadre/Stacks 12 | false 13 | F# wrapper for Stacks - reactive actor/network library 14 | Copyright Marcin Deptuła 15 | F# Socket Network Actor Reactive 16 | @dependencies@ 17 | 18 | -------------------------------------------------------------------------------- /Stacks.FSharp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Stacks.MessagePack/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Stacks.MessagePack")] 6 | [assembly: AssemblyDescription("MessagePack serializer for Stacks")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyProduct("Stacks")] 9 | 10 | [assembly: Guid("A5D6D241-A760-4FB2-BCCA-CDA387A0CB94")] 11 | -------------------------------------------------------------------------------- /Stacks.MessagePack/Serializers/MessagePackStacksSerializer.cs: -------------------------------------------------------------------------------- 1 | using MsgPack; 2 | using MsgPack.Serialization; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Stacks 12 | { 13 | public class MessagePackStacksSerializer : IStacksSerializer 14 | { 15 | private SerializationContext context; 16 | 17 | public MessagePackStacksSerializer() 18 | { 19 | 20 | } 21 | 22 | public void Initialize() 23 | { 24 | this.context = new SerializationContext(); 25 | } 26 | 27 | public T Deserialize(MemoryStream ms) 28 | { 29 | var d = MessagePackSerializer.Get(this.context); 30 | return d.Unpack(ms); 31 | } 32 | 33 | public void Serialize(T obj, MemoryStream ms) 34 | { 35 | var s = MessagePackSerializer.Get(this.context); 36 | s.Pack(ms, obj); 37 | } 38 | 39 | public void PrepareSerializerForType() 40 | { 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Stacks.MessagePack/Stacks.MessagePack.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stacks.MessagePack 5 | Stacks - MessagePack 6 | @build.number@ 7 | Marcin Deptuła 8 | Marcin Deptuła 9 | Stacks serialization extension which uses MessagePack 10 | https://raw.githubusercontent.com/Ravadre/Stacks/master/LICENSE 11 | https://github.com/Ravadre/Stacks 12 | false 13 | Stacks - reactive actor/network library 14 | Copyright Marcin Deptuła 15 | C# Socket Network Actor MessagePack Reactive 16 | @dependencies@ 17 | 18 | -------------------------------------------------------------------------------- /Stacks.MessagePack/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Stacks.Tests/Client/SslClientTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stacks.Tcp; 7 | using Xunit; 8 | 9 | namespace Stacks.Tests.Client 10 | { 11 | public class SslClientTests 12 | { 13 | private SocketServer server; 14 | private SslClient lClient, sClient; 15 | 16 | public SslClientTests() 17 | { 18 | } 19 | 20 | public void Cleanup() 21 | { 22 | server.StopAndAssertStopped(); 23 | lClient.Close(); 24 | sClient.Close(); 25 | } 26 | 27 | private byte[] PrepareBuffer(int size) 28 | { 29 | Random rng = new Random(); 30 | var buffer = new byte[size]; 31 | rng.NextBytes(buffer); 32 | 33 | return buffer; 34 | } 35 | 36 | [Fact] 37 | public void Ssl_should_establish_connection() 38 | { 39 | SslHelpers.CreateServerAndConnectedClient(out server, out lClient, out sClient); 40 | 41 | Assert.True(lClient.IsConnected); 42 | Assert.True(sClient.IsConnected); 43 | 44 | Cleanup(); 45 | } 46 | 47 | [Fact] 48 | public void Data_transfered_through_sockets_should_be_transfered_correctly() 49 | { 50 | SslHelpers.CreateServerAndConnectedClient(out server, out lClient, out sClient); 51 | 52 | var buffer = PrepareBuffer(20); 53 | 54 | var recvBuffer = lClient.ReceiveData(buffer.Length, 2000, () => 55 | { 56 | sClient.Send(buffer); 57 | }); 58 | var recvBuffer2 = sClient.ReceiveData(buffer.Length, 2000, () => 59 | { 60 | lClient.Send(buffer); 61 | }); 62 | 63 | Assert.Equal(buffer, recvBuffer); 64 | Assert.Equal(buffer, recvBuffer2); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Stacks.Tests/DataHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks.Tests 9 | { 10 | public class DataHelpers 11 | { 12 | public static byte[] CreateRandomBuffer(int size) 13 | { 14 | var rng = RandomNumberGenerator.Create(); 15 | var buffer = new byte[size]; 16 | rng.GetBytes(buffer); 17 | 18 | return buffer; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Stacks.Tests/ExecutorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Xunit; 4 | 5 | namespace Stacks.Tests 6 | { 7 | public class ExecutorTests 8 | { 9 | [Theory] 10 | [InlineData(typeof (ActionBlockExecutor))] 11 | public void PostTask_should_signal_task_when_action_is_completed(Type execType) 12 | { 13 | var exec = Activator.CreateInstance(execType) as IExecutor; 14 | 15 | var c = 0; 16 | 17 | var task = exec.PostTask(() => { Interlocked.Increment(ref c); }); 18 | 19 | task.Wait(); 20 | Assert.Equal(1, c); 21 | } 22 | 23 | [Theory] 24 | [InlineData(typeof (ActionBlockExecutor))] 25 | public void PostTask_should_signal_return_value_through_task(Type execType) 26 | { 27 | var exec = Activator.CreateInstance(execType) as IExecutor; 28 | 29 | var task = exec.PostTask(() => { return 5; }); 30 | 31 | Assert.Equal(5, task.Result); 32 | } 33 | 34 | [Theory] 35 | [InlineData(typeof (ActionBlockExecutor))] 36 | public void PostTask_should_rethrow_exception(Type execType) 37 | { 38 | var exec = Activator.CreateInstance(execType) as IExecutor; 39 | 40 | var task = exec.PostTask(() => { throw new Exception("test"); }); 41 | 42 | Assert.Throws(typeof (AggregateException), () => { var r = task.Result; }); 43 | } 44 | 45 | [Theory] 46 | [InlineData(typeof (ActionBlockExecutor))] 47 | public void Executor_should_not_be_stopped_after_posttask_throws_exception(Type execType) 48 | { 49 | var errorOccured = new ManualResetEventSlim(); 50 | var execIsRunning = new ManualResetEventSlim(); 51 | 52 | var exec = Activator.CreateInstance(execType) as IExecutor; 53 | 54 | exec.Error += exception => { errorOccured.Set(); }; 55 | 56 | var task = exec.PostTask(() => { throw new Exception("test"); }); 57 | 58 | Assert.Throws(typeof (AggregateException), () => { task.Wait(); }); 59 | 60 | 61 | exec.PostTask(() => { execIsRunning.Set(); }).Wait(); 62 | 63 | Assert.False(errorOccured.IsSet); 64 | Assert.True(execIsRunning.IsSet); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Stacks.Tests/IPHelpersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using Stacks; 8 | using System.Net; 9 | 10 | namespace Stacks.Tests 11 | { 12 | public class IPHelpersTests 13 | { 14 | [Fact] 15 | public void Localhost_should_return_loopback_address() 16 | { 17 | var ep = IPHelpers.Parse("tcp://localhost:1234").Result; 18 | 19 | Assert.Equal(IPAddress.Loopback, ep.Address); 20 | Assert.Equal(1234, ep.Port); 21 | } 22 | 23 | [Fact] 24 | public void IPv6_Localhost_should_return_loopback_address() 25 | { 26 | var ep = IPHelpers.Parse("tcp6://localhost:1234").Result; 27 | 28 | Assert.Equal(IPAddress.IPv6Loopback, ep.Address); 29 | Assert.Equal(1234, ep.Port); 30 | } 31 | 32 | [Fact] 33 | public void Numeric_ip_should_be_parsed_properly() 34 | { 35 | var ep = IPHelpers.Parse("tcp://10.43.12.43:1234").Result; 36 | 37 | Assert.Equal(IPAddress.Parse("10.43.12.43"), ep.Address); 38 | Assert.Equal(1234, ep.Port); 39 | } 40 | 41 | [Fact] 42 | public void Asterisk_should_be_parsed_as_any() 43 | { 44 | var ep = IPHelpers.Parse("tcp://*:1234").Result; 45 | 46 | Assert.Equal(IPAddress.Any, ep.Address); 47 | Assert.Equal(1234, ep.Port); 48 | } 49 | 50 | [Fact] 51 | public void Plus_should_be_parsed_as_any() 52 | { 53 | var ep = IPHelpers.Parse("tcp://+:1234").Result; 54 | 55 | Assert.Equal(IPAddress.Any, ep.Address); 56 | Assert.Equal(1234, ep.Port); 57 | } 58 | 59 | [Fact] 60 | public void IPv6_Asterisk_should_be_parsed_as_any() 61 | { 62 | var ep = IPHelpers.Parse("tcp6://*:1234").Result; 63 | 64 | Assert.Equal(IPAddress.IPv6Any, ep.Address); 65 | Assert.Equal(1234, ep.Port); 66 | } 67 | 68 | [Fact] 69 | public void IPv6_Plus_should_be_parsed_as_any() 70 | { 71 | var ep = IPHelpers.Parse("tcp6://+:1234").Result; 72 | 73 | Assert.Equal(IPAddress.IPv6Any, ep.Address); 74 | Assert.Equal(1234, ep.Port); 75 | } 76 | 77 | [Fact] 78 | public async Task IP_should_resolve_from_host() 79 | { 80 | var ep = IPHelpers.Parse("tcp://google.com:1234").Result; 81 | 82 | Assert.Equal(1234, ep.Port); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Stacks.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Stacks.Tests")] 6 | [assembly: AssemblyDescription("Tests for Stacks")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyProduct("Stacks")] 9 | 10 | [assembly: Guid("e75bf177-2b84-47cb-aa93-64226e6c081d")] 11 | -------------------------------------------------------------------------------- /Stacks.Tests/Serialization/MessagePackSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Moq; 8 | using Xunit; 9 | 10 | using MsgPack; 11 | using MsgPack.Serialization; 12 | using System.IO; 13 | using System.ComponentModel; 14 | using System.Threading; 15 | 16 | namespace Stacks.Tests.Serialization 17 | { 18 | public class MessagePackSerializerTests 19 | { 20 | protected MemoryStream CreateSerializedObject(T sample) 21 | { 22 | var serializer = MessagePackSerializer.Get(); 23 | var ms = new MemoryStream(); 24 | 25 | serializer.Pack(ms, sample); 26 | 27 | ms.Position = 0; 28 | 29 | return ms; 30 | } 31 | 32 | protected TestData CreateSampleTestData() 33 | { 34 | return new TestData { Bar = Math.PI , Zar = (decimal)Math.PI, Foo = 42, Sar = "Foo bar test" }; 35 | } 36 | 37 | public class Deserialize_should : MessagePackSerializerTests 38 | { 39 | [Fact] 40 | public void Deserialize_object_properly() 41 | { 42 | var test = CreateSampleTestData(); 43 | var obj = CreateSerializedObject(test); 44 | 45 | Console.WriteLine(new ArraySegment(obj.GetBuffer(), 0, (int)obj.Length).ToBinaryString()); 46 | 47 | var serializer = new MessagePackStacksSerializer(); 48 | serializer.Initialize(); 49 | var recv = serializer.Deserialize(obj); 50 | 51 | Assert.Equal(test.Foo, recv.Foo); 52 | Assert.Equal(test.Bar, recv.Bar); 53 | Assert.Equal(test.Sar, recv.Sar); 54 | Assert.Equal(test.Zar, recv.Zar); 55 | } 56 | } 57 | 58 | public class Serialize_should : MessagePackSerializerTests 59 | { 60 | [Fact] 61 | public void Serialize_data_so_it_can_be_later_deserialized_with_Deserialize() 62 | { 63 | var test = CreateSampleTestData(); 64 | 65 | var serializer = new MessagePackStacksSerializer(); 66 | serializer.Initialize(); 67 | var ms = new MemoryStream(); 68 | 69 | serializer.Serialize(test, ms); 70 | ms.Position = 0; 71 | var data = serializer.Deserialize(ms); 72 | 73 | Assert.Equal(test.Bar, data.Bar); 74 | Assert.Equal(test.Foo, data.Foo); 75 | Assert.Equal(test.Sar, data.Sar); 76 | Assert.Equal(test.Zar, data.Zar); 77 | } 78 | } 79 | 80 | 81 | 82 | public class TestData 83 | { 84 | public int Foo { get; set; } 85 | public double Bar { get; set; } 86 | public decimal Zar { get; set; } 87 | public string Sar { get; set; } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Stacks.Tests/Serialization/ProtoBufSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Moq; 8 | using ProtoBuf; 9 | using Xunit; 10 | 11 | 12 | //TODO: Refactor those and messagepack tests to share some code base 13 | namespace Stacks.Tests.Serialization 14 | { 15 | public class ProtoBufSerializerTests 16 | { 17 | protected MemoryStream CreateSerializedObject(T sample) 18 | { 19 | var ms = new MemoryStream(); 20 | Serializer.Serialize(ms, sample); 21 | ms.Position = 0; 22 | 23 | return ms; 24 | } 25 | 26 | protected TestData CreateSampleTestData() 27 | { 28 | return new TestData { Bar = Math.PI , Zar = (decimal)Math.PI, Foo = 42, Sar = "Foo bar test" }; 29 | } 30 | 31 | public class Deserialize_should : ProtoBufSerializerTests 32 | { 33 | [Fact] 34 | public void Deserialize_object_properly() 35 | { 36 | var test = CreateSampleTestData(); 37 | var obj = CreateSerializedObject(test); 38 | 39 | Console.WriteLine(new ArraySegment(obj.GetBuffer(), 0, (int)obj.Length).ToBinaryString()); 40 | 41 | var serializer = new ProtoBufStacksSerializer(); 42 | var data = serializer.Deserialize(obj); 43 | 44 | Assert.Equal(test.Bar, data.Bar); 45 | Assert.Equal(test.Foo, data.Foo); 46 | Assert.Equal(test.Sar, data.Sar); 47 | Assert.Equal(test.Zar, data.Zar); 48 | } 49 | } 50 | 51 | 52 | public class Serialize_should : ProtoBufSerializerTests 53 | { 54 | [Fact] 55 | public void Serialize_data_so_it_can_be_later_deserialized_with_Deserialize() 56 | { 57 | var test = CreateSampleTestData(); 58 | 59 | var serializer = new ProtoBufStacksSerializer(); 60 | var ms = new MemoryStream(); 61 | 62 | serializer.Serialize(test, ms); 63 | ms.Position = 0; 64 | var data = serializer.Deserialize(ms); 65 | 66 | Assert.Equal(test.Bar, data.Bar); 67 | Assert.Equal(test.Foo, data.Foo); 68 | Assert.Equal(test.Sar, data.Sar); 69 | Assert.Equal(test.Zar, data.Zar); 70 | } 71 | } 72 | 73 | 74 | [ProtoContract] 75 | public class TestData 76 | { 77 | [ProtoMember(1)] 78 | public int Foo { get; set; } 79 | [ProtoMember(2)] 80 | public double Bar { get; set; } 81 | [ProtoMember(3)] 82 | public decimal Zar { get; set; } 83 | [ProtoMember(4)] 84 | public string Sar { get; set; } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Stacks.Tests/SocketServerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace Stacks.Tests 12 | { 13 | public class SocketServerTests 14 | { 15 | 16 | public class Starting_and_stopping : SocketServerTests 17 | { 18 | [Fact] 19 | public void Starting_two_times_should_throw() 20 | { 21 | var server = ServerHelpers.CreateServer(); 22 | server.Start(); 23 | 24 | Assert.Throws(typeof(InvalidOperationException), () => 25 | { 26 | server.Start(); 27 | }); 28 | } 29 | 30 | [Fact] 31 | public void Starting_should_call_started_callback() 32 | { 33 | var started = new ManualResetEventSlim(); 34 | var server = ServerHelpers.CreateServer(); 35 | 36 | server.Started.Subscribe(u => { started.Set(); }); 37 | server.Start(); 38 | 39 | started.AssertWaitFor(2000); 40 | 41 | server.StopAndAssertStopped(); 42 | } 43 | 44 | [Fact] 45 | public void Starting_and_stopping_should_call_both_callbacks() 46 | { 47 | var started = new ManualResetEventSlim(); 48 | var stopped = new ManualResetEventSlim(); 49 | var server = ServerHelpers.CreateServer(); 50 | 51 | server.Started.Subscribe(_ => { started.Set(); }); 52 | server.Stopped.Subscribe(_ => { stopped.Set(); }); 53 | 54 | server.Start(); 55 | server.Stop(); 56 | 57 | started.AssertWaitFor(2000); 58 | stopped.AssertWaitFor(2000); 59 | } 60 | 61 | [Fact] 62 | public void When_exception_is_thrown_inside_callback_executor_should_signal_error() 63 | { 64 | var errOccured = new ManualResetEventSlim(); 65 | 66 | var exec = new ActionBlockExecutor(null, new ActionBlockExecutorSettings()); 67 | var server = ServerHelpers.CreateServer(exec); 68 | 69 | exec.Error += exc => { Assert.Equal("abcdef", exc.Message); errOccured.Set(); }; 70 | server.Started.Subscribe(_ => { throw new Exception("abcdef"); }); 71 | 72 | server.Start(); 73 | 74 | errOccured.AssertWaitFor(2000); 75 | 76 | server.Stop(); 77 | 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Stacks.Tests/StacksTest.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ravadre/Stacks/3c124dd62e3ef1e55d2168f75f36ba50ed4b29b5/Stacks.Tests/StacksTest.pfx -------------------------------------------------------------------------------- /Stacks.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Stacks/Client/FramedClientBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stacks 4 | { 5 | public struct FramedClientBuffer 6 | { 7 | public ArraySegment Packet { get; private set; } 8 | internal byte[] InternalBuffer => Packet.Array; 9 | 10 | public FramedClientBuffer(int packetLength) 11 | : this() 12 | { 13 | var intBuffer = new byte[packetLength + 4]; 14 | PrepareHeader(intBuffer, packetLength + 4); 15 | 16 | Packet = new ArraySegment(intBuffer, 4, packetLength); 17 | } 18 | 19 | public static FramedClientBuffer FromPacket(ArraySegment buffer) 20 | { 21 | Ensure.IsNotNull(buffer.Array, "buffer.Array"); 22 | 23 | var intBuffer = new byte[buffer.Count + 4]; 24 | PrepareHeader(intBuffer, buffer.Count + 4); 25 | 26 | Buffer.BlockCopy(buffer.Array, buffer.Offset, intBuffer, 4, buffer.Count); 27 | 28 | var framedBuffer = new FramedClientBuffer(); 29 | framedBuffer.Packet = new ArraySegment(intBuffer, 4, buffer.Count); 30 | 31 | return framedBuffer; 32 | } 33 | 34 | private static unsafe void PrepareHeader(byte[] b, int p) 35 | { 36 | fixed (byte* bPtr = b) 37 | { 38 | int* iPtr = (int*)bPtr; 39 | *iPtr = p; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Stacks/Client/IFramedClient.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 Stacks 8 | { 9 | public interface IFramedClient : ISocketClient 10 | { 11 | IObservable Sent { get; } 12 | IObservable> Received { get; } 13 | 14 | void SendPacket(byte[] packet); 15 | void SendPacket(ArraySegment packet); 16 | void SendPacket(FramedClientBuffer packet); 17 | FramedClientBuffer PreparePacketBuffer(int packetBytes); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stacks/Client/IMessageClient.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 Stacks 8 | { 9 | public interface IMessageClient : ISocketClient 10 | { 11 | IObservable Sent { get; } 12 | 13 | void Send(T obj); 14 | 15 | void PreLoadTypesFromAssemblyOfType(); 16 | void PreLoadType(); 17 | void PreLoadType(Type type); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stacks/Client/IRawByteClient.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 Stacks 8 | { 9 | public interface IRawByteClient : ISocketClient 10 | { 11 | IObservable> Received { get; } 12 | IObservable Sent { get; } 13 | 14 | void Send(byte[] buffer); 15 | void Send(ArraySegment buffer); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Stacks/Client/ISocketClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Reactive; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Stacks 10 | { 11 | public interface ISocketClient 12 | { 13 | IExecutor Executor { get; } 14 | bool IsConnected { get; } 15 | 16 | IObservable Connected { get; } 17 | IObservable Disconnected { get; } 18 | 19 | IPEndPoint RemoteEndPoint { get; } 20 | IPEndPoint LocalEndPoint { get; } 21 | 22 | IObservable Connect(IPEndPoint remoteEndPoint); 23 | IObservable Connect(string remoteEndPoint); 24 | void Close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Stacks/Client/RawByteClientStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Linq; 9 | 10 | namespace Stacks 11 | { 12 | public class RawByteClientStream : Stream 13 | { 14 | private IRawByteClient client; 15 | ResizableCyclicBuffer buffer; 16 | private ManualResetEventSlim hasDataEvent; 17 | private bool disposed; 18 | 19 | public RawByteClientStream(IRawByteClient client) 20 | { 21 | this.disposed = false; 22 | this.hasDataEvent = new ManualResetEventSlim(); 23 | this.client = client; 24 | this.buffer = new ResizableCyclicBuffer(4096); 25 | this.client.Received.Subscribe(DataReceived); 26 | } 27 | 28 | public override bool CanRead => true; 29 | public override bool CanSeek => false; 30 | public override bool CanWrite => true; 31 | 32 | public override void Flush() 33 | { 34 | 35 | } 36 | 37 | public override long Length 38 | { 39 | get { throw new NotSupportedException(); } 40 | } 41 | 42 | public override long Position 43 | { 44 | get 45 | { 46 | throw new NotSupportedException(); 47 | } 48 | set 49 | { 50 | throw new NotSupportedException(); 51 | } 52 | } 53 | 54 | public override int Read(byte[] buffer, int offset, int count) 55 | { 56 | this.hasDataEvent.Wait(); 57 | 58 | if (this.disposed) 59 | throw new ObjectDisposedException("Stream"); 60 | 61 | lock (this.buffer) 62 | { 63 | var segment = new ArraySegment(buffer, offset, count); 64 | var read = this.buffer.ReadRawBytes(segment); 65 | 66 | if (this.buffer.Count == 0) 67 | this.hasDataEvent.Reset(); 68 | 69 | return read; 70 | } 71 | } 72 | 73 | public override void Write(byte[] buffer, int offset, int count) 74 | { 75 | client.Send(new ArraySegment(buffer, offset, count)); 76 | } 77 | 78 | public override long Seek(long offset, SeekOrigin origin) 79 | { 80 | throw new NotImplementedException(); 81 | } 82 | 83 | public override void SetLength(long value) 84 | { 85 | throw new NotImplementedException(); 86 | } 87 | 88 | private void DataReceived(ArraySegment data) 89 | { 90 | lock (this.buffer) 91 | { 92 | this.buffer.AddData(data); 93 | this.hasDataEvent.Set(); 94 | } 95 | } 96 | 97 | protected override void Dispose(bool disposing) 98 | { 99 | base.Dispose(disposing); 100 | 101 | try 102 | { 103 | disposed = true; 104 | this.hasDataEvent.Set(); 105 | } 106 | catch { } 107 | } 108 | 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Stacks/Client/Tcp/FramedClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Reactive; 4 | using System.Reactive.Subjects; 5 | 6 | namespace Stacks.Tcp 7 | { 8 | public class FramedClient : IFramedClient 9 | { 10 | private readonly IRawByteClient client; 11 | private readonly Subject> received; 12 | private readonly ResizableCyclicBuffer recvBuffer; 13 | 14 | public FramedClient(IRawByteClient client) 15 | { 16 | received = new Subject>(); 17 | this.client = client; 18 | recvBuffer = new ResizableCyclicBuffer(4096); 19 | 20 | this.client.Received.Subscribe(ClientReceivedData); 21 | } 22 | 23 | public IObservable Connected 24 | { 25 | get { return client.Connected; } 26 | } 27 | 28 | public IObservable Disconnected 29 | { 30 | get { return client.Disconnected; } 31 | } 32 | 33 | public IObservable Sent 34 | { 35 | get { return client.Sent; } 36 | } 37 | 38 | public IObservable> Received 39 | { 40 | get { return received; } 41 | } 42 | 43 | public IPEndPoint LocalEndPoint 44 | { 45 | get { return client.LocalEndPoint; } 46 | } 47 | 48 | public IPEndPoint RemoteEndPoint 49 | { 50 | get { return client.RemoteEndPoint; } 51 | } 52 | 53 | public void SendPacket(byte[] packet) 54 | { 55 | Ensure.IsNotNull(packet, "packet"); 56 | 57 | SendPacket(new ArraySegment(packet)); 58 | } 59 | 60 | public void SendPacket(ArraySegment packet) 61 | { 62 | Ensure.IsNotNull(packet.Array, "packet.Array"); 63 | 64 | var buffer = FramedClientBuffer.FromPacket(packet); 65 | SendPacket(buffer); 66 | } 67 | 68 | public void SendPacket(FramedClientBuffer packet) 69 | { 70 | packet = OnBeforeSendPacket(packet); 71 | client.Send(packet.InternalBuffer); 72 | } 73 | 74 | public FramedClientBuffer PreparePacketBuffer(int packetBytes) 75 | { 76 | return new FramedClientBuffer(packetBytes); 77 | } 78 | 79 | public void Close() 80 | { 81 | client.Close(); 82 | } 83 | 84 | public IExecutor Executor 85 | { 86 | get { return client.Executor; } 87 | } 88 | 89 | public bool IsConnected 90 | { 91 | get { return client.IsConnected; } 92 | } 93 | 94 | public IObservable Connect(IPEndPoint remoteEndPoint) 95 | { 96 | return client.Connect(remoteEndPoint); 97 | } 98 | 99 | public IObservable Connect(string endPoint) 100 | { 101 | return Connect(endPoint); 102 | } 103 | 104 | private void ClientReceivedData(ArraySegment data) 105 | { 106 | recvBuffer.AddData(data); 107 | 108 | foreach (var packet in recvBuffer.GetPackets()) 109 | { 110 | OnReceived(packet); 111 | } 112 | } 113 | 114 | private void OnReceived(ArraySegment data) 115 | { 116 | data = OnBeforeReceivePacket(data); 117 | received.OnNext(data); 118 | } 119 | 120 | protected virtual ArraySegment OnBeforeReceivePacket(ArraySegment data) 121 | { 122 | return data; 123 | } 124 | 125 | protected virtual FramedClientBuffer OnBeforeSendPacket(FramedClientBuffer packet) 126 | { 127 | return packet; 128 | } 129 | 130 | } 131 | } -------------------------------------------------------------------------------- /Stacks/Client/Tcp/MessageClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Reactive; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Stacks.Tcp 12 | { 13 | public class MessageClient : MessageClientBase 14 | { 15 | private StacksSerializationHandler packetSerializationHandler; 16 | 17 | public MessageClient(IFramedClient framedClient, 18 | IStacksSerializer packetSerializer, 19 | IMessageHandler messageHandler, 20 | Func registration) 21 | : this(registration(new MessageIdRegistration()).CreateCache(), 22 | framedClient, packetSerializer, messageHandler) 23 | { 24 | } 25 | 26 | public MessageClient(IFramedClient framedClient, 27 | IStacksSerializer packetSerializer, 28 | IMessageHandler messageHandler) 29 | : this(new MessageIdCache(), 30 | framedClient, packetSerializer, messageHandler) 31 | { 32 | } 33 | 34 | private MessageClient(IMessageIdCache messageIdCache, 35 | IFramedClient framedClient, 36 | IStacksSerializer packetSerializer, 37 | IMessageHandler messageHandler) 38 | : base(framedClient, messageIdCache, packetSerializer) 39 | { 40 | 41 | this.packetSerializationHandler = new StacksSerializationHandler( 42 | messageIdCache, 43 | this, 44 | packetSerializer, 45 | messageHandler); 46 | 47 | this.framedClient.Received.Subscribe(PacketReceived); 48 | } 49 | 50 | private unsafe void PacketReceived(ArraySegment buffer) 51 | { 52 | fixed (byte* b = &buffer.Array[buffer.Offset]) 53 | { 54 | int messageId = *((int*)b); 55 | using (var ms = new MemoryStream(buffer.Array, buffer.Offset + 4, buffer.Count - 4)) 56 | { 57 | this.packetSerializationHandler.Deserialize(messageId, ms); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Stacks/Client/Tcp/MessageClientBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Reactive; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Stacks.Tcp 11 | { 12 | public abstract class MessageClientBase : IMessageClient 13 | { 14 | protected readonly IFramedClient framedClient; 15 | protected readonly IMessageIdCache messageIdCache; 16 | protected readonly IStacksSerializer packetSerializer; 17 | 18 | 19 | public IExecutor Executor => framedClient.Executor; 20 | public bool IsConnected => framedClient.IsConnected; 21 | 22 | public IObservable Connected => framedClient.Connected; 23 | public IObservable Disconnected => framedClient.Disconnected; 24 | public IObservable Sent => framedClient.Sent; 25 | 26 | public IPEndPoint LocalEndPoint => framedClient.LocalEndPoint; 27 | public IPEndPoint RemoteEndPoint => framedClient.RemoteEndPoint; 28 | 29 | protected MessageClientBase(IFramedClient client, IMessageIdCache messageIdCache, 30 | IStacksSerializer packetSerializer) 31 | { 32 | this.framedClient = client; 33 | this.messageIdCache = messageIdCache; 34 | this.packetSerializer = packetSerializer; 35 | } 36 | 37 | public IObservable Connect(IPEndPoint endPoint) => framedClient.Connect(endPoint); 38 | public IObservable Connect(string endPoint) => Connect(endPoint); 39 | 40 | public void Close() => framedClient.Close(); 41 | 42 | public unsafe void Send(T obj) 43 | { 44 | var messageId = messageIdCache.GetMessageId(); 45 | 46 | using (var ms = new MemoryStream()) 47 | { 48 | ms.SetLength(4); 49 | ms.Position = 4; 50 | this.packetSerializer.Serialize(obj, ms); 51 | ms.Position = 0; 52 | 53 | var buffer = ms.GetBuffer(); 54 | 55 | fixed (byte* buf = buffer) 56 | { 57 | int* iBuf = (int*)buf; 58 | *iBuf = messageId; 59 | } 60 | 61 | this.framedClient.SendPacket(new ArraySegment(buffer, 0, (int)ms.Length)); 62 | } 63 | } 64 | 65 | public void PreLoadTypesFromAssemblyOfType() 66 | { 67 | messageIdCache.PreLoadTypesFromAssemblyOfType(); 68 | } 69 | 70 | public void PreLoadType() 71 | { 72 | messageIdCache.PreLoadType(); 73 | } 74 | 75 | public void PreLoadType(Type type) 76 | { 77 | messageIdCache.PreLoadType(type); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Stacks/Client/Tcp/ReactiveClientModelBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks 9 | { 10 | public class ReactiveClientModelBuilder 11 | { 12 | private Dictionary propertyMappings; 13 | 14 | public bool HasMapping(string property) 15 | { 16 | return propertyMappings.ContainsKey(property); 17 | } 18 | 19 | public int? TryGetMapping(string property) 20 | { 21 | int v; 22 | if (propertyMappings.TryGetValue(property, out v)) 23 | return v; 24 | return null; 25 | } 26 | 27 | public ReactiveClientModelBuilder() 28 | { 29 | propertyMappings = new Dictionary(); 30 | } 31 | 32 | public Mapping Packet(Expression> expr) 33 | { 34 | LambdaExpression e = (LambdaExpression)expr; 35 | var e2 = (MemberExpression)e.Body; 36 | 37 | return new Mapping(propertyMappings, e2.Member.Name); 38 | } 39 | 40 | public class Mapping 41 | { 42 | private Dictionary propertyMappings; 43 | private string propName; 44 | 45 | public Mapping(Dictionary propertyMappings, string propName) 46 | { 47 | this.propertyMappings = propertyMappings; 48 | this.propName = propName; 49 | } 50 | 51 | public void HasId(int id) 52 | { 53 | propertyMappings[propName] = id; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Stacks/Client/Tcp/ReactiveMessageClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Reactive; 7 | using System.Reactive.Linq; 8 | using System.Reactive.Subjects; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace Stacks.Tcp 13 | { 14 | public class ReactiveMessageClient : MessageClientBase 15 | { 16 | private Dictionary> deserializeByMessageId; 17 | private ReactiveMessageReceiverCreator messageReceiverCreator; 18 | 19 | 20 | public T Packets { get; private set; } 21 | 22 | 23 | public ReactiveMessageClient(IFramedClient framedClient, 24 | IStacksSerializer packetSerializer, 25 | Action> modelBuilder) 26 | : base(framedClient, new MessageIdCache(), packetSerializer) 27 | { 28 | Ensure.IsNotNull(framedClient, "framedClient"); 29 | Ensure.IsNotNull(packetSerializer, "packetSerializer"); 30 | 31 | var mb = new ReactiveClientModelBuilder(); 32 | if (modelBuilder != null) 33 | modelBuilder(mb); 34 | 35 | this.messageReceiverCreator = new ReactiveMessageReceiverCreator(base.messageIdCache, packetSerializer, mb); 36 | 37 | this.Packets = this.messageReceiverCreator.CreateReceiverImplementation(out deserializeByMessageId); 38 | 39 | this.framedClient.Received.Subscribe(PacketReceived); 40 | } 41 | 42 | 43 | public ReactiveMessageClient(IFramedClient framedClient, 44 | IStacksSerializer packetSerializer) 45 | : this(framedClient, packetSerializer, null) 46 | { } 47 | 48 | 49 | private unsafe void PacketReceived(ArraySegment buffer) 50 | { 51 | Action handler; 52 | 53 | fixed (byte* b = &buffer.Array[buffer.Offset]) 54 | { 55 | int messageId = *((int*)b); 56 | using (var ms = new MemoryStream(buffer.Array, buffer.Offset + 4, buffer.Count - 4)) 57 | { 58 | if (deserializeByMessageId.TryGetValue(messageId, out handler)) 59 | { 60 | handler(ms); 61 | } 62 | else 63 | { 64 | throw new InvalidOperationException( 65 | string.Format("No registered message handler for message id {0}", messageId)); 66 | } 67 | } 68 | } 69 | } 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Stacks/Ensure.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 Stacks 8 | { 9 | public static class Ensure 10 | { 11 | public static void IsNotNull(T o, string name) where T: class 12 | { 13 | if (o == null) 14 | throw new ArgumentNullException(name); 15 | } 16 | 17 | public static void IsNotNullOrEmpty(string s, string name) 18 | { 19 | if (string.IsNullOrEmpty(s)) 20 | { 21 | if (s == null) 22 | throw new ArgumentNullException(name); 23 | else 24 | throw new ArgumentException("Argument can't be empty", name); 25 | } 26 | } 27 | 28 | public static void IsNotNullOrWhiteSpace(string s, string name) 29 | { 30 | IsNotNullOrEmpty(s, name); 31 | 32 | if (string.IsNullOrWhiteSpace(s)) 33 | throw new ArgumentException("Argument can't consist of white spaces only", name); 34 | } 35 | 36 | public static void IsInterface(Type type, string name, string message = null) 37 | { 38 | IsNotNull(type, name); 39 | 40 | message = message ?? "Given type must be an interface"; 41 | 42 | if (!type.IsInterface) 43 | throw new ArgumentException(message, name); 44 | } 45 | 46 | public static void IsClass(Type type, string name, string message = null) 47 | { 48 | IsNotNull(type, name); 49 | 50 | message = message ?? "Given type must be an instantiable class"; 51 | 52 | if (!type.IsClass || 53 | type.IsAbstract) 54 | throw new ArgumentException(message, name); 55 | 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Stacks/Executors/ActionBlockExecutorSettings.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.Threading.Tasks.Dataflow; 7 | 8 | namespace Stacks 9 | { 10 | public class ActionBlockExecutorSettings 11 | { 12 | public int QueueBoundedCapacity { get; set; } 13 | public int MaxDegreeOfParallelism { get; set; } 14 | public bool SupportSynchronizationContext { get; set; } 15 | 16 | public ActionBlockExecutorSettings() 17 | { 18 | QueueBoundedCapacity = DataflowBlockOptions.Unbounded; 19 | MaxDegreeOfParallelism = 1; 20 | SupportSynchronizationContext = true; 21 | } 22 | 23 | public static ActionBlockExecutorSettings Default 24 | { 25 | get { return new ActionBlockExecutorSettings(); } 26 | } 27 | 28 | public static ActionBlockExecutorSettings DefaultWith(bool supportSynchronizationContext = true) 29 | { 30 | var settings = new ActionBlockExecutorSettings(); 31 | settings.SupportSynchronizationContext = supportSynchronizationContext; 32 | return settings; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Stacks/Executors/CapturedContextExecutor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Dynamic; 4 | using System.Linq; 5 | using System.Reactive.Concurrency; 6 | using System.Reactive.Disposables; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace Stacks 12 | { 13 | public class CapturedContextExecutor : SynchronizationContext, IExecutor 14 | { 15 | private readonly SynchronizationContext context; 16 | private readonly TaskCompletionSource tcs; 17 | 18 | public Task Completion { get { return tcs.Task; } } 19 | 20 | #pragma warning disable 67 21 | public event Action Error; 22 | #pragma warning restore 67 23 | 24 | public string Name { get; set; } 25 | 26 | public CapturedContextExecutor(SynchronizationContext context) 27 | { 28 | Error = null; 29 | this.context = context; 30 | tcs = new TaskCompletionSource(); 31 | } 32 | 33 | public void Enqueue(Action action) 34 | { 35 | context.Post(_ => action(), null); 36 | } 37 | 38 | public Task Stop() 39 | { 40 | tcs.SetResult(0); 41 | return tcs.Task; 42 | } 43 | 44 | public Task Stop(bool stopImmediately) 45 | { 46 | return Stop(); 47 | } 48 | 49 | public SynchronizationContext Context 50 | { 51 | get { return context; } 52 | } 53 | 54 | public override SynchronizationContext CreateCopy() 55 | { 56 | return context.CreateCopy(); 57 | } 58 | 59 | public override void Post(SendOrPostCallback d, object state) 60 | { 61 | context.Post(d, state); 62 | } 63 | 64 | public override void Send(SendOrPostCallback d, object state) 65 | { 66 | throw new NotSupportedException(); 67 | } 68 | 69 | public Task PostTask(Action action) 70 | { 71 | return ExecutorHelper.PostTask(this, action); 72 | } 73 | 74 | public Task PostTask(Func func) 75 | { 76 | return ExecutorHelper.PostTask(this, func); 77 | } 78 | 79 | public override string ToString() 80 | { 81 | return "CapturedContext Executor " + 82 | (string.IsNullOrWhiteSpace(Name) ? "" : string.Format("({0})", Name)); 83 | } 84 | 85 | 86 | DateTimeOffset IScheduler.Now 87 | { 88 | get { return DateTimeOffset.UtcNow; } 89 | } 90 | 91 | public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) 92 | { 93 | var due = dueTime - DateTimeOffset.UtcNow; 94 | 95 | return Schedule(state, due, action); 96 | } 97 | 98 | public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) 99 | { 100 | Task.Delay(dueTime) 101 | .ContinueWith(t => 102 | { 103 | Schedule(state, action); 104 | }); 105 | 106 | return Disposable.Empty; 107 | } 108 | 109 | public IDisposable Schedule(TState state, Func action) 110 | { 111 | Enqueue(() => 112 | { 113 | action(this, state); 114 | }); 115 | 116 | return Disposable.Empty; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Stacks/Executors/Executor.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 Stacks 9 | { 10 | public static class Executor 11 | { 12 | public static bool IsInContext() 13 | { 14 | var ctx = SynchronizationContext.Current; 15 | 16 | if (ctx is IExecutor) 17 | return true; 18 | 19 | return false; 20 | } 21 | 22 | public static string GetCurrentFullName() 23 | { 24 | var ctx = SynchronizationContext.Current; 25 | 26 | if (ctx is IExecutor) 27 | return ctx.ToString(); 28 | 29 | return string.Empty; 30 | } 31 | 32 | public static string GetCurrentName() 33 | { 34 | var ctx = SynchronizationContext.Current; 35 | 36 | var executor = ctx as IExecutor; 37 | if (executor != null) 38 | return executor.Name; 39 | 40 | return string.Empty; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Stacks/Executors/ExecutorHelper.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 Stacks 8 | { 9 | static class ExecutorHelper 10 | { 11 | internal static Task PostTask(IExecutor exec, Action action) 12 | { 13 | //Not reusing implementation, so additional lambda allocation can be ommited 14 | var tcs = new TaskCompletionSource(); 15 | 16 | exec.Enqueue(() => 17 | { 18 | try 19 | { 20 | action?.Invoke(); 21 | tcs.SetResult(System.Reactive.Unit.Default); 22 | } 23 | catch (Exception exc) 24 | { 25 | tcs.SetException(exc); 26 | } 27 | }); 28 | 29 | return tcs.Task; 30 | } 31 | 32 | internal static Task PostTask(IExecutor exec, Func func) 33 | { 34 | var tcs = new TaskCompletionSource(); 35 | 36 | exec.Enqueue(() => 37 | { 38 | try 39 | { 40 | tcs.SetResult(func()); 41 | } 42 | catch (Exception exc) 43 | { 44 | tcs.SetException(exc); 45 | } 46 | }); 47 | 48 | return tcs.Task; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Stacks/Executors/IExecutor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive; 5 | using System.Reactive.Concurrency; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Stacks 11 | { 12 | public interface IExecutor : IScheduler 13 | { 14 | string Name { get; set; } 15 | 16 | event Action Error; 17 | 18 | void Enqueue(Action action); 19 | Task PostTask(Action action); 20 | Task PostTask(Func func); 21 | 22 | Task Stop(); 23 | Task Completion { get; } 24 | 25 | SynchronizationContext Context { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Stacks/MessageClient/IMessageIdCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stacks 4 | { 5 | public interface IMessageIdCache 6 | { 7 | int GetMessageId(Type t); 8 | int GetMessageId(); 9 | void PreLoadType(Type t); 10 | void PreLoadType(); 11 | void PreLoadTypesFromAssemblyOfType(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Stacks/MessageClient/ImperativeMessageIdCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Stacks 10 | { 11 | public class ImperativeMessageIdCache : IMessageIdCache 12 | { 13 | private readonly Dictionary messageIdByType; 14 | private readonly ReaderWriterLockSlim rwLock; 15 | 16 | public ImperativeMessageIdCache() 17 | { 18 | messageIdByType = new Dictionary(); 19 | rwLock = new ReaderWriterLockSlim(); 20 | } 21 | 22 | public void PreLoadTypesFromAssemblyOfType() 23 | { 24 | throw new InvalidOperationException( 25 | "Cannot load types from assembly if message ids were declared imperatively"); 26 | } 27 | 28 | internal void RegisterMessageId(int messageId, Type type) 29 | { 30 | try 31 | { 32 | rwLock.EnterWriteLock(); 33 | 34 | messageIdByType[type] = messageId; 35 | } 36 | finally 37 | { 38 | rwLock.ExitWriteLock(); 39 | } 40 | } 41 | 42 | public int GetMessageId() 43 | { 44 | return GetMessageId(typeof(T)); 45 | } 46 | 47 | public int GetMessageId(Type t) 48 | { 49 | try 50 | { 51 | rwLock.EnterReadLock(); 52 | 53 | int messageId; 54 | if (messageIdByType.TryGetValue(t, out messageId)) 55 | { 56 | return messageId; 57 | } 58 | else 59 | { 60 | throw new InvalidDataException(string.Format("Cannot resolve message id for type {0}. " + 61 | "It has no {1} attribute and it wasn't declared imperatively", 62 | t.Name, typeof(StacksMessageAttribute).Name)); 63 | } 64 | } 65 | finally 66 | { 67 | rwLock.ExitReadLock(); 68 | } 69 | } 70 | 71 | public void PreLoadType() 72 | { 73 | PreLoadType(typeof(T)); 74 | } 75 | 76 | public void PreLoadType(Type t) 77 | { 78 | try 79 | { 80 | rwLock.EnterReadLock(); 81 | 82 | if (messageIdByType.ContainsKey(t)) 83 | return; 84 | } 85 | finally 86 | { 87 | rwLock.ExitReadLock(); 88 | } 89 | 90 | throw new InvalidOperationException( 91 | "Message client tried to preload new type " + t.Name + ". " + 92 | "In most cases this means that message handler tries to handle packet type " + 93 | "which was not registered"); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Stacks/MessageClient/MessageIdCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Stacks 11 | { 12 | public class MessageIdCache : IMessageIdCache 13 | { 14 | private readonly Dictionary messageIdByType; 15 | private readonly ReaderWriterLockSlim rwLock; 16 | 17 | public MessageIdCache() 18 | { 19 | messageIdByType = new Dictionary(); 20 | rwLock = new ReaderWriterLockSlim(); 21 | } 22 | 23 | public void PreLoadTypesFromAssemblyOfType() 24 | { 25 | var idByTypeLocal = new Dictionary(); 26 | 27 | foreach (var t in typeof(T).Assembly.GetTypes() 28 | .Where(t => !t.IsInterface) 29 | .Where(t => !t.IsAbstract)) 30 | { 31 | var attr = t.GetCustomAttribute(); 32 | 33 | if (attr != null) 34 | { 35 | idByTypeLocal[t] = attr.MessageId; 36 | } 37 | } 38 | 39 | try 40 | { 41 | rwLock.EnterWriteLock(); 42 | 43 | foreach (var kv in idByTypeLocal) 44 | { 45 | messageIdByType[kv.Key] = kv.Value; 46 | } 47 | } 48 | finally 49 | { 50 | rwLock.ExitWriteLock(); 51 | } 52 | } 53 | 54 | public int GetMessageId() 55 | { 56 | return GetMessageId(typeof(T)); 57 | } 58 | 59 | public int GetMessageId(Type t) 60 | { 61 | try 62 | { 63 | rwLock.EnterUpgradeableReadLock(); 64 | 65 | int messageid; 66 | if (messageIdByType.TryGetValue(t, out messageid)) 67 | { 68 | return messageid; 69 | } 70 | else 71 | { 72 | var attribute = t.GetCustomAttribute(); 73 | 74 | if (attribute == null) 75 | { 76 | throw new InvalidDataException(string.Format("Cannot resolve type id for type {0}. " + 77 | "It has no {1} attribute and it wasn't declared imperatively", 78 | t.Name, typeof(StacksMessageAttribute).Name)); 79 | } 80 | 81 | try 82 | { 83 | rwLock.EnterWriteLock(); 84 | 85 | messageIdByType[t] = attribute.MessageId; 86 | 87 | return attribute.MessageId; 88 | } 89 | finally { rwLock.ExitWriteLock(); } 90 | } 91 | } 92 | finally 93 | { 94 | rwLock.ExitUpgradeableReadLock(); 95 | } 96 | } 97 | 98 | public void PreLoadType() 99 | { 100 | PreLoadType(typeof(T)); 101 | } 102 | 103 | public void PreLoadType(Type t) 104 | { 105 | var attr = t.GetCustomAttribute(); 106 | 107 | if (attr != null) 108 | { 109 | try 110 | { 111 | rwLock.EnterWriteLock(); 112 | 113 | messageIdByType[t] = attr.MessageId; 114 | } 115 | finally 116 | { 117 | rwLock.ExitWriteLock(); 118 | } 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Stacks/MessageClient/MessageIdRegistration.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 Stacks 8 | { 9 | public class MessageIdRegistration 10 | { 11 | private readonly ImperativeMessageIdCache cache; 12 | 13 | internal MessageIdRegistration() 14 | { 15 | cache = new ImperativeMessageIdCache(); 16 | } 17 | 18 | public MessageIdRegistration RegisterMessage(int messageId) 19 | { 20 | cache.RegisterMessageId(messageId, typeof(T)); 21 | 22 | return this; 23 | } 24 | 25 | public MessageIdRegistration RegisterMessage(int messageId, Type type) 26 | { 27 | cache.RegisterMessageId(messageId, type); 28 | 29 | return this; 30 | } 31 | 32 | internal IMessageIdCache CreateCache() 33 | { 34 | return cache; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Stacks/MessageClient/StacksMessageAttribute.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 Stacks 8 | { 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 10 | public class StacksMessageAttribute : Attribute 11 | { 12 | public int MessageId { get; private set; } 13 | 14 | public StacksMessageAttribute(int messageId) 15 | { 16 | MessageId = messageId; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stacks/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Stacks")] 6 | [assembly: AssemblyDescription("Stacks actor and network library")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyProduct("Stacks")] 9 | 10 | [assembly: Guid("f572e5fc-29d7-4826-952e-4b6dad405aa0")] 11 | -------------------------------------------------------------------------------- /Stacks/ResizableCyclicBuffer.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 Stacks 8 | { 9 | internal class ResizableCyclicBuffer 10 | { 11 | private byte[] buffer; 12 | private int endOffset; 13 | private int beginOffset; 14 | 15 | private static IEnumerable> EmptyPacketList 16 | = new List>(); 17 | 18 | public ResizableCyclicBuffer(int initSize) 19 | { 20 | buffer = new byte[initSize]; 21 | } 22 | 23 | public int Count 24 | { 25 | get 26 | { 27 | return endOffset - beginOffset; 28 | } 29 | } 30 | 31 | public void AddData(ArraySegment data) 32 | { 33 | CleanupBuffer(); 34 | 35 | while (buffer.Length - endOffset < data.Count) 36 | ResizeBuffer(); 37 | 38 | Buffer.BlockCopy(data.Array, data.Offset, 39 | buffer, endOffset, data.Count); 40 | endOffset += data.Count; 41 | } 42 | 43 | private void ResizeBuffer() 44 | { 45 | byte[] newBuffer = new byte[buffer.Length * 2]; 46 | Buffer.BlockCopy(buffer, 0, newBuffer, 0, endOffset); 47 | this.buffer = newBuffer; 48 | } 49 | 50 | public int ReadRawBytes(ArraySegment buffer) 51 | { 52 | CleanupBuffer(); 53 | 54 | int totalBytes = endOffset - beginOffset; 55 | int toRead = Math.Min(buffer.Count, totalBytes); 56 | 57 | if (toRead == 0) 58 | return 0; 59 | 60 | Buffer.BlockCopy(this.buffer, this.beginOffset, 61 | buffer.Array, buffer.Offset, toRead); 62 | 63 | this.beginOffset += toRead; 64 | 65 | return toRead; 66 | } 67 | 68 | public unsafe IEnumerable> GetPackets() 69 | { 70 | CleanupBuffer(); 71 | 72 | if (endOffset < 4) 73 | return EmptyPacketList; 74 | 75 | var packets = new List>(); 76 | 77 | fixed (byte* b = buffer) 78 | { 79 | byte* bPtr = b; 80 | 81 | while (true) 82 | { 83 | if (beginOffset + 4 > endOffset) 84 | break; 85 | 86 | int size = *((int*)bPtr); 87 | 88 | if (beginOffset + size > endOffset) 89 | break; 90 | 91 | packets.Add(new ArraySegment 92 | (buffer, beginOffset + 4, size - 4)); 93 | 94 | beginOffset += size; 95 | bPtr += size; 96 | } 97 | } 98 | 99 | return packets; 100 | } 101 | 102 | private void CleanupBuffer() 103 | { 104 | if (beginOffset == 0) 105 | return; 106 | 107 | Buffer.BlockCopy(buffer, beginOffset, buffer, 0, endOffset - beginOffset); 108 | endOffset -= beginOffset; 109 | beginOffset = 0; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Stacks/Serializers/IMessageHandler.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 Stacks 8 | { 9 | public interface IMessageHandler 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Stacks/Serializers/IStacksSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stacks 9 | { 10 | public interface IStacksSerializer 11 | { 12 | void Initialize(); 13 | 14 | //Func CreateDeserializer(); 15 | T Deserialize(MemoryStream ms); 16 | void Serialize(T obj, MemoryStream ms); 17 | 18 | void PrepareSerializerForType(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Stacks/Serializers/ProtobufStacksSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ProtoBuf; 3 | 4 | namespace Stacks 5 | { 6 | public class ProtoBufStacksSerializer : IStacksSerializer 7 | { 8 | public void Initialize() 9 | { 10 | } 11 | 12 | public T Deserialize(MemoryStream ms) 13 | { 14 | return Serializer.Deserialize(ms); 15 | } 16 | 17 | public void Serialize(T obj, MemoryStream ms) 18 | { 19 | Serializer.Serialize(ms, obj); 20 | } 21 | 22 | public void PrepareSerializerForType() 23 | { 24 | Serializer.PrepareSerializer(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Stacks/Stacks.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stacks 5 | Stacks 6 | @build.number@ 7 | Marcin Deptuła 8 | Marcin Deptuła 9 | Simple to use actor/network library 10 | https://raw.githubusercontent.com/Ravadre/Stacks/master/LICENSE 11 | https://github.com/Ravadre/Stacks 12 | false 13 | Stacks - reactive actor/network library 14 | Copyright Marcin Deptuła 15 | C# Socket Network Actor Reactive 16 | @dependencies@ 17 | 18 | -------------------------------------------------------------------------------- /Stacks/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | .\tools\NuGet\nuget.exe install FAKE -OutputDirectory tools\ -ExcludeVersion 4 | .\tools\FAKE\tools\Fake.exe Build.fsx "All" %* -------------------------------------------------------------------------------- /tools/NuGet/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ravadre/Stacks/3c124dd62e3ef1e55d2168f75f36ba50ed4b29b5/tools/NuGet/nuget.exe --------------------------------------------------------------------------------