Quantcast
Channel: Category Name
Viewing all articles
Browse latest Browse all 10804

Using named events to coordinate foreground apps and background agents

$
0
0

In Windows Phone OS 7.1, Microsoft introduced the background agents feature which enables you to periodically run a small piece of code in the background. The same technology is used for background audio playback, and in Windows Phone 8 it was extended for use in some Wallet and VoIP scenarios, too.

The crux of the background agent design is that the background code runs in a completely separate process from the foreground app. This enables the system to place tight limits on the resource usage of background functionality and minimize the impact on the foreground experience.

For many simple agent scenarios, this process separation isn’t really a concern. For example, if the background agent is simply updating a Live Tile every 30 minutes, it can operate pretty much independently of the foreground app. At most, if the background agent needs to coordinate access to a shared resource – for example, an IsolatedStorage file– it could use a named Mutex object to ensure all access is synchronized correctly.

For more complex agents, specifically audio or VoIP agents, process isolation is more problematic because the background agent and the foreground process often need to communicate with each other and require more sophisticated coordination primitives than just mutual exclusion. For example, the agent may need to tell the foreground “I just started pre-downloading the next track,” or “I updated a database table and you should refresh your state”. Such notifications are impossible to create with Windows Phone OS 7.1; at best you can model them by using polling techniques, but this approach is inefficient and prone to errors.

Windows Phone 8 enables these more complex interactions by exposing Win32 inter-process communication (IPC) primitives.

Running the sample code

The sample code that accompanies this post (Using named events for IPC) contains a simple helper library you can use to perform signaling across your foreground and background processes. It also comes with a sample UI and background agent to show you how it works. The app creates and schedules an agent to run approximately every 15 seconds, and it updates its UI to show the current status of the agent based on events it receives from the background process.

If you load the sample code project in Visual Studio 2012 and press F5, you’ll have an app that looks like this:

startup

Now position the emulator so that you can see the Visual Studio Output window in the background, and then press the create agent button. You’ll see the following in the Output window:

Creating agent...
Launching agent shortly...

