CreateThread for Windows 8 Metro


The WinRT programming environment, which is used to create Metro applications for Windows 8, replaces the old Win32 threading APIs such as CreateThread with a new ThreadPool::RunAsync API.  This is generally a good thing (ThreadPool is better than CreateThread for several reasons) but it can be a pain when trying to port existing code that depends on those older APIs.

Porting shim to the rescue!  This code emulates a subset of the Win32 threading APIs as a wrapper on top of WinRT ThreadPool:

  • CreateThread
  • CREATE_SUSPENDED and ResumeThread
  • Partial support for SetThreadPriority  (see comments in the header for details)
  • Sleep
  • Thread local storage  (TlsAlloc, TlsFree, TlsGetValue, TlsSetValue, plus see header comments about TlsShutdown)

Get it here:  ThreadEmulation.zip

I recommend only using this for porting legacy code.  When writing new Metro applications, it is better to directly use WinRT ThreadPool.


Comments (24)

  1. ChaosDev says:

    Oh. It looks so hard. Especially after Thread/BackgroundWorker in C#..

  2. ShawnHargreaves says:

    > It looks so hard

    WinRT ThreadPool is very simple and easy to use.

    This porting shim is for people who want to easily port legacy Win32 code, rather than easily write new WinRT code.

  3. Michael Hansen says:

    a nice sample once again

    shawn can you explain or does this divide the workload on multiple cores

    if not can you post a smaple on multiple cores, or point me in the right direction to find info on this

    as i think that when the tablets comes it has multiple cores running as showen at your videos running the nvidia tablets

    Thanks

    Michael

  4. XNALover says:

    Shawn, PLEASE PLEASE PLEASE I want to port my XNA windws phone game to Win 8 metro. Will there be a XNA GS 5.0 with support for metro style apps?

  5. neat says:

    although these api's are only for Metro apps,

  6. Use WinRT threadpool instead says:

    "shawn can you explain or does this divide the workload on multiple cores"

    That's what WinRT Threadpool was designed to do. If you want that, use it directly and you will get all the multi-core scalability. If apps decide to manage their threads directly, as in tradicional models, they need to do everything manually (and probably not get it right).

  7. Joseph says:

    I was wondering if there is a function in WinRT that will tell me how many logical CPU are in the system (cores ( * 2 when hyperthreaded))

    the reason I am asking is to avoid starting too many thread.

    Rule of thumbs should me no more then 4 thread per CPU.

    Any ideas?

  8. ShawnHargreaves says:

    > the reason I am asking is to avoid starting too many thread.

    This is exactly why it is better to use ThreadPool rather than managing your own legacy style long running threads!  ThreadPool understands CPU utilization at a system wide level, and will only submit the appropriate number of work items for simultaneous execution, automatically batching things if you queue up more work than there are available CPU cores to execute it.

    You can query the number of hardware cores from the NUMBER_OF_PROCESSORS environment variable, but this isn't enough to do proper thread scheduling for a couple of reasons:

    – Some cores may be parked (switched off) to conserve power, and this could change at any time

    – Some cores may be in use by other processes – you can't assume you always have exclusive access to all of them

    ThreadPool is part of the OS, so it understands these situations and can respond appropriately. If you care about this level of efficiency, you should use ThreadPool, not try to build your own work management system on top of just a few long running threads.

  9. Experienced Software Engineer says:

    Hi Shawn,

    I see that you have written several times that it is probably better to use ThreadPool than to "roll your own" threading framework. I have two questions:

    1. Is ThreadPool native code, as in truly native, or is it some kind of semi-native wrapper framework?

    2. There are a few of us who are quite comfortable with multi-threading. I wrote my first kernel-mode switcher for the iAPX 386 in 1989. For such people, pulling out CreateThread and forcing them to use something else is a bit abrupt. I can understand that Microsoft might have done this because they were worried that providing choice would have been insufficient to induce people to migrate. Whatever the reason, I think they should have left a choice. That said, do you see any possibility of exposing CreateThread and friends in the foreseeable future?

  10. ShawnHargreaves says:

    ESE:

    1) ThreadPool is a Windows kernel component, so yes, all native code. This functionality is exposed as a WinRT interface (ie a vtable containing pointers to code).

    2) I don't work on the kernel/threading team who owns that decision, so I can't comment on what they might decide to do in the future. I do know they had good reasons for the decisions made for Win8 Store apps, though.

  11. Alecazam says:

    We're having to implement a thread wrapper for legacy code as well.  Not only is it way too complex, but I see that your impl has the same problem as ours.  

    The thread pool recycles the thread handle as soon as the task completes.  That differs from a real thread which stays valid until CloseHandle.  We have legacy code that is tracking it's own data off the thread handle.  When it see the same thread handle appear, it thinks that thread is being used before it's been released.  There needs to be something to prevent the thread/task from going back to the thread pool (yet another event/waitforsingleobjectex at the end of the task).

  12. ShawnHargreaves says:

    Alcazam: my implementation handles the thread exit behavior just fine. The handle returned from the emulated CreateThread remains valid (and can be waited on) until the caller decides to call CloseHandle on it.  This isn't tied to when the task completes (they are related because task completion will signal that handle, but it doesn't close it, which is exactly the same as the original Win32 thread behavior).

  13. Alecazam says:

    Thanks Shawn.  I didn't realize that you could still wait on a thread handle under WinRT.  I've done the DuplicateHandle like you od, but we've been tracking an event with each thread handle.  When we get to a Wait, we wait on the event, and not the thread handle.  The thought was that the thread handle never signals, because it's just thrown back into the pool.

    There's still a need for the physical core count.  Logical cores is already obtained from GetNativeSystemInfo.  We have SIMD tasks that doesn't benefit from the 2x threads.   The unfortunate aspect of a WinRT thread pool is not being able to require a physical core.  We need some hinting mechanism to request that.

  14. ShawnHargreaves says:

    There isn't directly any such thing as a "thread handle" in WinRT – the actual threadpool task is a WinRT interface, not a Win32 handle at all.  The handle returned by my CreateThread emulation is part of the emulation layer, not directly part of the WinRT threadpool implementation.  So it's up to my emulation layer to make this work however I see fit  (which in the case of this implementation was to copy the behavior of the Win32 CreateThread API).

  15. Alecazam says:

    The code still needed a wait at the end of the async task to prevent the thread from being returned to the thread pool.  Otherwise elements that are tied to the thread handle or thread id get very confused when they see that same thread from the thread pool reused before CloseHandle has been hit.  If you try to make your emulation return a threadID, you'll find that the code and suspended thread emulation become a lot more difficult (ie. Microsoft should be adding back this API).

  16. ShawnHargreaves says:

    Alecazam, I don't understand your point here.  There is no such thing as a thread handle or thread ID in the Windows Store programming model.  All the Win32 APIs that return or accept such values are no longer supported, so these concepts no longer exist.

    This emulation code provides an emulation of one method (CreateThread) that does return a 'thread handle', but this is an emulated handle, not a real Win32 thread handle.  It works exactly like a real thread handle in that you can block on it using any of the Win32 wait APIs, and it will be signalled as soon as the 'thread' has finished running, and you can CloseHandle on it when you are done using it, but it is not a real thread handle at the Win32 level, any more than this emulated CreateThread method creates a real Win32 thread  (actually, the 'thread handle' returned by the CreateThread emulation is a Win32 manual reset event).

    This distinction is unimportant, however, because the emulated handle works correctly with the emulated CreateThread, and you do not have access to any of the various Win32 API methods that use real thread handles, so what difference does it make whether the returned value is a 'real' thread handle or not?

  17. cb says:

    Shawn, good stuff.

    I'm curious why you don't just use the Fls functions to replace Tls?  (eg. FlsAlloc for TlsAlloc).  Should be simpler and possibly faster.

  18. Alecazam says:

    I had various routines that would obtain the current thread handle and pull up data off of that.  Since your emulation returns the thread back to the pool after the task completes, legacy code does not expect to see the same "thread handle" (emulated or real) appear until CloseHandle is called on the "thread handle".  I'm only pointing out that the emulation isn't quite right for legacy code.   I know it's an event handle, I use the same approach.

  19. Alecazam says:

    I should clarify that my data was tied to thread id (and that is obtainable in a store app).  It's just the thread id of the thread in the thread pool.   I could change the legacy code to reference off the thread (event) handle, but I wanted to keep that code unchanged.

  20. Jim Wheaton says:

    My legacy code is using GetThreadID.  Is there any way to have the code that is running on a thread know what it's handle is?

  21. ShawnHargreaves says:

    Jim, I looked briefly at trying to emulate the thread ID family of APIs, but obviously didn't get as far as actually doing that. My initial thought is this should be possible but will be quite subtle and in the end I just wasn't convinced how necessary it really was.  My experience is that most of the things you can do with thread IDs can also be done using thread local storage  (in fact, thread ID is most often used just as a building block for some kind of roll-your-own TLS equivalent).

    I'm curious what your code is using GetThreadID for?  Could this be changed to use TLS instead?

  22. Ricky Gai says:

    …I am having issues on calling via C++ thread routine such as shown below

    where no compile error but p->f_iOnThreadProc() seems not calling.

    Please advise.

    //======================================================================================

    //MainPage.Xaml.h

    //

    namespace App1

    {

       public ref class MainPage sealed

       {

       …

       static DWORD WINAPI fnThread (__in void* pv);

       int f_iOnThreadProc (void);

       …

       };

    }

    //=======================================================================================

    // MainPage.Xaml.cpp

    //

    using namespace ThreadEmulation;

    ….

    DWORD WINAPI App1::MainPage::fnThread (__in void* pv)

    {

       App1::MainPage^                   p;

       p = reinterpret_cast<App1::MainPage^>(pv);

       p->f_iOnThreadProc(); // NOTE: No error, but f_iOnThreadProc() is not called.

       return 0;

    }

    int App1::MainPage::f_iOnThreadProc (void)

    {

       HRESULT                     hr;

       int                         x = 0;

       // lbx is Type Listbox

       lbx->Items->Append( "f_iOnThreadProc()…OK" );

       TextBlock^ tbx = ref new TextBlock();

       while( 1 )

       {

           hr = WaitForSingleObjectEx( m_hEvent, 512, TRUE );

           if (hr == WAIT_OBJECT_0)

               break;

           x++;

           tbx->Text = "cnt=[" + (x).ToString() +"]";

           // btnTest is Type Button.

           btnTest->Content = tbx;

       }

       return 0;

    }

    int App1::MainPage::f_iOnThreadOpen (void)

    {

       if( m_hEvent )

          return -1;

       m_hEven = CreateEventEx( nullptr, nullptr, CREATE_EVENT_INITIAL_SET, EVENT_ALL_ACCESS);

       if( m_hEvent == nullptr )

          return -1;

       if( m_hThread )

          return -1;

       m_hThread = CreateThread( 0x00,

                                 0,

                                 this->fnThread,

                                 reinterpret_cast<void*>(this),

                                 0,

                                 &m_dwThreadID ); // m_dwThreadID default to 0x00,

                                                  // assert(unusedThreadId == nullptr); is remarked under ThreadEmulation.cpp

       if (m_hThread == nullptr )

          return -1;

       return 0;

    }

  23. Bill Lowrey says:

    Very nicely done. I have avoided a port to RT for the last 2 years because I have long running threads that occasionally sleep and wait, and the effort to port the 500,000 lines of affected code was too high, given RT's (lack of) market. Just throwing items at a thread pool worked for a number of items, but the long running ones concerned me, especially with thread pool starvation issues. Your use of WorkItemOptions::TimeSliced appears to solve that, although, of course, it requires verification.

    This is the first time I have been hopeful about RT in quite a while. I wish I had seen this 2 years ago!

  24. Brian Salry says:

    Interesting article. Is it at all possible to say wake up a thread when a metro app is asleep aka all threads are suspended in Win32 API? ResumeThread doesn't seem to actually do anything from an external Win32 application.