Category Archives: Framework

Cinchoo – Abortable long running aync task

Introduction

In .NET, there are number of ways you can start asynchronous long running tasks by using Thread / Task / ThreadPool. Every approach has pros and cons to it. Please refer MSDN for more information about them. In this article I’m going to elobarate about method exists in Cinchoo framework where you can start abortable long running task asnychrously.

First, each windows application has a single main thread it uses to render the window / execute main task, process events and execute custom code. In a single threaded environment, any long running tasks will block the main thread. In particlular it will block the main UI thread and the application becomes unresponsive in Windows application.

Cinchoo simplifies the model to execute a long running tasks asynchronously and gives control to the caller to abort it anytime. Besides the standard behaviour, it offers few useful features like number of retries in case of failure, timeout period via the API. Alright lets cut to the chase on to how to use them using the code.

Download the Latest Cinchoo Binary Here. (Nuget Command: Install-Package Cinchoo)

Using the Code

The API is exposed via ChoActionEx class. The signature of the API looks below

public static class ChoActionEx
{
    public static ChoAbortableAsyncResult RunAsync(this Action action, ChoAbortableAsyncCallback callback = null, object state = null, int timeout = -1, int maxNoOfRetry = 0, int sleepBetweenRetry = 5000);
}

Where

  • action – A long running task method.
  • callback – References a method to be called when a corresponding asynchronous operation completes.
  • state – A user-defined object that qualifies or contains information about an asynchronous operation.
  • timeout – A time-out value in milliseconds. -1, infinite.
  • maxNoOfRetry – Maximum number of retry attempt made in case of failure.
  • sleepBetweenRetry – Number of milliseconds to sleep between retry. Default is 5000 ms.

This API method returns ChoAbortableAsyncResult object. This encapsulates the results of an asynchronous operation on a delegate. It exposes the below members

  • AsyncState – Gets the object provided in the ‘state’ parameter of a RunAsync method call.
  • AsyncWaitHandle – Gets a WaitHandle that encapsulates Win32 synchronization handles, and allows the implementation of various synchronization schemes.
  • CompletedSynchronously – Gets a value indicating whether the RunAsync call completed synchronously.
  • IsAborted – Gets a value indicating whether the server has aborted the call.
  • IsCompleted – Gets a value indicating whether the server has completed the call.
  • IsRetryAttempt – Gets a value indicating whether the server has retried the call.
  • IsTimeout – Gets a value indicating whether the method has completed with the timeout.
  • Result – Gets the result value from the async call if any.
  • RetryCount – Gets a value of the number of retry attempt made by the server.
  • Exception – Gets any exception captured during task execution.
  • CanContinue – Gets or Sets where to continue the retry.
  • EndInvoke() – Retrieves the return value of the asynchronous operation. If the asynchronous operation has not been completed, this function will block until the result is available.
  • Abort() – Abort the currently executing asynchrounous call.

Sample #1

Below sample shows how to execute a method asynchrounously, wait for it to complete.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine();
 
        //Run method async, wait for it to complete
        Console.WriteLine("TEST #1: Run method, wait for it to complete...");
        ChoAbortableAsyncResult r = ChoActionEx.RunAsync(LongRunningTask);
        Console.WriteLine("Waiting for worker thread to complete.");
        r.EndInvoke();
        Console.WriteLine();
    }
 
    private static void LongRunningTask()
    {
        Console.WriteLine("Starting task... (Sleeping for 10 secs)");
        Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10 * 1000);
        Console.WriteLine("Task completed.");
    }
}

Sample #2

Below sample shows how to execute a method asynchrounously, abort it after 5 secs.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine();

        //Run method async, abort it after 5 secs
        Console.WriteLine("TEST #2: Run method, abort after 5 secs...");
        ChoAbortableAsyncResult r1 = ChoActionEx.RunAsync(LongRunningTask);
        Console.WriteLine("Waiting for 5 secs...");
        Thread.Sleep(5000);
        Console.WriteLine("Aborting working thread.");
        r1.Abort();
        Console.WriteLine();
    }
 
    private static void LongRunningTask()
    {
        Console.WriteLine("Starting task... (Sleeping for 10 secs)");
        Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10 * 1000);
        Console.WriteLine("Task completed.");
    }
}

Sample #3

