Skip to content
Browse files

source code to stackless C# blog post

  • Loading branch information...
1 parent 1fe7fe9 commit 214f7814aefa23557d2f392f30cfbf5990226cc3 @refractalize committed
View
74 erlang/Erlang.Tests/Erlang.Tests.csproj
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{A4615941-EE95-4BA1-8511-A18389B09E51}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Erlang.Tests</RootNamespace>
+ <AssemblyName>Erlang.Tests</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Moq, Version=3.0.204.1, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\References\Moq\Moq.dll</HintPath>
+ </Reference>
+ <Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\References\NUnit\nunit.framework.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="MailBoxTest.cs" />
+ <Compile Include="MergeSorterTest.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Erlang\Erlang.csproj">
+ <Project>{B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}</Project>
+ <Name>Erlang</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
38 erlang/Erlang.Tests/MailBoxTest.cs
@@ -0,0 +1,38 @@
+using NUnit.Framework;
+
+namespace Erlang.Tests {
+ [TestFixture]
+ public class MailBoxTest {
+ [Test]
+ public void ShouldReturnMessageWhenOneAvailable() {
+ var mailbox = new MailBox<string>();
+ mailbox.AddMessage("hi");
+
+ string message;
+ Assert.That(mailbox.TryGetNextMessage(out message));
+ Assert.That(message, Is.EqualTo("hi"));
+ }
+
+ [Test]
+ public void ShouldReturnFalseIfNoMessages() {
+ var mailbox = new MailBox<string>();
+
+ string message;
+ Assert.That(mailbox.TryGetNextMessage(out message), Is.False);
+ }
+
+ [Test]
+ public void ShouldReturnMessagesInOrderTheyWereAdded() {
+ var mailbox = new MailBox<string>();
+ mailbox.AddMessage("one");
+ mailbox.AddMessage("two");
+
+ string message;
+ Assert.That(mailbox.TryGetNextMessage(out message));
+ Assert.That(message, Is.EqualTo("one"));
+
+ Assert.That(mailbox.TryGetNextMessage(out message));
+ Assert.That(message, Is.EqualTo("two"));
+ }
+ }
+}
View
21 erlang/Erlang.Tests/MergeSorterTest.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+
+namespace Erlang.Tests {
+ [TestFixture]
+ public class MergeSorterTest {
+ [Test]
+ public void ShouldMerge2ItemsAscendings() {
+ Assert.That(MergeSorter.Merge(new[] {2}, new[] {1}), Is.EqualTo(new[] {1, 2}));
+ }
+
+ [Test]
+ public void ShouldMergeDifferentSizedArrays() {
+ Assert.That(MergeSorter.Merge(new[] {2}, new[] {1, 3}), Is.EqualTo(new[] {1, 2, 3}));
+ }
+
+ [Test]
+ public void ShouldMergeArraysWithEqualEntries() {
+ Assert.That(MergeSorter.Merge(new[] {1, 3, 5}, new[] {1, 2, 3}), Is.EqualTo(new[] {1, 1, 2, 3, 3, 5}));
+ }
+ }
+}
View
36 erlang/Erlang.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Erlang.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("BBC Worldwide")]
+[assembly: AssemblyProduct("Erlang.Tests")]
+[assembly: AssemblyCopyright("Copyright © BBC Worldwide 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a215b123-c5a9-4a8a-a85e-ce4888cb3582")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
26 erlang/Erlang.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erlang", "Erlang\Erlang.csproj", "{B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erlang.Tests", "Erlang.Tests\Erlang.Tests.csproj", "{A4615941-EE95-4BA1-8511-A18389B09E51}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A4615941-EE95-4BA1-8511-A18389B09E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A4615941-EE95-4BA1-8511-A18389B09E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A4615941-EE95-4BA1-8511-A18389B09E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A4615941-EE95-4BA1-8511-A18389B09E51}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
View
69 erlang/Erlang/Erlang.csproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{B0F7CF4E-E9B0-448C-84AE-D2860305AEC5}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Erlang</RootNamespace>
+ <AssemblyName>Erlang</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="IScheduler.cs" />
+ <Compile Include="MailBox.cs" />
+ <Compile Include="MergeSort.cs" />
+ <Compile Include="MergeSorter.cs" />
+ <Compile Include="MergeSortMessage.cs" />
+ <Compile Include="PingPong.cs" />
+ <Compile Include="Process.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Scheduler.cs" />
+ <Compile Include="SpawnLots.cs" />
+ <Compile Include="Yield.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
6 erlang/Erlang/IScheduler.cs
@@ -0,0 +1,6 @@
+namespace Erlang {
+ public interface IScheduler {
+ int ProcessCount { get; }
+ void Spawn(params IProcess[] processes);
+ }
+}
View
25 erlang/Erlang/MailBox.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Erlang {
+ public class MailBox<T> {
+ private Queue<T> Messages;
+
+ public MailBox() {
+ Messages = new Queue<T>();
+ }
+
+ public void AddMessage(T message) {
+ Messages.Enqueue(message);
+ }
+
+ public bool TryGetNextMessage(out T message) {
+ if (Messages.Count > 0) {
+ message = Messages.Dequeue();
+ return true;
+ } else {
+ message = default(T);
+ return false;
+ }
+ }
+ }
+}
View
24 erlang/Erlang/MergeSort.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Erlang {
+ public class MergeSort : Process<MergeSortMessage> {
+ private readonly int[] Items;
+ public int[] Results { get; set; }
+
+ public MergeSort(int[] items) {
+ Items = items;
+ }
+
+ public override Yield Start() {
+ var mergeSorter = new MergeSorter();
+ mergeSorter.Send(new MergeSortMessage {Caller = this, Items = Items});
+
+ Spawn(mergeSorter);
+
+ return Receive(m => {
+ Results = m.Items;
+ return null;
+ });
+ }
+ }
+}
View
6 erlang/Erlang/MergeSortMessage.cs
@@ -0,0 +1,6 @@
+namespace Erlang {
+ public class MergeSortMessage {
+ public int[] Items;
+ public Process<MergeSortMessage> Caller;
+ }
+}
View
49 erlang/Erlang/MergeSorter.cs
@@ -0,0 +1,49 @@
+using System.Linq;
+
+namespace Erlang {
+ public class MergeSorter : Process<MergeSortMessage> {
+ public override Yield Start() {
+ return Receive(qs => {
+ if (qs.Items.Length <= 1) {
+ qs.Caller.Send(new MergeSortMessage { Items = qs.Items });
+ return null;
+ } else {
+ int split = qs.Items.Length / 2;
+ int[] first = qs.Items.Take(split).ToArray();
+ int[] second = qs.Items.Skip(split).ToArray();
+
+ var firstProcess = new MergeSorter();
+ firstProcess.Send(new MergeSortMessage { Caller = this, Items = first });
+ Spawn(firstProcess);
+ var secondProcess = new MergeSorter();
+ secondProcess.Send(new MergeSortMessage { Caller = this, Items = second });
+ Spawn(secondProcess);
+
+ return Receive(result1 => Receive(result2 => {
+ qs.Caller.Send(new MergeSortMessage { Items = Merge(result1.Items, result2.Items) });
+ return null;
+ }));
+ }
+ });
+ }
+
+ internal static int[] Merge(int[] items1, int[] items2) {
+ var merged = new int[items1.Length + items2.Length];
+
+ int i1 = 0, i2 = 0;
+ for (int m = 0; m < merged.Length; m++) {
+ if (items1.Length <= i1) {
+ merged[m] = items2[i2++];
+ } else if (items2.Length <= i2) {
+ merged[m] = items1[i1++];
+ } else if (items1[i1] < items2[i2]) {
+ merged[m] = items1[i1++];
+ } else {
+ merged[m] = items2[i2++];
+ }
+ }
+
+ return merged;
+ }
+ }
+}
View
20 erlang/Erlang/PingPong.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Erlang {
+ class PingPong : Process<string> {
+ public Process<string> Friend { get; set; }
+ private string Name;
+
+ public PingPong(string name) {
+ Name = name;
+ }
+
+ public override Yield Start() {
+ Friend.Send(Name);
+ return Receive(n => {
+ Console.WriteLine(Name + " received message from " + n);
+ return Start;
+ });
+ }
+ }
+}
View
37 erlang/Erlang/Process.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Erlang {
+ public abstract class IProcess {
+ public abstract IScheduler Scheduler { get; set; }
+ public abstract Yield Start();
+ }
+
+ public abstract class Process : IProcess {
+ public override IScheduler Scheduler { get; set; }
+
+ protected void Spawn(Process process) {
+ Scheduler.Spawn(process);
+ }
+ }
+
+ public abstract class Process<T> : Process {
+ private MailBox<T> MailBox;
+
+ protected Process() {
+ MailBox = new MailBox<T>();
+ }
+
+ public void Send(T msg) {
+ MailBox.AddMessage(msg);
+ }
+
+ protected Yield Receive(Func<T, Yield> continuation) {
+ T message;
+ if (MailBox.TryGetNextMessage(out message)) {
+ return () => continuation(message);
+ } else {
+ return () => Receive(continuation);
+ }
+ }
+ }
+}
View
43 erlang/Erlang/Program.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Linq;
+using System.Text;
+
+namespace Erlang {
+ class Program {
+ static void Main(string[] args) {
+// RunPingPong();
+ RunMergeSort();
+// RunLotsOfProcesses();
+ }
+
+ private static void RunPingPong() {
+ var scheduler = new Scheduler();
+
+ var ping = new PingPong("ping");
+ var pong = new PingPong("pong");
+
+ ping.Friend = pong;
+ pong.Friend = ping;
+
+ scheduler.Spawn(ping, pong);
+ scheduler.Schedule();
+ }
+
+ private static void RunLotsOfProcesses() {
+ var scheduler = new Scheduler();
+
+ scheduler.Spawn(new SpawnLots());
+ scheduler.Schedule();
+ }
+
+ private static void RunMergeSort() {
+ var scheduler = new Scheduler();
+
+ var mergeSort = new MergeSort(new[] { 3, 4, 3, 2, 7, 5, 9, 0 });
+ scheduler.Spawn(mergeSort);
+ scheduler.Schedule();
+
+ Console.WriteLine(String.Join(", ", mergeSort.Results.Select(i => i.ToString()).ToArray()));
+ }
+ }
+}
View
37 erlang/Erlang/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Erlang")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("BBC Worldwide")]
+[assembly: AssemblyProduct("Erlang")]
+[assembly: AssemblyCopyright("Copyright © BBC Worldwide 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2bf3ee47-2eee-4d35-80e6-a51cefca2c51")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: InternalsVisibleTo("Erlang.Tests")]
View
34 erlang/Erlang/Scheduler.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+
+namespace Erlang {
+ public class Scheduler : IScheduler {
+ private List<Yield> Processes;
+
+ public Scheduler() {
+ Processes = new List<Yield>();
+ }
+
+ public int ProcessCount {
+ get {
+ return Processes.Count;
+ }
+ }
+
+ public void Schedule() {
+ while (Processes.Count > 0) {
+ for (int p = 0; p < Processes.Count; p++) {
+ Processes[p] = Processes[p]();
+ }
+
+ Processes.RemoveAll(p => p == null);
+ }
+ }
+
+ public void Spawn(params IProcess[] processes) {
+ foreach (var process in processes) {
+ process.Scheduler = this;
+ Processes.Add(process.Start);
+ }
+ }
+ }
+}
View
13 erlang/Erlang/SpawnLots.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Erlang {
+ class SpawnLots : Process {
+ public override Yield Start() {
+ return () => {
+ Console.WriteLine("number of processes: {0}", Scheduler.ProcessCount);
+ Spawn(new SpawnLots());
+ return Start;
+ };
+ }
+ }
+}
View
3 erlang/Erlang/Yield.cs
@@ -0,0 +1,3 @@
+namespace Erlang {
+ public delegate Yield Yield();
+}
View
BIN erlang/References/Moq/Moq.dll
Binary file not shown.
View
BIN erlang/References/NUnit/nunit.framework.dll
Binary file not shown.

0 comments on commit 214f781

Please sign in to comment.
Something went wrong with that request. Please try again.