Avoid TLS calls in services/device drivers while processing IPC calls on WinCE

This post is another of the pretty low-level, under the hood about interprocess communication on WinCE.  I considered not even posting it, but it meets my geek threshold (5 people in the world will need it sooner or later, but probably not more than 5) so I'm going ahead.

One of the main functions of Services.exe on Windows CE is that it provides glue to tie services to applications running on the same device.  There's a bunch of low-level kernel magic that has to happen to make Interprocess Communication (IPC) work and services.exe hides that from you.  An example of this is that an application call to DeviceIoControl() gets turned into the specified service's xxx_IOControl() handler.

When writing these xxx_ handlers (like xxx_Open, xxx_Init, xxx_IOControl -- see here) you must not make any calls that require TLS (thread local storage).  The reason is that the kernel does not support TLS when doing interprocess calls.

The main user of TLS you'll run into is COM.  On PSL threads you can call pre-existing COM objects (provided you know they're clear of TLS calls), but you can't call COM API's like CoCreateInstance and friends.   Fortunately most services avoid COM in the first place, but should you absolutely need to do COM from within a xxx_IOControl() you'll need to spin up a worker thread to do the work, and block until it's completed.  We're aware this is inconvenient and are looking into adding cross-process TLS support in a future version of WinCE (it's not in CE 6.0).

Also note that a lot of DLLs in their DllMain() tend to do TLS just by virtue of doing TlsAlloc, since if they'll  do TLS at any point they'll want the slot reserved.  This means that loading or unloading DLLs from within an xxx_* function can cause problems.  Services.exe in fact spins up a worker thread to load your service when processing an ActivateService() API call (rather than using the same thread that the ActivateService() came in on) in case your service is using TLS in its DllMain.  This worker thread is also used to call your xxx_Init().  Similarly, services.exe spins up a worker thread to unload your service and call its xxx_DeInit().

Everything I say here (save one point) applies to device drivers as well.  I just am biased toward services since that's where most of my experience is so that's why I use it as my examples.  The only difference is that device.exe doesn't spin up worker threads for DLL Load/unload/xxx_Init/xxx_DeInit, since for whatever reason service DLLs are much more likely to bring in stuff that wants to use TLS than device drivers.

As far as what happens with TLS messing up, it depends.  On a fully DEBUG build you'll get a DEBUGCHK if you try to do TLS across a PSL call.  In any event, it's up to the caller to deal with the error gracefully.  This is a good reminder always to handle error conditions even on functions that "shouldn't" fail like the TLS fcns.

[Author: John Spaith]

Comments

  • Anonymous
    January 11, 2007
    I suppose I'm one of those five people.I'm currently working on a Smart Card Driver for WinCE (Windows Mobile 5.0), and I've run into a tangled web of problems just trying to get one example to work.I believe that this article may help us when we create our own driver.
  • Anonymous
    January 12, 2007
    The comment has been removed
  • Anonymous
    February 03, 2007
    This is an excellent post.  I'd like to see more like this.  While I appreciate articles of broad interest it is articles like these that help get that last 10% needed to get a product shipped.  I've been working with CE for 10 years and had no idea about this limitation.  
  • Anonymous
    November 01, 2007
    The comment has been removed
  • Anonymous
    February 21, 2008
    Thanks a lot for the post. If I didn't find it, I would never fix the problem that I had.