From: Peter Eisentraut Date: Mon, 24 Nov 2003 13:11:27 +0000 (+0000) Subject: Rename USE_THREADS to ENABLE_THREAD_SAFETY to avoid name clash with Perl. X-Git-Url: http://waps.l3s.uni-hannover.de/gitweb/?a=commitdiff_plain;h=dfc5a3bfafd59a860adc34306d49a8a313317db1;p=users%2Fbernd%2Fpostgres.git Rename USE_THREADS to ENABLE_THREAD_SAFETY to avoid name clash with Perl. Fixes compliation failure with --enable-thread-safety --with-perl and Perl 5.6.1. --- diff --git a/configure b/configure index 14fcc49916..c4cd0ec594 100755 --- a/configure +++ b/configure @@ -2909,7 +2909,7 @@ if test "${enable_thread_safety+set}" = set; then yes) cat >>confdefs.h <<\_ACEOF -#define USE_THREADS 1 +#define ENABLE_THREAD_SAFETY 1 _ACEOF ;; diff --git a/configure.in b/configure.in index 3b40440787..639f1f9695 100644 --- a/configure.in +++ b/configure.in @@ -351,7 +351,7 @@ IFS=$ac_save_IFS # AC_MSG_CHECKING([allow thread-safe client libraries]) PGAC_ARG_BOOL(enable, thread-safety, no, [ --enable-thread-safety make client libraries thread-safe], - [AC_DEFINE([USE_THREADS], 1, + [AC_DEFINE([ENABLE_THREAD_SAFETY], 1, [Define to 1 to build client libraries as thread-safe code. (--enable-thread-safety)])]) AC_MSG_RESULT([$enable_thread_safety]) AC_SUBST(enable_thread_safety) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index cce536adb2..b53fed2257 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -39,6 +39,10 @@ /* Define to 1 if you want National Language Support. (--enable-nls) */ #undef ENABLE_NLS +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#undef ENABLE_THREAD_SAFETY + /* Define to 1 if gettimeofday() takes only 1 argument. */ #undef GETTIMEOFDAY_1ARG @@ -609,10 +613,6 @@ /* Define to select SysV-style shared memory. */ #undef USE_SYSV_SHARED_MEMORY -/* Define to 1 to build client libraries as thread-safe code. - (--enable-thread-safety) */ -#undef USE_THREADS - /* Define to select unnamed POSIX semaphores. */ #undef USE_UNNAMED_POSIX_SEMAPHORES diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 7a76a2f56a..6c37aed4c1 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -3,7 +3,7 @@ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY #include #endif #include "ecpgtype.h" @@ -12,7 +12,7 @@ #include "extern.h" #include "sqlca.h" -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER; #endif static struct connection *all_connections = NULL; @@ -45,13 +45,13 @@ ECPGget_connection(const char *connection_name) { struct connection *ret = NULL; -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_lock(&connections_mutex); #endif ret = ecpg_get_connection_nr(connection_name); -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&connections_mutex); #endif @@ -355,7 +355,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p realname = strdup(dbname); /* add connection to our list */ -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_lock(&connections_mutex); #endif if (connection_name != NULL) @@ -387,7 +387,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p char *db = realname ? realname : ""; ecpg_finish(this); -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&connections_mutex); #endif ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n\t%s\n", @@ -411,7 +411,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p ECPGfree(dbname); return false; } -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&connections_mutex); #endif @@ -440,7 +440,7 @@ ECPGdisconnect(int lineno, const char *connection_name) struct sqlca_t *sqlca = ECPGget_sqlca(); struct connection *con; -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_lock(&connections_mutex); #endif @@ -461,7 +461,7 @@ ECPGdisconnect(int lineno, const char *connection_name) if (!ECPGinit(con, connection_name, lineno)) { -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&connections_mutex); #endif return (false); @@ -470,7 +470,7 @@ ECPGdisconnect(int lineno, const char *connection_name) ecpg_finish(con); } -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&connections_mutex); #endif diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c index ffca6327d9..0a50f50961 100644 --- a/src/interfaces/ecpg/ecpglib/misc.c +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -5,7 +5,7 @@ #include #include -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY #include #endif #include "ecpgtype.h" @@ -55,7 +55,7 @@ static struct sqlca_t sqlca_init = } }; -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY static pthread_key_t sqlca_key; static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT; @@ -88,7 +88,7 @@ static struct sqlca_t sqlca = }; #endif -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER; #endif @@ -117,7 +117,7 @@ ECPGinit(const struct connection * con, const char *connection_name, const int l return (true); } -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY static void ecpg_sqlca_key_init(void) { @@ -128,7 +128,7 @@ ecpg_sqlca_key_init(void) struct sqlca_t * ECPGget_sqlca(void) { -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY struct sqlca_t *sqlca; pthread_once(&sqlca_key_once, ecpg_sqlca_key_init); @@ -211,7 +211,7 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction) void ECPGdebug(int n, FILE *dbgs) { -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_lock(&debug_init_mutex); #endif @@ -219,7 +219,7 @@ ECPGdebug(int n, FILE *dbgs) debugstream = dbgs; ECPGlog("ECPGdebug: set to %d\n", simple_debug); -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&debug_init_mutex); #endif } @@ -229,7 +229,7 @@ ECPGlog(const char *format,...) { va_list ap; -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_lock(&debug_mutex); #endif @@ -239,7 +239,7 @@ ECPGlog(const char *format,...) if (f == NULL) { -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&debug_mutex); #endif return; @@ -255,7 +255,7 @@ ECPGlog(const char *format,...) ECPGfree(f); } -#ifdef USE_THREADS +#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&debug_mutex); #endif } diff --git a/src/port/thread.c b/src/port/thread.c new file mode 100644 index 0000000000..962181a20c --- /dev/null +++ b/src/port/thread.c @@ -0,0 +1,281 @@ +/*------------------------------------------------------------------------- + * + * thread.c + * + * Prototypes and macros around system calls, used to help make + * threaded libraries reentrant and safe to use from threaded applications. + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * + * $Id$ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include +#if defined(WIN32) && defined(_MSC_VER) +#undef ERROR +#else +#include +#endif +#if defined(ENABLE_THREAD_SAFETY) +#include +#endif + +/* + * Threading sometimes requires specially-named versions of functions + * that return data in static buffers, like strerror_r() instead of + * strerror(). Other operating systems use pthread_setspecific() + * and pthread_getspecific() internally to allow standard library + * functions to return static data to threaded applications. And some + * operating systems have neither, meaning we have to do our own locking. + * + * Additional confusion exists because many operating systems that + * use pthread_setspecific/pthread_getspecific() also have *_r versions + * of standard library functions for compatibility with operating systems + * that require them. However, internally, these *_r functions merely + * call the thread-safe standard library functions. + * + * For example, BSD/OS 4.3 uses Bind 8.2.3 for getpwuid(). Internally, + * getpwuid() calls pthread_setspecific/pthread_getspecific() to return + * static data to the caller in a thread-safe manner. However, BSD/OS + * also has getpwuid_r(), which merely calls getpwuid() and shifts + * around the arguments to match the getpwuid_r() function declaration. + * Therefore, while BSD/OS has getpwuid_r(), it isn't required. It also + * doesn't have strerror_r(), so we can't fall back to only using *_r + * functions for threaded programs. + * + * The current setup is to try threading in this order: + * + * use non-*_r function names if they are all thread-safe + * (NEED_REENTRANT_FUNCS=no) + * use *_r functions if they exist (configure test) + * do our own locking and copying of non-threadsafe functions + * + * The disadvantage of the last option is not the thread overhead but + * the fact that all function calls are serialized, and with gethostbyname() + * requiring a DNS lookup, that could be slow. + * + * One thread-safe solution for gethostbyname() might be to use getaddrinfo(). + * + * See src/tools/thread to see if your operating system has thread-safe + * non-*_r functions. + */ + + +/* + * Wrapper around strerror and strerror_r to use the former if it is + * available and also return a more useful value (the error string). + */ +char * +pqStrerror(int errnum, char *strerrbuf, size_t buflen) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && defined(HAVE_STRERROR_R) + /* reentrant strerror_r is available */ + /* some early standards had strerror_r returning char * */ + strerror_r(errnum, strerrbuf, buflen); + return strerrbuf; + +#else + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_STRERROR_R) + static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&strerror_lock); +#endif + + /* no strerror_r() available, just use strerror */ + StrNCpy(strerrbuf, strerror(errnum), buflen); + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_STRERROR_R) + pthread_mutex_unlock(&strerror_lock); +#endif + + return strerrbuf; +#endif +} + +/* + * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() + * behaviour, if it is not available or required. + */ +#ifndef WIN32 +int +pqGetpwuid(uid_t uid, struct passwd *resultbuf, char *buffer, + size_t buflen, struct passwd **result) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && defined(HAVE_GETPWUID_R) + /* + * Early POSIX draft of getpwuid_r() returns 'struct passwd *'. + * getpwuid_r(uid, resultbuf, buffer, buflen) + * Do we need to support it? bjm 2003-08-14 + */ + /* POSIX version */ + getpwuid_r(uid, resultbuf, buffer, buflen, result); + +#else + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETPWUID_R) + static pthread_mutex_t getpwuid_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&getpwuid_lock); +#endif + + /* no getpwuid_r() available, just use getpwuid() */ + *result = getpwuid(uid); + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETPWUID_R) + + /* Use 'buffer' memory for storage of strings used by struct passwd */ + if (*result && + strlen((*result)->pw_name) + 1 + + strlen((*result)->pw_passwd) + 1 + + strlen((*result)->pw_gecos) + 1 + + /* skip class if it exists */ + strlen((*result)->pw_dir) + 1 + + strlen((*result)->pw_shell) + 1 <= buflen) + { + memcpy(resultbuf, *result, sizeof(struct passwd)); + strcpy(buffer, (*result)->pw_name); + resultbuf->pw_name = buffer; + buffer += strlen(resultbuf->pw_name) + 1; + strcpy(buffer, (*result)->pw_passwd); + resultbuf->pw_passwd = buffer; + buffer += strlen(resultbuf->pw_passwd) + 1; + strcpy(buffer, (*result)->pw_gecos); + resultbuf->pw_gecos = buffer; + buffer += strlen(resultbuf->pw_gecos) + 1; + strcpy(buffer, (*result)->pw_dir); + resultbuf->pw_dir = buffer; + buffer += strlen(resultbuf->pw_dir) + 1; + strcpy(buffer, (*result)->pw_shell); + resultbuf->pw_shell = buffer; + buffer += strlen(resultbuf->pw_shell) + 1; + + *result = resultbuf; + } + else + { + *result = NULL; + errno = ERANGE; + } + + pthread_mutex_unlock(&getpwuid_lock); +#endif +#endif + return (*result == NULL) ? -1 : 0; +} +#endif + +/* + * Wrapper around gethostbyname() or gethostbyname_r() to mimic + * POSIX gethostbyname_r() behaviour, if it is not available or required. + * This function is called _only_ by our getaddinfo() portability function. + */ +#ifndef HAVE_GETADDRINFO +int +pqGethostbyname(const char *name, + struct hostent *resultbuf, + char *buffer, size_t buflen, + struct hostent **result, + int *herrno) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && defined(HAVE_GETHOSTBYNAME_R) + /* + * broken (well early POSIX draft) gethostbyname_r() which returns + * 'struct hostent *' + */ + *result = gethostbyname_r(name, resbuf, buffer, buflen, herrno); + return (*result == NULL) ? -1 : 0; + +#else + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETHOSTBYNAME_R) + static pthread_mutex_t gethostbyname_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&gethostbyname_lock); +#endif + + /* no gethostbyname_r(), just use gethostbyname() */ + *result = gethostbyname(name); + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETHOSTBYNAME_R) + + /* + * Use 'buffer' memory for storage of structures used by struct hostent. + * The layout is: + * + * addr pointers + * alias pointers + * addr structures + * alias structures + * name + */ + if (*result) + { + int i, pointers = 2 /* for nulls */, len = 0; + char **pbuffer; + + for (i = 0; (*result)->h_addr_list[i]; i++, pointers++) + len += (*result)->h_length; + for (i = 0; (*result)->h_aliases[i]; i++, pointers++) + len += (*result)->h_length; + + if (pointers * sizeof(char *) + MAXALIGN(len) + strlen((*result)->h_name) + 1 <= buflen) + { + memcpy(resultbuf, *result, sizeof(struct hostent)); + + pbuffer = (char **)buffer; + resultbuf->h_addr_list = pbuffer; + buffer += pointers * sizeof(char *); + + for (i = 0; (*result)->h_addr_list[i]; i++, pbuffer++) + { + memcpy(buffer, (*result)->h_addr_list[i], (*result)->h_length); + resultbuf->h_addr_list[i] = buffer; + buffer += (*result)->h_length; + } + resultbuf->h_addr_list[i] = NULL; + pbuffer++; + + resultbuf->h_aliases = pbuffer; + + for (i = 0; (*result)->h_aliases[i]; i++, pbuffer++) + { + memcpy(buffer, (*result)->h_aliases[i], (*result)->h_length); + resultbuf->h_aliases[i] = buffer; + buffer += (*result)->h_length; + } + resultbuf->h_aliases[i] = NULL; + pbuffer++; + + /* Place at end for cleaner alignment */ + buffer = MAXALIGN(buffer); + strcpy(buffer, (*result)->h_name); + resultbuf->h_name = buffer; + buffer += strlen(resultbuf->h_name) + 1; + + *result = resultbuf; + } + else + { + *result = NULL; + errno = ERANGE; + } + } +#endif + + if (*result != NULL) + *herrno = h_errno; + +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETHOSTBYNAME_R) + pthread_mutex_unlock(&gethostbyname_lock); +#endif + + if (*result != NULL) + return 0; + else + return -1; +#endif +} +#endif diff --git a/src/tools/thread/thread_test.c b/src/tools/thread/thread_test.c new file mode 100644 index 0000000000..20ef11251f --- /dev/null +++ b/src/tools/thread/thread_test.c @@ -0,0 +1,155 @@ +/*------------------------------------------------------------------------- + * + * test_thread_funcs.c + * libc thread test program + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Header$ + * + * This program tests to see if your standard libc functions use + * pthread_setspecific()/pthread_getspecific() to be thread-safe. + * See src/port/thread.c for more details. + * + * This program first tests to see if each function returns a constant + * memory pointer within the same thread, then, assuming it does, tests + * to see if the pointers are different for different threads. If they + * are, the function is thread-safe. + * + *------------------------------------------------------------------------- + */ + + +#ifdef ENABLE_THREAD_SAFETY +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +void func_call_1(void); +void func_call_2(void); + +#ifndef HAVE_GETADDRINFO +struct hostent *hostent_p1; +struct hostent *hostent_p2; +#endif + +struct passwd *passwd_p1; +struct passwd *passwd_p2; + +char *strerror_p1; +char *strerror_p2; + +int main(int argc, char *argv[]) +{ + pthread_t thread1, + thread2; + + if (argc > 1) + { + fprintf(stderr, "Usage: %s\n", argv[0]); + return 1; + } + + printf("\ +Make sure you have added any needed 'THREAD_CPPFLAGS' and 'THREAD_LIBS'\n\ +defines to your template/$port file before compiling this program.\n\n" +); + pthread_create(&thread1, NULL, (void * (*)(void *)) func_call_1, NULL); + pthread_create(&thread2, NULL, (void * (*)(void *)) func_call_2, NULL); + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + +#ifndef HAVE_GETADDRINFO + if (hostent_p1 == hostent_p2) + printf("Your gethostbyname() is _not_ thread-safe\n"); +#endif + if (passwd_p1 == passwd_p2) + printf("Your getpwuid() is _not_ thread-safe\n"); + if (strerror_p1 == strerror_p2) + printf("Your strerror() is _not_ thread-safe\n"); + + if ( +#ifndef HAVE_GETADDRINFO + hostent_p1 != hostent_p2 && +#endif + passwd_p1 != passwd_p2 && + strerror_p1 != strerror_p2) + { + printf("All your non-*_r functions are thread-safe.\n"); + printf("Add this to your template/$port file:\n\n"); + printf("NEED_REENTRANT_FUNCS=no\n"); + } + else + { + printf("Not all non-*_r functions are thread-safe.\n"); + printf("Add this to your template/$port file:\n\n"); + printf("NEED_REENTRANT_FUNCS=yes\n"); + } + + return 0; +} + +void func_call_1(void) { + void *p; + +#ifndef HAVE_GETADDRINFO + hostent_p1 = gethostbyname("yahoo.com"); + p = gethostbyname("slashdot.org"); + if (hostent_p1 != p) + { + printf("Your gethostbyname() changes the static memory area between calls\n"); + hostent_p1 = NULL; /* force thread-safe failure report */ + } +#endif + + passwd_p1 = getpwuid(0); + p = getpwuid(1); + if (passwd_p1 != p) + { + printf("Your getpwuid() changes the static memory area between calls\n"); + passwd_p1 = NULL; /* force thread-safe failure report */ + } + + strerror_p1 = strerror(EACCES); + /* + * If strerror() uses sys_errlist, the pointer might change for different + * errno values, so we don't check to see if it varies within the thread. + */ +} + + +void func_call_2(void) { + void *p; + +#ifndef HAVE_GETADDRINFO + hostent_p2 = gethostbyname("google.com"); + p = gethostbyname("postgresql.org"); + if (hostent_p2 != p) + { + printf("Your gethostbyname() changes the static memory area between calls\n"); + hostent_p2 = NULL; /* force thread-safe failure report */ + } +#endif + + passwd_p2 = getpwuid(2); + p = getpwuid(3); + if (passwd_p2 != p) + { + printf("Your getpwuid() changes the static memory area between calls\n"); + passwd_p2 = NULL; /* force thread-safe failure report */ + } + + strerror_p2 = strerror(EINVAL); + /* + * If strerror() uses sys_errlist, the pointer might change for different + * errno values, so we don't check to see if it varies within the thread. + */ +}