Below sample shows how to execute a method asynchrounously, timeout after 5 secs.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine();

        //Run method async, with timeout 5 secs
        Console.WriteLine("TEST #3: Run method with 5 secs timeout...");
        ChoAbortableAsyncResult r2 = ChoActionEx.RunAsync(LongRunningTask, null, null, 5000);
        Console.WriteLine("Waiting for worker thread to complete or timeout.");
        try
        {
            r2.EndInvoke();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.WriteLine();
    }
 
    private static void LongRunningTask()
    {
        Console.WriteLine("Starting task... (Sleeping for 10 secs)");
        Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10 * 1000);
        Console.WriteLine("Task completed.");
    }
}

Sample #4

Below sample shows how to execute a method asynchrounously, retry couple of attempts.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine();

        //Run a exception thrown method async 
        Console.WriteLine("TEST #4: Run method with 2 retries...");
        ChoAbortableAsyncResult r3 = ChoActionEx.RunAsync(LongRunningTaskWithException, null, null, -1, 2, 5000);
        Console.WriteLine("Waiting for worker thread to complete.");
        try
        {
            r3.EndInvoke();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.WriteLine();
    }
    
    private static void LongRunningTaskWithException()
    {
        Console.WriteLine("Starting task... (Sleeping for 10 secs)");
        Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10 * 1000);
        throw new ApplicationException("Test task exception.");
    }
}

Sample #5

Below sample shows how to execute a method asynchrounously, cancel it in the callback.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine();

        Console.WriteLine("TEST #5: Run method with 2 retries, but cancel the retry on callback...");
        ChoAbortableAsyncResult r5 = ChoActionEx.RunAsync(LongRunningTaskWithException, (t) =>
        {
            ChoAbortableAsyncResult t1 = t as ChoAbortableAsyncResult;
            Console.WriteLine("Canceling the task...");
            if (t1.Exception != null)
                t1.CanContinue = false;
        }, null, -1, 2, 5000);
        try
        {
            r5.EndInvoke();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.WriteLine();
    }
    
    private static void LongRunningTaskWithException()
    {
        Console.WriteLine("Starting task... (Sleeping for 10 secs)");
        Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(10 * 1000);
        throw new ApplicationException("Test task exception.");
    }
}
Advertisements

Cinchoo – Allow multi-instance application

Multi-Instance Application

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. It also offers few other key functionalities like Common ApplicationHost, BCL etc.

When you choose to use this library in your application, it will inherently allow only one instance of your application running at any time. This is the default behavior. When you run the second instance, it will throw below application error and followed by application termination.

Already another instance of this application running.

This behavior can be override able to allow more than one instance of your application can be runnable at any time. There are couple of ways you can do so.

Programmatically

This section illustrates on how to allow multiple instances of your application run at the same time. Simply subscribe to ChoApplication.ApplyGlobalApplicationSettingsOverrides event as below

class Program
{
    static void Main(string[] args)
    {
        ChoApplication.ApplyGlobalApplicationSettingsOverrides += new EventHandler<ChoEventArgs<ChoGlobalApplicationSettings>>(ChoApplication_ApplyGlobalApplicationSettingsOverrides);

        ChoConsole.PauseLine();
    }

    static void ChoApplication_ApplyGlobalApplicationSettingsOverrides(object sender, ChoEventArgs<ChoGlobalApplicationSettings> e)
    {
        e.Value.ApplicationBehaviourSettings.SingleInstanceApp = false;
    }
}

PS: Make sure these initializations happens at the application entry point. In Windows Service, the event subscriptions are done at OnStart() method in ServiceBase subclass. In ASP.NET, the subscriptions are done in Application_Start() in Global.aspx file.

Configuration

This section illustrates how to make this through via configuration. Open the ChoCodeFrx.xml file. Look for singleInstanceApp attribute and assign ‘false’ as value. See sample below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="ConfigFileOverride.Test.exe" eventLogSourceName="ConfigFileOverride.Test.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    ...
  </globalApplicationSettings>
  <metaDataFilePathSettings>
    ...
  </metaDataFilePathSettings>
</configuration>

 

Cinchoo – Framework Tips

Here I’m going to talk about couple of tips on using Cinchoo framework in your project.

At the start of the application, you must call ChoFramework.Initialize() to initialize the framework and start necessary services. Usually you must place this statement in application Main entry point as below.

Calling ChoFramework.Shutdown() method is not mandatory to call. But it is safe to make this call for graceful shutdown of all the background services.

