Home .NET Asynchronous programming – graph editor

Asynchronous programming – graph editor

by admin

Asynchronous programming - graph editor
Sometimes in the process of describing business logic, you need to graph asynchronous operations with internal dependencies, i.e. when tasks are executed asynchronously, but some tasks depend on others and thus have to "wait" until they can be run. In this post I want to show how this problem can be solved by creating graphic DSL which will allow the developer to visually define the dependency graph.
N.b.: English article and source code are here

Introduction

Generally speaking, domain-specific languages (DSL for short) come in three varieties. The first kind is textual DSL, which is defined solely through text and structure, and is associated with a specific process of converting that text into code. The second type is structural DSL, where the content is defined through a tree-like or graph-like editor. The third type I want to discuss is graphical DSL, where the developer works with a graph editor to create a visual structure, which can then be turned into code.
In this article, we will create a simple graphical DSL that allows the end user to define asynchronous operations to be organized using the Pulse Wait mechanism. To build the attached example, you will need Visual Studio 2008 with the Visual Studio SDK. We will use the Microsoft DSL Tools (included in the SDK) to build our DSL.

Problem description

Since it is difficult to work with Pulse Wait, I want to make a graphical DSL that would allow me to define a sequence of operations that can be organized using the Pulse Wait mechanism. In particular, I want to be able to drag and drop asynchronous blocks in the editor, and to be able to define links between them to form asynchronous, dependent execution rules.

DSL creation

Before we begin, let me outline the most important points when working with DSL Tools:

  • In DSL Tools, graphical DSLs are themselves made with a graphical DSL. This may seem confusing at first glance, but, in principle, you should understand that most of our asynchronous DSL (which I call AsyncDsl here) will be developed using visual elements-not a programming language. Of course, there will be a lot of code behind the scenes, but we won’t run into it much.
  • DSL tools make extensive use of T4 technology. Our graphical DSL is really just a visual representation of XML, and T4 turns that XML into code. So when you edit visual elements with DSL Tools, you are really editing XML.
  • Your DSL is still created using C#, and it compiles. You can extend it with partial classes and so on, which will let your DSL behave a certain way. We won’t do anything like that in this article.
  • Creating a DSL with DSL Tools concerns only the visual part – the part that allows the user visually create XML models. The part that turns it into plain text code is a separate problem that we’ll deal with later.

To create a DSL project in Visual Studio, select New Project and then Other Project Types → Extensibility → Domain-Specific Language Designer.
Asynchronous programming - graph editor
After clicking OK, you will be shown the wizard where you can define some features of the DSL you are creating.

  • On the first page, in addition to defining the name of your language, you can also choose an initial template. This template determines what initial features the DSL has – for example, by choosing Task Flow, you determine that the initial elements of the DSL will refer to flowchart-like structures. Regardless of which pattern you choose at this point, you can always override the behavior of your DSL by removing the originally generated elements.
  • The second page allows you to choose a file extension for your DSL. This extension will appear where you will insert your DSL into your own projects. In addition to the extension, wizard generates an icon.
  • The third page lets you define some strings which define your DSL, such as the name of the product to which your DSL belongs.
  • The fourth page actually forces you to sign your assembly, either with an existing or a new key.

