From owner-freebsd-current Wed Sep 17 17:24:01 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.7/8.8.7) id RAA21625 for current-outgoing; Wed, 17 Sep 1997 17:24:01 -0700 (PDT) Received: from usr04.primenet.com (tlambert@usr04.primenet.com [206.165.6.204]) by hub.freebsd.org (8.8.7/8.8.7) with ESMTP id RAA21599 for ; Wed, 17 Sep 1997 17:23:54 -0700 (PDT) Received: (from tlambert@localhost) by usr04.primenet.com (8.8.5/8.8.5) id RAA19855; Wed, 17 Sep 1997 17:23:33 -0700 (MST) From: Terry Lambert Message-Id: <199709180023.RAA19855@usr04.primenet.com> Subject: Re: Thread safe libc To: ianh@saturn-tech.com (Ian Hungerford) Date: Thu, 18 Sep 1997 00:23:32 +0000 (GMT) Cc: jb@cimlogic.com.au, freebsd-current@FreeBSD.ORG In-Reply-To: from "Ian Hungerford" at Sep 17, 97 02:35:18 pm X-Mailer: ELM [version 2.4 PL23] Content-Type: text Sender: owner-freebsd-current@FreeBSD.ORG X-Loop: FreeBSD.org Precedence: bulk > 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.