LinuxQuestions.org
Visit the LQ Articles and Editorials section
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 07-08-2010, 03:53 AM   #1
jork
LQ Newbie
 
Registered: Jun 2010
Location: beweth
Distribution: Ubuntu, RHEL, Monta Vista, QNX
Posts: 16

Rep: Reputation: 0
Practical uses of pthread_key_create() and pthread_set_specific)(


Hi all,

Sorry if I'm coming up with a newbie question. But I never understood any practical use cases of pthread_key_create and set_specific and similar functions. I read the manual still I couldn't understand any particular/specific use cases for these..
Appreciate if anybody can clarify these.

Thanks,
jork
 
Old 07-08-2010, 05:44 PM   #2
wje_lq
Member
 
Registered: Sep 2007
Location: Mariposa
Distribution: Debian lenny, Slackware 12
Posts: 809

Rep: Reputation: 178Reputation: 178
[Oops; I clicked "submit" before I'm done. I'll be back when I'm done!]

Last edited by wje_lq; 07-08-2010 at 05:45 PM.
 
Old 07-08-2010, 06:43 PM   #3
wje_lq
Member
 
Registered: Sep 2007
Location: Mariposa
Distribution: Debian lenny, Slackware 12
Posts: 809

Rep: Reputation: 178Reputation: 178
Caution: in the following, I haven't even compiled any of the code, much less tested it. Use it at your own risk.

Right. Where was I? Oh, yes. Harrumph.

Everyone knows what global variables are. But suppose you want a variable that's almost global; that is, suppose you want a "global" variable that has a unique instance for each thread.

Let's explore a typical use of this. Suppose you want to write a library of C functions which are thread safe and have a "global" variable, one such global variable per thread. You might want a global variable to keep track of the state of your library between one call to it and the next, say.

Suppose, say, you're defining a function fred(). The first time fred() is called by the end user, you want to initialize some data, and each time after that you want to be able to see that data and perhaps modify it. This data should be kept separate for each thread.

Remember, you can't change the code that's calling fred(); fred() is a library function, remember?

Ok, let's make it even more complex. Suppose you want to maintain two such global sets of data (with one instance per thread per set). Under some conditions you'll want to initialize both of them, sometimes just one or the other, sometimes none of them.

Let's see how you'll do this.

Let's call the two sets of data wilma and betty. For purposes of simplicity (for now), let's say that each set of data is a struct of some sort.

The first step, to be done only once for betty and once for wilma while the program is running, not once per thread, is to get a key for wilma and a key for betty. To get a key, use pthread_key_create(). Store the value for the key in a global variable. (You might actually call that variable wilma or betty.)

You'll notice that the second parameter to the function pthread_key_create() is the name of a destructor function. What's that all about?

Suppose that each time you create an instance of wilma (at most once per thread, remember?), your code will first do a malloc() to get the memory for the struct. At some time you want to give that memory back; this will typically be at the time that the thread exits. The function that will be used to give the memory back is this destructor function. You know, at the time the program starts and you get a key for wilma, that each instance of wilma will be the result of a malloc() call, so when you get the key, you specify free() as the destructor function.

Suppose, though, that betty is more complex. Suppose that it's a struct containing a field that's a pointer to another struct . Suppose that each thread, when initializing its instance of betty, will malloc() the struct for betty, and then also malloc() another struct, a pointer to which is stored inside that instance of betty.

In that case, specifying free() as the destructor function for each instance of betty isn't good enough; you'll be losing (not freeing, just losing) that second chunk of memory. What you need is a function which frees that auxiliary chunk of memory first, and then frees the primary betty struct. So write that function yourself, and specify it as the destructor function for betty.

Here's a sample:
Code:
void bettyfree(void *bettychunk)
{
  free(((struct bettystruct *)bettychunk)->secondchunk);
  free(bettychunk);

} /* bettyfree() */
Having talked about the destructor function, let's go back to another question about pthread_key_create(): How do you make sure that you run it only once per running of the whole program, and not once per thread? You're writing a library, remember, so you can't put the call to pthread_key_create() in, say, main().

The answer is pthread_once().

So your library should define these global variables:
Code:
pthread_once_t mylib_once_var=PTHREAD_ONCE_INIT;
pthread_key_t  mylib_wilma;
pthread_key_t  mylib_betty;
You should have an initialization function in your library that looks something like this:
Code:
void mylib_init
{
  if(pthread_key_create(&mylib_wilma,free())
  {
    /* put your error recovery code here */
  }

  if(pthread_key_create(&mylib_betty,bettyfree())
  {
    /* put your error recovery code here */
  }

} /* mylib_init() */
Then, to be safe, each function in your library should have code like this at its beginning, to make sure that the initialization code is called once and only once for the whole running of the program:
Code:
if(pthread_once(mylib_once_var,mylib_init))
{
  /* put your error recovery code here */
}
Then each function which needs an instance of wilma for that thread should do this:
Code:
struct wilma pointer_to_my_wilma;

/* ... */

pointer_to_my_wilma=pthread_getspecific(mylib_wilma);

if(pointer_to_my_wilma==NULL)
{
  /* We haven't set up an instance of wilma for this thread yet. */

  pointer_to_my_wilma=malloc(sizeof(struct wilma));

  if(pointer_to_my_wilma==NULL)
  {
    /* malloc() failed; do error recovery here */
  }

  /* We have just allocated space for wilma's instance for this thread.
     Write code here which fills that space with your initializing data
     for wilma for this thread.
  */

  /* Then inform pthreads about this instance. */

  if(pthread_setspecific(mylib_wilma,pointer_to_my_wilma))
  {
    /* put your error recovery code here */
  }
}
Note that you don't need to execute the above code until you actually need wilma data for this thread. Some threads might never execute this code at all, if that's what you want.

Doing the same thing for betty is left as an exercise for the reader.

Now, jork, I have two questions for you.
  1. What is this pthreads "manual" you are reading? If you wish to do any serious POSIX threads programming, then run (do not walk) to your nearest bookseller and order Programming with POSIX Threads, by David R. Butenhof.

    The reason I recommend this book is that pthreads programming can involve you in hours and hours and HOURS of fun chasing down subtle bugs that
    1. are not repeatable; and
    2. give you symptoms of misbehavior in one thread where the actual bug lies in another.

    These bugs can be simple C errors; they can also be failures to understand some of the implications of POSIX threads. Butenhof is an excellent guide to POSIX threads pitfalls.

    My copy of an O'Reilly book on the same topic is, in theory, complete, but does not warn you of many of these pitfalls, so I'd stay away from it.
  2. Your location is beweth. Where is beweth?
 
1 members found this post helpful.
Old 07-09-2010, 02:49 PM   #4
jork
LQ Newbie
 
Registered: Jun 2010
Location: beweth
Distribution: Ubuntu, RHEL, Monta Vista, QNX
Posts: 16

Original Poster
Rep: Reputation: 0
Dear wje_lq,

Thanks a lot for taking the time to write a detailed explanatory reply like this. Great souls like you are real asset of LQ and the reason why ppl love it. And yeah, it helps a lot to clarify my doubts.

And the answers for your Qs.

1. I have been doing the thread and network programming for sometime now. But never had to use these functions, possibly because I never understood the actual use case of these. So instead I might be doing other possibly round about ways for doing the same. The manual I was referring to was nothing but the man pages itself. Generally as soon as I get a doubt or need clarification, google die.net <fun_name> is what I do. Not much reading otherthan that nowadays. :-(
I've heard about this book many times in the past also, but never got a chance to buy it. Now I think I seriously think of grabbing one.

2. Just read it the opposite way, I meant the-web what ever you read it, yeah I know there is a mistake in that. but I preferred it that way.

Again thanks for your help,
jork
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
RHCE Practical Question roni_atx Red Hat 4 11-17-2008 06:07 AM
practical UTF-8 question rblampain Linux - General 4 04-16-2007 05:06 AM
What is the practical use of ip aliase? Niceman2005 Linux - Networking 1 11-24-2005 11:51 PM
Getting practical knowledge? (C++) oot Programming 2 01-19-2003 01:54 PM


All times are GMT -5. The time now is 08:45 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration