Thread-safe global variables in C
I'm making a C library, and in it, I want to be able to create a global variable (probably more later, too) that can be used in multiple programs and multiple threads of those programs safely. Of course, the naïve way to do it would be to put this in a header file:
extern int Error; and this in a C file of the library: int Error; The problem with doing this: what if one thread calls a function that sets an error code, then another thread calls a function that also sets an error code? One will interfere with the other. I'd rather not do passing-by-pointer since I think code would look cleaner in the global form. What would be a good way of going about this? Also, I guess it's okay to use errno for my own function errors, right? Either way, I might want to make my own global variables in the future, and it'd be neat to know how to do them in a thread-safe manner. Thanks! |
Quote:
-------------- Steve Stites |
I was thinking of having macros that refer to functions that access copies of the "global variables" stored either in a buffer provided by the library or in the program's address space. Perhaps it'd be something like this (bear with the made-up functions and oversimplified array accesses):
Code:
typedef struct LibraryGlobals |
It turns out that the folks who developed the POSIX threads standard have already thought of this requirement. Surprise, surprise.
In the following discussion, any variable names in red are arbitrary, and you can use almost any names you wish. Fasten your seatbelts. Here we go. Before starting to code these steps, begin to think of all the "global" variables you'll need for which you want one instance per thread. (In your example, there'll be only one such variable: Error.) Imagine these variables all in a struct, because that's where you'll be putting them. The steps are simple. If you have only one function in your library, these steps should be done in that function; if you have more than one function in your library, these steps should be done in each library function you write; all the associated library functions in the same library should refer to the same pair of globals wilma and fred I discuss below.
When a thread terminates, the destructor that you write should be used to deallocate the struct that contains the data specific to this thread. The destructor should be defined something like this: Code:
void To cover the all-important fine print, be sure to read all the apropos man pages. And if you're going to do any serious work with POSIX threads, be sure to buy David R. Butenhof's excellent book Programming with POSIX Threads. Accept no substitutes. My copy of the O'Reilly book on the same subject does not begin to cover the material with the same depth of insight. Hope this helps. |
Hi, JoeyAdams -
You might also want to look at "thread local storage" (TLS): http://linux.web.cern.ch/linux/scien...ead-local.html http://linux.die.net/man/2/set_thread_area http://linux.die.net/man/2/get_thread_area Here's a discussion for TLS (and threading in general) under Windows: http://courses.washington.edu/css443...gWithWin32.pdf |
Wow, thanks for the awesome replies. I wonder what would be smarter to use, since the first link about TLS suggests that it might not be widely available due to the extensive support by the linker and libraries (and it's probably only on GCC), but pthread isn't present on Windows (or is it?).
I have a simple question about the pthread stuff you posted above. Where should Code:
pthread_once_t wilma=PTHREAD_ONCE_INIT; |
Four things.
Thing one: errno is one of those weird animals, a per-thread global. Each thread gets its own. Thing two: Code:
pthread_once_t wilma=PTHREAD_ONCE_INIT; Code:
pthread_once_t wilma; If you want to relax this requirement, though, keep the following things in mind: There is nothing different between types pthread_once_t and, say, int in this regard. If you're going to be building an industrial-strength library, it would be good to experiment with how your compiling environment handles multiple definitions of global Code:
int foo=5; Then take careful notes as to what you did, and what the results were, so you can replicate the experiment on each platform for which you ship your library. Much easier to just obey the rules. (grin) Thing three: pthreads is, and is not, available for Windows. For a gloriously complex answer, google this: Code:
pthread windows The whole point of pthread_once() is that you can call it as many times in each thread as you want, and it calls your once-function exactly once per thread. So you can safely put all that stuff at the beginning of each of your library functions (or perhaps put it in a common function that each of your top-level functions calls). |
Thread-local storage (TLS) is the way to go. Many languages provide a built-in facility for giving easy access to it, so don't overlook all possibilities: make things easy on yourself.
The essential idea behind TLS is simple: every thread is defined, in the system, by a so-called "thread control block" (TCB) and within that TCB there are a handful of otherwise-unused slots... TLS. And what you (or your language) do with them is to use one of 'em as a pointer to whatever you want to store. Just be sure that, when the thread terminates, it cleans up whatever it has allocated. If what you need is a "thread-safe global pool" of (anything), then you have to use mutexes of some kind to protect it. |
Thanks. I probably won't be making thread-safe globals in my library any time soon, but when I do, this will be a lot of help. I think I have just one more question though:
Quote:
|
Yes, that would work, because each compiled module in the library will (I hope) "see" that line of code, since it's in the header.
I'm uncertain if it will raise a problem in the one C source file that contains the PTHREAD_ONCE_INIT, since that C source file will also see the uninitialized version. But you can work that out if the problem arises, I'm sure. |
POSIX therad that terms pthread, can keep value of variables of each thread for the same thread, the name of this technique is specific-data , you can google with specific-data pthread C
|
Because the C library uses global variables such as "errno", they only way to truly have a thread safe C library is if the underlying operating system keeps a separate memory address space for each thread. The address of "errno" would then be the same for each thread, but refer to a different physical memory location. One can play tricks such as defining C macros to replace global variables like "errno", but then the global variables will not behave as normal variables.
EDIT I obviously misunderstood the question. The question is about writing a library using the C language. I thought it was asking how to write "the" standard C library. All the other answers are good suggestions. Which one to use will depend on the operating systems and C compilers used with the library. |
Using current Linux kernels and development tools, one can use thread-local storage simply by adding the __thread keyword to the variable definition. It, or its variant, are available in practically all current C environments, including Windows, although the details vary a bit.
In Linux, if you want to have your own variable with errno-like semantics, just use Code:
extern __thread int my_errno; Code:
__thread int my_errno = 0; |
All times are GMT -5. The time now is 01:04 AM. |