Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

help with thread specific storage.

1 view
Skip to first unread message

ator...@monolith.bellcore.com

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
Hi,
I'm trying to learn to use Thread Specific data, so I coded this sample
program which gives me wrong results.

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

static pthread_once_t my_once = PTHREAD_ONCE_INIT;

static pthread_key_t my_key;

int *my_errno; // per thread data

static void
my_init(void)
{

if (pthread_key_create(&my_key, NULL) !=0) {
fprintf(stderr,"myinit: cannot create key\n");
exit(1);

}
}

static void
my_gettsdp()
{

my_errno = (int *) pthread_getspecific(my_key); flockfile(stderr);
fprintf(stderr,"my_errno %x for id = %d\n", my_errno, (int) pthread_self());
funlockfile(stderr); if (my_errno == NULL) { flockfile(stderr);
fprintf(stderr,"my_errno NULL for id = %d\n", (int) pthread_self());
funlockfile(stderr); my_errno = (int *)malloc(sizeof(int));
flockfile(stderr); fprintf(stderr,"my_errno %x for id = %d\n", my_errno,
(int) pthread_self()); funlockfile(stderr); pthread_setspecific(my_key,
(void *) my_errno); } }


static void
my_set(int i)
{

pthread_once(&my_once, my_init);
my_gettsdp();
*my_errno = (int) pthread_self();
}

static int
my_thread(void *i)
{

int *k = (int *)i;
for(;;)
{
if (*k < 3)
sleep(*k);
else
sleep(*k-1);
my_set(*k);
flockfile(stderr);
fprintf(stderr,"self = %d --> my_errno = %d\n", (int) pthread_self(),
*my_errno);
funlockfile(stderr);
}

}


int main()
{

pthread_t thr;
for (int j=0; j < 5; j++)
pthread_create( &thr, NULL, (void*(*)(void*)) my_thread,(void *) &j);

for (;;)
pause();
}


I would expect my_errno to be always equal to pthread_self, but, sometimes
I get the following output:


my_errno 0 for id = 2
my_errno NULL for id = 2
my_errno 400055c8 for id = 2
self = 2 --> my_errno = 2
my_errno 0 for id = 4
my_errno NULL for id = 4
my_errno 40005de0 for id = 4
my_errno 40005de0 for id = 3
self = 3 --> my_errno = 3
self = 4 --> my_errno = 3 -------> this is wrong
my_errno 0 for id = 5
my_errno NULL for id = 5
my_errno 400065f8 for id = 5
self = 5 --> my_errno = 5
my_errno 0 for id = 6
my_errno NULL for id = 6
my_errno 40006e10 for id = 6
self = 6 --> my_errno = 6

Is there something wrong into my code ?
I'm using pthreads on HP-UX B.11.00.
Thanks in advance for any help.

Antonio

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

Patrick TJ McPhee

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
In article <7808qb$ct3$1...@nnrp1.dejanews.com>,
<ator...@monolith.bellcore.com> wrote:

% I'm trying to learn to use Thread Specific data, so I coded this sample
% program which gives me wrong results.

% int *my_errno; // per thread data

This is a global variable.

% my_errno = (int *) pthread_getspecific(my_key); flockfile(stderr);

So it hardly matters that you temporarily set it to point to a thread-specific
data pointer. my_errno is shared between all threads.

What you want to do is have one function with this code:

int * my_errno = pthread_getspecific(my_key);
if (my_errno) *my_errno = something;

and then another function with this code:

int * my_errno = pthread_getspecific(my_key);
if (my_errno) printf("%d\n", *my_errno);
--

Patrick TJ McPhee
East York Canada
pt...@interlog.com

ator...@monolith.bellcore.com

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
In article <780uct$kiq$1...@news.interlog.com>,

