Commit 983770aa authored by Staale Smedseng's avatar Staale Smedseng

Bug #45058 init_available_charsets uses double checked locking

      
As documented in the bug report, the double checked locking
pattern has inherent issues, and cannot guarantee correct
initialization.

This patch replaces the logic in init_available_charsets()
with the use of pthread_once(3). A wrapper function,
my_pthread_once(), is introduced and is used in lieu of direct
calls to init_available_charsets(). Related defines
MY_PTHREAD_ONCE_* are also introduced.

For the Windows platform, the implementation in lp:sysbench is
ported. For single-thread use, a simple define calls the
function and sets the pthread_once control variable.

Charset initialization is modified to use my_pthread_once().
parent 33aa8f59
...@@ -47,4 +47,12 @@ ...@@ -47,4 +47,12 @@
#define rw_unlock(A) #define rw_unlock(A)
#define rwlock_destroy(A) #define rwlock_destroy(A)
typedef int my_pthread_once_t;
#define MY_PTHREAD_ONCE_INIT 0
#define MY_PTHREAD_ONCE_DONE 1
#define my_pthread_once(C,F) do { \
if (*(C) != MY_PTHREAD_ONCE_DONE) { F(); *(C)= MY_PTHREAD_ONCE_DONE; } \
} while(0)
#endif #endif
...@@ -69,6 +69,11 @@ typedef int pthread_mutexattr_t; ...@@ -69,6 +69,11 @@ typedef int pthread_mutexattr_t;
#define pthread_handler_t EXTERNC void * __cdecl #define pthread_handler_t EXTERNC void * __cdecl
typedef void * (__cdecl *pthread_handler)(void *); typedef void * (__cdecl *pthread_handler)(void *);
typedef volatile LONG my_pthread_once_t;
#define MY_PTHREAD_ONCE_INIT 0
#define MY_PTHREAD_ONCE_INPROGRESS 1
#define MY_PTHREAD_ONCE_DONE 2
/* /*
Struct and macros to be used in combination with the Struct and macros to be used in combination with the
windows implementation of pthread_cond_timedwait windows implementation of pthread_cond_timedwait
...@@ -114,6 +119,7 @@ int pthread_attr_init(pthread_attr_t *connect_att); ...@@ -114,6 +119,7 @@ int pthread_attr_init(pthread_attr_t *connect_att);
int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack); int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack);
int pthread_attr_setprio(pthread_attr_t *connect_att,int priority); int pthread_attr_setprio(pthread_attr_t *connect_att,int priority);
int pthread_attr_destroy(pthread_attr_t *connect_att); int pthread_attr_destroy(pthread_attr_t *connect_att);
int my_pthread_once(my_pthread_once_t *once_control,void (*init_routine)(void));
struct tm *localtime_r(const time_t *timep,struct tm *tmp); struct tm *localtime_r(const time_t *timep,struct tm *tmp);
struct tm *gmtime_r(const time_t *timep,struct tm *tmp); struct tm *gmtime_r(const time_t *timep,struct tm *tmp);
...@@ -210,6 +216,10 @@ extern int my_pthread_getprio(pthread_t thread_id); ...@@ -210,6 +216,10 @@ extern int my_pthread_getprio(pthread_t thread_id);
#define pthread_handler_t EXTERNC void * #define pthread_handler_t EXTERNC void *
typedef void *(* pthread_handler)(void *); typedef void *(* pthread_handler)(void *);
#define my_pthread_once_t pthread_once_t
#define MY_PTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
#define my_pthread_once(C,F) pthread_once(C,F)
/* Test first for RTS or FSU threads */ /* Test first for RTS or FSU threads */
#if defined(PTHREAD_SCOPE_GLOBAL) && !defined(PTHREAD_SCOPE_SYSTEM) #if defined(PTHREAD_SCOPE_GLOBAL) && !defined(PTHREAD_SCOPE_SYSTEM)
......
...@@ -951,7 +951,6 @@ extern my_bool resolve_collation(const char *cl_name, ...@@ -951,7 +951,6 @@ extern my_bool resolve_collation(const char *cl_name,
CHARSET_INFO *default_cl, CHARSET_INFO *default_cl,
CHARSET_INFO **cl); CHARSET_INFO **cl);
extern void free_charsets(void);
extern char *get_charsets_dir(char *buf); extern char *get_charsets_dir(char *buf);
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2); extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
extern my_bool init_compiled_charsets(myf flags); extern my_bool init_compiled_charsets(myf flags);
......
...@@ -211,7 +211,6 @@ void STDCALL mysql_server_end() ...@@ -211,7 +211,6 @@ void STDCALL mysql_server_end()
} }
else else
{ {
free_charsets();
mysql_thread_end(); mysql_thread_end();
} }
......
...@@ -321,7 +321,6 @@ static int add_collation(CHARSET_INFO *cs) ...@@ -321,7 +321,6 @@ static int add_collation(CHARSET_INFO *cs)
#define MY_CHARSET_INDEX "Index.xml" #define MY_CHARSET_INDEX "Index.xml"
const char *charsets_dir= NULL; const char *charsets_dir= NULL;
static int charset_initialized=0;
static my_bool my_read_charset_file(const char *filename, myf myflags) static my_bool my_read_charset_file(const char *filename, myf myflags)
...@@ -399,30 +398,15 @@ static void *cs_alloc(size_t size) ...@@ -399,30 +398,15 @@ static void *cs_alloc(size_t size)
} }
#ifdef __NETWARE__ static my_pthread_once_t charsets_initialized= MY_PTHREAD_ONCE_INIT;
my_bool STDCALL init_available_charsets(myf myflags)
#else static void init_available_charsets(void)
static my_bool init_available_charsets(myf myflags)
#endif
{ {
char fname[FN_REFLEN + sizeof(MY_CHARSET_INDEX)]; char fname[FN_REFLEN + sizeof(MY_CHARSET_INDEX)];
my_bool error=FALSE;
/*
We have to use charset_initialized to not lock on THR_LOCK_charset
inside get_internal_charset...
*/
if (!charset_initialized)
{
CHARSET_INFO **cs; CHARSET_INFO **cs;
/*
To make things thread safe we are not allowing other threads to interfere
while we may changing the cs_info_table
*/
pthread_mutex_lock(&THR_LOCK_charset);
if (!charset_initialized)
{
bzero(&all_charsets,sizeof(all_charsets)); bzero(&all_charsets,sizeof(all_charsets));
init_compiled_charsets(myflags); init_compiled_charsets(MYF(0));
/* Copy compiled charsets */ /* Copy compiled charsets */
for (cs=all_charsets; for (cs=all_charsets;
...@@ -438,24 +422,13 @@ static my_bool init_available_charsets(myf myflags) ...@@ -438,24 +422,13 @@ static my_bool init_available_charsets(myf myflags)
} }
strmov(get_charsets_dir(fname), MY_CHARSET_INDEX); strmov(get_charsets_dir(fname), MY_CHARSET_INDEX);
error= my_read_charset_file(fname,myflags); my_read_charset_file(fname, MYF(0));
charset_initialized=1;
}
pthread_mutex_unlock(&THR_LOCK_charset);
}
return error;
}
void free_charsets(void)
{
charset_initialized=0;
} }
uint get_collation_number(const char *name) uint get_collation_number(const char *name)
{ {
init_available_charsets(MYF(0)); my_pthread_once(&charsets_initialized, init_available_charsets);
return get_collation_number_internal(name); return get_collation_number_internal(name);
} }
...@@ -463,7 +436,7 @@ uint get_collation_number(const char *name) ...@@ -463,7 +436,7 @@ uint get_collation_number(const char *name)
uint get_charset_number(const char *charset_name, uint cs_flags) uint get_charset_number(const char *charset_name, uint cs_flags)
{ {
CHARSET_INFO **cs; CHARSET_INFO **cs;
init_available_charsets(MYF(0)); my_pthread_once(&charsets_initialized, init_available_charsets);
for (cs= all_charsets; for (cs= all_charsets;
cs < all_charsets+array_elements(all_charsets)-1 ; cs < all_charsets+array_elements(all_charsets)-1 ;
...@@ -480,7 +453,7 @@ uint get_charset_number(const char *charset_name, uint cs_flags) ...@@ -480,7 +453,7 @@ uint get_charset_number(const char *charset_name, uint cs_flags)
const char *get_charset_name(uint charset_number) const char *get_charset_name(uint charset_number)
{ {
CHARSET_INFO *cs; CHARSET_INFO *cs;
init_available_charsets(MYF(0)); my_pthread_once(&charsets_initialized, init_available_charsets);
cs=all_charsets[charset_number]; cs=all_charsets[charset_number];
if (cs && (cs->number == charset_number) && cs->name ) if (cs && (cs->number == charset_number) && cs->name )
...@@ -538,7 +511,7 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags) ...@@ -538,7 +511,7 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags)
if (cs_number == default_charset_info->number) if (cs_number == default_charset_info->number)
return default_charset_info; return default_charset_info;
(void) init_available_charsets(MYF(0)); /* If it isn't initialized */ my_pthread_once(&charsets_initialized, init_available_charsets);
if (!cs_number || cs_number >= array_elements(all_charsets)-1) if (!cs_number || cs_number >= array_elements(all_charsets)-1)
return NULL; return NULL;
...@@ -560,7 +533,7 @@ CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags) ...@@ -560,7 +533,7 @@ CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags)
{ {
uint cs_number; uint cs_number;
CHARSET_INFO *cs; CHARSET_INFO *cs;
(void) init_available_charsets(MYF(0)); /* If it isn't initialized */ my_pthread_once(&charsets_initialized, init_available_charsets);
cs_number=get_collation_number(cs_name); cs_number=get_collation_number(cs_name);
cs= cs_number ? get_internal_charset(cs_number,flags) : NULL; cs= cs_number ? get_internal_charset(cs_number,flags) : NULL;
...@@ -585,7 +558,7 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, ...@@ -585,7 +558,7 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
DBUG_ENTER("get_charset_by_csname"); DBUG_ENTER("get_charset_by_csname");
DBUG_PRINT("enter",("name: '%s'", cs_name)); DBUG_PRINT("enter",("name: '%s'", cs_name));
(void) init_available_charsets(MYF(0)); /* If it isn't initialized */ my_pthread_once(&charsets_initialized, init_available_charsets);
cs_number= get_charset_number(cs_name, cs_flags); cs_number= get_charset_number(cs_name, cs_flags);
cs= cs_number ? get_internal_charset(cs_number, flags) : NULL; cs= cs_number ? get_internal_charset(cs_number, flags) : NULL;
......
...@@ -165,7 +165,6 @@ void my_end(int infoflag) ...@@ -165,7 +165,6 @@ void my_end(int infoflag)
my_print_open_files(); my_print_open_files();
} }
} }
free_charsets();
my_error_unregister_all(); my_error_unregister_all();
my_once_free(); my_once_free();
......
...@@ -137,4 +137,36 @@ int win_pthread_setspecific(void *a,void *b,uint length) ...@@ -137,4 +137,36 @@ int win_pthread_setspecific(void *a,void *b,uint length)
return 0; return 0;
} }
/*
One time initialization. For simplicity, we assume initializer thread
does not exit within init_routine().
*/
int my_pthread_once(my_pthread_once_t *once_control,
void (*init_routine)(void))
{
LONG state= InterlockedCompareExchange(once_control, MY_PTHREAD_ONCE_INPROGRESS,
MY_PTHREAD_ONCE_INIT);
switch(state)
{
case MY_PTHREAD_ONCE_INIT:
/* This is initializer thread */
(*init_routine)();
*once_control= MY_PTHREAD_ONCE_DONE;
break;
case MY_PTHREAD_ONCE_INPROGRESS:
/* init_routine in progress. Wait for its completion */
while(*once_control == MY_PTHREAD_ONCE_INPROGRESS)
{
Sleep(1);
}
break;
case MY_PTHREAD_ONCE_DONE:
/* Nothing to do */
break;
}
return 0;
}
#endif #endif
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "my_global.h" #include "my_global.h"
my_bool init_available_charsets(myf myflags); void init_available_charsets(void);
/* this function is required so that global memory is allocated against this /* this function is required so that global memory is allocated against this
library nlm, and not against a paticular client */ library nlm, and not against a paticular client */
...@@ -31,7 +31,7 @@ int _NonAppStart(void *NLMHandle, void *errorScreen, const char *commandLine, ...@@ -31,7 +31,7 @@ int _NonAppStart(void *NLMHandle, void *errorScreen, const char *commandLine,
{ {
mysql_server_init(0, NULL, NULL); mysql_server_init(0, NULL, NULL);
init_available_charsets(MYF(0)); init_available_charsets();
return 0; return 0;
} }
......
...@@ -1287,7 +1287,6 @@ void clean_up(bool print_message) ...@@ -1287,7 +1287,6 @@ void clean_up(bool print_message)
lex_free(); /* Free some memory */ lex_free(); /* Free some memory */
item_create_cleanup(); item_create_cleanup();
set_var_free(); set_var_free();
free_charsets();
if (!opt_noacl) if (!opt_noacl)
{ {
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment