How can we get this information? I guess it is OS dependent, and I'm running Windows so my question refers to the windows API.
Is there any function that can do that for us - get the remaining stack memory for the calling thread?
Alternatively, if we could find out the following details, we will be able to calculate that on our own:
CreateThread
). But if it is the main thread, which was started by the OS for our program, or any other thread we did not start explicitly, how do we find it?esp
, or take the address of a local variable, to get a close location.This is for educational purposes, but I guess it could be used to stop a recursive algorithm from causing a stack overflow - Instead of using any max-depth limiting function.
It depends on your operating system. On Windows, the typical maximum size for a stack is 1MB, whereas it is 8MB on a typical modern Linux, although those values are adjustable in various ways.
Yes, stack is always limited. In several languages/compilers you can set the requested size.
This is a bug. free() frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is NULL, no operation is performed.
Because all threads in a process share the same address space, they have to divide it between them. And after the operating system has taken its part, there is "only" 2-3 GB left for an application. And that size is the limit for both the physical and the virtual memory, because there just aren't any more addresses.
You can use NtCurrentTeb(), which gets you a pointer to the TEB. This has NT_TIB as its first member:
typedef struct _NT_TIB
{
PEXCEPTION_REGISTRATION_RECORD ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
// ....
} NT_TIB, *PNT_TIB;
Not directly answering the OP's question, but referring to the idea mentioned at it's end: "... it could be used to stop a recursive algorithm from causing a stack overflow - Instead of using any max-depth limiting function."
The Windows API offers the method SetThreadStackGuarantee()
which allows to define a minimum stack size to keep available when throwing a stack overflow exception. This method together with the VC runtime library method _resetstkoflw()
may make it possible to recover from stack overflows.
See this on MSDN for details.
Get the thread stack base address: As wj32 showed, use the StackBase
of the thread information block.
Get the thread stack size: Determine a threads reserved stack size (which is it's maximum size) is different. What the StackLimit
shows is the lowest commited address, which can show how large the stack has ever grown, not it's limit. Also not that the stack size you pass to CreateThread
is the initial commit size, not reserve size unless you pass the STACK_SIZE_PARAM_IS_A_RESERVATION
flag. Your program's stack size is specified by a linker parameter and defaults to 1MB if you don't specify. So most likely all of threads have a 1MB stack reservation.
Since last page of the stack is a guard page you could conceivably start from the StackPage
and check each lower stack page VirtualQuery
to find the gaurd page that would be the end of the stack. This is of course completely relying on implementation defined behavior.
Get the current stack pointer: You could use the StackLimit
to get the maximum commited size of your stack, but that's not the same as the current pointer. esp
is obviously the current stack position and could be higher than StackLimit
.
Note on reserved vs commited. In Windows reserved means that virtual addresses have been reserved for use and can't be taken for something else. Reserved addresses do not consume any physical or virtual memory. Once it is commited it the address will be mapped to physical or virtual memory and can be used. Windows user threads have a fixed stack reserve size - address space is reserved for the stack and cannot be increased and a variable commit size - the stack will only use (commit) memory as it needs it.
Edit
My thoughts on checking the gaurd page won't work. I wrote a test program and the guard page is set at the commit limit, so that doesn't work. But I did find that running a VirtualQuery
anywhere on the stack will give the AllocationBase
of the lowest address on the stack, since the reserve size was allocated at once. The following example shows this in action:
#include <windows.h>
#include <WinNT.h>
#include <stdio.h>
DWORD GetThreadStackSize()
{
SYSTEM_INFO systemInfo = {0};
GetSystemInfo(&systemInfo);
NT_TIB *tib = (NT_TIB*)NtCurrentTeb();
DWORD_PTR stackBase = (DWORD_PTR)tib->StackBase;
MEMORY_BASIC_INFORMATION mbi = {0};
if (VirtualQuery((LPCVOID)(stackBase - systemInfo.dwPageSize), &mbi, sizeof(MEMORY_BASIC_INFORMATION)) != 0)
{
DWORD_PTR allocationStart = (DWORD_PTR)mbi.AllocationBase;
return stackBase - allocationStart;
}
return 0;
}
DWORD WINAPI ThreadRtn(LPVOID param)
{
DWORD stackSize = GetThreadStackSize();
printf("%d\n", stackSize);
return 0;
}
int main()
{
ThreadRtn(NULL);
HANDLE thread1 = CreateThread(NULL, 65535, ThreadRtn, NULL, 0, NULL);
WaitForSingleObject(thread1, -1);
HANDLE thread2 = CreateThread(NULL, 65535, ThreadRtn, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
WaitForSingleObject(thread2, -1);
return 0;
}
This outputs:
1048576 1048576 65536
As it should.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With