/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Functions to get threads more portable */ #define DONT_REMAP_PTHREAD_FUNCTIONS #include "mysys_priv.h" #ifdef THREAD #include #include #include #include #if (defined(__BSD__) || defined(_BSDI_VERSION)) && !defined(HAVE_mit_thread) #define SCHED_POLICY SCHED_RR #else #define SCHED_POLICY SCHED_OTHER #endif uint thd_lib_detected; #ifndef my_pthread_setprio void my_pthread_setprio(pthread_t thread_id,int prior) { #ifdef HAVE_PTHREAD_SETSCHEDPARAM struct sched_param tmp_sched_param; bzero((char*) &tmp_sched_param,sizeof(tmp_sched_param)); tmp_sched_param.sched_priority=prior; VOID(pthread_setschedparam(thread_id,SCHED_POLICY,&tmp_sched_param)); #endif } #endif #ifndef my_pthread_getprio int my_pthread_getprio(pthread_t thread_id) { #ifdef HAVE_PTHREAD_SETSCHEDPARAM struct sched_param tmp_sched_param; int policy; if (!pthread_getschedparam(thread_id,&policy,&tmp_sched_param)) { DBUG_PRINT("thread",("policy: %d priority: %d", policy,tmp_sched_param.sched_priority)); return tmp_sched_param.sched_priority; } #endif return -1; } #endif #ifndef my_pthread_attr_setprio void my_pthread_attr_setprio(pthread_attr_t *attr, int priority) { #ifdef HAVE_PTHREAD_SETSCHEDPARAM struct sched_param tmp_sched_param; bzero((char*) &tmp_sched_param,sizeof(tmp_sched_param)); tmp_sched_param.sched_priority=priority; VOID(pthread_attr_setschedparam(attr,&tmp_sched_param)); #endif } #endif /* To allow use of pthread_getspecific with two arguments */ #ifdef HAVE_NONPOSIX_PTHREAD_GETSPECIFIC #undef pthread_getspecific #ifdef HAVE_UNIXWARE7_THREADS #define pthread_getspecific thr_getspecific #endif void *my_pthread_getspecific_imp(pthread_key_t key) { void *value; if (pthread_getspecific(key,(void *) &value)) return 0; return value; } #endif #ifdef __NETWARE__ /* Don't kill the LibC Reaper thread or the main thread */ #include #undef pthread_exit void my_pthread_exit(void *status) { NXThreadId_t tid = NXThreadGetId(); NXContext_t ctx; char name[PATH_MAX] = ""; /* Do not call pthread_exit if it is not a LibC thread */ if (tid != 0) { NXThreadGetContext(tid, &ctx); NXContextGetName(ctx, name, PATH_MAX); /* "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread" with a debug build of LibC the reaper can have different names */ if (!strindex(name, "\'s")) { pthread_exit(status); } } } #endif /* Some functions for RTS threads, AIX, Siemens Unix and UnixWare 7 (and DEC OSF/1 3.2 too) */ int my_pthread_create_detached=1; #if defined(HAVE_NONPOSIX_SIGWAIT) || defined(HAVE_DEC_3_2_THREADS) int my_sigwait(const sigset_t *set,int *sig) { int signal=sigwait((sigset_t*) set); if (signal < 0) return errno; *sig=signal; return 0; } #endif /* localtime_r for SCO 3.2V4.2 */ #ifndef HAVE_LOCALTIME_R extern pthread_mutex_t LOCK_localtime_r; struct tm *localtime_r(const time_t *clock, struct tm *res) { struct tm *tmp; pthread_mutex_lock(&LOCK_localtime_r); tmp=localtime(clock); *res= *tmp; pthread_mutex_unlock(&LOCK_localtime_r); return res; } #endif /**************************************************************************** ** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) ** ** Note: ** This version of sigwait() is assumed to called in a loop so the signalmask ** is permanently modified to reflect the signal set. This is done to get ** a much faster implementation. ** ** This implementation isn't thread safe: It assumes that only one ** thread is using sigwait. ** ** If one later supplies a different signal mask, all old signals that ** was used before are unblocked and set to SIGDFL. ** ** Author: Gary Wisniewski , much modified by Monty ****************************************************************************/ #if !defined(HAVE_SIGWAIT) && !defined(HAVE_mit_thread) && !defined(sigwait) && !defined(__WIN__) && !defined(HAVE_rts_threads) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) && !defined(OS2) #if !defined(DONT_USE_SIGSUSPEND) static sigset_t sigwait_set,rev_sigwait_set,px_recd; void px_handle_sig(int sig) { sigaddset(&px_recd, sig); } void sigwait_setup(sigset_t *set) { int i; struct sigaction sact,sact1; sigset_t unblock_mask; sact.sa_flags = 0; sact.sa_handler = px_handle_sig; memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ sigemptyset(&unblock_mask); pthread_sigmask(SIG_UNBLOCK,(sigset_t*) 0,&rev_sigwait_set); for (i = 1; i <= sizeof(sigwait_set)*8; i++) { if (sigismember(set,i)) { sigdelset(&rev_sigwait_set,i); if (!sigismember(&sigwait_set,i)) sigaction(i, &sact, (struct sigaction*) 0); } else { sigdelset(&px_recd,i); /* Don't handle this */ if (sigismember(&sigwait_set,i)) { /* Remove the old handler */ sigaddset(&unblock_mask,i); sigdelset(&rev_sigwait_set,i); sact1.sa_flags = 0; sact1.sa_handler = SIG_DFL; sigemptyset(&sact1.sa_mask); sigaction(i, &sact1, 0); } } } memcpy_fixed(&sigwait_set,set,sizeof(*set)); pthread_sigmask(SIG_BLOCK,(sigset_t*) set,(sigset_t*) 0); pthread_sigmask(SIG_UNBLOCK,&unblock_mask,(sigset_t*) 0); } int sigwait(sigset_t *setp, int *sigp) { if (memcmp(setp,&sigwait_set,sizeof(sigwait_set))) sigwait_setup(setp); /* Init or change of set */ for (;;) { /* This is a fast, not 100% portable implementation to find the signal. Because the handler is blocked there should be at most 1 bit set, but the specification on this is somewhat shady so we use a set instead a single variable. */ ulong *ptr= (ulong*) &px_recd; ulong *end=ptr+sizeof(px_recd)/sizeof(ulong); for ( ; ptr != end ; ptr++) { if (*ptr) { ulong set= *ptr; int found= (int) ((char*) ptr - (char*) &px_recd)*8+1; while (!(set & 1)) { found++; set>>=1; } *sigp=found; sigdelset(&px_recd,found); return 0; } } sigsuspend(&rev_sigwait_set); } return 0; } #else /* !DONT_USE_SIGSUSPEND */ /**************************************************************************** ** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) ** ** Note: ** This version of sigwait() is assumed to called in a loop so the signalmask ** is permanently modified to reflect the signal set. This is done to get ** a much faster implementation. ** ** This implementation uses a extra thread to handle the signals and one ** must always call sigwait() with the same signal mask! ** ** BSDI 3.0 NOTE: ** ** pthread_kill() doesn't work on a thread in a select() or sleep() loop? ** After adding the sleep to sigwait_thread, all signals are checked and ** delivered every second. This isn't that terrible performance vice, but ** someone should report this to BSDI and ask for a fix! ** Another problem is that when the sleep() ends, every select() in other ** threads are interrupted! ****************************************************************************/ static sigset_t pending_set; static bool inited=0; static pthread_cond_t COND_sigwait; static pthread_mutex_t LOCK_sigwait; void sigwait_handle_sig(int sig) { pthread_mutex_lock(&LOCK_sigwait); sigaddset(&pending_set, sig); VOID(pthread_cond_signal(&COND_sigwait)); /* inform sigwait() about signal */ pthread_mutex_unlock(&LOCK_sigwait); } extern pthread_t alarm_thread; void *sigwait_thread(void *set_arg) { sigset_t *set=(sigset_t*) set_arg; int i; struct sigaction sact; sact.sa_flags = 0; sact.sa_handler = sigwait_handle_sig; memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ sigemptyset(&pending_set); for (i = 1; i <= sizeof(pending_set)*8; i++) { if (sigismember(set,i)) { sigaction(i, &sact, (struct sigaction*) 0); } } sigaddset(set, thd_lib_detected == THD_LIB_LT ? SIGALRM : SIGUSR1); pthread_sigmask(SIG_UNBLOCK,(sigset_t*) set,(sigset_t*) 0); alarm_thread=pthread_self(); /* For thr_alarm */ for (;;) { /* Wait for signals */ #ifdef HAVE_NOT_BROKEN_SELECT fd_set fd; FD_ZERO(&fd); select(0,&fd,0,0,0); #else sleep(1); /* Because of broken BSDI */ #endif } } int sigwait(sigset_t *setp, int *sigp) { if (!inited) { pthread_attr_t thr_attr; pthread_t sigwait_thread_id; inited=1; sigemptyset(&pending_set); pthread_mutex_init(&LOCK_sigwait,MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_sigwait,NULL); pthread_attr_init(&thr_attr); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&thr_attr,8196); my_pthread_attr_setprio(&thr_attr,100); /* Very high priority */ VOID(pthread_create(&sigwait_thread_id,&thr_attr,sigwait_thread,setp)); VOID(pthread_attr_destroy(&thr_attr)); } pthread_mutex_lock(&LOCK_sigwait); for (;;) { ulong *ptr= (ulong*) &pending_set; ulong *end=ptr+sizeof(pending_set)/sizeof(ulong); for ( ; ptr != end ; ptr++) { if (*ptr) { ulong set= *ptr; int found= (int) ((char*) ptr - (char*) &pending_set)*8+1; while (!(set & 1)) { found++; set>>=1; } *sigp=found; sigdelset(&pending_set,found); pthread_mutex_unlock(&LOCK_sigwait); return 0; } } VOID(pthread_cond_wait(&COND_sigwait,&LOCK_sigwait)); } return 0; } #endif /* DONT_USE_SIGSUSPEND */ #endif /* HAVE_SIGWAIT */ /***************************************************************************** ** Implement pthread_signal for systems that can't use signal() with threads ** Currently this is only used with BSDI 3.0 *****************************************************************************/ #ifdef USE_PTHREAD_SIGNAL int pthread_signal(int sig, void (*func)()) { struct sigaction sact; sact.sa_flags= 0; sact.sa_handler= func; sigemptyset(&sact.sa_mask); sigaction(sig, &sact, (struct sigaction*) 0); return 0; } #endif /**************************************************************************** The following functions fixes that all pthread functions should work according to latest posix standard ****************************************************************************/ /* Undefined wrappers set my_pthread.h so that we call os functions */ #undef pthread_mutex_init #undef pthread_mutex_lock #undef pthread_mutex_unlock #undef pthread_mutex_destroy #undef pthread_mutex_wait #undef pthread_mutex_timedwait #undef pthread_mutex_trylock #undef pthread_mutex_t #undef pthread_cond_init #undef pthread_cond_wait #undef pthread_cond_timedwait #undef pthread_cond_t #undef pthread_attr_getstacksize /***************************************************************************** ** Patches for AIX and DEC OSF/1 3.2 *****************************************************************************/ #if (defined(HAVE_NONPOSIX_PTHREAD_MUTEX_INIT) && !defined(HAVE_UNIXWARE7_THREADS)) || defined(HAVE_DEC_3_2_THREADS) #include int my_pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *attr) { int error; if (!attr) error=pthread_mutex_init(mp,pthread_mutexattr_default); else error=pthread_mutex_init(mp,*attr); return error; } int my_pthread_cond_init(pthread_cond_t *mp, const pthread_condattr_t *attr) { int error; if (!attr) error=pthread_cond_init(mp,pthread_condattr_default); else error=pthread_cond_init(mp,*attr); return error; } #endif /***************************************************************************** Patches for HPUX We need these because the pthread_mutex.. code returns -1 on error, instead of the error code. Note that currently we only remap pthread_ functions used by MySQL. If we are depending on the value for some other pthread_xxx functions, this has to be added here. ****************************************************************************/ #if defined(HPUX10) || defined(HAVE_BROKEN_PTHREAD_COND_TIMEDWAIT) int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *abstime) { int error=pthread_cond_timedwait(cond, mutex, abstime); if (error == -1) /* Safety if the lib is fixed */ { if (!(error=errno)) error= ETIMEDOUT; /* Can happen on HPUX */ } if (error == EAGAIN) /* Correct errno to Posix */ error= ETIMEDOUT; return error; } #endif #if defined(HPUX10) void my_pthread_attr_getstacksize(pthread_attr_t *connection_attrib, size_t *stack_size) { *stack_size= pthread_attr_getstacksize(*connection_attrib); } #endif #ifdef HAVE_POSIX1003_4a_MUTEX /* In HP-UX-10.20 and other old Posix 1003.4a Draft 4 implementations pthread_mutex_trylock returns 1 on success, not 0 like pthread_mutex_lock From the HP-UX-10.20 man page: RETURN VALUES If the function fails, errno may be set to one of the following values: Return | Error | Description _______|__________|_________________________________________ 1 | | Successful completion. 0 | | The mutex is locked; therefore, it was | | not acquired. -1 | [EINVAL] | The value specified by mutex is invalid. */ /* Convert pthread_mutex_trylock to return values according to latest POSIX RETURN VALUES 0 If we are able successfully lock the mutex. EBUSY Mutex was locked by another thread # Other error number returned by pthread_mutex_trylock() (Not likely) */ int my_pthread_mutex_trylock(pthread_mutex_t *mutex) { int error= pthread_mutex_trylock(mutex); if (error == 1) return 0; /* Got lock on mutex */ if (error == 0) /* Someon else is locking mutex */ return EBUSY; if (error == -1) /* Safety if the lib is fixed */ error= errno; /* Probably invalid parameter */ return error; } #endif /* HAVE_POSIX1003_4a_MUTEX */ /* Some help functions */ int pthread_no_free(void *not_used __attribute__((unused))) { return 0; } int pthread_dummy(int ret) { return ret; } #endif /* THREAD */