Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 18 Sep 1997 00:23:32 +0000 (GMT)
From:      Terry Lambert <tlambert@primenet.com>
To:        ianh@saturn-tech.com (Ian Hungerford)
Cc:        jb@cimlogic.com.au, freebsd-current@FreeBSD.ORG
Subject:   Re: Thread safe libc
Message-ID:  <199709180023.RAA19855@usr04.primenet.com>
In-Reply-To: <Pine.BSF.3.95.970917142907.6836A-100000@hobbes.saturn-tech.com> from "Ian Hungerford" at Sep 17, 97 02:35:18 pm

next in thread | previous in thread | raw e-mail | index | archive | help
> Some clarification is necessary for the second part, though.  Should the
> replacements for statics be allocated using thread specific data
> (destroyed when the thread terminates) or malloc() (with the caller
> assuming responsibility for the free() call)?  I prefer the first method -
> the latter is indescribably ugly, and programs that want to pass pointers
> to these objects between threads should simply use the new _r functions.

How do you propose to implement agregate allocation (ie: library open
time) of thread local storage?

PS: you don't have per thread library attach and detach events.  Even
if you could clean the crap up in thread_exit().

> Just my $.02, of course - I'll implement it, however ugly it ends up
> needing to be. :)

Personally, I'd implement it like:

In the library:
/*
 * The real getpwent; excuse the crappy name for the function as it
 * probably should have been since day one.
 *
 * Note: you may call this code from either threaded or unthreaded
 * applications.
 */
int
getpwent_r( struct passwd *usrbuf)
{
	... {
		return 1;	/* success*/
	}
	return 0;		/* failure*/
}


In pwd.h:
/*
 * The legacy getpwent; excuse the subsumption of the good name by a
 * crappy function.  The buffer will be instanced each time the function
 * is inlined.
 *
 * Note: do not call this code from a threaded application.
 */
static __inline struct passwd *
getpwent(void)
{
	static struct passwd usrbuf;

	if( getpwent_r( &userbuf))
		return &userbuf;
	else	return NULL;
}


In this case, getpwent_r() is thread safe, but getpwent() is not (hint:
two threads running the same code will get the same static buffer instance).


Alternately:

1)	In the thread initialization, create keys for all "static"
	buffers.

2)	Initialize the key so you don't have to take the memory hit
	at the start of life.

3)	Allocate the buffer from the heap and use pthread_setspecific()
	to point to it.

4)	Fill in the allocate area with the function result.

5)	Return the pointer to the allocated area

6)	Realize that a getpwent() call by thread 2 can't have its
	result passed to thread 1 if it goes out of scope.  Example:

	A)	I have a set of work-to-do threads
	B)	I have a client request to start a session
	C)	The client request is handled by starting a
		session initialization thread.
	D)	The thread allocates from a shared memory segment
		instead of thread local storage in order to create
		a client context shared between the worker threads.
	E)	Postinitialization is done by the thread that
		scheduled the work.
	F)	Oop!  The buffer area was destroyed before the
		postinitialization could copy it because the
		buffer was allocated in TLS instead of acting
		like getpwent() is transparent.
	G)	Guess you should have used getpwent_r() and provided
		your own buffer...
	H)	Hey, I bet that explains all these *_r() functions...

So doing it using TLS will cause it to fail erratically in implementation
specific ways that are not obvious to the programmer.

Alternately, you could define a non-transparent interface, where
keys are ference counted (they aren't in pthreads, you would have to
write this).

Then you coul increment the reference count as part of passing the
data between threads, so that when it went out of scope in one thread,
it would not be destroyed.  you would need to create a key in the target
thread to contain the data pointer so the thread_exit() cleanup would
still free it.

You could call this process "marshalling", and we could all buy:

	Inside COM
	Microsoft's Component Object Model
	Dale Rogerson
	Microsoft Press
	ISBN: 1-57231-349-8

And read about how it works, since they use the same TLS model to
deal with static buffers and C++ pure-virtual-derived-object instances.


Needless to say, I think Thread Local Storage is evil.  The only
*possible* justifcation is automatic stack growth, and you can do
that without TLS as well, if you are clever about it.

I would definitely prefer that the user supply the buffers to an *_r()
routine (I think these routines should replace the standard ones, since
the standard ones are obviously defective, so they don't have the dumb
"_r" appendage), and the scoping of data in threads be forthright and
obvious to anyone reading the code.

					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199709180023.RAA19855>