The “stack” thread attributes

The thread attribute stack parameters are prototyped as follows:

int
pthread_attr_setstack (pthread_attr_t *attr, void *addr, size_t ssize );

int
pthread_attr_setstackaddr (pthread_attr_t *attr, void *addr);

int
pthread_attr_setguardsize (pthread_attr_t *attr, size_t gsize);

int
pthread_attr_setstacklazy (pthread_attr_t *attr, int lazystack);

int
pthread_attr_setstackprealloc (pthread_attr_t * attr, size_t psize);

int
pthread_attr_setstacksize (pthread_attr_t *attr, size_t ssize);

These functions all take the attribute structure as their first parameter; the other parameters are selected from the following:

addr
The address of the stack, if you're providing one.
gsize
The size of the “guard” area.
lazystack
Indicates if the stack should be allocated on demand or up front from physical memory.
psize
The the amount of memory to preallocate for a thread's MAP_LAZY stack.
ssize
The size of the stack.

The guard area is a memory area immediately after the stack that the thread can't write to. If it does (meaning that the stack was about to overflow), the thread will get hit with a SIGSEGV. If the guardsize is 0, it means that there's no guard area. This also implies that there's no stack overflow checking. If the guardsize is nonzero, then it's set to at least the system-wide default guardsize (which you can obtain with a call to sysconf() with the constant _SC_PAGESIZE). Note that the guardsize will be at least as big as a “page” (for example, 4 KB on an x86 processor). Also, note that the guard page doesn't take up any physical memory—it's done as a virtual address (MMU) “trick.”

The addr is the address of the stack, in case you're providing it. You can set it to NULL meaning that the system will allocate (and will free!) the stack for the thread. The advantage of specifying a stack is that you can do postmortem stack depth analysis. This is accomplished by allocating a stack area, filling it with a “signature” (for example, the string “STACK” repeated over and over), and letting the thread run. When the thread has completed, you'd look at the stack area and see how far the thread had scribbled over your signature, giving you the maximum depth of the stack used during this particular run.

The ssize parameter specifies how big the stack is. If you provide the stack in addr, then ssize should be the size of that data area. If you don't provide the stack in addr (meaning you passed a NULL), then the ssize parameter tells the system how big a stack it should allocate for you. If you specify a 0 for ssize, the system will select the default stack size for you. Obviously, it's bad practice to specify a 0 for ssize and specify a stack using addr—effectively you're saying “Here's a pointer to an object, and the object is some default size.” The problem is that there's no binding between the object size and the passed value.

Note: If a stack is being provided via addr, no automatic stack overflow protection exists for that thread (i.e., there's no guard area). However, you can certainly set this up yourself using mmap() and mprotect().

Finally, the lazystack parameter indicates if the physical memory should be allocated as required (use the value PTHREAD_STACK_LAZY) or all up front (use the value PTHREAD_STACK_NOTLAZY). The advantage of allocating the stack “on demand” (as required) is that the thread won't use up more physical memory than it absolutely has to. The disadvantage (and hence the advantage of the “all up front” method) is that in a low-memory environment the thread won't mysteriously die some time during operating when it needs that extra bit of stack, and there isn't any memory left. If you are using PTHREAD_STACK_NOTLAZY, you'll most likely want to set the actual size of the stack instead of accepting the default, because the default is quite large.