static void Main(string[] args)
{
    ChoFramework.Initialize();
    try
    {
        ChoApplication.ApplyFrxParamsOverrides += new EventHandler<ChoFrxParamsEventArgs>(ChoApplication_ApplyFrxParamsOverrides);

        SampleConfigSection ApplicationSettings = new SampleConfigSection();
    }
    catch (Exception ex)
    {
        Console.WriteLine("ERROR: " + ex.Message);
    }
    finally
    {
        ChoFramework.Shutdown();
    }
}

ChoAppDomain.

Cinchoo – Overriding Framework Configuration Parameters

In this section, I’ll go over the list of Cinchoo framework configuration parameters can be overridden to control the run-time environment of the framework either through configuration or programmatically.

Framework will run fine without overriding these parameters. Only when you need, you can override them. In those situation, it can be done through configuration or programmatically.

First, we will see how to override them through configuration. All the framework parameters must be stored in ChoCoreFrx.config file under application executable folder. This is the default behavior of discovering this file by the framework. But this can be overridden by specifying the path to this file through appFrxSettings custom configuration section in App.config file as below

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="appFrxSettings" type="Cinchoo.Core.ChoAppFrxSettings, Cinchoo.Core" />
    </configSections>
    <appFrxSettings appFrxFilePath="C:\ConfigFolder" />
</configuration>

GlobalApplicationSettings

There are the application level settings used by framework. Below are the attributes and their usages.

  • globalApplicationSettings – Global application parameters used by framework. All the variables are optional.
    • singleInstanceApp [bool] – true, to ensure there is only one instance of the application running at any point in time. false, lets you to run multiple instances of the application. Default is false.
    • applicationId [string] – the custom application name. Default value is the executable name.
    • eventLogSourceName [string] – Event log source name where all the errors are posted on this name, default value is applicationId.
    • useApplicationDataFolderAsLogFolder [bool] – true, it uses the current roaming user directory. Default is false.
    • logFolder [string] – A log directory, where all the log files are created by framework. Default is ‘[appExePath]\Logs’.
    • appConfigPath [string] – configuration file path. If not specified, this will be [appExeFolder].[appExeName].xml
    • logTimeStampFormat [string] – A time stamp format used when logging information to log files. Default value is “yyyy-MM-dd hh:mm:ss.fffffff”
    • traceLevel [int] – Specifies what message to output for the Trace/ChoTrace classes. 0-Off, 1-Error, 2-Warning, 3-Info, 4-Verbose.

Here is the sample Global Application Settings section in ChoCoreFrx.config file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <globalApplicationSettings singleInstanceApp="false" applicationId="TestApplication" eventLogSourceName="TestApplication" useApplicationDataFolderAsLogFolder="false">
    <logFolder>C:\Logs</logFolder>
    <appConfigPath>c:\config\TestApplication.xml</appConfigPath>
    <logTimeStampFormat>yyyy-MM-dd hh:mm:ss.fffffff</logTimeStampFormat>
  </globalApplicationSettings>
</configuration>

MetaDataFilePathSettings

Cinchoo framework creates as well as uses number of Meta-Data files, these file paths can be configured through ChoCoreFrx.config file.

  • configurationMetaDataFilePath – meta data file path used by Configuration manager. By default, it’s value is [appExeName].config.meta
  • pcMetaDataFilePath – meta data file path used by Performance Counter manager. By default, its value is [appExeName].perf.meta

Here is the sample xml section looks like

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <metaDataFilePathSettings>
    <configurationMetaDataFilePath>c:\Config\configurationMetaFile.meta</configurationMetaDataFilePath>
    <pcMetaDataFilePath>c:\Config\PCMetaFile.meta</pcMetaDataFilePath>
  </metaDataFilePathSettings>
</configuration>

Run-Time approach

Lets see how we can override the system parameters at run-time in here. Cinchoo framework exposes ChoApplication.ApplyFrxParamsOverrides event. By subscribing to this event, you have a chance to override the framework parameters at run-time. See below the sample code on how to override some of the system parameters

static void Main(string[] args)
{
    try
    {
        ChoApplication.ApplyFrxParamsOverrides += new EventHandler<ChoFrxParamsEventArgs>(ChoApplication_ApplyFrxParamsOverrides);
        XmlApplicationSettings ApplicationSettings = new XmlApplicationSettings();
    }
    finally
    {
        ChoAppDomain.Exit();
    }
}

static void ChoApplication_ApplyFrxParamsOverrides(object sender, ChoFrxParamsEventArgs e)
{
    e.GlobalApplicationSettings.SingleInstanceApp = true;
    e.MetaDataFilePathSettings.ConfigurationMetaDataFilePath = @"C:\Config\MetaDataFilePath.meta";
}