New multi-threading techniques with UCMA 4.0

Recently I've written a lot about using some of the new multi-threading capabilities in .NET 4.5 with the UCMA SDK, and I wanted to put together a complete example in an application that might actually be useful. I like to take a break every so often to get up from the computer, walk around, and stretch, and I've tried a few break reminder applications that give you a pop-up reminder after a period of time. The trouble I always have is that I forget to disable them when I'm on calls, or presenting, and they pop up and get in the way. So I thought I would write a simple app to send IM break reminders, but only when the user is in an Available state in Lync.

I created a C# console app project, targeting .NET Framework 4.5, and changing the platform to x64. I also added a reference to Microsoft.Rtc.Collaboration from the UCMA 4.0 SDK. You can find the code for the whole app here. Once it starts, you send an IM to the app to tell it to start monitoring you, and another IM to tell it to stop.

Let's start with the extension methods. These allow the code to perform the UCMA operations that I need with the await keyword. Here's the static class with the extension methods:

[csharp] using System.Threading.Tasks; using Microsoft.Rtc.Collaboration; using Microsoft.Rtc.Signaling;

namespace LyncBreakReminder { public static class ExtensionMethods { public static Task StartupAsync(this CollaborationPlatform platform) { return Task.Factory.FromAsync(platform.BeginStartup, platform.EndStartup, null); }

public static Task ShutdownAsync (this CollaborationPlatform platform) { return Task.Factory.FromAsync(platform.BeginShutdown, platform.EndShutdown, null); }

public static Task EstablishAsync(this LocalEndpoint endpoint) { return Task.Factory.FromAsync( endpoint.BeginEstablish, endpoint.EndEstablish, null); }

public static Task TerminateAsync(this LocalEndpoint endpoint) { return Task.Factory.FromAsync(endpoint.BeginTerminate, endpoint.EndTerminate, null); }

public static Task AcceptAsync(this Call call) { return Task.Factory.FromAsync(call.BeginAccept, call.EndAccept, null); }

public static Task EstablishAsync(this Call call, string destinationUri, CallEstablishOptions options) { return Task.Factory.FromAsync( call.BeginEstablish, call.EndEstablish, destinationUri, options, null); }

public static Task TerminateAsync(this Call call) { return Task.Factory.FromAsync(call.BeginTerminate, call.EndTerminate, null); }

public static Task SendInstantMessageAsync(this InstantMessagingFlow flow, string textBody) { return Task.Factory.FromAsync( flow.BeginSendInstantMessage, flow.EndSendInstantMessage, textBody, null); } } } [/csharp]

There's a pretty obvious pattern here, so you can probably extrapolate to any other methods you want to create. Any parameters that need to be passed to the Begin method go between the End method and the null at the end (which is for the async state parameter).

With these extension methods in place, I can greatly simplify my UCMA code. The resulting code in the application is a lot more "normal" looking than your average UCMA 3.0 code; it's easier to read linearly, although it's still of course executing in the same asynchronous way. Here is the startup and shutdown code, for example:

[csharp] internal async void Start() { string applicationId = ConfigurationManager.AppSettings["applicationId"];

ProvisionedApplicationPlatformSettings settings = new ProvisionedApplicationPlatformSettings("breakreminder", applicationId);

_platform = new CollaborationPlatform(settings); _platform.RegisterForApplicationEndpointSettings( OnApplicationEndpointDiscovered);

try { await _platform.StartupAsync(); } catch (InvalidOperationException iex) { Console.WriteLine(iex); } catch (RealTimeException rex) { Console.WriteLine(rex); } }

internal async void Stop() { try { await _endpoint.TerminateAsync(); await _platform.ShutdownAsync(); } catch (InvalidOperationException iex) { Console.WriteLine(iex); } catch (RealTimeException rex) { Console.WriteLine(rex); } } [/csharp]

The await keyword along with those Async extension methods allow you to consolidate all the shutdown code into two statements within a single try/catch block:

[csharp] try { await _endpoint.TerminateAsync(); await _platform.ShutdownAsync(); } catch (InvalidOperationException iex) { Console.WriteLine(iex); } catch (RealTimeException rex) { Console.WriteLine(rex); } [/csharp]

For comparison, here's what that same thing would look like the old way:

[csharp] try { _endpoint.BeginTerminate(terminateAsyncResult => { try { _endpoint.EndTerminate(terminateAsyncResult);

try { _platform.BeginShutdown(shutdownAsyncResult => { try { _platform.EndShutdown(shutdownAsyncResult); } catch (RealTimeException ex) { Console.WriteLine(ex); } }, null); } catch (InvalidOperationException ex) { Console.WriteLine(ex); } } catch (RealTimeException ex) { Console.WriteLine(ex); } }, null); } catch (InvalidOperationException ex) { Console.WriteLine(ex); } [/csharp]

I'm also able to use a ConcurrentDictionary to hold instances of the session class I created for each user who is being tracked, allowing me to avoid some locks while still avoiding race conditions:

[csharp] // Collection of break reminder sessions currently in use readonly ConcurrentDictionary _sessions = new ConcurrentDictionary();

...

private void OnIncomingInstantMessagingCallReceived(object sender, CallReceivedEventArgs e) { // Grab the SIP URI of the user sending the IM. string sipUri = e.Call.RemoteEndpoint.Participant.Uri;

// Create a new session to use if there isn't already one, // and subscribe to session status changes in advance to avoid // race conditions. BreakReminderSession newSession = new BreakReminderSession(sipUri); newSession.BreakReminderSessionStatusChanged += OnBreakReminderSessionStatusChanged;

// Get the existing session or add the new one in a threadsafe way. BreakReminderSession sessionToUse = _sessions.GetOrAdd(sipUri, newSession);

// Pass the IM along to the session. sessionToUse.HandleMessage(e.Call, e.ToastMessage);

// If we didn't end up using the new session, unsubscribe our // event handler to avoid memory leaks. if (newSession != sessionToUse) { newSession.BreakReminderSessionStatusChanged -= OnBreakReminderSessionStatusChanged; } } [/csharp]

Another part where the differences are really striking is when the app sends reminder messages after the timer interval has expired. Here is the code:

[csharp] private async void OnAvailableTimerElapsed(object state) { try { Conversation reminderConversation = new Conversation(_endpoint); InstantMessagingCall reminderCall = new InstantMessagingCall(reminderConversation);

await reminderCall.EstablishAsync(this.SipUri, null); await reminderCall.Flow.SendInstantMessageAsync( "This is your reminder to take a break."); await reminderCall.TerminateAsync(); } catch (InvalidOperationException ex) { Console.WriteLine(ex); } catch (RealTimeException rex) { Console.WriteLine(rex); } } [/csharp]

It's incredibly awesome to me that we can now write code like this in UCMA applications. Before, this would have involved at least three separate callback methods, six try/catch blocks, and generally much less intuitive code.