* miscfuncs.cc: Revert change from 2012-02-13 which used the
Windows-provided stack rather than an own stack created in CygwinCreateThread. (struct thread_wrapper_arg): Rename commitsize to stacklimit. (CygwinCreateThread): Rename commitsize to real_stacklimit.
This commit is contained in:
parent
2d192e1ebb
commit
fb97e87479
|
@ -1,3 +1,11 @@
|
||||||
|
2012-02-15 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* miscfuncs.cc: Revert change from 2012-02-13 which used the
|
||||||
|
Windows-provided stack rather than an own stack created in
|
||||||
|
CygwinCreateThread.
|
||||||
|
(struct thread_wrapper_arg): Rename commitsize to stacklimit.
|
||||||
|
(CygwinCreateThread): Rename commitsize to real_stacklimit.
|
||||||
|
|
||||||
2012-02-15 Corinna Vinschen <corinna@vinschen.de>
|
2012-02-15 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* dtable.cc (dtable::init_std_file_from_handle): Use tmp_pathbuf for
|
* dtable.cc (dtable::init_std_file_from_handle): Use tmp_pathbuf for
|
||||||
|
|
|
@ -444,7 +444,7 @@ struct thread_wrapper_arg
|
||||||
PVOID arg;
|
PVOID arg;
|
||||||
PBYTE stackaddr;
|
PBYTE stackaddr;
|
||||||
PBYTE stackbase;
|
PBYTE stackbase;
|
||||||
ULONG guardsize;
|
PBYTE stacklimit;
|
||||||
};
|
};
|
||||||
|
|
||||||
DWORD WINAPI
|
DWORD WINAPI
|
||||||
|
@ -458,58 +458,25 @@ thread_wrapper (VOID *arg)
|
||||||
thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
|
thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
|
||||||
cfree (arg);
|
cfree (arg);
|
||||||
|
|
||||||
PTEB teb = NtCurrentTeb ();
|
|
||||||
|
|
||||||
if (!wrapper_arg.stackaddr)
|
|
||||||
{
|
|
||||||
/* The simple case: Windows-provided stack. */
|
|
||||||
|
|
||||||
/* If guardsize is exactly the page_size, we can assume that the
|
|
||||||
application will behave Windows conformant in terms of stack usage.
|
|
||||||
We can especially assume that it never allocates more than one
|
|
||||||
page at a time (alloca/_chkstk). Therefore, this is the default
|
|
||||||
case which allows a Windows compatible stack setup with a
|
|
||||||
reserved region, a guard page, and a commited region. We don't
|
|
||||||
need to set up a POSIX guardpage since Windows already handles
|
|
||||||
stack overflow: Trying to extend the stack into the last three
|
|
||||||
pages of the stack results in a SEGV. */
|
|
||||||
if (wrapper_arg.guardsize != wincap.page_size ())
|
|
||||||
{
|
|
||||||
/* However, if guardsize is set to something other than the
|
|
||||||
page size, we commit the entire stack, remove the Windows
|
|
||||||
guardpage and, if guardsize is > 0, set up a POSIX guardpage. */
|
|
||||||
DWORD old_prot;
|
|
||||||
ULONG stacksize = (uintptr_t) teb->Tib.StackBase
|
|
||||||
- (uintptr_t) teb->DeallocationStack;
|
|
||||||
|
|
||||||
VirtualAlloc (teb->DeallocationStack, stacksize,
|
|
||||||
MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
VirtualProtect (teb->DeallocationStack, stacksize,
|
|
||||||
PAGE_READWRITE, &old_prot);
|
|
||||||
VirtualProtect (teb->DeallocationStack, wrapper_arg.guardsize,
|
|
||||||
PAGE_NOACCESS, &old_prot);
|
|
||||||
teb->Tib.StackLimit = (PVOID) ((PBYTE) teb->DeallocationStack
|
|
||||||
+ wrapper_arg.guardsize);
|
|
||||||
}
|
|
||||||
wrapper_arg.func (wrapper_arg.arg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The tricky case: Application-provided stack. */
|
|
||||||
|
|
||||||
/* Remove _cygtls from this stack since it won't be used anymore. */
|
/* Remove _cygtls from this stack since it won't be used anymore. */
|
||||||
_my_tls.remove (0);
|
_my_tls.remove (0);
|
||||||
|
|
||||||
/* Set stack values in TEB */
|
/* Set stack values in TEB */
|
||||||
|
PTEB teb = NtCurrentTeb ();
|
||||||
teb->Tib.StackBase = wrapper_arg.stackbase;
|
teb->Tib.StackBase = wrapper_arg.stackbase;
|
||||||
teb->Tib.StackLimit = wrapper_arg.stackaddr;
|
teb->Tib.StackLimit = wrapper_arg.stacklimit ?: wrapper_arg.stackaddr;
|
||||||
/* Set DeallocationStack value. If we have an application-provided
|
/* Set DeallocationStack value. If we have an application-provided stack,
|
||||||
stack, we set DeallocationStack to NULL, so NtTerminateThread does
|
we set DeallocationStack to NULL, so NtTerminateThread does not deallocate
|
||||||
not deallocate any stack. Store the OS-provided DeallocationStack
|
any stack. If we created the stack in CygwinCreateThread, we set
|
||||||
address in wrapper_arg.stackaddr. The below assembler code will
|
DeallocationStack to the stackaddr of our own stack, so it's automatically
|
||||||
release the OS stack after switching to our new stack. */
|
deallocated when the thread is terminated. */
|
||||||
wrapper_arg.stackaddr = (PBYTE) teb->DeallocationStack;
|
PBYTE dealloc_addr = (PBYTE) teb->DeallocationStack;
|
||||||
teb->DeallocationStack = NULL;
|
teb->DeallocationStack = wrapper_arg.stacklimit ? wrapper_arg.stackaddr
|
||||||
|
: NULL;
|
||||||
|
/* Store the OS-provided DeallocationStack address in wrapper_arg.stackaddr.
|
||||||
|
The below assembler code will release the OS stack after switching to our
|
||||||
|
new stack. */
|
||||||
|
wrapper_arg.stackaddr = dealloc_addr;
|
||||||
|
|
||||||
/* Initialize new _cygtls. */
|
/* Initialize new _cygtls. */
|
||||||
_my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE,
|
_my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE,
|
||||||
|
@ -578,7 +545,6 @@ thread_wrapper (VOID *arg)
|
||||||
call *%%eax # Call thread func \n"
|
call *%%eax # Call thread func \n"
|
||||||
: : [WRAPPER_ARG] "r" (&wrapper_arg),
|
: : [WRAPPER_ARG] "r" (&wrapper_arg),
|
||||||
[CYGTLS] "i" (CYGTLS_PADSIZE));
|
[CYGTLS] "i" (CYGTLS_PADSIZE));
|
||||||
}
|
|
||||||
/* Never return from here. */
|
/* Never return from here. */
|
||||||
ExitThread (0);
|
ExitThread (0);
|
||||||
}
|
}
|
||||||
|
@ -588,9 +554,11 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
PVOID stackaddr, ULONG stacksize, ULONG guardsize,
|
PVOID stackaddr, ULONG stacksize, ULONG guardsize,
|
||||||
DWORD creation_flags, LPDWORD thread_id)
|
DWORD creation_flags, LPDWORD thread_id)
|
||||||
{
|
{
|
||||||
|
PVOID real_stackaddr = NULL;
|
||||||
ULONG real_stacksize = 0;
|
ULONG real_stacksize = 0;
|
||||||
ULONG real_guardsize = 0;
|
ULONG real_guardsize = 0;
|
||||||
thread_wrapper_arg *wrapper_arg;
|
thread_wrapper_arg *wrapper_arg;
|
||||||
|
HANDLE thread = NULL;
|
||||||
|
|
||||||
wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
|
wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
|
||||||
sizeof *wrapper_arg);
|
sizeof *wrapper_arg);
|
||||||
|
@ -610,7 +578,9 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* If not, we let CreateThread reserve the stack. */
|
PBYTE real_stacklimit;
|
||||||
|
|
||||||
|
/* If not, we have to create the stack here. */
|
||||||
real_stacksize = roundup2 (stacksize, wincap.page_size ());
|
real_stacksize = roundup2 (stacksize, wincap.page_size ());
|
||||||
real_guardsize = roundup2 (guardsize, wincap.page_size ());
|
real_guardsize = roundup2 (guardsize, wincap.page_size ());
|
||||||
/* Add the guardsize to the stacksize */
|
/* Add the guardsize to the stacksize */
|
||||||
|
@ -622,17 +592,76 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
/* Now roundup the result to the next allocation boundary. */
|
/* Now roundup the result to the next allocation boundary. */
|
||||||
real_stacksize = roundup2 (real_stacksize,
|
real_stacksize = roundup2 (real_stacksize,
|
||||||
wincap.allocation_granularity ());
|
wincap.allocation_granularity ());
|
||||||
wrapper_arg->guardsize = real_guardsize;
|
/* Reserve stack.
|
||||||
|
FIXME? If the TOP_DOWN method tends to collide too much with
|
||||||
|
other stuff, we should provide our own mechanism to find a
|
||||||
|
suitable place for the stack. Top down from the start of
|
||||||
|
the Cygwin DLL comes to mind. */
|
||||||
|
real_stackaddr = VirtualAlloc (NULL, real_stacksize,
|
||||||
|
MEM_RESERVE | MEM_TOP_DOWN,
|
||||||
|
PAGE_READWRITE);
|
||||||
|
if (!real_stackaddr)
|
||||||
|
return NULL;
|
||||||
|
/* Set up committed region. Two cases: */
|
||||||
|
if (real_guardsize != wincap.page_size ())
|
||||||
|
{
|
||||||
|
/* If guardsize is set to something other than the page size, we
|
||||||
|
commit the entire stack and, if guardsize is > 0, we set up a
|
||||||
|
POSIX guardpage. We don't set up a Windows guardpage. */
|
||||||
|
if (!VirtualAlloc (real_stackaddr, real_guardsize, MEM_COMMIT,
|
||||||
|
PAGE_NOACCESS))
|
||||||
|
goto err;
|
||||||
|
real_stacklimit = (PBYTE) real_stackaddr + real_guardsize;
|
||||||
|
if (!VirtualAlloc (real_stacklimit, real_stacksize - real_guardsize,
|
||||||
|
MEM_COMMIT, PAGE_READWRITE))
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
/* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter. This doesn't
|
else
|
||||||
work on Windows 2000, but either we deallocate the OS stack in
|
{
|
||||||
thread_wrapper anyway, or the reservation is rounded to the next Meg.
|
/* If guardsize is exactly the page_size, we can assume that the
|
||||||
Nothing to worry about.
|
application will behave Windows conformant in terms of stack usage.
|
||||||
Note that we reserve a 256K stack as minimum size, not 64K, otherwise
|
We can especially assume that it never allocates more than one
|
||||||
the thread creation might crash the process due to a stack overflow. */
|
page at a time (alloca/_chkstk). Therefore, this is the default
|
||||||
return CreateThread (&sec_none_nih, stackaddr ? 4 * PTHREAD_STACK_MIN
|
case which allows a Windows compatible stack setup with a
|
||||||
: real_stacksize,
|
reserved region, a guard page, and a commited region. We don't
|
||||||
|
need to set up a POSIX guardpage since Windows already handles
|
||||||
|
stack overflow: Trying to extend the stack into the last three
|
||||||
|
pages of the stack results in a SEGV.
|
||||||
|
We always commit 64K here, starting with the guardpage. */
|
||||||
|
real_stacklimit = (PBYTE) real_stackaddr + real_stacksize
|
||||||
|
- wincap.allocation_granularity ();
|
||||||
|
if (!VirtualAlloc (real_stacklimit, wincap.page_size (), MEM_COMMIT,
|
||||||
|
PAGE_READWRITE | PAGE_GUARD))
|
||||||
|
goto err;
|
||||||
|
real_stacklimit += wincap.page_size ();
|
||||||
|
if (!VirtualAlloc (real_stacklimit, wincap.allocation_granularity ()
|
||||||
|
- wincap.page_size (), MEM_COMMIT,
|
||||||
|
PAGE_READWRITE))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
wrapper_arg->stackaddr = (PBYTE) real_stackaddr;
|
||||||
|
wrapper_arg->stackbase = (PBYTE) real_stackaddr + real_stacksize;
|
||||||
|
wrapper_arg->stacklimit = real_stacklimit;
|
||||||
|
}
|
||||||
|
/* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter so only the
|
||||||
|
minimum size for a thread stack is reserved by the OS. This doesn't
|
||||||
|
work on Windows 2000, but we deallocate the OS stack in thread_wrapper
|
||||||
|
anyway, so this should be a problem only in a tight memory condition.
|
||||||
|
Note that we reserve a 256K stack, not 64K, otherwise the thread creation
|
||||||
|
might crash the process due to a stack overflow. */
|
||||||
|
thread = CreateThread (&sec_none_nih, 4 * PTHREAD_STACK_MIN,
|
||||||
thread_wrapper, wrapper_arg,
|
thread_wrapper, wrapper_arg,
|
||||||
creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
|
creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
|
||||||
thread_id);
|
thread_id);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (!thread && real_stackaddr)
|
||||||
|
{
|
||||||
|
/* Don't report the wrong error even though VirtualFree is very unlikely
|
||||||
|
to fail. */
|
||||||
|
DWORD err = GetLastError ();
|
||||||
|
VirtualFree (real_stackaddr, 0, MEM_RELEASE);
|
||||||
|
SetLastError (err);
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue