0

we have a big and old software project. This software runs in older days on an old OS, so it has an OS-Wrapper. Today it runs on windows. In the OS-Wrapper we have structs to manage threads. One Member of this struct is the thread-Id, but it is defined with an uint16_t. The thread-Ids will be generated with the Win-API createThreadEx. Since some month at one of our customers thread-Ids appears which are greater than

numeric_limits<uint16_t>::max()

We run in big troubles, if we try to change this member to an uint32_t. And even if we fix it, we had to test the fix.

So my question is: How is it possible in windows to get thread-Ids which are greater than 0xffff? How must be the circumstances to reach this?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
cpp-progger
  • 406
  • 3
  • 6
  • 2
    I refer you to [Raymond Chen](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/06/10112270.aspx), who has covered this. Although he talks about Process ID's, it's essentially the same thing as far as you're concerned. – icabod Jun 04 '14 at 15:21
  • There is no Win API createThreadEx function. Are you talking about handles or really about thread IDs (DWORD)? – Werner Henze Jun 04 '14 at 15:22
  • possible duplicate of [What is the maximum process Id on Windows?](http://stackoverflow.com/questions/17868218/what-is-the-maximum-process-id-on-windows) – Roman R. Jun 04 '14 at 15:26
  • It turns out that HANDLE always fits in a DWORD for kernel handle values (file, socket, process, thread, mutex, etc.). INVALID_HANDLE_VALUE won't though. – Joshua Jun 04 '14 at 15:27
  • There is no `CreateThreadEx` but there is a `CreateRemoteThreadEx`. Which thread creation function do you mean. – Cheers and hth. - Alf Jun 04 '14 at 15:32
  • It doesn't really matter how you create the threads, the IDs all come from the same pool – David Heffernan Jun 04 '14 at 15:37
  • @Heffernan: re "It doesn't really matter how you create the threads", of course it does. When the description is incorrect it says nothing about e.g. your assumption of "all come from the same pool". That's unknown, we don't know the threading library being used, until such time as the OP gives a not obviously incorrect spec. – Cheers and hth. - Alf Jun 04 '14 at 15:41
  • @icabod, thanks - that was the right hint. Now I was able to write a program which generates so large thread-ids. – cpp-progger Jun 04 '14 at 15:44
  • @Werner Henze, I talk about thread-Ids (DWORD) - and Yes, the function is CreateThread. – cpp-progger Jun 04 '14 at 15:47
  • @Cheersandhth.-Alf, Threads are an OS concept, it doesn't matter if they are created with WINAPI, CRT, or C++ libraries, they still end up calling into the OS to create the threads, thus the Thread IDs are still defined by how the OS creates them. – josh poley Jun 04 '14 at 15:51
  • @joshpoley: re "thus the Thread IDs are still defined by how the OS creates them", nope. A threading library can define thread id's and limits on number of threads, any way it wants. Easy proof: you can write that yourself (or at least I can). – Cheers and hth. - Alf Jun 04 '14 at 15:53
  • @joshpoley: re "Threads are an OS concept", nope. C++11 supports thread directly in the standard C++ library. – Cheers and hth. - Alf Jun 04 '14 at 15:53
  • @Cheersandhth.-Alf, That isn't a Thread ID, that is a random number that is arbitrarily assigned to a thread, (that has a Thread ID). – josh poley Jun 04 '14 at 15:54
  • @joshpoley: re " it doesn't matter if they are created with WINAPI, CRT, or C++ libraries, they still end up calling into the OS to create the threads", nope. E.g. in the early 1990's I created (limited) threading based on `longjmp`. – Cheers and hth. - Alf Jun 04 '14 at 15:54
  • This comment sequence starts to look like "let's overwhelm with a really large number of incorrect not totally implausible assertions". Argh. – Cheers and hth. - Alf Jun 04 '14 at 15:56
  • @Cheers no need for swearing here thanks. Any abstraction of the os thread will include a way to identify threads. The C++ std lib does so. And that identification must support at least as many values as the underlying system does. So for the purpose of the question it's clear that 16 bit ids are insufficient for any threading library on windows. – David Heffernan Jun 04 '14 at 16:24

2 Answers2

3

Windows thread IDs are 32 bit unsigned integers, of type DWORD. There's no requirement for them to be less than 0xffff. Whatever thought process led you to that belief was flawed.

If you want to stress test your system to create a scenario where you have thread IDs that go above 0xffff then you simply need to create a large number of threads. To make this tenable, without running out of virtual address space, create threads with very small stacks. You can create the threads suspended too because you don't need the threads to do anything.

Of course, it might still be a little tricky to force the system to allocate that many threads. I found that my simple test application would not readily generate thread IDs above 0xffff when run as a 32 bit process, but would do so as a 64 bit process. You could certainly create a 64 bit process that would consume the low-numbered thread IDs and then allow your 32 bit process to go to work and so deal with lower numbered thread IDs.

Here's the program that I experimented with:

#include <Windows.h>
#include <iostream>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    return 0;
}

