.NET
Run elevated task at any point of the Azure Rol lifetime.
by Guerrerotook on Apr.30, 2011, under .NET, Azure, wcf
There are two mostly common used for Windows Azure, starting a new 100% .NET project on Windows Azure, or migrate a legacy project to the cloud. Talking from this point legacy project could be the most difficult project of all, because can use COM components during him lifetime. As you may know all the Windows Azure roles are clean machine so it’s means that you need to install manually all your component COM. Normally this issue is solved by placing on the startup node of the Role configuration file, a .cmd file with all the registration of the COM components.
But what happens if during role’s lifetime you need to execute a task elevated? You can’t do that because the hosting process of the worker role and web role is not executing with elevated rights. So we have to find a place where we can execute task with elevated rights, and this places is the roll’s startup node. So what we have to do is have a sentinel program up and running at roll’s startup and make this sentinel program to accept requests to execute process.
So this is exactly what we are going to do, using WCF to open a Windows pipe to enable communication between two different process in the same machine, enabling the process to send message with the necessary information to run this elevated task.
Let’s do it!
Service definition.
Since all we need is a WCF service be exposed on NetNamedPipeBinding, first of all we have to define a service contract with the operation contract we need. In this case we only need one operation, ExecuteTask.
[ServiceContract(Namespace = "http://azure.plainconcepts.com/schemas/04/2011/azure/executionhost")] public interface IExecutionHost { [OperationContract] void ExecuteTask(ProcessTask host); }
Once we define the service contract now is the time to create the service itself, who is responsible to finally execute the tasks.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class ExecutionHostService : IExecutionHost { public void ExecuteTask(ProcessTask host) { Process process = new Process(); process.StartInfo = host.StartInfo; process.Start(); } }
When we have done this, the next step is host the server itself on the sentinel process that will handle the requests, as we said before we’re going to use the NetNamedPipeBinding.
public class ExecutionHostServiceManager { public ExecutionHostServiceManager() { service = new ExecutionHostService(); ServiceHost host = new ServiceHost(service); string address = "net.pipe://PlainConcepts/Azure/ExecutionHost"; NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); host.AddServiceEndpoint(typeof(IExecutionHost), binding, address); // Add a mex endpoint long maxBufferPoolSize = binding.MaxBufferPoolSize; int maxBufferSize = binding.MaxBufferSize; int maxConnections = binding.MaxConnections; long maxReceivedMessageSize = binding.MaxReceivedMessageSize; NetNamedPipeSecurity security = binding.Security; string scheme = binding.Scheme; XmlDictionaryReaderQuotas readerQuotas = binding.ReaderQuotas; BindingElementCollection bCollection = binding.CreateBindingElements(); HostNameComparisonMode hostNameComparisonMode = binding.HostNameComparisonMode; bool TransactionFlow = binding.TransactionFlow; TransactionProtocol transactionProtocol = binding.TransactionProtocol; EnvelopeVersion envelopeVersion = binding.EnvelopeVersion; TransferMode transferMode = binding.TransferMode; host.Open(); } private ExecutionHostService service; }
And that is!
Now we have to put this together in a console application and wait forever.
class Program { static void Main(string[] args) { new ExecutionHostServiceManager(); Thread.Sleep(Timeout.Infinite); } }
Making call to the service
We now have the service contract, the service definition and the hosting process, now it’s time to define the client and make call to the servicer. Since we’re using WCF we need to define a class that will handle the request to the pipe. To accomplish this it’s necessary to inherit from ClientBase<T>, and T need to the service contract of the service.
public class ExecutionHostClient : ClientBase<IExecutionHost> { static ExecutionHostClient() { string address = "net.pipe://PlainConcepts/Azure/ExecutionHost"; NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); binding.CloseTimeout = TimeSpan.MaxValue; binding.ReceiveTimeout = TimeSpan.MaxValue; binding.SendTimeout = TimeSpan.MaxValue; EndpointAddress endpoint = new EndpointAddress(address); client = new ExecutionHostClient(binding, endpoint); } public ExecutionHostClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public void ExecuteTask(ProcessTask task) { Channel.ExecuteTask(task); } public static void ExecuteRemoteTask(ProcessTask task) { client.ExecuteTask(task); } private static ExecutionHostClient client; }
It’s important to create this proxy with the same configuration of the server, because we’re not using the default Visual Studio code generated proxy, and it’s our responsibility to create the binding and the address of the endpoint.
Invoking services
In this example we are registering COM components by calling the regsvc32.exe in code. We looking for dll files on a known folder of our Windows Azure Solution and invoke the service to make the elevated call.
public class RegisterComHelper { public RegisterComHelper() { } public void Register() { // hay que buscar la localizacion en el servidor de azure de donde estan los ensamblados // como no sabemos donde estan los ficheros tenemos que buscar el modulo // Habitania.RegisterCom.dll que es especifico para este ejemplo // así nos aseguramos que estamos buscando la dll correcta Process current = Process.GetCurrentProcess(); var found = (from p in current.Modules.Cast<ProcessModule>().ToList() where p.ModuleName == "PlainConcepts.Azure.WorkerRoleDemo.dll" select p).FirstOrDefault(); if (found != null) { // a partir de la locacion del modulo cargada por el proceso // somos capaces de encontrar la informacion del directorio y buscar // la carpeta dlls que contiene la lista de dlls que queremos registar string directoryLocation = Path.GetDirectoryName(found.FileName); string dllPath = Path.Combine(directoryLocation, "V3COM30"); string[] files = Directory.GetFiles(dllPath); foreach (var item in files) { if (item.EndsWith(".dll")) RegisterComObject(item); } dllPath = Path.Combine(directoryLocation, "V3COM"); files = Directory.GetFiles(dllPath); foreach (var item in files) { if (item.EndsWith(".dll")) RegisterComObject(item); } } } private void RegisterComObject(string filePath) { ProcessStartInfo info = new ProcessStartInfo(); info.FileName = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.System), "regsvr32.exe"); info.Arguments = string.Format("/i {0}", filePath); info.UseShellExecute = false; ExecutionHostClient.ExecuteRemoteTask(new ProcessTask() { StartInfo = info }); } }
This way of invoking task on Windows Azure seems a little bit complicated, but once is done can be super flexible and permit to extend this demo with custom task and background tasks.
The complete source code can be downloaded from here.
Luis Guererro.
Concurrent programming and Managed Extensibilty Framework
by Guerrerotook on Mar.04, 2010, under .NET, Concurrent, MEF
Here they are! Last month I have been talking about concurrent programming on .NET Framework with Task Parallel Library and MEF (Managed Extensibility Framework), so here you have all the resources, the presentation in PowerPoint format and some demos.
Task Parallel Lirarty demos in Visual Studio 2010 RC format
There are no demos from MEF but you can download some from http://www.codeplex.com/mef/
Writing high performance parallel code with monitors
by Guerrerotook on Nov.29, 2009, under .NET, Concurrent
Multithread application is going to be next issue for developer, and we need to be ready for this big change. In .NET Framework 4, Microsoft introduced Task Parallel Library, a set of API that helps developer creating concurrent applications. That’s means that you don’t have to take care about all those concurrent issues anymore, its mean now is easier to create concurrent code.
Currently I’m working in an application that makes a hard use of all TLP code. What I do is creating a lot of Task objects to manage the executing of a set of rules that have to be executed for each Uri. I maintain a list with all the Task object to observer during time, how much Task are now running, and make some statistics about software speed.
This means I have UI, created in WPF that need to communicate with this code to get all running Task and display this information in the UI. Since I’m using a simple List<Task> to get a reference to all task created during my application execution, I need a way to lock and add Tasks to this list, enumerate this list and remove Tasks. But I want this lock to be shortest one and only lock to important operations like add and remove items when Task are finished.
So I have two important operations, create new Task and add to this list, and when the Task is finish removes this Task from the list, and for that I need to wait if the lock is acquired by other thread, because multiple Tasks can finish at the same time.
Creating task is done by a timer inside my application and this timer is set to run each second to check if the running Task in the system are lower that minimum Task running. If this is true is the moment create more Task and add to the list. Since this timer is executed every second I’ll need to lock my list of Task, do my work and then release the lock. I’m using the list as lock object. But this timer can wait more than one second until the lock is released, because other Task are now executing and finish work and remove items from my list, so It’s mean I’m creating a classic convoy in my application. Every time my timer method is executed and wait until the lock to release, this time is bigger that timer elapsed time, so I need a way to wait only a certain period of time.
What I do in this method is using the new API for Monitor in .NET 4 that attempts, for a number of milliseconds, to acquire an exclusive lock and atomically set a value that indicates whether the lock was taken or not. In my case I set these milliseconds in 400, because in this method I do more stuff than work with my list of Task. If the lock is bigger than 400ms the lock will not be acquired.
bool taken = false; try { Monitor.TryEnter(task, 400, ref taken); if (taken) { if (!cancelationTokenSource.IsCancellationRequested) { var count = (from p in task where p.Status == TaskStatus.Running || p.Status == TaskStatus.WaitingToRun select p).Count(); } } } finally { if (taken) { Monitor.Exit(task); } }
As you can see in this code I’m calling Monitor.TryEnter inside a try/catch block, and then in the finally block if the lock was acquired I release the lock by calling Monitor.Exit. I do in that way because if during the execution of my code an exception is throw, the call to Monitor.Exit at the end of the code will not be executed anymore, causing that I have a lock that will never be released.
My other scenario is when I want to get all Task to count how much task are now running, I do the same inside get accessor of the property Task, but in this case I only wait for 250ms instead of 400ms. If the lock is acquired I copy the collection into a new list and there I can safety count how much Task are running.
public List<Task> Tasks { get { bool taken = false; List<Task> list = null; try { Monitor.TryEnter(task, 250, ref taken); if (taken) { list = new List<Task>(task.ToArray()); } } finally { if (taken) { Monitor.Exit(task); } } return list; } }
This solution is simple and keeps your code with the lowest contention rate giving boots to those important operations that need to wait until the lock is released.
I could also create a wrap over List<T> list using ReaderWriterLockSlim to control read and write operation during lock, but ReaderWriterLockSlim is focus on multiple readers and only one writer, and I my application I have multiple writers and only one reader.
All the best.
Luis.