I have a non-thread-safe closed-source C library which basically works ok in AOLserver, but over time shows heap corruption under Purify, will eventually segfault, etc.
Turns out, there's supposed to be a pretty good way for me to fix this! I can find all the unsafe functions in the library with "nm", then set the LD_PRELOAD environment variable to my OWN special library which makes the C library functions thread-safe by using Thread Local Storage. Some links that explain the LD_PRELOAD stuff at greater length: http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html http://www-106.ibm.com/developerworks/linux/library/l-glibc.html http://www.uberhip.com/people/godber/interception/index.html Anyway, I went ahead and did that. Naturally, I decided to use AOLserver's nice TLS API for all my thread local storage code. In fact, in many cases, I didn't have to do any work at all because AOLserver already includes a TLS-threasafe version of standard library calls. E.g., "Ns_Localtime". Here's the catch: I tested this under Purify, and I get LOTS of errors! All "IPW Invalid pointer" write and "IPR Invalid pointer read" though (see below), which I've never seen before. Interestingly, I seem to get these when I call Ns_Strtok and Ns_Localtime, but NOT when I call my own gethostbyname, which I implemented with Ns_TlsAlloc, etc. (See functions below.) But from the ns_log statements I know my gethostbyname function is being called twice during AOLserver startup, it just doesn't seem to generate any Purify errors. Any ideas what's going on here, or what I should do about it?? Is there some reason I should e.g. implement my own TLS strtok using Ns_TlsAlloc, rather than simply calling Ns_Strtok? Thanks! IPW: Invalid pointer write: * This is occurring while in thread 10: ns_strtok [reentrant.c:324] strtok [mt-safe.c:205] IPR: Invalid pointer read: * This is occurring while in thread 10: ns_strtok [reentrant.c:330] strtok [mt-safe.c:205] IPW: Invalid pointer write: * This is occurring while in thread 9: ns_localtime [reentrant.c:134] localtime [mt-safe.c:110] ^L /* * ---------------------------------------------------------------------- * * strtok -- * * Reentrant version using thread local storage. * * Notes: * * Simply calls AOLserver's Ns_Strtok. * * Again, this is only going to work properly when Ns_Strtok is * implemented with strtok_r. For AOLserver 3.x, this is the case * on all platforms except for MS-Windows. [EMAIL PROTECTED], * 2003/02/07 14:58 EST * * ---------------------------------------------------------------------- */ char * strtok(char *s1, const char *s2) { return Ns_Strtok(s1, s2); } struct tm * localtime(const time_t *clock) { static const char func_name[] = "localtime"; return Ns_Localtime(clock); } #define GETHOSTBYNAME_BUF_LEN 16384 static Ns_Tls gethostbyname_tls = NULL; static Ns_Mutex tls_lock; typedef struct { struct hostent result; char buf[GETHOSTBYNAME_BUF_LEN]; } gethostbyname_t; static void Mt_TlsCleanup_gethostbyname(void * ptr) { /* * It's this simple only because there are no points to other * dynamically allocated storage inside the gethostbyname_t struct. */ Ns_Free(ptr); } struct hostent * gethostbyname(const char * name) { static const char func_name[] = "gethostbyname"; int rc_err = 0; gethostbyname_t * state_ptr = NULL; /* It makes little or no sense for gethostbyname_r to return a hostent* rather than just an int, but that's the way it is: [EMAIL PROTECTED], 2003/02/09 08:05 EST */ struct hostent * rc = NULL; Ns_Log(Notice, "%s: %s: Reimplementing library function:", MODULE_NAME, func_name); if (gethostbyname_tls == NULL) { Ns_MutexLock(&tls_lock); if (gethostbyname_tls == NULL) { state_ptr = (gethostbyname_t *) Ns_Calloc(sizeof(gethostbyname_t), 1); Ns_TlsAlloc(&gethostbyname_tls, Mt_TlsCleanup_gethostbyname); Ns_TlsSet(&gethostbyname_tls, state_ptr); } Ns_MutexUnlock(&tls_lock); } if (state_ptr == NULL) { state_ptr = Ns_TlsGet(&gethostbyname_tls); } rc = gethostbyname_r(name, &(state_ptr->result), state_ptr->buf, GETHOSTBYNAME_BUF_LEN, &rc_err); if (rc == NULL) { if (rc_err == ERANGE) { Ns_Log(Error, "%s: %s: Failed with error code ERANGE, buffer too small.", MODULE_NAME, func_name); } else { Ns_Log(Error, "%s: %s: Failed with error code '%i'.", MODULE_NAME, func_name, rc_err); } } else { /* rc == &(state_ptr->result) */ } return rc; } -- Andrew Piskorski <[EMAIL PROTECTED]> http://www.piskorski.com