pt...@interlog.com (Patrick TJ McPhee) wrote:
> In article <7808qb$ct3$1...@nnrp1.dejanews.com>,
> <ator...@monolith.bellcore.com> wrote:
>
> % I'm trying to learn to use Thread Specific data, so I coded this sample
> % program which gives me wrong results.
>
> % int *my_errno; // per thread data
>
> This is a global variable.
>
> % my_errno = (int *) pthread_getspecific(my_key); flockfile(stderr);
>
> So it hardly matters that you temporarily set it to point to a thread-specific
> data pointer. my_errno is shared between all threads.
>

Isn't Thread Specific Storage a facility which allows the user to
live with functions returning pointers to static data or with
global variables in a multithreaded environment?

Dave Butenhof

unread,
Jan 20, 1999, 3:00:00 AM1/20/99
to
ator...@monolith.bellcore.com wrote:

> Hi,


> I'm trying to learn to use Thread Specific data, so I coded this sample

> program which gives me wrong results.

Actually, it's giving you correct results. That is, you're getting more or less
what you asked for, which is an unpredictable sequence of meaningless outputs.

> static pthread_once_t my_once = PTHREAD_ONCE_INIT;
> static pthread_key_t my_key;

> int *my_errno; // per thread data
>

> static void
> my_init(void)
> {
> if (pthread_key_create(&my_key, NULL) !=0) {
> fprintf(stderr,"myinit: cannot create key\n");
> exit(1);
> }
> }

In this sort of simple case, it'd be easier to simply create the TSD
(thread-specific data) key in main() rather than using a one-time init
function. Just do it before you create any threads, and you're fine. One-time
init is only necessary when you don't control what's in main(). (And even then,
it's usually easier to just use a statically initialized mutex. When POSIX
added static initialization of mutexes, one-time init became obsolete. After
some discussion, we decided not to bother removing it, but it has little
value.)

> static void
> my_gettsdp()
> {


> my_errno = (int *) pthread_getspecific(my_key); flockfile(stderr);

> fprintf(stderr,"my_errno %x for id = %d\n", my_errno, (int) pthread_self());
> funlockfile(stderr); if (my_errno == NULL) { flockfile(stderr);
> fprintf(stderr,"my_errno NULL for id = %d\n", (int) pthread_self());
> funlockfile(stderr); my_errno = (int *)malloc(sizeof(int));
> flockfile(stderr); fprintf(stderr,"my_errno %x for id = %d\n", my_errno,
> (int) pthread_self()); funlockfile(stderr); pthread_setspecific(my_key,
> (void *) my_errno); } }

A couple of problems and comments here.

Thread safety: You're using the global variable, which has the same value for
all threads. You're setting the global variable without synchronization, which
means that, when you have multiple threads running this code, the value is
indeterminate. Patrick McPhee has already pointed out that your "my_errno"
should be a local variable, private to each thread that calls the routine.
That's your serious bug -- the rest is just advice and nits.

Portability/Conformance: You cannot legally/portably case pthread_self() to an
int, nor format the value using %d. POSIX declares pthread_t as an opaque and
unspecified type. It could be a structure, for example, as in DCE threads. I
don't offhand know of any POSIX thread implementation where pthread_t IS a
structure, but it's legal -- and a program that wouldn't work on such an
implementation is NOT legal. On Solaris, pthread_t happens to be a "small
integer", so you're fine -- just be aware that the code is still wrong.

Efficiency/redundancy: All of your flockfile/funlock file calls are
unnecessary. They won't hurt, but you only need them when you want to use
multiple output calls and ensure that the sequence of outputs cannot be
separated by the action of some other thread. E.g.,

flockfile(stderr);
fprintf(stderr,"my_errno %x", my_errno);
fprintf(stderr," for id = %d\n", (int) pthread_self());
funlockfile(stderr);

> static void


> my_set(int i)
> {
> pthread_once(&my_once, my_init);
> my_gettsdp();
> *my_errno = (int) pthread_self();
> }

Your "my_errno" could be changed by another thread between the call to
my_gettsdp() and your assignment. You cannot do this. It'd be reasonable for
the function to return your private "errno" value, since it's called "get".
Otherwise, it's just your mechanism to make sure that the calling thread has a
value for the TSD key. You need to call pthread_getspecific() to retrieve the
value.