When you’re done with the wizard, you’ll get a DSL definition framework. If you’ve never worked with the DSL functionality in Visual Studio, this screenshot may come as a bit of a shock.
Asynchronous programming - graph editor
The following elements take part in the DSL editing process :

  • Dsl Designer Toolbox. This toolbox contains all the elements that you will work with when designing your DSLs. They are used in the same way as for example in WinForms – you take an element and drag it to the editor window (that central window with the weird boxes).
  • The DSL designer himself. This is actually a file with the extension dsl but as you can see, the editor is megavisual – as I said, the DSLs themselves are built with other DSLs. This DSL has two parts – on the left side are the classes and the relationships between them, and on the right side are the visual elements, i.e. the visual reflections of the DSL concepts that the end user will work with. Thus, we can think of DSL as follows:on the right side there is visualization, and on the left side there is logic.
  • Solution Explorer. When you create a DSL, you will get two projects-one that defines the DSL you are making, and one that defines the component editor associated with the DSL. We’ll talk more about this later – for now it’s important just to note the one single button that transforms all the templates :
    Asynchronous programming - graph editor
    This is very important button. As I said before, DSLs are XML specifications which are transformed into code. This means that in order to update our DSL definition (and the definition is also a DSL), we need to transform all the templates into C#. The button above does exactly that. So if your final DSL suddenly doesn’t reflect some change you’re sure you’ve made, then you forgot to click that button.
  • The DSL explorer. This is a brand new panel in the studio that presents your DSL in a tree shape, and looks something like this :
    Asynchronous programming - graph editor
    This tree encapsulates many structural aspects of the DSL. It is important to note that some nodes in the tree have their own set of properties, which you can see by pressing F4.
  • Property pages exist for both the DSL Explorer tree elements and the visual elements in the DSL editor. Some DSL items can be edited "in the editor", for example, you can define a one-to-many, many-to-many, etc. relationship between two items without opening the properties panel. It’s up to you how you want to do it.

Let’s now go deeper into the process of creating our DSL.

Arranging visual elements

As I wrote before, the toolbox contains all the elements you have to work with. These elements are divided into two groups, "logical" and "visual". The logical elements are those that define the structure (i.e. the domain) in your DSL. Visual elements reflect those rectangles, lines, and side elements that the user operates with in the DSL.
The central concept of the logical structure of DSL is domain class (domain class). This class can represent anything , depending on which domain you are working with. Since we are working with asynchronous operations, one of our domain classes will be called Operation :
Asynchronous programming - graph editor
A domain class can have properties , i.e., values that the user can set. Our class has Operation has the property Timeout , Name and Description which the end user can determine after dragging and dropping the object instance Operation to itself in the model.
There’s a little problem here – the user doesn’t actually drag and drop a domain class directly into the model. Instead, he drags and drops OperationShape which is a visual reflection of Operation This class is formed from GeometryShape (taken from the same toolbox):
Asynchronous programming - graph editor
By defining the domain class Operation as well as its visual representation of OperationShape , they have to be bound together (if you run it as is, nothing will work). To do this you use a Diagram Element Map. Actually this thing is a line that connects two elements by defining an association between them. But even if you add it, nothing will work yet.

Relationship between elements

Before we get started with creating toolbox controls for our DSL (which is fun), we need to talk about relationships between elements. There are two types of relationships – Embedding Relationship and Reference Relationship. If you use an embedding relationship, element A will be completely enclosed in element B. For example, if I have a swimlane (a big horizontal piece of visual space) and I want to embed entire classes into it, then it makes sense to use an embedding relationship. If, on the other hand, I just have blocks that I need to attach comments to, then a reference relationship will do.
Let’s look at how we will use elements for our specific task. At the "root" of our can, we have the element ExampleModel I won’t even change the name of this element, because it won’t appear in the final DSL. In order to determine that my model contains processes and comments I draw embedding relationship lines between the corresponding classes and get the following picture :
Asynchronous programming - graph editor
The orange boxes represent relationships, with names and cardinality on both sides. The cardinality is later regulated by the DSL designer, so that the end user cannot break it. As for relationships, the point of these orange boxes is that they allow different domain classes to be linked together when editing an already finished DSL.
Warning : the DSL designer applies a number of rules to your language, one of which requires that all elements are part of something. This means that all elements must be reduced to a single, "contra" container. (If you remember that DSL == XML, the reason for this requirement should be obvious.)
We used the embedding relationship to tell our DSL that both processes and comments are part of a common model. Now we can use reference relationship to determine that processes can have comments and that the two elements can be linked.
Asynchronous programming - graph editor
The dotted line above indicates a reference relationship, i.e., in our case, the operation can simply refer to to a comment – rather than containing it.Of course, such a relation has its own visual element (the line that links the operation and the comment), which is what we are going to talk about now.