Tip: If you see lots of other messages in the Output window (starting with 'TaskHost.exe'...") you can right-click on the window and disable all messages except "Program Output" to make it easier to see the messages from the agent.

Soon the following output will appear in the Output window:

*AGENT* Creating events and sleeping for 2 seconds
*AGENT* Press 'start waiting' button NOW!

This is your signal to press the start waiting button in the app, at which time you’ll see something like this:

running

If you don’t tap the button quickly enough, you’ll see the following text in the Output window:

Couldn't open yet; try again once created

This means that the agent completed its work before the app could start listening. If you wait a little while the cycle will repeat, giving you another chance to start waiting (note this is not something you’d typically need to deal with in your app; it’s a side-effect of the way the example shows how to use one particular API).

Once you’ve successfully got the foreground app listening for the agent’s events, one of the following images will appear depending on whether or not the agent completes successfully (the sample is designed to simulate a crash about one-third of the time).

stoppedcrashed

As you might expect, you can press the stop waiting button to stop waiting on the event, or press the cancel agent button to cancel the background agent. Note that the agent displays some additional information in the Output window, but you can mostly ignore it other than the “Press ‘start waiting’ button NOW!” prompt.

How it works

In Windows Phone 8, the ability to write native components and use Win32 APIs opens up a much richer set of features than previous releases, particularly for IPC (inter-process communication). One of these features is named events. Named events are implemented differently than .NET events, but they have the same basic purpose – a decoupled signaling mechanism for one piece of code to notify other pieces of code that something interesting has happened.

Rather than using the += syntax to connect an event handler to an event source, you create (or open) an event by using its name, and then wait for it to be signaled by another thread (or process) that has opened the event using the same name. Here’s the basic pattern:

  1. The source of the event creates the event with a name, for example “data_ready
  2. The consumer opens the event using the same name
  3. The consumer calls WaitAsync() and then waits for the producer to signal the event
  4. When the source needs to signal the consumer, it calls Set(), which causes WaitAsync() to complete
  5. When both sides are done, they Dispose() the object

Typically, step 3 is run inside a loop, waiting for the producer to signal itself again and again. It also doesn’t really matter whether the producer or the consumer creates the event; Windows Phone returns a reference to the already-created event if you try to open the same event twice.

The code for the event source - the background agent in this example – looks something like this:

const string STARTED_EVENT_NAME = "AGENT_STARTED";protected override void OnInvoke(ScheduledTask task)
{
  NamedEvent startedEvent = new NamedEvent(STARTED_EVENT_NAME, true);
  Debug.WriteLine("*AGENT* Press 'start waiting' button NOW!");

  Thread.Sleep(2000);
  startedEvent.Set();
  startedEvent.Dispose();
  NotifyComplete(); }

This code simply creates the event using a known name (reopening the event if it already existed), waits a couple of seconds to give you time to press the start waiting button in the UI, and then calls Set() on the event. It then disposes of the resources and exits.

The code for the event consumer - the foreground process in this example – looks something like this:

// Same name as used in the agentconst string STARTED_EVENT_NAME = "AGENT_STARTED";NamedEvent agentStartedEvent;bool waiting;private void StartClicked(object sender, RoutedEventArgs e)
{
  // This just illustrates how to open an already-created event 
// without actually
creating it. Normally you would just create the
// event since the agent doesn't care
  if (!NamedEvent.TryOpen(STARTED_EVENT_NAME, out agentStartedEvent))
  {
    Debug.WriteLine("Couldn't open yet; try again once created");
    return;
  }

  waiting = true;

  ThreadPool.QueueUserWorkItem(new WaitCallback(async delegate
  {
    while (waiting)
    {
      await agentStartedEvent.WaitAsync();
      if (!waiting)
        break;
      // Update the UI to let us know the agent is running.
      UpdateAgentUI(agentRunningText);
    }

    // Fell off the loop since we were told to stop
    agentStartedEvent.Dispose();
    agentStartedEvent = null;
    UpdateAgentUI(agentUnknownText);
  })); }

The foreground app uses the same name as the agent to open the event, and then it starts running a loop on a worker thread that repeatedly calls WaitAsync() and updates the UI until told to stop (by setting waiting to false in the event handler for the stop waiting button). Note that the app could have created the event by using the exact same code as the agent (new NamedEvent(STARTED_EVENT_NAME, true)), but this sample shows you how to detect whether the event has been created (or not) in case that information is meaningful to you. Note also that because the underlying Win32 event object is shared between your two processes, as long as at least one reference to the event exists in either process, the Windows Phone OS keeps the (native) object alive. If you create the event in the agent and then open it in the foreground, even though the agent calls Dispose() on its copy, the event itself still exists; the next time the agent executes, it will get a reference to the existing (native) object rather than creating a new one. If, however, your foreground app is not running (or it is running but you have not opened the event, or you have disposed the event), the Win32 object is created and destroyed every time the agent executes.

Note that this simplistic mechanism really only works when you have a single producer and a single consumer for the event, and it relies on the event being an “auto-reset” event. If you have multiple consumers and they all call WaitAsync(), only one of them will complete each time Set() is called. Dealing with anything more complex than this is beyond the scope of this post, but luckily for us a Windows Phone app can have at most one foreground process and one background process, so this problem likely won’t arise.

You might be wondering, “what happens if another app uses the same event name as my app?” The answer is that Win32 object names are scoped to your app’s security context - only your foreground and background processes can use your named events (or other IPC primitives); other apps (and the system) have their own versions of these objects, even if they appear to have the same name as yours.

What if you like your event notifications “.NET style”?

For those of you who would like a more traditional .NET-style eventing model, the sample also includes a helper class that wraps the basic NamedEvent class and provides a .NET Signaled event to let you know when the event has been signaled. Although this helper class itself can have many subscribed listeners (using normal += notation), there can be at most one NamedEvent object that is waiting on a single Win32 named event object because, as noted earlier, there is only ever one underlying Win32 object with that name. The NamedEventHelper class handles this limitation by storing a static dictionary of registered wrappers and handing out an existing instance if one already exists:

static Dictionary<string, NamedEventHelper> knownHelpers = 
new Dictionary<string, NamedEventHelper>();public static NamedEventHelper GetEvent(string name) {
  NamedEventHelper helper;

  lock (typeof(NamedEventHelper))
  {
    if (!knownHelpers.TryGetValue(name, out helper))
    {
      helper = new NamedEventHelper(name);
      knownHelpers.Add(name, helper);
    }
  }

  return helper; }

Even more complex IPC scenarios

Note that with Windows Phone 8 you can also perform much richer IPC between your agent and your foreground process by using a technique called WinRT remoting, but this is a more complex topic that requires writing custom C++ code and is best suited to VoIP-style scenarios. Perhaps a future blog post will cover that topic.


Viewing all articles
Browse latest Browse all 10804

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>