/---------------------------[ Dave Butenhof ]--------------------------\
| Compaq Computer Corporation bute...@zko.dec.com |
| 110 Spit Brook Rd ZKO2-3/Q18 http://members.aol.com/drbutenhof |
| Nashua NH 03062-2698 http://www.awl.com/cseng/titles/0-201-63392-2/ |
\-----------------[ Better Living Through Concurrency ]----------------/


Patrick TJ McPhee

unread,
Jan 21, 1999, 3:00:00 AM1/21/99
to
In article <7826dt$o8$1...@nnrp1.dejanews.com>,
<ator...@monolith.bellcore.com> wrote:

% Isn't Thread Specific Storage a facility which allows the user to
% live with functions returning pointers to static data or with
% global variables in a multithreaded environment?

No. Loosely, thread specific storage allows you store malloced memory
independently so that you can get much the same effect as having
a global in a single threaded program. For instance, if your single
thread program is like this:

char x[100];

void xx(char * s)
{
strcpy(x, s);
}

void xxx()
{
puts(x);
}

void * myfunc(void * s)
{
xx(s);
xxx();
}

main()
{
myfunc("hello");
myfunc("goodbye");
}

You can re-implemented it without changing the interfaces to xx() and xxx().
For instance (I might have the calls syntax wrong here):

char x[2][100];
pthread_key_t key;

void xx(char * s)
{
char * x = pthread_getspecific(key);
strcpy(x, s);
}

void xxx()
{
char * x = pthread_getspecific(key);
puts(x);
}

struct xxxx {
int index;
char * s;
};
void * myfunc(void * s)
{
struct xxxx * xxxxx = s;
pthread_setspecific(key, x+xxxxx->index);
xx(xxxxx->s);
xxx();
}

main()
{
struct xxxx x[2];
pthread_t tid;

x[1].index = 1;
x[0].index = 0;
x[0].s = "hello";
x[1].s = "goodbye";

pthread_key_create(&key, NULL);

pthread_create(&tid, NULL, myfunc, x);
myfunc(x+1);
pthread_join(&tid, NULL);
}

You see that the values returned from pthread_getspecific() have to
be assigned to local variables (or globals protected by mutex, but
what's the good of that?), but the data has the effect of being
static or global.

Dave Butenhof

unread,
Jan 21, 1999, 3:00:00 AM1/21/99
to
Patrick TJ McPhee wrote:

> In article <7826dt$o8$1...@nnrp1.dejanews.com>,
> <ator...@monolith.bellcore.com> wrote:
>
> % Isn't Thread Specific Storage a facility which allows the user to
> % live with functions returning pointers to static data or with
> % global variables in a multithreaded environment?
>
> No. Loosely, thread specific storage allows you store malloced memory
> independently so that you can get much the same effect as having
> a global in a single threaded program. For instance, if your single
> thread program is like this:

I think an easier way to think about TSD (thread-specific data) is this:

TSD is a simple indexed DATABASE. In an office filled with little cubicles,
each cube has a desk to your immediate left as you enter. And on the right
front corner of that desk is the office phone. Each phone has a different phone
number -- but you can walk into any office and immediately find the correct
phone for that office.

All threads share a TSD key value -- it's global data. But each thread
associates its own private data with that key. When you write a routine using
TSD to manage data, it doesn't need to know in which thread it's running. That
doesn't matter, because it always knows how to find the proper data for that
thread.

Yeah, you can think of it as "global" data, if you want. It really doesn't
matter. Usually you'll assign to the key a pointer to some heap storage. It
could just as easily be an element of an array, or even an integer. (Beware,
though, that, despite the naive intentions of the POSIX working group, ANSI C
does not actually say that you can safely cast an integer to a (void*) and back
without changing the value. It does, however, work on nearly every modern
platform.)

0 new messages