> -----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 */);
}

Reply via email to