Welcome to the Dart White Paper repository. We have created this section to publish White Papers about our technology, ideas for IT solutions implementing TCP and UDP based communications, tips for using our products, and opinions about current and coming technologies. Along with these articles, readers will have the opportunity to respond with their own ideas and we will publish those comments along with the articles. If you have a comment on any article, please submit it to comments@dart.com.

Model View Controller (MVC) Design
Published January 2011

Wikipedia defines Model View Controller (MVC) as a design pattern that isolates application domain logic from the user interface. In this article we will illustrate why Dart favors the MVC pattern for building applications that incorporate communications. First, let's summarize the terminology published by Wikipedia:

  • The model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller). In event-driven systems, the model notifies observers (usually views) when the information changes so that they can react.
  • The view renders the model into a form suitable for interaction, typically a user interface element. Multiple views can exist for a single model for different purposes. A viewport typically has a one to one correspondence with a display surface and knows how to render to it.
  • The controller receives input and initiates a response by making calls on model objects. A controller accepts input from the user and instructs the model and viewport to perform actions based on that input.

Let's examine how ASP.NET exhibits MVC characteristics and VB6 does not.

ASP.NET

The model is our code-behind that performs our business logic, the view is updated by HTML sent to the client, and the controller is the IIS server that provides run-time objects that our code-behind operates on. It is a big improvement over ASP where the model was scripted and not isolated into compiled code-behind modules.

VB6

I've seen how easily a VB6 application can turn into spaghetti code. Good object oriented design techniques (use of classes) helps, but the fundamental problem with VB6 is that program logic is tightly coupled to the UI. It is almost impossible to isolate the model (business logic) from the view (UI) because the model and view logic share the UI thread. So at a very fundamental level, any extensive business logic will interfere with the responsiveness of the UI. With all its benefits, the achilles' heel of VB6 is that lengthy operations cause the UI to freeze. How many times have we been forced to use DoEvents to work around this problem?

Going all the way back to Windows 1.0, the single-thread limitation created the need for an asynchronous programming model. Asynchronous methods are often provided by single-purpose ActiveX controls that spawn a thread under the hood, execute the desired functionality on that thread, and raise an event on the UI thread when done. Although a little awkward, it gets the job done and the UI thread stays responsive.

.NET

Progammers became used to the asynchronous programming model and features were added to .NET to support it (the BeginXXX and EndXXX programming model was added to many basic .NET classes like Stream, etc.). By doing so, Microsoft inadvertently encouraged many of us to continue building VB6-style Model T's when we could be writing multi-threaded Porsches. For example, to read a long file over a Network stream there are only 2 basic techiques when a UI event (from the controller) signals the user's desire to get the file:

Asynchronous Programming Technique
This example uses multiple BeginRead() statements to fill a memory stream. Each BeginRead() uses a new worker thread that is pulled from the ThreadPool and performs a blocking Read() under the hood. The readResult() callback is raised each time BeginRead() completes.

NetworkStream networkStream = new NetworkStream(socket);
byte[] buffer = new byte
[1024];

void button1_Click(object sender, EventArgs e)
{
    // start asynchronous cycle
    networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback
(readResult), null)
}

void readResult(IAsyncResult result)
{
    MemoryStream memoryStream = new MemoryStream();
   
 // Get the results and store in memoryStream for later
    int
 count = networkStream.EndRead(result);
    if
 (count == 0)
    {
        networkStream.Close();
        // done, so perform any post-processing on memoryStream
        // now inform UI thread we are done by marshaling memoryStream to UI
       
 
(result.AsyncState as Control).Invoke(new WaitCallback(notifyUI), memoryStream );
    }
    else
    {
        memoryStream.Write(buffer, 0, count);
        networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback
(readResult), null);
    }
}

void notifyUI(object memoryStream )
{
    // on UI thread now, so update any UI element
}

MVC Programming Technique
This example is truer to the MVC architecture, as all model processing related to the downloaded file is performed within the concise work() method on a worker thread. This example has the same functionality as the Asynchronous sample above, but with additional benefits:

  • All objects are on the stack, localizing all code to the work() method
  • Any conceivable business logic placed in work() is easy to understand and maintain because all code is localized
  • Since the work() method executes on a worker thread, it can easily incorporate other blocking methods that would normally interfere with the UI (such as resolving a hostname, connecting to the server, perform post-processing, accessing databases, etc)
  • Since worker methods are self-contained and independent, this technique easily scales to incorporate multiple concurrent models

void button2_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(work), null
);
}

void work(object nothing)
{
    NetworkStream networkStream = new System.Net.Sockets.NetworkStream(socket);
    MemoryStream memoryStream = new MemoryStream();
    byte[] buffer = new byte[1024];

    int count = networkStream.Read(buffer, 0, buffer.Length);
    while
 (count > 0)
    {
        memoryStream.Write(buffer, 0, count);
        networkStream.Read(buffer, 0, buffer.Length);
    }
    networkStream.Close();
    // done, so perform post-processing on memoryStream
    // now inform UI thread we are done by marshaling memoryStream to UI
    (button as Control).Invoke(new WaitCallback(notifyUI), memoryStream );
}

void notifyUI(object memoryStream )
{
    // on UI thread now, so update any UI element
}

User Interface Updates (View)
The MVC architecture as described abstracts out the view as a sub-system that is event-driven. So instead of mixing model logic with view logic, it is proper to program the model as described above, and simply marshal data to the UI thread for display. Model data moves in one direction only: the model that is executing on a worker thread updates UI elements via marshaling calls. The UI does not poll the model.

Historical Perspective

Dart's technical staff recently discussed the merits of the MVC architecture and made an interesting observation: the asynchronous programming model is really only necessary to overcome the single-threaded limitations of the pre-Windows95 operatings systems and the single-threaded development environments. Once you start developing using .NET on a modern platform the only serious asynchronous programming you have left is asynchronous IO (using IO completion port technology).

When .NET was first released, I have to admit that Dart held onto its asynchronous model bias while internally using synchronous blocking calls on worker threads. We found that by using this new technique (blocking calls on worker threads instead of asynchronous calls) we were able to substantially decrease our code base because we eliminated state information and housekeeping overhead. In addition, our product testing and maintenance became easier to manage.

So we asked ourselves: shouldn't we provide our customers with the same simplicity of programming directly on worker threads?

PowerTCP and PowerSNMP Design Reinvented (4.x)

It became apparent that Dart would provide greater value if we redesigned our product so our customers could program their models directly on worker threads. The following table summarizes how thread-related features changed in Dart's 4.x products:

Feature Traditional Designs PowerTCP & PowerSNMP 4.x
Asynchronous Programming Model Full support. All blocking methods have an asynchronous counterpart. No asynchronous methods needed.
Pseudo-Blocking Blocking methods process UI events while blocking. No UI events to process.
Pure Blocking Full support. Used for ASP.NET, console apps, services. Full support. Optimized for standard worker thread operation.
Events Events for asynchronous completion and progress notification have automatic marshaling. No asynchronous completion events needed; progress and marshaling events provided.
Marshaling None needed. Full support.

Asynchronous Programming Model
By embracing the MVC architecture over traditional asynchronous programming, Dart's new design:

  • Simplifies programming by removing unnecessary asynchronous elements. For example, Dart's newly release Tcp class removes 3 overloaded BeginConnect() methods, 2 BeginReceive() methods, and 2 BeginSend() methods.
  • Provides a Start() method that simplifies the initiation of a worker thread for the model to use.
  • Produces transportable model code: the business logic code is the same across targets (Windows Forms, ASP.NET, console apps, services, devices).
  • Makes Windows Forms development a specialized case that is loosely coupled to the UI.

Pseudo Blocking
This term describes a technique that attempts to provide the benefits of synchronous blocking programming on the UI thread. Dart's previous design started a worker thread that executed the desired function while a DoEvents() loop executed on the UI thread until the worker thread completed. Dart's new design does not include pseudo blocking (however, nothing precludes our customers from implementing this scheme):

  • Removes the DoEvents boolean that controlled pseudo blocking
  • Removes the reentrancy problem that:
    • confused customers (UI events processed by pseudo blocking methods would appear on the stack trace)
    • required internal code to provide some measure of thread safety

Pure Blocking
After removing unnecessary asynchronous and pseudo blocking support, what remains is an uncluttered design that is all business:

  • Responsiveness is optimized and thread context switching overhead is minimized. Results are immediately returned from blocking socket calls in many cases. Socket calls conveniently block while waiting for data (just like UNIX).
  • The developer is led to an optimal application design by eliminating the use of techniques that can lead to unintended side effects.
  • A proven MVC design that reduces both software development risk and life-cycle costs.

Events
The impact on event design is neutral. Some asynchronous completion events were removed (like EndConnect, EndReceive and EndSend), but new events were added that are raised when Marshal() methods are used.

Marshaling
Marshaling data from the model (worker thread) to the view (UI thread) might be tedious, but is necessary because in .NET UI elements must be updated from the UI thread. Dart's new design simplifies the process:

  • The SynchronizingObject property persists to the new design, and is used under the hood to marshal data.
  • Overloaded Marshal() methods (with corresponding events) marshal data and state to the UI where they can be displayed in UI elements.
  • This model can be bypassed by calling Invoke() or BeginInvoke() using any UI control and your own delegate.

Asynchronous IO

The foregoing discussion has focused on the use of blocking methods on worker threads, but asynchronous IO may be preferred for some applications. In fact, Dart uses Socket.AcceptAsync() and Socket.ReceiveFromAsync() internally, and exposes a ReadAsync() method on the Tcp class for specific performance reasons:

  • Dart's Server class uses Socket.AcceptAsync() so it's always armed and ready to accept passive connections. The user's delegate handler is called on the IO completion thread provided by the system. This technique saves the overhead incurred by worker thread context switching.
  • Dart's SNMP classes use Socket.ReceiveFromAsync() to quickly accept SNMP packets without worker thread context switching overhead.
  • Dart's Tcp class exposes a ReadAsync() method for asynchronous socket reads. This allows hundreds of accepted connections to efficiently read their sockets without using hundreds of worker threads (necessary for creating scalable high-performance servers).

The typical client application, however, still functions best operating on a deterministic quantity of worker threads as previously described.

Summary

Dart favors MVC design techniques for building communication applications, and the .NET environment makes it easy to do so. Our customers are more comfortable using worker threads, and are creating more sophisticated multi-threaded applications. It is no longer a single-threaded world. For these reasons we created our 4.x design.

We hope this paper has assisted your design needs. Please email your comments to Michael Baldwin.