Skip to content

Commit c88c58b

Browse files
committed
Several fixes for thread_local_locale and strengthening test.
1 parent 648c18f commit c88c58b

File tree

4 files changed

+64
-25
lines changed

4 files changed

+64
-25
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -163,40 +163,72 @@ namespace details
163163
/// <summary>
164164
/// Cross platform RAII container for setting thread local locale.
165165
/// </summary>
166+
#ifdef _MS_WINDOWS
166167
class thread_local_locale
167168
{
168169
public:
169-
170170
thread_local_locale(const char * locale)
171171
{
172-
#ifdef _MS_WINDOWS
173-
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
174-
m_prevLocale = setlocale(LC_ALL, locale);
175-
#else
176-
m_prevLocale = uselocale(newlocale(LC_ALL, locale, nullptr));
177-
#endif
172+
char *prevLocale = setlocale(LC_ALL, nullptr);
173+
if (prevLocale == nullptr)
174+
{
175+
throw std::runtime_error("Unable to retrieve current locale.");
176+
}
177+
178+
// Copy to a string because later calls can invalidate the returned pointer.
179+
m_prevLocale = prevLocale;
180+
181+
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
182+
if (m_prevThreadSetting == -1)
183+
{
184+
throw std::runtime_error("Unable to enable per thread locale.");
185+
}
186+
if (setlocale(LC_ALL, locale) == nullptr)
187+
{
188+
throw std::runtime_error("Unable to set locale");
189+
}
178190
}
179-
180191
~thread_local_locale()
181192
{
182-
if (m_prevLocale != nullptr)
183-
{
184-
#ifdef _MS_WINDOWS
185-
setlocale(LC_ALL, m_prevLocale);
193+
setlocale(LC_ALL, m_prevLocale.c_str());
194+
_configthreadlocale(m_prevThreadSetting);
195+
}
196+
private:
197+
std::string m_prevLocale;
198+
int m_prevThreadSetting;
199+
thread_local_locale(const thread_local_locale &);
200+
thread_local_locale & operator=(const thread_local_locale &);
201+
};
186202
#else
187-
locale_t original = uselocale(m_prevLocale);
203+
class thread_local_locale
204+
{
205+
public:
206+
thread_local_locale(const char * locale)
207+
{
208+
m_changedLocale = newlocale(LC_ALL, locale, nullptr);
209+
if(m_changedLocale == nullptr)
210+
{
211+
throw std::runtime_error("Unable to create new locale.");
212+
}
213+
m_prevLocale = uselocale(original);
214+
if(m_prevLocale == nullptr)
215+
{
188216
freelocale(original);
189-
#endif
217+
throw std::runtime_error("Unable to set locale");
190218
}
191219
}
192-
220+
~thread_local_locale()
221+
{
222+
uselocale(m_prevLocale);
223+
freelocale(m_changedLocale);
224+
}
193225
private:
194-
#ifdef _MS_WINDOWS
195-
char * m_prevLocale;
196-
#else
197226
locale_t m_prevLocale;
198-
#endif
227+
locale_t m_changedLocale;
228+
thread_local_locale(const thread_local_locale &);
229+
thread_local_locale & operator=(const thread_local_locale &);
199230
};
231+
#endif
200232

201233
/// <summary>
202234
/// Our own implementation of alpha numeric instead of std::isalnum to avoid

Release/src/json/json.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ bool web::json::details::_Object::has_field(const utility::string_t &key) const
365365

366366
utility::string_t json::value::to_string() const
367367
{
368-
utility::details::thread_local_locale("C");
368+
utility::details::thread_local_locale locale("C");
369369
return m_value->to_string();
370370
}
371371

Release/src/json/json_serialization.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ using namespace utility::conversions;
4141
#ifdef _MS_WINDOWS
4242
void web::json::value::serialize(std::ostream& stream) const
4343
{
44-
utility::details::thread_local_locale("C");
44+
utility::details::thread_local_locale locale("C");
4545

4646
// This has better performance than writing directly to stream.
4747
std::string str;
@@ -56,7 +56,7 @@ void web::json::value::format(std::basic_string<wchar_t> &string) const
5656

5757
void web::json::value::serialize(utility::ostream_t &stream) const
5858
{
59-
utility::details::thread_local_locale("C");
59+
utility::details::thread_local_locale locale("C");
6060

6161
// This has better performance than writing directly to stream.
6262
utility::string_t str;
@@ -238,6 +238,6 @@ utility::string_t web::json::value::as_string() const
238238

239239
utility::string_t json::value::serialize() const
240240
{
241-
utility::details::thread_local_locale("C");
241+
utility::details::thread_local_locale locale("C");
242242
return m_value->to_string();
243243
}

Release/tests/Functional/json/parsing_tests.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,13 +568,20 @@ TEST(keep_order_while_parsing)
568568

569569
TEST(non_default_locale)
570570
{
571-
char * prevLocale = setlocale(LC_ALL, "fr-FR");
571+
std::string originalLocale = setlocale(LC_ALL, nullptr);
572+
std::string changedLocale("fr-FR");
573+
setlocale(LC_ALL, changedLocale.c_str());
572574

573575
// string serialize
574576
utility::string_t str(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
575577
json::value v = json::value::parse(str);
578+
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
576579
VERIFY_ARE_EQUAL(str, v.serialize());
580+
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
577581

582+
setlocale(LC_ALL, originalLocale.c_str());
583+
setlocale(LC_NUMERIC, changedLocale.c_str());
584+
578585
// cpprestsdk stream serialize
579586
utility::stringstream_t stream;
580587
stream << v;
@@ -589,7 +596,7 @@ TEST(non_default_locale)
589596
stdStream >> stdStr;
590597
VERIFY_ARE_EQUAL(str, utility::conversions::to_string_t(stdStr));
591598

592-
setlocale(LC_ALL, prevLocale);
599+
setlocale(LC_ALL, originalLocale.c_str());
593600
}
594601

595602
} // SUITE(parsing_tests)

0 commit comments

Comments
 (0)