Tulboxes, finally

Once you have the logical and visual part of your DSL, you need to let users drag and drop items from that DSL to their designer. That’s where you need to start, with the Editor node in the DSL Explorer:
Asynchronous programming - graph editor
To create a newitem for the toolbox, right-click on the entire DSL. You will see the following menu :
Asynchronous programming - graph editor
There are two options here – connectors and elements. Connectors are lines (maybe even with arrows) that connect the elements together. And elements are block-like structures.
After creating a newelement, press F4 and you will see the properties of that element :
Asynchronous programming - graph editor
What’s important here is that several of these properties have to be filled in – otherwise the DSL won’t run. The ones that obviously need to be defined are the definition of the domain classwhich reflects the element and also the definition of the icon. (A couple of default icons are already provided, so ifyou’re too lazy to create your own, you can use ready-made ones).

Launching!

Let’s go through the process of creating a DSL:

  1. Made a basic DSL usingwizard
  2. We have added domain classes to represent the concepts we want, such as process
  3. We added relationships between duomen classes-in our case, we defined the fact that operations belong to a common model, and that they have comments. We also added transition operations between operations, as well as start and end elements.
  4. Defined the visual elements that our DSL will use.
  5. Have linked visual elements to domain classes.
  6. Create toolbox controls and link them to the corresponding classes.

Our DSL is halfway done : we have only defined the visual part. After we transformed all the templates and started our language, we can finally start playing with our DSL:
Asynchronous programming - graph editor

Concepts

For our asynchronous DSL, we have defined the following idioms :

  • Operation
    This is our unit of work, for example ‘make tea’. We imply that the operation can proceed without any failure.
  • Process
    A process is a sequence of operations in a graph. The only reason we added this element is to be able to keep multiple graphs of operations in one class.
  • Start and Finish
    The process has to start and end somewhere, so we created two elements to mark the start and end states.
  • Finish-to-start transition
    This transition specifies that an operation can only be started after another operation has been completed.
  • Start-to-start transition
    This transition defines that an operation can start only when another operation has been started and not before.

Let’s look at a real-life example : the process of eating breakfast (I know, not very clever). To make breakfast, I have to put the kettle on, and put the bread in the toaster – in any order. While everything is cooking, I want to get the jam, but only ifI’ve already turned on the toaster. When I have both the bread ready and the jam out, I can make a sandwich. And only when both the sandwich and the tea are ready can I proceed to devour my breakfast.
Using our DSL, the whole process can be defined like this :
Asynchronous programming - graph editor
As you may have guessed by now, the bold lines represent finish-to-start, and the dashed lines represent start-to-start.

Transforming the model with T4

The breakfast visual model only exists as a DSL, so we need T4 to turn it into full-fledged code. Fortunately, by the time we have to do the conversion, the model is already converted to XML format, so all we have to do is bypass it and generate what we need.
The production of the final result in T4 is driven by several methods, such as WriteLine() (writes a string to an end file) and Push/PopIndent() (keeps the number of indents on the stack).
I’m not going to present the T4 transformation code here – you can download it from the link above. Instead, I will show what our DSL will produce from the definition of breakfast.

