Multi-threading questions

I've been doing some reading lately about threading (Posix threads) and I'm really curious about a couple things that I've read. I'm not sure if many people here have threading experience, but I thought it would be nice to be able to discuss some questions about it.

(For the record, I did search, and found tons of threads with "thread" in them for obvious reasons, refining my search to "posix thread" revealed very limited results - and one general thread that was from 2002 - similar searching for "multi thread" provided minimal results as well. Anyway, I just wanted you all to know that I searched before starting a somewhat general thread).

On to the questions:

1) I know that errno is thread safe when the _REENTRANT macro is defined...what about when it is not? I.E. I'm calling a library function from a library that wasn't compiled with the _REENTRANT macro, what errno value do those functions get? Always the errno from the first thread (this is what I'd hope such that a library can declare that it is safe if called from the primary thread).

2) I've seen functions re-written to be thread safe by doing their work in a local variable, then copying that local variable out to a user passed in storage area. What I wonder is, what about just using the user defined storage area directly? I.E. Why do they do this:

int func(struct f_struct *outBuf)
{
   struct f_struct work;

   /* set everything in work */

   memcpy(outBuf, &work, sizeof(struct f_struct));
}

While it would seem just as appropriate to do the work directly in outBuf?

I would think that with large structures, the memcpy at the end would be more expensive than working with outBuf directly?

What I'm wondering is are both methods appropriate, and my literature only mentions their preference?

3) In general, it seems that an application using threads needs to link with the pthread library. Therefore, how do I provide both thread safe interfaces and standard interfaces in one library without requring an application to link with the pthread library. Obviously to call the functions that use pthread_ functions, I need that library. However, I'd prefer NOT to make a library dependant on the availabillity of a thread library. How do the standard libraries overcome this?

Anyway, I'm sure I'll have more questions...but that's all I can think of right now...off to read some more.

I'll try to answer your questions, but I have only written a few sample pthread programs. But it's not like I'm a thread expert.

Then errno is not thread safe. If you insist on invoking a non thread safe library then your expected results are undefined. My guess is that the library will use the one-per-process errno and get the last error of any system call invoked by any thread. I would not expect repeatable results.

When I've seen stuff like that, it was to warn against using a local static variable and then passing the address of that local static variable back to the caller. If you have a already written function that does that, a simple memcopy at the end will render it thread safe. I don't see a real problem in using the caller's area directly. But if you do that, you will be dereferencing a pointer, perhaps repeatedly. That may not be a speed win.

So they were not rewriten to USE a local variable. They were rewritten to not return a pointer to a local variable.

BTW, at very high optimization levels a memcpy will be "inlined", that is, it becomes a sequence of instructions rather than a true call. It may even become a single machine language instruction.

Actually you have it backwards. Thread-safe interfaces are standard. A process is always a collection of threads. Sometimes it simply has one thread. You don't need a thread library unless you are assuming multiple threads, so don't assume that.

I haven't looked at the source code for the standard libraries, but I guess that there are 3 cases.

  1. Some stuff is so primative that it always was thread safe.

  2. Some stuff was not thread safe, but it could be made thread safe with changing the interface presented to the caller. So this was done.

  3. Some stuff could not be made thread safe without visible changes. So seperate versions were made. For the most these are functions that tripped on the issue in your 2nd question.