int main()
{
    for (int i = 0; i < 10000; i++)
    {
        DWORD threadID;
        if (CreateThread(NULL, 64, ThreadProc, NULL, CREATE_SUSPENDED, &threadID) == NULL)
            return 1;
        std::cout << std::hex << threadID << std::endl;
    }
    return 0;
}
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I know this - of course. But that is not the question! We have thousands of this applications in the world, but only at one customer appears this problem. Consider this is an old project, which was not written for Windows! – cpp-progger Jun 04 '14 at 15:39
  • 1
    So what is the question then? If you already know that thread IDs do not fit into a 16 bit type, why are you storing them in 16 bit types? The only reason I could imagine for you storing them in 16 bit types is that you believed that the values would fit. If you know that they don't fit then you know that your code is broken, and you know what you need to do to fix it. – David Heffernan Jun 04 '14 at 15:45
  • As currently given this source code does not compile by default, since it uses `_tmain` without including the `` header. My favorite fix would be to use a standard `main`. Another fix is to include the missing header in the source code, and a third fix is to use the source code as is but specify the header as a forced include, e.g. with Visual C++ using the option `/FI"tchar.h"`. – Cheers and hth. - Alf Jun 04 '14 at 17:52
  • @Cheers Thank you my fault for blindly copying the weirdo main from the default VS project. I agree, standard main is best. Now modified. – David Heffernan Jun 04 '14 at 18:03
  • @cpp-progger: if a client has David's example code (or any software that behaves similarly) running at the same time as your application, both applications will start getting thread IDs over 64K. This is because thread IDs are global, i.e., two processes can't both have a thread with the same ID. – Harry Johnston Jun 05 '14 at 05:26
2

Re

We run in big troubles, if we try to change this member to an uint32_t. And even if we fix it, we had to test the fix.

Your current software’s use of a 16-bit object to store a value that requires 32 bits, is a bug. So you have to fix it, and test the fix. There are at least two practical fixes:

  • Changing the declaration of the id, and all uses of it.
    It can really help with finding all copying of the id, to introduce a dedicated type that is not implicitly convertible to integer, e.g. a C++11 based enumeration type.

  • Adding a layer of indirection.
    Might be possible without changing the data, only changing the threading library implementation.

A deeper fix might be to replace the current threading with C++11 standard library threading.

Anyway you're up for a bit of work, and/or some cost.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    Would C++11 standard library threading actually help in this scenario? Surely most implementations use the native thread ID as the identifier? (Even if an implementation did provide a non-native numeric identifier, I wouldn't expect any guarantee that said identifier would be only 16 bits long.) – Harry Johnston Jun 05 '14 at 05:32
  • @HarryJohnston: I suggested C++11 threading as a "deeper fix". One can be almost sure that with old legacy code that uses 16-bit thread identifiers, there's much else wrong. Getting up an abstraction level or two can help. – Cheers and hth. - Alf Jun 05 '14 at 08:08