1 /* miscfuncs.cc: misc funcs that don't belong anywhere else
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include "miscfuncs.h"
18 #include <sys/param.h>
30 #include "exception.h"
34 const char case_folded_lower[] NO_COPY = {
35 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
36 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
37 32, '!', '"', '#', '$', '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
38 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
39 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
40 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', 92, ']', '^', '_',
41 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
42 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 127,
43 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
44 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
45 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
46 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
47 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
48 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
49 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
50 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
53 const char case_folded_upper[] NO_COPY = {
54 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
55 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
56 32, '!', '"', '#', '$', '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
57 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
58 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
59 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 92, ']', '^', '_',
60 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
61 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127,
62 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
63 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
64 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
65 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
66 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
67 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
68 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
69 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
72 const char isalpha_array[] NO_COPY = {
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
78 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0, 0, 0, 0, 0,
79 0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
80 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
91 extern "C" int __stdcall
92 cygwin_wcscasecmp (const wchar_t *ws, const wchar_t *wt)
94 UNICODE_STRING us, ut;
96 RtlInitUnicodeString (&us, ws);
97 RtlInitUnicodeString (&ut, wt);
98 return RtlCompareUnicodeString (&us, &ut, TRUE);
101 extern "C" int __stdcall
102 cygwin_wcsncasecmp (const wchar_t *ws, const wchar_t *wt, size_t n)
104 UNICODE_STRING us, ut;
105 size_t ls = 0, lt = 0;
107 while (ws[ls] && ls < n)
109 RtlInitCountedUnicodeString (&us, ws, ls * sizeof (WCHAR));
110 while (wt[lt] && lt < n)
112 RtlInitCountedUnicodeString (&ut, wt, lt * sizeof (WCHAR));
113 return RtlCompareUnicodeString (&us, &ut, TRUE);
116 extern "C" int __stdcall
117 cygwin_strcasecmp (const char *cs, const char *ct)
119 UNICODE_STRING us, ut;
122 len = (strlen (cs) + 1) * sizeof (WCHAR);
123 RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len);
124 us.Length = sys_mbstowcs (us.Buffer, us.MaximumLength, cs) * sizeof (WCHAR);
125 len = (strlen (ct) + 1) * sizeof (WCHAR);
126 RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len);
127 ut.Length = sys_mbstowcs (ut.Buffer, ut.MaximumLength, ct) * sizeof (WCHAR);
128 return RtlCompareUnicodeString (&us, &ut, TRUE);
131 extern "C" int __stdcall
132 cygwin_strncasecmp (const char *cs, const char *ct, size_t n)
134 UNICODE_STRING us, ut;
136 size_t ls = 0, lt = 0;
138 while (cs[ls] && ls < n)
140 len = (ls + 1) * sizeof (WCHAR);
141 RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len);
142 us.Length = sys_mbstowcs (us.Buffer, ls + 1, cs, ls) * sizeof (WCHAR);
143 while (ct[lt] && lt < n)
145 len = (lt + 1) * sizeof (WCHAR);
146 RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len);
147 ut.Length = sys_mbstowcs (ut.Buffer, lt + 1, ct, lt) * sizeof (WCHAR);
148 return RtlCompareUnicodeString (&us, &ut, TRUE);
151 extern "C" char * __stdcall
152 cygwin_strlwr (char *string)
155 size_t len = (strlen (string) + 1) * sizeof (WCHAR);
157 us.MaximumLength = len; us.Buffer = (PWCHAR) alloca (len);
158 us.Length = sys_mbstowcs (us.Buffer, len, string) * sizeof (WCHAR)
160 RtlDowncaseUnicodeString (&us, &us, FALSE);
161 sys_wcstombs (string, len / sizeof (WCHAR), us.Buffer);
165 extern "C" char * __stdcall
166 cygwin_strupr (char *string)
169 size_t len = (strlen (string) + 1) * sizeof (WCHAR);
171 us.MaximumLength = len; us.Buffer = (PWCHAR) alloca (len);
172 us.Length = sys_mbstowcs (us.Buffer, len, string) * sizeof (WCHAR)
174 RtlUpcaseUnicodeString (&us, &us, FALSE);
175 sys_wcstombs (string, len / sizeof (WCHAR), us.Buffer);
180 check_invalid_virtual_addr (const void *s, unsigned sz)
182 MEMORY_BASIC_INFORMATION mbuf;
185 for (end = (char *) s + sz; s < end;
186 s = (char *) mbuf.BaseAddress + mbuf.RegionSize)
187 if (!VirtualQuery (s, &mbuf, sizeof mbuf))
192 static char __attribute__ ((noinline))
193 dummytest (volatile char *p)
199 check_iovec (const struct iovec *iov, int iovcnt, bool forwrite)
201 if (iovcnt <= 0 || iovcnt > IOV_MAX)
208 if (efault.faulted (EFAULT))
215 if (iov->iov_len > SSIZE_MAX || (tot += iov->iov_len) > SSIZE_MAX)
221 volatile char *p = ((char *) iov->iov_base) + iov->iov_len - 1;
233 assert (tot <= SSIZE_MAX);
235 return (ssize_t) tot;
238 /* Try hard to schedule another thread. */
242 int prio = GetThreadPriority (GetCurrentThread ());
243 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
244 for (int i = 0; i < 2; i++)
246 /* MSDN implies that SleepEx(0,...) will force scheduling of other
247 threads. Unlike SwitchToThread() the documentation does not mention
248 other cpus so, presumably (hah!), this + using a lower priority will
249 stall this thread temporarily and cause another to run. */
252 SetThreadPriority (GetCurrentThread (), prio);
255 /* Get a default value for the nice factor. When changing these values,
256 have a look into the below function nice_to_winprio. The values must
257 match the layout of the static "priority" array. */
259 winprio_to_nice (DWORD prio)
263 case REALTIME_PRIORITY_CLASS:
265 case HIGH_PRIORITY_CLASS:
267 case ABOVE_NORMAL_PRIORITY_CLASS:
269 case NORMAL_PRIORITY_CLASS:
271 case BELOW_NORMAL_PRIORITY_CLASS:
273 case IDLE_PRIORITY_CLASS:
279 /* Get a Win32 priority matching the incoming nice factor. The incoming
280 nice is limited to the interval [-NZERO,NZERO-1]. */
282 nice_to_winprio (int &nice)
284 static const DWORD priority[] NO_COPY =
286 REALTIME_PRIORITY_CLASS, /* 0 */
287 HIGH_PRIORITY_CLASS, /* 1 */
293 HIGH_PRIORITY_CLASS, /* 7 */
294 ABOVE_NORMAL_PRIORITY_CLASS, /* 8 */
295 ABOVE_NORMAL_PRIORITY_CLASS,
296 ABOVE_NORMAL_PRIORITY_CLASS,
297 ABOVE_NORMAL_PRIORITY_CLASS,
298 ABOVE_NORMAL_PRIORITY_CLASS,
299 ABOVE_NORMAL_PRIORITY_CLASS,
300 ABOVE_NORMAL_PRIORITY_CLASS,
301 ABOVE_NORMAL_PRIORITY_CLASS, /* 15 */
302 NORMAL_PRIORITY_CLASS, /* 16 */
303 NORMAL_PRIORITY_CLASS,
304 NORMAL_PRIORITY_CLASS,
305 NORMAL_PRIORITY_CLASS,
306 NORMAL_PRIORITY_CLASS,
307 NORMAL_PRIORITY_CLASS,
308 NORMAL_PRIORITY_CLASS,
309 NORMAL_PRIORITY_CLASS, /* 23 */
310 BELOW_NORMAL_PRIORITY_CLASS, /* 24 */
311 BELOW_NORMAL_PRIORITY_CLASS,
312 BELOW_NORMAL_PRIORITY_CLASS,
313 BELOW_NORMAL_PRIORITY_CLASS,
314 BELOW_NORMAL_PRIORITY_CLASS,
315 BELOW_NORMAL_PRIORITY_CLASS,
316 BELOW_NORMAL_PRIORITY_CLASS,
317 BELOW_NORMAL_PRIORITY_CLASS, /* 31 */
318 IDLE_PRIORITY_CLASS, /* 32 */
325 IDLE_PRIORITY_CLASS /* 39 */
329 else if (nice > NZERO - 1)
331 DWORD prio = priority[nice + NZERO];
335 /* Minimal overlapped pipe I/O implementation for signal and commune stuff. */
338 CreatePipeOverlapped (PHANDLE hr, PHANDLE hw, LPSECURITY_ATTRIBUTES sa)
340 int ret = fhandler_pipe::create (sa, hr, hw, 0, NULL,
341 FILE_FLAG_OVERLAPPED);
348 ReadPipeOverlapped (HANDLE h, PVOID buf, DWORD len, LPDWORD ret_len,
354 memset (&ov, 0, sizeof ov);
355 ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
356 ret = ReadFile (h, buf, len, NULL, &ov);
357 if (ret || GetLastError () == ERROR_IO_PENDING)
359 if (!ret && WaitForSingleObject (ov.hEvent, timeout) != WAIT_OBJECT_0)
361 ret = GetOverlappedResult (h, &ov, ret_len, FALSE);
363 CloseHandle (ov.hEvent);
368 WritePipeOverlapped (HANDLE h, PCVOID buf, DWORD len, LPDWORD ret_len,
374 memset (&ov, 0, sizeof ov);
375 ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
376 ret = WriteFile (h, buf, len, NULL, &ov);
377 if (ret || GetLastError () == ERROR_IO_PENDING)
379 if (!ret && WaitForSingleObject (ov.hEvent, timeout) != WAIT_OBJECT_0)
381 ret = GetOverlappedResult (h, &ov, ret_len, FALSE);
383 CloseHandle (ov.hEvent);
387 /* backslashify: Convert all forward slashes in src path to back slashes
388 in dst path. Add a trailing slash to dst when trailing_slash_p arg
392 backslashify (const char *src, char *dst, bool trailing_slash_p)
394 const char *start = src;
406 && !isdirsep (src[-1]))
411 /* slashify: Convert all back slashes in src path to forward slashes
412 in dst path. Add a trailing slash to dst when trailing_slash_p arg
416 slashify (const char *src, char *dst, bool trailing_slash_p)
418 const char *start = src;
430 && !isdirsep (src[-1]))
435 /* CygwinCreateThread.
437 Replacement function for CreateThread. What we do here is to remove
438 parameters we don't use and instead to add parameters we need to make
439 the function pthreads compatible. */
441 struct thread_wrapper_arg
443 LPTHREAD_START_ROUTINE func;
451 thread_wrapper (VOID *arg)
453 /* Just plain paranoia. */
455 return ERROR_INVALID_PARAMETER;
457 /* Fetch thread wrapper info and free from cygheap. */
458 thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
461 /* Remove _cygtls from this stack since it won't be used anymore. */
464 /* Set stack values in TEB */
465 PTEB teb = NtCurrentTeb ();
466 teb->Tib.StackBase = wrapper_arg.stackbase;
467 teb->Tib.StackLimit = wrapper_arg.commitaddr ?: wrapper_arg.stackaddr;
468 /* Set DeallocationStack value. If we have an application-provided stack,
469 we set DeallocationStack to NULL, so NtTerminateThread does not deallocate
470 any stack. If we created the stack in CygwinCreateThread, we set
471 DeallocationStack to the stackaddr of our own stack, so it's automatically
472 deallocated when the thread is terminated. */
473 char *dealloc_addr = (char *) teb->DeallocationStack;
474 teb->DeallocationStack = wrapper_arg.commitaddr ? wrapper_arg.stackaddr
476 /* Store the OS-provided DeallocationStack address in wrapper_arg.stackaddr.
477 The below assembler code will release the OS stack after switching to our
479 wrapper_arg.stackaddr = dealloc_addr;
481 /* Initialize new _cygtls. */
482 _my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE,
483 (DWORD (*)(void*, void*)) wrapper_arg.func);
485 /* Copy exception list over to new stack. I'm not quite sure how the
486 exception list is extended by Windows itself. What's clear is that it
487 always grows downwards and that it starts right at the stackbase.
488 Therefore we first count the number of exception records and place
489 the copy at the stackbase, too, so there's still a lot of room to
490 extend the list up to where our _cygtls region starts. */
491 _exception_list *old_start = (_exception_list *) teb->Tib.ExceptionList;
493 teb->Tib.ExceptionList = NULL;
494 for (_exception_list *e_ptr = old_start;
495 e_ptr && e_ptr != (_exception_list *) -1;
500 _exception_list *new_start = (_exception_list *) wrapper_arg.stackbase
502 teb->Tib.ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)
506 new_start->handler = old_start->handler;
507 if (old_start->prev == (_exception_list *) -1)
509 new_start->prev = (_exception_list *) -1;
512 new_start->prev = new_start + 1;
513 new_start = new_start->prev;
514 old_start = old_start->prev;
519 movl %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx \n\
520 movl (%%ebx), %%eax # Load thread func into eax \n\
521 movl 4(%%ebx), %%ecx # Load thread arg into ecx \n\
522 movl 8(%%ebx), %%edx # Load stackaddr into edx \n\
523 movl 12(%%ebx), %%ebx # Load stackbase into ebx \n\
524 subl %[CYGTLS], %%ebx # Subtract CYGTLS_PADSIZE \n\
525 subl $4, %%ebx # Subtract another 4 bytes \n\
526 movl %%ebx, %%esp # Set esp \n\
527 xorl %%ebp, %%ebp # Set ebp to 0 \n\
528 # Make gcc 3.x happy and align the stack so that it is \n\
529 # 16 byte aligned right before the final call opcode. \n\
530 andl $-16, %%esp # 16 bit align \n\
531 addl $-12, %%esp # 12 bytes + 4 byte arg = 16 \n\
532 # Now we moved to the new stack. Save thread func address\n\
533 # and thread arg on new stack \n\
534 pushl %%ecx # Push thread arg onto stack \n\
535 pushl %%eax # Push thread func onto stack \n\
536 # Now it's safe to release the OS stack. \n\
537 pushl $0x8000 # dwFreeType: MEM_RELEASE \n\
538 pushl $0x0 # dwSize: 0 \n\
539 pushl %%edx # lpAddress: stackaddr \n\
540 call _VirtualFree@12 # Shoot \n\
541 # All set. We can pop the thread function address from \n\
542 # the stack and call it. The thread arg is still on the \n\
543 # stack in the expected spot. \n\
544 popl %%eax # Pop thread_func address \n\
545 call *%%eax # Call thread func \n"
546 : : [WRAPPER_ARG] "r" (&wrapper_arg),
547 [CYGTLS] "i" (CYGTLS_PADSIZE));
548 /* Never return from here. */
552 /* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */
553 #define DEFAULT_STACKSIZE (512 * 1024)
556 CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
557 PVOID stackaddr, ULONG stacksize, ULONG guardsize,
558 DWORD creation_flags, LPDWORD thread_id)
560 PVOID real_stackaddr = NULL;
561 ULONG real_stacksize = 0;
562 ULONG real_guardsize = 0;
563 thread_wrapper_arg *wrapper_arg;
564 HANDLE thread = NULL;
566 wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
567 sizeof *wrapper_arg);
570 SetLastError (ERROR_OUTOFMEMORY);
573 wrapper_arg->func = thread_func;
574 wrapper_arg->arg = thread_arg;
577 real_stacksize = stacksize ?: DEFAULT_STACKSIZE;
578 if (real_stacksize < PTHREAD_STACK_MIN)
579 real_stacksize = PTHREAD_STACK_MIN;
582 /* If the application provided the stack, just use it. */
583 wrapper_arg->stackaddr = (char *) stackaddr;
584 wrapper_arg->stackbase = (char *) stackaddr + real_stacksize;
588 /* If not, we have to create the stack here. */
589 real_stacksize = roundup2 (real_stacksize, wincap.page_size ());
590 /* If no guardsize has been specified by the application, use the
591 system pagesize as default. */
592 real_guardsize = (guardsize != (ULONG) -1)
593 ? guardsize : wincap.page_size ();
595 real_guardsize = roundup2 (real_guardsize, wincap.page_size ());
596 /* Add the guardsize to the stacksize, but only if the stacksize and
597 the guardsize have been explicitely specified. */
598 if (stacksize || guardsize != (ULONG) -1)
599 real_stacksize += real_guardsize;
600 /* Now roundup the result to the next allocation boundary. */
601 real_stacksize = roundup2 (real_stacksize,
602 wincap.allocation_granularity ());
604 FIXME? If the TOP_DOWN method tends to collide too much with
605 other stuff, we should provide our own mechanism to find a
606 suitable place for the stack. Top down from the start of
607 the Cygwin DLL comes to mind. */
608 real_stackaddr = VirtualAlloc (NULL, real_stacksize,
609 MEM_RESERVE | MEM_TOP_DOWN,
610 PAGE_EXECUTE_READWRITE);
613 /* Set up committed region. In contrast to the OS we commit 64K and
614 set up just a single guard page at the end. */
615 char *commitaddr = (char *) real_stackaddr
617 - wincap.allocation_granularity ();
618 if (!VirtualAlloc (commitaddr, wincap.page_size (), MEM_COMMIT,
619 PAGE_EXECUTE_READWRITE | PAGE_GUARD))
621 commitaddr += wincap.page_size ();
622 if (!VirtualAlloc (commitaddr, wincap.allocation_granularity ()
623 - wincap.page_size (), MEM_COMMIT,
624 PAGE_EXECUTE_READWRITE))
627 VirtualAlloc (real_stackaddr, real_guardsize, MEM_COMMIT,
629 wrapper_arg->stackaddr = (char *) real_stackaddr;
630 wrapper_arg->stackbase = (char *) real_stackaddr + real_stacksize;
631 wrapper_arg->commitaddr = commitaddr;
633 /* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter so only the
634 minimum size for a thread stack is reserved by the OS. This doesn't
635 work on Windows 2000, but we deallocate the OS stack in thread_wrapper
636 anyway, so this should be a problem only in a tight memory condition.
637 Note that we reserve a 256K stack, not 64K, otherwise the thread creation
638 might crash the process due to a stack overflow. */
639 thread = CreateThread (&sec_none_nih, 4 * PTHREAD_STACK_MIN,
640 thread_wrapper, wrapper_arg,
641 creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
645 if (!thread && real_stackaddr)
647 /* Don't report the wrong error even though VirtualFree is very unlikely
649 DWORD err = GetLastError ();
650 VirtualFree (real_stackaddr, 0, MEM_RELEASE);