Return-Path: Delivered-To: apmail-stdcxx-commits-archive@www.apache.org Received: (qmail 71451 invoked from network); 6 May 2008 22:51:57 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 6 May 2008 22:51:57 -0000 Received: (qmail 54965 invoked by uid 500); 6 May 2008 22:51:59 -0000 Delivered-To: apmail-stdcxx-commits-archive@stdcxx.apache.org Received: (qmail 54947 invoked by uid 500); 6 May 2008 22:51:59 -0000 Mailing-List: contact commits-help@stdcxx.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@stdcxx.apache.org Delivered-To: mailing list commits@stdcxx.apache.org Received: (qmail 54935 invoked by uid 99); 6 May 2008 22:51:58 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 06 May 2008 15:51:58 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 06 May 2008 22:51:12 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 7E68E2388A14; Tue, 6 May 2008 15:51:32 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r653946 - in /stdcxx/branches/4.2.x: src/catalog.cpp src/messages.cpp tests/localization/22.locale.messages.mt.cpp Date: Tue, 06 May 2008 22:51:32 -0000 To: commits@stdcxx.apache.org From: vitek@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080506225132.7E68E2388A14@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: vitek Date: Tue May 6 15:51:31 2008 New Revision: 653946 URL: http://svn.apache.org/viewvc?rev=653946&view=rev Log: 2008-05-06 Travis Vitek STDCXX-845 * src/messages.cpp (__rw_manage_cat_data): Employ a simple paging scheme and one level of indirection to avoid moving catalog data around when adding or removing message catalogs from the cache. Ensure that we do cleanup when closing last catalog regardless of the order that catalogs are closed. * src/catalog.cpp (catclose): Fix off-by-one error exposed with updated test. * tests/localization/22.locale.messages.mt.cpp: Updated to test multiple catalogs with multiple locales simultaneously. Modified: stdcxx/branches/4.2.x/src/catalog.cpp stdcxx/branches/4.2.x/src/messages.cpp stdcxx/branches/4.2.x/tests/localization/22.locale.messages.mt.cpp Modified: stdcxx/branches/4.2.x/src/catalog.cpp URL: http://svn.apache.org/viewvc/stdcxx/branches/4.2.x/src/catalog.cpp?rev=653946&r1=653945&r2=653946&view=diff ============================================================================== --- stdcxx/branches/4.2.x/src/catalog.cpp (original) +++ stdcxx/branches/4.2.x/src/catalog.cpp Tue May 6 15:51:31 2008 @@ -140,6 +140,8 @@ __rw_catlist[j-1] = __rw_catlist[j]; if (j < __rw_catlist.size()) __rw_catlist[j] = 0; + else + __rw_catlist[__rw_catlist.size () - 1] = 0; return 0; } return -1; Modified: stdcxx/branches/4.2.x/src/messages.cpp URL: http://svn.apache.org/viewvc/stdcxx/branches/4.2.x/src/messages.cpp?rev=653946&r1=653945&r2=653946&view=diff ============================================================================== --- stdcxx/branches/4.2.x/src/messages.cpp (original) +++ stdcxx/branches/4.2.x/src/messages.cpp Tue May 6 15:51:31 2008 @@ -70,6 +70,14 @@ } loc; }; +struct __rw_open_cat_page +{ + static const size_t _C_size = 8; + + __rw_open_cat_page* _C_next; // next page + __rw_open_cat_data _C_data [_C_size]; +}; + // manages a global, per-process repository of open catalogs according // to the following table: @@ -90,21 +98,25 @@ static __rw_open_cat_data* __rw_manage_cat_data (int &cat, __rw_open_cat_data *pcat_data) { - // a per-process array of catalog data structs - static __rw_open_cat_data catalog_buf [8]; - static __rw_open_cat_data* catalogs = catalog_buf; + // a per-process array of pointers to catalog data structs + static __rw_open_cat_data* catalog_buf [__rw_open_cat_page::_C_size]; + static __rw_open_cat_data** catalogs = 0; + + // first page of a per-process list of pages of catalog data structs + static __rw_open_cat_page catalog_page; static size_t n_catalogs = 0; static size_t catalog_bufsize = sizeof catalog_buf / sizeof *catalog_buf; static size_t largest_cat = 0; - static int init = 0; - - if (0 == init) { + if (!catalogs) { + for (size_t i = 0; i < catalog_bufsize; ++i) { - catalogs [i].catd = _RWSTD_BAD_CATD; + catalog_page._C_data [i].catd = _RWSTD_BAD_CATD; + catalog_buf [i] = &catalog_page._C_data [i]; } - init = 1; + + catalogs = catalog_buf; } if (-1 == cat) { @@ -115,27 +127,40 @@ if (pcat_data) { if (n_catalogs == catalog_bufsize) { - // reallocate buffer of facet pointers - __rw_open_cat_data* const tmp = - new __rw_open_cat_data[n_catalogs * 2]; + // allocate new page of catalog data + __rw_open_cat_page* const page = + new __rw_open_cat_page; + + // insert new page into singly-linked page list + page->_C_next = catalog_page._C_next; + catalog_page._C_next = page; + + // initialize new page + for (size_t i = 0; i < __rw_open_cat_page::_C_size; ++i) { + page->_C_data [i].catd = _RWSTD_BAD_CATD; + } + + // rwallocate buffer of catalog data pointers + __rw_open_cat_data** const data = + new __rw_open_cat_data* [n_catalogs + __rw_open_cat_page::_C_size]; - memcpy (tmp, catalogs, n_catalogs * sizeof *tmp); + memcpy (data, catalogs, n_catalogs * sizeof *data); if (catalogs != catalog_buf) delete[] catalogs; - catalogs = tmp; - catalog_bufsize *= 2; + catalogs = data; + catalog_bufsize += __rw_open_cat_page::_C_size; - for (size_t i = n_catalogs; i < catalog_bufsize; ++i) { - catalogs [i].catd = _RWSTD_BAD_CATD; + for (size_t i = 0; i < __rw_open_cat_page::_C_size; ++i) { + catalogs [n_catalogs + i] = &page->_C_data [i]; } cat = int (n_catalogs); - memcpy (&catalogs [cat].loc, &pcat_data->loc, - sizeof (_STD::locale)); - catalogs [cat].catd = pcat_data->catd; + catalogs [cat]->catd = pcat_data->catd; + memcpy (&catalogs [cat]->loc, &pcat_data->loc, + sizeof (_STD::locale)); if (size_t (cat) > largest_cat) largest_cat = size_t (cat); @@ -145,38 +170,42 @@ else { // find the first open slot and use it. cat = 0; - while (catalogs [cat].catd != _RWSTD_BAD_CATD) { + while (catalogs [cat]->catd != _RWSTD_BAD_CATD) { ++cat; } + catalogs [cat]->catd = pcat_data->catd; + memcpy (&catalogs [cat]->loc, &pcat_data->loc, + sizeof (_STD::locale)); + if (size_t (cat) > largest_cat) largest_cat = size_t (cat); - memcpy (&catalogs [cat].loc, &pcat_data->loc, - sizeof (_STD::locale)); - - catalogs [cat].catd = pcat_data->catd; ++n_catalogs; } } } else { + if (0 == pcat_data) { // find struct and return it if (size_t (cat) < catalog_bufsize) - return catalogs + cat; + return catalogs [cat]; return 0; } // initialize the struct to an invalid state --n_catalogs; - catalogs [cat].catd = _RWSTD_BAD_CATD; + catalogs [cat]->catd = _RWSTD_BAD_CATD; + if (size_t (cat) == largest_cat) { // find the next smallest valid slot + largest_cat = 0; + for (int i = cat; i >= 0; --i) { - if (catalogs [i].catd != _RWSTD_BAD_CATD) { + if (catalogs [i]->catd != _RWSTD_BAD_CATD) { largest_cat = size_t (i); break; } @@ -186,11 +215,9 @@ sizeof catalog_buf / sizeof *catalog_buf; if ((largest_cat < bufsize / 2) && (catalogs != catalog_buf)) { - // when there are no more open catalogs indexed beyond - // second half of the statically allocated repository, copy - // the open catalogs back into the statically allocated - // repository. + // second half of the static pointer repository, copy + // the open catalog pointers back into the repository. catalog_bufsize = bufsize; @@ -198,7 +225,19 @@ catalog_bufsize * sizeof (*catalogs)); delete[] catalogs; + catalogs = catalog_buf; + + // remove all pages, they're not in use + while (catalog_page._C_next) + { + // remove next page from page list + __rw_open_cat_page* page = catalog_page._C_next; + catalog_page._C_next = page->_C_next; + + // deallocate that page + delete page; + } } } } Modified: stdcxx/branches/4.2.x/tests/localization/22.locale.messages.mt.cpp URL: http://svn.apache.org/viewvc/stdcxx/branches/4.2.x/tests/localization/22.locale.messages.mt.cpp?rev=653946&r1=653945&r2=653946&view=diff ============================================================================== --- stdcxx/branches/4.2.x/tests/localization/22.locale.messages.mt.cpp (original) +++ stdcxx/branches/4.2.x/tests/localization/22.locale.messages.mt.cpp Tue May 6 15:51:31 2008 @@ -30,44 +30,73 @@ #include // for rw_test() #include // for rw_create_catalog() -#include +#include // for rw_thread_pool() +#include // for rw_system() +#include // for SHELL_RM_F #include // for rw_strncmp () #include // for strlen() #include // for remove() // maximum number of threads allowed by the command line interface +#define MAX_CATALOGS 32 #define MAX_THREADS 32 #define MAX_LOOPS 100000 +// deault number of catalogs +int opt_ncatalogs = 11; + // default number of threads (will be adjusted to the number // of processors/cores later) -int rw_opt_nthreads = 1; +int opt_nthreads = 1; // the number of times each thread should iterate (unless specified // otherwise on the command line) -int rw_opt_nloops = 100000; +int opt_nloops = 10000; + +#if !defined (_RWSTD_OS_HP_UX) || defined (_ILP32) + +// number of locales to use +int opt_nlocales = MAX_THREADS; + +#else // HP-UX in LP64 mode -// locale for threads to share -static const -std::locale locale; - -// message catalog for threads to share -static -std::messages_base::catalog catalog; +// work around a small cache size on HP-UX in LP64 mode +// in LP64 mode (see STDCXX-812) +int opt_nlocales = 9; -static -std::messages_base::catalog wcatalog; +#endif // HP-UX 32/64 bit mode + +// should all threads share the same set of locale objects instead +// of creating their own? +int opt_shared_locale; /**************************************************************************/ -#ifndef _WIN32 -# define CAT_NAME "./rwstdmessages.cat" -# define MSG_NAME "rwstdmessages.msg" -#else -# define CAT_NAME "rwstdmessages.dll" -# define MSG_NAME "rwstdmessages.rc" -#endif +// array of locale names to use for testing +static const char* +locales [MAX_THREADS]; + +// number of locale names in the array +static std::size_t +nlocales; + +/**************************************************************************/ + +// +struct MyMessageData +{ + // name of the locale the data corresponds to + const char* locale_name_; + + // optionally set to the named locale for threads to share + std::locale locale_; + +} my_message_data [MAX_THREADS]; + +char my_catalog_names [64][MAX_CATALOGS]; + +/**************************************************************************/ #define MAX_SETS 5 #define MAX_MESSAGES 5 @@ -123,38 +152,6 @@ } }; -static std::string str_messages; - -/**************************************************************************/ - -template -void test_open_close (const std::locale& loc, - const std::messages& msgs, - const std::string& name) -{ - std::messages_base::catalog cat = - (msgs.open) (name, loc); - - RW_ASSERT (! (cat < 0)); - - (msgs.close) (cat); -} - -template -void test_get (const std::messages& msgs, - const std::messages_base::catalog cat, - int set, int msgid, - const std::basic_string& dflt) -{ - // the msg_id() thing seems like a bug to me. if anything, the user - // should never need to write or call msg_id(). - - const typename std::messages::string_type res = - msgs.get (cat, set, msg_id (set, msgid), dflt); - - RW_ASSERT (!rw_strncmp (messages [set-1][msgid-1], res.c_str ())); -} - /**************************************************************************/ extern "C" { @@ -165,50 +162,143 @@ static void* thread_func (void*) { - const std::string name (CAT_NAME); - - const std::messages& nmsgs = - std::use_facet >(locale); - const std::string ndflt ("\1\2\3\4"); + struct { + std::messages_base::catalog cat; + std::locale loc; + } ncatalogs [4]; + + const unsigned n_ncatalogs = sizeof (ncatalogs) / sizeof (*ncatalogs); + + // 22.2.7.1.2 says values less than 0 returned if catalog can't + // be opened, so we use -1 as a sentinel. + for (unsigned c = 0; c < n_ncatalogs; ++c) + ncatalogs [c].cat = -1; + #ifndef _RWSTD_NO_WCHAR_T - const std::messages& wmsgs = - std::use_facet >(locale); const std::wstring wdflt (L"\1\2\3\4"); -#endif // _RWSTD_NO_WCHAR_T - for (int i = 0; i != rw_opt_nloops; ++i) { + struct { + std::messages_base::catalog cat; + std::locale loc; + } wcatalogs [5]; - int set = 1 + i % MAX_SETS; - int msgid = 1 + i % MAX_MESSAGES; + const unsigned n_wcatalogs = sizeof (wcatalogs) / sizeof (*wcatalogs); + + for (unsigned c = 0; c < n_wcatalogs; ++c) + wcatalogs [c].cat = -1; + +#endif // _RWSTD_NO_WCHAR_T + + /////////////////////////////////////////////////////////////////////// + + for (int i = 0; i < opt_nloops; ++i) { + + const MyMessageData& data = my_message_data [i % nlocales]; + + // construct a named locale, get a reference to the money_get + // facet from it and use it to format a random money value + const std::locale loc = + opt_shared_locale ? data.locale_ + : std::locale (data.locale_name_); + + const int set = 1 + i % MAX_SETS; + const int msgid = 1 + i % MAX_MESSAGES; if (test_char) { - if (i & 1) { - test_get(nmsgs, catalog, set, msgid, ndflt); + // exercise the narrow char specialization of the facet + + const std::messages &nm = + std::use_facet >(loc); + + const unsigned cat_idx = i % n_ncatalogs; + if (! (ncatalogs [cat_idx].cat < 0)) { + (nm.close)(ncatalogs [cat_idx].cat); } - else { - test_open_close(locale, nmsgs, name); + + const unsigned name_idx = i % opt_ncatalogs; + + const std::messages_base::catalog cat = + (nm.open)(my_catalog_names [name_idx], loc); + + ncatalogs [cat_idx].cat = cat; + ncatalogs [cat_idx].loc = loc; + + if (i & 1) { + + // get a message from the catalog every odd iteration + const std::messages::string_type res = + nm.get (cat, set, msg_id (set, msgid), ndflt); + } } +#ifndef _RWSTD_NO_WCHAR_T + if (test_wchar) { + // exercise the wide char specialization of the facet -#ifndef _RWSTD_NO_WCHAR_T + const std::messages &wm = + std::use_facet >(loc); - if (i & 1) { - test_get(wmsgs, wcatalog, set, msgid, wdflt); + const unsigned cat_idx = i % n_wcatalogs; + if (! (wcatalogs [cat_idx].cat < 0)) { + (wm.close)(wcatalogs [cat_idx].cat); } - else { - test_open_close(locale, wmsgs, name); + + const unsigned name_idx = i % opt_ncatalogs; + + const std::messages_base::catalog cat = + (wm.open)(my_catalog_names [name_idx], loc); + + RW_ASSERT (! (cat < 0)); + + wcatalogs [cat_idx].cat = cat; + wcatalogs [cat_idx].loc = loc; + + if (! (i & 1)) { + + // get a message from the catalog every even iteration + const std::messages::string_type res = + wm.get (cat, set, msg_id (set, msgid), wdflt); + } + } -#endif // _RWSTD_NO_WCHAR_T +#endif // _RWSTD_NO_WCHAR_T + + } + /////////////////////////////////////////////////////////////////////// + + // close any catalogs that are still open + + for (unsigned c = 0; c < n_ncatalogs; ++c) { + if (! (ncatalogs [c].cat < 0)) { + + const std::messages &nm = + std::use_facet >(ncatalogs[c].loc); + + (nm.close)(ncatalogs [c].cat); } } +#ifndef _RWSTD_NO_WCHAR_T + + for (unsigned c = 0; c < n_wcatalogs; ++c) { + if (! (wcatalogs [c].cat < 0)) { + + const std::messages &wm = + std::use_facet >(wcatalogs[c].loc); + + (wm.close)(wcatalogs [c].cat); + } + } + +#endif // _RWSTD_NO_WCHAR_T + return 0; } @@ -219,36 +309,96 @@ static int run_test (int, char**) { + std::string catalog; + + // initialize the catalog data for (int i = 0; i < MAX_SETS; ++i) { - for (int j = 0; j < MAX_MESSAGES; ++j) - str_messages.append (messages [i][j], std::strlen (messages [i][j]) + 1); - str_messages.append (1, '\0'); + for (int j = 0; j < MAX_MESSAGES; ++j) { + catalog.append (messages [i][j], + std::strlen (messages [i][j]) + 1); + } + + catalog.append (1, '\0'); } - // generate a message catalog - rw_create_catalog (MSG_NAME, str_messages.c_str ()); - const std::string name (CAT_NAME); + /////////////////////////////////////////////////////////////////////// - const std::messages& nmsgs = - std::use_facet >(locale); + // create the catalogs and initialize array of catalog names + for (int i = 0; i < opt_ncatalogs; ++i) { - catalog = (nmsgs.open) (name, locale); + char* msg_name = my_catalog_names [i]; -#ifndef _RWSTD_NO_WCHAR_T - - const std::messages& wmsgs = - std::use_facet >(locale); +#ifndef _WIN32 + std::sprintf (msg_name, "rwstdmessages_%d.msg", i); +#else + std::sprintf (msg_name, "rwstdmessages_%d.rc", i); +#endif - wcatalog = (wmsgs.open) (name, locale); + const int failed = rw_create_catalog (msg_name, catalog.c_str ()); + rw_fatal (!failed, 0, __LINE__, + "failed to create message catalog from %s", + msg_name); +#ifndef _WIN32 + std::sprintf (msg_name, "./rwstdmessages_%d.cat", i); +#else + std::sprintf (msg_name, "rwstdmessages_%d.dll", i); #endif + } + + /////////////////////////////////////////////////////////////////////// + + // find all installed locales for which setlocale (LC_ALL) succeeds + const char* const locale_list = + rw_opt_locales ? rw_opt_locales : rw_locales (_RWSTD_LC_ALL); + + const std::size_t maxinx = RW_COUNT_OF (locales); + + for (const char* name = locale_list; + *name; + name += std::strlen (name) + 1) { + + const std::size_t inx = nlocales; + locales [inx] = name; + + // fill in the money and results for this locale + MyMessageData& data = my_message_data [inx]; + data.locale_name_ = name; + + try { + const std::locale loc (data.locale_name_); + + const std::messages& nm = + std::use_facet >(loc); + +#ifdef _RWSTD_NO_WCHAR_T + + const std::messages& nm = + std::use_facet >(loc); + +#endif // _RWSTD_NO_WCHAR_T + + if (opt_shared_locale) + data.locale_ = loc; + + nlocales += 1; + + } + catch (...) { + rw_warn (!rw_opt_locales, 0, __LINE__, + "unable to use locale(%#s)", name); + } + + if (nlocales == maxinx || nlocales == std::size_t (opt_nlocales)) + break; + } rw_info (0, 0, 0, "testing std::messages with %d thread%{?}s%{;}, " "%d iteration%{?}s%{;} each", - rw_opt_nthreads, 1 != rw_opt_nthreads, - rw_opt_nloops, 1 != rw_opt_nloops); + opt_nthreads, 1 != opt_nthreads, + opt_nloops, 1 != opt_nloops); /////////////////////////////////////////////////////////////////////// @@ -258,12 +408,12 @@ rw_info (0, 0, 0, "exercising std::messages"); int result = - rw_thread_pool (0, std::size_t (rw_opt_nthreads), 0, + rw_thread_pool (0, std::size_t (opt_nthreads), 0, thread_func, 0); rw_error (result == 0, 0, __LINE__, "rw_thread_pool(0, %d, 0, %{#f}, 0) failed", - rw_opt_nthreads, thread_func); + opt_nthreads, thread_func); /////////////////////////////////////////////////////////////////////// @@ -275,12 +425,12 @@ rw_info (0, 0, 0, "exercising std::messages"); result = - rw_thread_pool (0, std::size_t (rw_opt_nthreads), 0, + rw_thread_pool (0, std::size_t (opt_nthreads), 0, thread_func, 0); rw_error (result == 0, 0, __LINE__, "rw_thread_pool(0, %d, 0, %{#f}, 0) failed", - rw_opt_nthreads, thread_func); + opt_nthreads, thread_func); /////////////////////////////////////////////////////////////////////// @@ -291,26 +441,18 @@ "std::messages"); result = - rw_thread_pool (0, std::size_t (rw_opt_nthreads), 0, + rw_thread_pool (0, std::size_t (opt_nthreads), 0, thread_func, 0); rw_error (result == 0, 0, __LINE__, "rw_thread_pool(0, %d, 0, %{#f}, 0) failed", - rw_opt_nthreads, thread_func); + opt_nthreads, thread_func); #endif // _RWSTD_NO_WCHAR_T /////////////////////////////////////////////////////////////////////// - (nmsgs.close) (catalog); - -#ifndef _RWSTD_NO_WCHAR_T - - (wmsgs.close) (wcatalog); - -#endif // _RWSTD_NO_WCHAR_T - - std::remove (CAT_NAME); + rw_system (SHELL_RM_F "rwstdmessages_*"); return result; } @@ -323,9 +465,9 @@ // set nthreads to the greater of the number of processors // and 2 (for uniprocessor systems) by default - rw_opt_nthreads = rw_get_cpus (); - if (rw_opt_nthreads < 2) - rw_opt_nthreads = 2; + opt_nthreads = rw_get_cpus (); + if (opt_nthreads < 2) + opt_nthreads = 2; #endif // _RWSTD_REENTRANT @@ -333,8 +475,11 @@ "lib.locale.messages", "thread safety", run_test, "|-nloops#0 " // must be non-negative + "|-ncatalogs#0-* " // must be non-negative "|-nthreads#0-* ", // must be in [0, MAX_THREADS] - &rw_opt_nloops, + &opt_nloops, + int (MAX_CATALOGS), + &opt_ncatalogs, int (MAX_THREADS), - &rw_opt_nthreads); + &opt_nthreads); }