1

If I create a thread with _beginthreadex, and in the thread I used std::vector<WCHAR> that consumes 200MB of memory - when the thread ends, the memory is not released. Even after CloseHandle, the memory is not released.

Here is a working example:

#include <windows.h>
#include <process.h>
#include <vector>
using namespace std;

unsigned __stdcall   Thread_RestartComputer(void* pComputerName)
{
    std::vector<WCHAR> asdf(100000000);
    _endthreadex(0);
    return 0;
}
int main()
{
    UINT threadID = 0;
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, &Thread_RestartComputer, 0, CREATE_SUSPENDED, &threadID);
    ResumeThread(hThread);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
}

I thought that std::vector<WCHAR> released the memory when it went out of scope?

JeffR
  • 765
  • 2
  • 8
  • 23
  • 4
    How do you measure the memory? Are you sure it's not a false positive? – Some programmer dude Mar 22 '22 at 16:35
  • 2
    That's not 200kB, more like 200MB. – user17732522 Mar 22 '22 at 16:37
  • 2
    https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/endthread-endthreadex?view=msvc-170 explicitly says that you must not call `CloseHandle` when using `_beginthreadex`. – Iziminza Mar 22 '22 at 16:39
  • 3
    Your `_endthreadex` call kills thread before destructor has a chance to run. – freakish Mar 22 '22 at 16:40
  • 1
    Even if `std::vector` (and its allocator) releases memory, there is no guarantee that the implementation of `operator new()` will release it to the host system. Using OS specific functions for managing threads (like `_beginthreadex()`) which have no relationship to the implementation of `std::vector` (or any other memory management done by the C++ standard library) is even less likely to release memory. – Peter Mar 22 '22 at 16:40
  • I suggest you use [`std::thread`](https://en.cppreference.com/w/cpp/thread/thread) instead. – Some programmer dude Mar 22 '22 at 17:04
  • Instead of std::vector? Why? – JeffR Mar 22 '22 at 17:04
  • No, instead of the Windows low-level threading functions. `std::thread t(ThreadFunction, possible, arguments, to, the, thread, function); t.join();` – Some programmer dude Mar 22 '22 at 17:10
  • 1
    @JeffR -- The implementers of the C++ 11 standard compiler you're using for Windows already has worked out the details that you are getting wrong. The `std::thread` and relevant functions and classes do everything you are trying to do, only correctly. Unless there is a compelling reason to do so (like you're using a C++98 compiler), go with the standard threading functions, and leave the hard work of figuring out how to implement `std::thread` to the compiler experts. – PaulMcKenzie Mar 22 '22 at 17:34

2 Answers2

7

C++ doesn't know that calling _endthreadex makes the thread go away. So it doesn't call the destructors of local variables like asdf before it calls _endthreadex.

Solution: Don't do that. return 0; ends the thread and calls the destructor.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • But the MS docs say to call `_endthreadex` if you call `_beginthreadex`, or did I misread them? – JeffR Mar 22 '22 at 16:43
  • 2
    @JeffR "You can call _endthread or _endthreadex explicitly to terminate a thread; however, _endthread or _endthreadex is called automatically when the thread returns from the routine passed as a parameter to _beginthread or _beginthreadex" – user253751 Mar 22 '22 at 16:46
5

As you can see in the documentation for _endthreadex, _endthreadex causes C++ destructors pending in the thread not to be called.

So just remove the call to _endthreadex and you should be fine.

The reasons for this behaviour are explained in this answer.

Iziminza
  • 374
  • 3
  • 8
  • 2
    The sentence "Terminating a thread with a call to `endthread` or `_endthreadex` helps ensure proper recovery of resources allocated for the thread." is severely misleading, as it rather does the opposite in your case! – Iziminza Mar 22 '22 at 16:48
  • Yes, it is very misleading! Thanks. – JeffR Mar 22 '22 at 17:24