namespace Debugging<br/>
{<br/>
using System.Threading;<br/>
partial class Breakfast<br/>
{<br/>
private readonly object MakeSandwichLock = new object ();<br/>
private readonly object EatBreakfastLock = new object ();<br/>
private readonly object GetJamLock = new object ();<br/>
private bool MakeTeaIsDone;<br/>
private bool ToastBreadIsDone;<br/>
private bool GetJamIsDone;<br/>
private bool MakeSandwichIsDone;<br/>
private bool MakeTeaStarted;<br/>
private bool ToastBreadStarted;<br/>
private bool GetJamStarted;<br/>
private bool MakeSandwichStarted;<br/>
protected internal void MakeTea()<br/>
{<br/>
MakeTeaImpl();<br/>
lock (EatBreakfastLock)<br/>
{<br/>
MakeTeaIsDone = true ;<br/>
Monitor.PulseAll(EatBreakfastLock);<br/>
}<br/>
}<br/>
protected internal void ToastBread()<br/>
{<br/>
lock (GetJamLock)<br/>
{<br/>
ToastBreadIsDone = true ;<br/>
Monitor.PulseAll(GetJamLock);<br/>
}<br/>
ToastBreadImpl();<br/>
lock (MakeSandwichLock)<br/>
{<br/>
ToastBreadIsDone = true ;<br/>
Monitor.PulseAll(MakeSandwichLock);<br/>
}<br/>
}<br/>
protected internal void GetJam()<br/>
{<br/>
lock (GetJamLock)<br/>
if (!(ToastBreadStarted))<br/>
Monitor.Wait(GetJamLock);<br/>
GetJamImpl();<br/>
lock (MakeSandwichLock)<br/>
{<br/>
GetJamIsDone = true ;<br/>
Monitor.PulseAll(MakeSandwichLock);<br/>
}<br/>
}<br/>
protected internal void MakeSandwich()<br/>
{<br/>
lock (MakeSandwichLock)<br/>
if (!(ToastBreadIsDone GetJamIsDone))<br/>
Monitor.Wait(MakeSandwichLock);<br/>
MakeSandwichImpl();<br/>
lock (EatBreakfastLock)<br/>
{<br/>
MakeSandwichIsDone = true ;<br/>
Monitor.PulseAll(EatBreakfastLock);<br/>
}<br/>
}<br/>
protected internal void EatBreakfast()<br/>
{<br/>
lock (EatBreakfastLock)<br/>
if (!(MakeTeaIsDone MakeSandwichIsDone))<br/>
Monitor.Wait(EatBreakfastLock);<br/>
EatBreakfastImpl();<br/>
}<br/>
}<br/>
}<br/>

That’s a lot of code! But this code reflects the structure we defined. All we have to do now is to use the generated structure :

namespace Debugging<br/>
{<br/>
partial class Breakfast<br/>
{<br/>
AutoResetEvent eatHandle = new AutoResetEvent( false );<br/>
Random rand = new Random();<br/>
public void Prepare()<br/>
{<br/>
ThreadStart[] ops = new ThreadStart[] {<br/>
MakeTea, <br/>
GetJam, <br/>
ToastBread, <br/>
MakeSandwich, <br/>
EatBreakfast };<br/>
foreach (ThreadStart op in ops)<br/>
op.BeginInvoke( null , null );<br/>
eatHandle.WaitOne();<br/>
}<br/>
private int RandomInterval<br/>
{<br/>
get<br/>
{<br/>
return (1 + rand.Next() % 10) * 100;<br/>
}<br/>
}<br/>
public void MakeTeaImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Make tea" );<br/>
}<br/>
public void ToastBreadImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Toast bread" );<br/>
}<br/>
public void GetJamImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Get jam" );<br/>
}<br/>
public void MakeSandwichImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Make sandwich" );<br/>
}<br/>
public void EatBreakfastImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Eat breakfast" );<br/>
eatHandle.Set();<br/>
}<br/>
}<br/>
}<br/>

The result of calling this code is about :

Make tea
Toast bread
Get jam
Make sandwich
Eat breakfast
All done

Although of course Make tea and Toast bread may also appear in a different order.

Conclusion

DSL Tools is a complex but powerful toolkit. The key characteristic of this package is the ease of working with the language once it has been defined. Here I could only describe the work with DSL Tools superficially, because There are a lot of features and nuances. I hope that this post will motivate someone to do their own research. ■ Asynchronous programming - graph editor

You may also like