> -----Original Message-----
> From: Martin Sebor [mailto:[EMAIL PROTECTED]
> Sent: Friday, July 07, 2006 3:46 AM
> To: [email protected]
> Subject: Re: string methods thread safety
>
[...]
> The thread safety tests I envision for our test suite would
> exercise a subset (as small as a pair) of functions per each
> run. The one constant (as in the same across all test runs)
> function would be the copy ctor (or its equivalent such as
> the assignment operator) and the other a modifying string
> function. The copy ctor would be used to create a copy of a
> shared string object for each thread and the modifying
> function would then operate on the copy.
Martin, I implemented the first draft of the test.
The append method overloads are tested only. Other methods I
will append to the test tomorrow.
Farid.
Index: 21.strings.cpp
===================================================================
--- 21.strings.cpp (revision 423998)
+++ 21.strings.cpp (working copy)
@@ -414,7 +414,7 @@
// FUNCALL: a string describing the call to the basic_string function
// with function with function arguments expanded (as specified
// by the TestCase argument)
-static void
+/*static */void
_rw_setvars (const StringFunc &func,
const StringTestCase *pcase = 0)
{
Index: rwthread.cpp
===================================================================
--- rwthread.cpp (revision 423417)
+++ rwthread.cpp (working copy)
@@ -425,3 +425,70 @@
}
} // extern "C"
+
+/**************************************************************************/
+
+#if defined (_WIN32) || defined (_WIN64)
+
+#include <process.h> // for spawnp(), cwait()
+
+#else
+
+#include <sys/types.h>
+#include <sys/wait.h> // for waitpid()
+#include <unistd.h> // for fork(), execv()
+
+#include <stdlib.h> // for exit()
+
+#include <driver.h> // for rw_fatal()
+
+#ifndef P_NOWAIT
+#define P_NOWAIT 1
+#endif
+
+#ifndef WAIT_CHILD
+#define WAIT_CHILD 0
+#endif
+
+static rw_pid_t spawnv(int, const char* path, char* const argv [])
+{
+ if (rw_pid_t child_pid = fork ())
+ // the parent process
+ return child_pid;
+
+ // the child process
+ execv (path, argv);
+
+ // the execvp returns only if an error occurs
+ rw_fatal (0, __FILE__, __LINE__, "execvp failed: : errno = %{#m} (%{m})");
+
+ exit (1);
+}
+
+static rw_pid_t cwait(int* status, rw_pid_t pid, int)
+{
+ const rw_pid_t res = waitpid (pid, status, 0);
+
+ if (res == pid && status)
+ *status = WEXITSTATUS (*status);
+
+ return res;
+}
+
+#endif
+
+extern "C" {
+
+_TEST_EXPORT rw_pid_t
+rw_process_create (const char* path, char* const argv[])
+{
+ return spawnv (P_NOWAIT, path, argv);
+}
+
+_TEST_EXPORT rw_pid_t
+rw_process_join (rw_pid_t pid, int* result)
+{
+ return cwait (result, pid, WAIT_CHILD);
+}
+
+} // extern "C"
Index: rwthread.h
===================================================================
--- rwthread.h (revision 423417)
+++ rwthread.h (working copy)
@@ -29,6 +29,8 @@
struct rw_thread_attr_t;
+typedef long rw_pid_t;
+
struct rw_thread_t
{
long threadno;
@@ -54,6 +56,19 @@
void* (*)(void*),
void**);
+// returns pid of the created process
+// returns -1 if failed
+// note: argv[0] should be equal to path
+_TEST_EXPORT rw_pid_t
+rw_process_create (const char*, char* const []);
+
+// returns pid of the specified process
+// returns -1 if failed
+// result is a pointer to a buffer where the result code
+// of the specified process will be stored, or NULL
+_TEST_EXPORT rw_pid_t
+rw_process_join (rw_pid_t, int*);
+
} // extern "C"
#endif // RW_RWTHREAD_H_INCLUDED
/***************************************************************************
*
* $Id: $
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**************************************************************************/
#include <string> // for string
#include <stddef.h> // for size_t
#include <stdlib.h> // for strtoul()
#include <stdio.h> // for sprintf()
#include <string.h> // for memcpy()
#include <limits.h> // for CHARBIT
#include <21.strings.h> // for StringMembers
#include <driver.h> // for rw_test()
#include <rwthread.h> // for rw_thread_pool(), rw_process_create()
#include <rw_allocator.h> // for UserAlloc
/**************************************************************************/
#define MAX_THREADS 32
#define MAX_LOOPS 100000
/**************************************************************************/
int rw_opt_nthreads = 4;
int rw_opt_nloops = MAX_LOOPS;
/**************************************************************************/
static const StringIds::OverloadId overloads [] = {
#undef TEST
#define TEST(sig) StringIds:: sig
TEST (append_cptr),
TEST (append_cstr),
TEST (append_cptr_size),
TEST (append_cstr_size_size),
TEST (append_size_val),
TEST (append_range),
TEST (extractor_istream_str)
};
static const size_t test_count = sizeof (overloads) / sizeof (*overloads);
static const char param_name[] = "--overload_id=";
static const size_t param_len = sizeof (param_name) - 1;
/**************************************************************************/
extern void
_rw_setvars (const StringFunc &func,
const StringTestCase *pcase);
/**************************************************************************/
template <class charT, class Traits, class Allocator>
struct MtTestCaseDataT
{
typedef std::basic_string <charT, Traits, Allocator> String;
typedef StringTestCaseData<charT> TestCaseData;
const String &str_;
const TestCaseData &tdata_;
};
template <class charT, class Traits, class Allocator>
static void*
thread_func (void *th_arg)
{
typedef MtTestCaseDataT<charT, Traits, Allocator> MtTestCaseData;
typedef typename MtTestCaseData::String String;
typedef typename MtTestCaseData::TestCaseData TestCaseData;
const MtTestCaseData &mt_tdata =
**_RWSTD_STATIC_CAST (MtTestCaseData**, th_arg);
const TestCaseData &tdata = mt_tdata.tdata_;
const StringFunc &func = tdata.func_;
const StringTestCase &tcase = tdata.tcase_;
const String arg (tdata.arg_, tdata.arglen_);
const charT val_arg = (make_char (char (tcase.val), (charT*)0));
for (size_t i = 0; i != size_t (rw_opt_nloops); ++i) {
//String str = mt_tdata.str_;
String str (mt_tdata.str_);
const charT* const ptr_arg = tcase.arg ? arg.c_str () : str.c_str ();
const String& str_arg = tcase.arg ? arg : str;
switch (func.which_) {
case StringIds::append_cptr:
str.append (ptr_arg);
break;
case StringIds::append_cstr:
str.append (str_arg);
break;
case StringIds::append_cptr_size:
str.append (ptr_arg, tcase.size);
break;
case StringIds::append_cstr_size_size:
str.append (str_arg, tcase.off2, tcase.size2);
break;
case StringIds::append_size_val:
str.append (tcase.size, val_arg);
break;
case StringIds::append_range:
break;
case StringIds::extractor_istream_str:
break;
default:
RW_ASSERT (!"test logic error: unknown mt overload");
}
}
return 0;
}
/**************************************************************************/
template <class charT, class Traits, class Allocator>
void test_mt (charT*, Traits*, Allocator*,
const StringTestCaseData<charT> &tdata)
{
typedef std::basic_string <charT, Traits, Allocator> String;
typedef MtTestCaseDataT<charT, Traits, Allocator> MtTestCaseData;
const String cstr (tdata.str_, tdata.strlen_);
// save the state of the const string object before the call
// to any (changes to the state of the object after a call)
const StringState cstr_state (rw_get_string_state (cstr));
MtTestCaseData tdata_ = { cstr, tdata };
void* args [MAX_THREADS];
for (size_t i = 0; i < sizeof (args) / sizeof (*args); ++i)
args [i] = _RWSTD_STATIC_CAST(void*, &tdata_);
// create and start a pool of threads and wait for them to finish
const int result = rw_thread_pool (0, size_t (rw_opt_nthreads),
0, &thread_func<charT, Traits, Allocator>, args);
// verify that const string object was not modified
cstr_state.assert_equal (rw_get_string_state (cstr),
__LINE__, tdata.tcase_.line, "call");
}
/**************************************************************************/
DEFINE_STRING_TEST_FUNCTIONS (test_mt);
static int
run_test (int argc, char** argv)
{
RW_ASSERT (0 < argc);
rw_info (0, 0, 0, "running %d thread%{?}s%{;}, %zu iteration%{?}s%{;} each",
rw_opt_nthreads, 1 != rw_opt_nthreads,
rw_opt_nloops, 1 != rw_opt_nloops);
//size_t size = argc * sizeof (char*);
// allocate argv_ buffer + additional 4 elements:
// 1) --overload_id=xxx
// 2) --no-stdout
// 3) --no-UserAlloc
// 4) terminating NUL
char ** argv_ = _RWSTD_STATIC_CAST (char**,
malloc ((argc + 4) * sizeof (*argv_)));
if (!argv_)
return 1;
// path
argv_ [0] = argv [0];
// leave the argv_[1] and argv_[2] and argv_[3] to init later
memcpy (argv_ + 4, argv + 1, (argc - 1) * sizeof (*argv_));
// terminating NUL
argv_ [2] = "--no-stdout";
argv_ [3] = "--no-UserAlloc";
argv_ [argc + 3] = 0;
int fails = 0;
char overload_id [sizeof (param_name) +
(CHAR_BIT * sizeof (unsigned)) * 301L / 1000 + 1];
memcpy (overload_id, param_name, param_len);
char* const pos = overload_id + param_len;
for (size_t i = 0; i < test_count; ++i) {
const StringIds::OverloadId which = overloads [i];
sprintf (pos, "%u", unsigned(which));
argv_ [1] = overload_id;
const StringFunc func = {
StringIds::Char,
StringIds::DefaultTraits,
StringIds::DefaultAlloc,
StringIds::None,
which
};
_rw_setvars (func, 0);
// determine whether the function is a member function
const bool is_member = 0 != (StringIds::bit_member & which);
rw_info (0, 0, 0,
"exercising the %{?}%{$CLASS}::%{;}%{$FUNCSIG}"
" (overload_id = %d)",
is_member, int (which));
int result = 1;
const rw_pid_t pid = rw_process_create (argv_ [0], argv_);
if (-1 != pid)
rw_process_join (pid, &result);
if (0 != result)
++fails;
rw_assert (!result, 0, 0,
"the test of %{?}%{$CLASS}::%{;}%{$FUNCSIG}"
" (overload_id = %d) failed",
is_member, int (which));
}
free (argv_);
return fails;
}
/**************************************************************************/
int main (int argc, char *argv[])
{
if (argc > 1) {
int oid;
int index = 1;
for (; index < argc; ++index) {
if (char* pos = strstr (argv [index], param_name)) {
oid = strtoul (pos + param_len, 0, 10);
break;
}
}
if (argc > index) {
// overload_id is specified
const StringIds::OverloadId which =
_RWSTD_STATIC_CAST (StringIds::OverloadId, oid);
// check that specified overload_id is present in overloads
size_t i = 0;
for (; i < test_count; ++i)
if (which == overloads [i])
break;
if (i >= test_count) {
rw_assert (0, __FILE__, __LINE__,
"invalid overload_id: %d", oid);
return 1;
}
// test the specified overload_id
static const StringTestCase dummy_cases [1] = {
#undef TEST
#define TEST(str, arg, off, size, off2, size2, val) { \
__LINE__, off, size, off2, size2, val, \
str, sizeof (str) - 1, arg, sizeof (arg) - 1, \
0, -1, 0 \
}
TEST ("[EMAIL PROTECTED]", "abcd", 1, 2, 1, 2, 'y')
};
const StringTest test = { which, dummy_cases, 1 };
// remove --overload_id= parameter
--argc;
memmove (argv + index, argv + index + 1,
(argc - index) * sizeof (*argv));
return rw_run_string_test (argc, argv, __FILE__,
"lib.string.mt",
test_mt_func_array,
&test, 1);
}
}
// test the all methods
return rw_test (argc, argv, __FILE__,
"lib.string",
"thread safety", run_test,
"",
(void*)0 /* sentinel */);
}