Pages

Friday, October 21, 2005

Solaris: pthread_attr_getstack() broken?

Apparently there seems to be some confusion1 2 around when the structure pthread_attr_t gets updated. In one of the above threads, it appears that the initiator believes that the attribute structure must be updated at least when a thread was created by calling pthread_create().

Let's start with an example:(Taken from: pthread_attr_getstack(), pthread_attr_getstackaddr() thread -- for simplicity, I just removed the error checking code)
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

void* Proc(void* param)
{
sleep(3);
return 0;
}

int main(int argc, char *argv[])
{
pthread_attr_t Attr;
pthread_t Id;
void *Stk;
size_t Siz;
int err;

pthread_attr_init(&Attr);

pthread_attr_getstacksize(&Attr, &Siz);
pthread_attr_getstackaddr(&Attr, &Stk);

printf("Default: Addr=%08x Size=%d\n", Stk, Siz);

pthread_create(&Id, &Attr, Proc, NULL);

pthread_attr_getstackaddr(&Attr, &Stk);
pthread_attr_getstacksize(&Attr, &Siz);

printf("Stack : Addr=%08x Size=%d\n", Stk, Siz);

return (0);
}
On Solaris, this piece of code produces a result of:
Default: Addr=00000000  Size=0
Stack : Addr=00000000 Size=0
This result may surprise (and confuse) some of the users, who expects a valid base address and a valid size for the stack. However on Solaris, a value of NULL for stack address, and 0 for stack size indicates that the new thread will have system defined stack address and system allocated stack size (1M for 32-bit and 2M for 64-bit processes) respectively. Other default values for attribute (Attr, in the example) is mentioned in Solaris 10 Multithreaded Programming Guide. To understand when the interfaces pthread_attr_getstackaddr() and pthread_attr_getstacksize(), return valid values, first we need to know a little about attribute object.

What is an Attribute object?

Attributes are a way to specify behavior that is different from the default. Any variable of type pthread_attr_t can be used specify the new behavior. Attribute object can be specified, when a thread is created with pthread_create() or when a synchronization variable is initialized. Note that these attributes cannot be altered while the thread is in use.

Now it all makes sense for the OS to return NULL and 0 for base address and size of a stack. Since the POSIX specification didn't specify the default values to be returned, the output for the same program might be different on different platforms (see previous paragraphs for the default values chosen by Sun).

Let's modify the C code a little to place a 3M stack, at a different location than the system defined address. (The newly introduced code is in green color)
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/mman.h>

void* Proc(void* param)
{
sleep(3);
return 0;
}

int main(int argc, char *argv[])
{
pthread_attr_t Attr;
pthread_t Id;
void *Stk;
size_t Siz;
int err;

size_t stksize = 0x300000;
void *stack;


pthread_attr_init(&Attr);

pthread_attr_getstacksize(&Attr, &Siz);
pthread_attr_getstackaddr(&Attr, &Stk);

printf("Default: Addr=%08x Size=%d\n", Stk, Siz);

stack = mmap(NULL, stksize, PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED, -1, 0);
pthread_attr_setstack(&Attr, stack, stksize);


pthread_create(&Id, &Attr, Proc, NULL);

pthread_attr_getstackaddr(&Attr, &Stk);
pthread_attr_getstacksize(&Attr, &Siz);

printf("Stack : Addr=%08x Size=%d\n", Stk, Siz);

return (0);
}
Here's the output from the above program:
Default: Addr=00000000  Size=0
Stack : Addr=fee80000 Size=3145728
Moral of the story:
pthread_attr_getstack() is not broken; and the structure pthread_attr_t (ie., the attribute object) gets updated only if we update it, with the help of pthread_attr_setxxxx() interfaces defined in pthread.h ie.,
% grep "pthread_attr_set*" /usr/include/pthread.h

int pthread_attr_setstack(pthread_attr_t *, void *, size_t);
int pthread_attr_setstacksize(pthread_attr_t *, size_t);
int pthread_attr_setstackaddr(pthread_attr_t *, void *);
int pthread_attr_setdetachstate(pthread_attr_t *, int);
int pthread_attr_setscope(pthread_attr_t *, int);
int pthread_attr_setinheritsched(pthread_attr_t *, int);
int pthread_attr_setschedpolicy(pthread_attr_t *, int);
int pthread_attr_setschedparam(pthread_attr_t *_RESTRICT_KYWD,
int pthread_attr_setguardsize(pthread_attr_t *, size_t);
Otherwise system is going to return the default values for all pthread_attr_getxxxx() calls.
________________
Technorati tag: | |

2 comments:

  1. Giri,
    Since this is my sample code, you might at least let me know you posted it here. There is a lot more to this story, which I will reveal on this newsgroup thread.

    http://groups.google.com/group/comp.programming.threads/browse_frm/thread/cb0eb84f300b7e99

    You left out details like ... what am I trying to accomplish? It certainly wasn't to see if Sun implemented getstack() correctly.

    I was trying to find the kernel provided stack: start_addr, size, end_addr ... so that I could determine if a recursive function was about to overflow the stack.

    This very simple problem has generated a lot of kernel->diving exercises to ensure it works on all *nix platforms.

    The platforms we're interested in covering is Solaris, AIX, HPUX, Linux, and (regrettably) Windows.

    ReplyDelete
  2. Hey Dick,

    My intention is to address only part of the problem, you were trying to resolve ie., why pthread_attr_getstackaddr() and pthread_attr_getstacksize() are not returning the proper address and size of the stack, as soon as we create a new thread.

    I found another thread with title pthread_attr_getstack broken? at opensolaris.org, with the same question. So, I just thought of documenting my interpretation, in here.

    ReplyDelete