Iconv: Ensure that OTTD2FS/FS2OTTD are thread-safe

This commit is contained in:
Jonathan G Rennison
2024-02-17 12:04:16 +00:00
parent 838b166726
commit 94d326b1e0

View File

@@ -24,6 +24,7 @@
#include <time.h> #include <time.h>
#include <signal.h> #include <signal.h>
#include <pthread.h> #include <pthread.h>
#include <atomic>
#ifdef WITH_SDL2 #ifdef WITH_SDL2
#include <SDL.h> #include <SDL.h>
@@ -160,6 +161,8 @@ static std::string convert_tofrom_fs(iconv_t convd, const std::string &name)
return buf; return buf;
} }
std::mutex _iconv_convd_mutex;
/** /**
* Convert from OpenTTD's encoding to that of the local environment * Convert from OpenTTD's encoding to that of the local environment
* @param name pointer to a valid string that will be converted * @param name pointer to a valid string that will be converted
@@ -167,17 +170,25 @@ static std::string convert_tofrom_fs(iconv_t convd, const std::string &name)
*/ */
std::string OTTD2FS(const std::string &name) std::string OTTD2FS(const std::string &name)
{ {
static iconv_t convd = (iconv_t)(-1); static std::atomic<iconv_t> convd{(iconv_t)(-1)};
if (convd == (iconv_t)(-1)) { iconv_t local_convd = convd.load(std::memory_order_relaxed);
if (local_convd == (iconv_t)(-1)) {
{
std::unique_lock<std::mutex> lock(_iconv_convd_mutex);
local_convd = convd.load(); // Load a second time now that the lock is acquired
if (local_convd == (iconv_t)(-1)) {
const char *env = GetLocalCode(); const char *env = GetLocalCode();
convd = iconv_open(env, INTERNALCODE); local_convd = iconv_open(env, INTERNALCODE);
if (convd == (iconv_t)(-1)) { if (local_convd != (iconv_t)(-1)) convd.store(local_convd);
}
}
if (local_convd == (iconv_t)(-1)) {
DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env); DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env);
return name; return name;
} }
} }
return convert_tofrom_fs(convd, name); return convert_tofrom_fs(local_convd, name);
} }
/** /**
@@ -187,17 +198,25 @@ std::string OTTD2FS(const std::string &name)
*/ */
std::string FS2OTTD(const std::string &name) std::string FS2OTTD(const std::string &name)
{ {
static iconv_t convd = (iconv_t)(-1); static std::atomic<iconv_t> convd{(iconv_t)(-1)};
if (convd == (iconv_t)(-1)) { iconv_t local_convd = convd.load(std::memory_order_relaxed);
if (local_convd == (iconv_t)(-1)) {
{
std::unique_lock<std::mutex> lock(_iconv_convd_mutex);
local_convd = convd.load(); // Load a second time now that the lock is acquired
if (local_convd == (iconv_t)(-1)) {
const char *env = GetLocalCode(); const char *env = GetLocalCode();
convd = iconv_open(INTERNALCODE, env); local_convd = iconv_open(INTERNALCODE, env);
if (convd == (iconv_t)(-1)) { if (local_convd != (iconv_t)(-1)) convd.store(local_convd);
}
}
if (local_convd == (iconv_t)(-1)) {
DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE); DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE);
return name; return name;
} }
} }
return convert_tofrom_fs(convd, name); return convert_tofrom_fs(local_convd, name);
} }
#endif /* WITH_ICONV */ #endif /* WITH_ICONV */