Iconv: Ensure that OTTD2FS/FS2OTTD are thread-safe
This commit is contained in:
		| @@ -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); | ||||||
| 		const char *env = GetLocalCode(); | 	if (local_convd == (iconv_t)(-1)) { | ||||||
| 		convd = iconv_open(env, INTERNALCODE); | 		{ | ||||||
| 		if (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(); | ||||||
|  | 				local_convd = iconv_open(env, INTERNALCODE); | ||||||
|  | 				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); | ||||||
| 		const char *env = GetLocalCode(); | 	if (local_convd == (iconv_t)(-1)) { | ||||||
| 		convd = iconv_open(INTERNALCODE, env); | 		{ | ||||||
| 		if (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(); | ||||||
|  | 				local_convd = iconv_open(INTERNALCODE, env); | ||||||
|  | 				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 */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jonathan G Rennison
					Jonathan G Rennison