/*============================================================================= Copyright (c) 2003 Martin Wille http://spirit.sourceforge.net/ Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #include #include #include #if defined(DONT_HAVE_BOOST) || !defined(BOOST_HAS_THREADS) || defined(BOOST_DISABLE_THREADS) // we end here if we can't do multithreading static void skipped() { std::cout << "skipped\n"; } int main() { skipped(); return 0; } #else // the real MT stuff #undef BOOST_SPIRIT_THREADSAFE #define BOOST_SPIRIT_THREADSAFE #include #include #include #include #include #include #include #include static boost::mutex simple_mutex; static int simple_definition_count = 0; struct simple : public BOOST_SPIRIT_CLASSIC_NS::grammar { template struct definition { definition(simple const& /*self*/) { top = BOOST_SPIRIT_CLASSIC_NS::epsilon_p; boost::unique_lock lock(simple_mutex); simple_definition_count++; } BOOST_SPIRIT_CLASSIC_NS::rule top; BOOST_SPIRIT_CLASSIC_NS::rule const &start() const { return top; } }; }; struct count_guard { count_guard(int &c) : counter(c) {} ~count_guard() { counter = 0; } private: int &counter; }; static void milli_sleep(unsigned long milliseconds) { static long const nanoseconds_per_second = 1000L*1000L*1000L; boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC_); xt.nsec+=1000*1000*milliseconds; while (xt.nsec > nanoseconds_per_second) { xt.nsec -= nanoseconds_per_second; xt.sec++; } boost::thread::sleep(xt); } static void nap() { // this function is called by various threads to ensure // that thread lifetime actually overlap milli_sleep(300); } template static void make_definition(GrammarT &g) { char const *text="blah"; BOOST_SPIRIT_CLASSIC_NS::scanner<> s(text, text+4); g.parse(s); } template static void make_definition3(GrammarT &g) { char const *text="blah"; BOOST_SPIRIT_CLASSIC_NS::scanner<> s(text, text+4); g.parse(s); nap(); g.parse(s); g.parse(s); } //////////////////////////////////////////////////////////////////////////////// #define exactly_one_instance_created simple_definition_count == 1 #define exactly_two_instances_created simple_definition_count == 2 #define exactly_four_instances_created simple_definition_count == 4 #define exactly_eight_instances_created simple_definition_count == 8 //////////////////////////////////////////////////////////////////////////////// static void multiple_attempts_to_instantiate_a_definition_from_a_single_thread() { // checks wether exactly one definition per grammar // object is created count_guard guard(simple_definition_count); simple simple1_p; simple simple2_p; make_definition(simple1_p); make_definition(simple1_p); make_definition(simple1_p); BOOST_TEST(exactly_one_instance_created); make_definition(simple2_p); make_definition(simple2_p); make_definition(simple2_p); BOOST_TEST(exactly_two_instances_created); } //////////////////////////////////////////////////////////////////////////////// struct single_grammar_object_task { void operator()() const { make_definition3(simple1_p); }; simple simple1_p; }; //////////////////////////////////////////////////////////////////////////////// template class callable_reference_wrapper : public boost::reference_wrapper { public: explicit callable_reference_wrapper(T& t) : boost::reference_wrapper(t) {} inline void operator()() { this->get().operator()(); } }; template callable_reference_wrapper callable_ref(T &t) { return callable_reference_wrapper(t); } //////////////////////////////////////////////////////////////////////////////// static void single_local_grammar_object_multiple_threads() { // check wether independent definition objects are // created count_guard guard(simple_definition_count); single_grammar_object_task task1, task2, task3, task4; boost::thread t1(callable_ref(task1)); boost::thread t2(callable_ref(task2)); boost::thread t3(callable_ref(task3)); boost::thread t4(callable_ref(task4)); t1.join(); t2.join(); t3.join(); t4.join(); BOOST_TEST(exactly_four_instances_created); } //////////////////////////////////////////////////////////////////////////////// struct two_grammar_objects_task { void operator()() const { make_definition3(simple1_p); make_definition3(simple2_p); }; simple simple1_p; simple simple2_p; }; static void multiple_local_grammar_objects_multiple_threads() { // check wether exactly one definition per thread // and per grammar object is created count_guard guard(simple_definition_count); two_grammar_objects_task task1, task2, task3, task4; boost::thread t1(callable_ref(task1)); boost::thread t2(callable_ref(task2)); boost::thread t3(callable_ref(task3)); boost::thread t4(callable_ref(task4)); t1.join(); t2.join(); t3.join(); t4.join(); BOOST_TEST(exactly_eight_instances_created); } //////////////////////////////////////////////////////////////////////////////// static simple global_simple1_p; struct single_global_grammar_object_task { void operator()() const { make_definition3(global_simple1_p); }; }; static void single_global_grammar_object_multiple_threads() { // check wether exactly one definition per thread is // created count_guard guard(simple_definition_count); single_global_grammar_object_task task1, task2, task3, task4; boost::thread t1(callable_ref(task1)); boost::thread t2(callable_ref(task2)); boost::thread t3(callable_ref(task3)); boost::thread t4(callable_ref(task4)); t1.join(); t2.join(); t3.join(); t4.join(); BOOST_TEST(exactly_four_instances_created); } //////////////////////////////////////////////////////////////////////////////// static simple global_simple2_p; static simple global_simple3_p; struct multiple_global_grammar_objects_task { void operator()() const { make_definition3(global_simple2_p); make_definition3(global_simple3_p); }; }; static void multiple_global_grammar_objects_multiple_threads() { // check wether exactly one definition per thread // and per grammar object is created count_guard guard(simple_definition_count); multiple_global_grammar_objects_task task1, task2, task3, task4; boost::thread t1(callable_ref(task1)); boost::thread t2(callable_ref(task2)); boost::thread t3(callable_ref(task3)); boost::thread t4(callable_ref(task4)); t1.join(); t2.join(); t3.join(); t4.join(); BOOST_TEST(exactly_eight_instances_created); } //////////////////////////////////////////////////////////////////////////////// int main() { multiple_attempts_to_instantiate_a_definition_from_a_single_thread(); single_local_grammar_object_multiple_threads(); multiple_local_grammar_objects_multiple_threads(); single_global_grammar_object_multiple_threads(); multiple_global_grammar_objects_multiple_threads(); return boost::report_errors(); } //////////////////////////////////////////////////////////////////////////////// static BOOST_SPIRIT_CLASSIC_NS::parse_info pi; //////////////////////////////////////////////// // These macros are used with BOOST_TEST #endif // MT mode