calling pthread_self (on ubuntu), expensive?

Hi all,

Is anyone aware of what operations are involved when a call to pthread_self() is made, obtaining the unique thread ID on a Ubuntu system (or even any Linux flavour)?

Specifically, to retrieve the thread id, is there any locking required or atomic operations?

I'm building an application for multi-core systems (in C using pthreads) which needs to be scalable and I'm planning to call this function quite often, hence I need some idea how "expensive" it is to do so.

Any help much appreciated.

Thanks.

The internals of this function are some pretty hairy inline assembly, differing wildly from architecture to architecture. I can't tell precisely what it's doing. But from the comments, it sure looks like they're trying hard to make it minimal(down to the instruction-level, even) and nonblocking.

/* Return the thread descriptor for the current thread.

   The contained asm must *not* be marked volatile since otherwise
   assignments like
        pthread_descr self = thread_self();
   do not get optimized away.  */
# define THREAD_SELF \
  ({ struct pthread *__self;                                                  \
     asm ("movq %%fs:%c1,%q0" : "=r" (__self)                                 \
          : "i" (offsetof (struct pthread, header.self)));                    \
     __self;})
1 Like

Corona beat me to it.

The only thing I can add - pthread_self is not atomic.

1 Like

Thanks for your replay Corona688.

I found similar code in glibc (tls.h)...

/* Return the TCB address of the current thread.  */
# define THREAD_SELF                                   \
  ({ tcbhead_t *__tcb;                                  \
     __asm__ ("movl %%gs:%c1,%0" : "=r" (__tcb)                      \
          : "i" (offsetof (tcbhead_t, tcb)));                  \
     __tcb;})

Which I believe is accessing the Thread Control Block of the running thread which resides here in the gs register, specifically the offset containing the tcb address, I think.

This should result in a pretty quick, "inexpensive" and scalable operation then. Wondered if there was a chance that another thread could overwrite the register where this value is written before the calling thread retrieved it, (the volatile keyword isn't used) but I suppose this function would be useless if that was a possibility. This function must be thread safe, right?

Cheers,

There's a different tls.h for every architecture.

Of course it's thread-safe, not much point having it otherwise. It may be using registers that it wouldn't have write-access to, as well; unlike the old linuxthreads, NPTL operates with pretty low-level support by the kernel. (not that NPTL runs as root -- more that things more fundamental than system calls exist in the kernel for it. On MIPS for example, it monopolizes one of only four special hardware registers a MIPS chip has.)

I can't think of a good reason to call pthread_self a lot.
why?
I doubt it's atomic, why should it need to be, it's unique to the thread.
it's only used for detaching or scheduling.
i would start detached if need be. scheduling I haven't tried.
"keep it simple".

"I can't think of a good reason to call pthread_self a lot.
why?"

It's a long story, but basically I need a way of accessing application specific "context" information I have stored which is different depending on which thread is running. Using pthread_self() provides a way of having a unique key to that information regardless of where within a program, the execution is. Because pthread_self doesn't require any arguments, I don't need to carry around a parameter everywhere with that key, hence the application doesn't even have to be aware of the mechanism.

Hopefully that makes sort-of sense.

You could just call it once and save the value.

No. That wouldn't work unfortunately. I'd have to save it in something that I would then need to tag along to every function as a parameter. I don't want to do that because I'm building 2 applications in layers, the application in the upper layer shouldn't know about the mechanism of the lower layer, it just calls its header functions.

---------- Post updated 08-06-10 at 02:17 PM ---------- Previous update was 07-06-10 at 10:04 PM ----------

Dunno if this is of interest to anyone but I found a way of retrieving information without resorting to pthread_self as I was describing in this thread...

pthread_key_t key; /* global key */
key_ret_code = pthread_key_create(&key, NULL); /* initialises the key */

This creates a global key which all threads can access. Then each thread can store a value with that key...(in the form of a void*)

void* value_data;
ret_code = pthread_setspecific(key, value_data);

Now any thread can call...

void* value_data = pthread_getspecific(key);

And get access to that data. Just what I was looking for!

Lots more in this useful document...

http://dlc.sun.com/pdf/816-5137/816-5137.pdf

As I suspected it would, pthread_getspecific uses THREAD_SELF anyway -- and that's only in the best case. Worst case it has to plunge in and access other thread memory other ways. Stop trying to be so clever and just use pthread_self, that's what it's there for :wink:

Thanks for the heads-up Corona688,

You might be right, but the thing is I had to store the "pthread_t" values in an array anyway, so in my application, after calling pthread_self() I would then always have to search the array for the corresponding "pthread_t" value. (At the moment this search is O(n) but I had planned to get it down to O(log n) using a binary-search algorithm if the number of threads is "high").

The good thing about "pthread_getspecific" is it returns the actual data I wanted, but as you pointed out, it looks a bit "involved" when it's not the best case. Maybe I'll try both approaches and see which is quicker.

Using "pthread_getspecific" might be better in avoiding a cache colision though, rather than threads accessing data in a global table.

Trying to be clever ain't so bad if I can manage to pull it off now and again;)

Ah. If you're looking up data, then you're probably right. Helps to know your actual question. :wink: