243 lines
5.7 KiB
C++
243 lines
5.7 KiB
C++
/*=============================================================================
|
|
Copyright (c) 2002-2004 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)
|
|
=============================================================================*/
|
|
|
|
// std::lower_bound seems to perform awfully slow with _GLIBCXX_DEBUG enabled
|
|
#undef _GLIBCXX_DEBUG
|
|
|
|
#include <iostream>
|
|
#include <boost/config.hpp>
|
|
#include <boost/detail/lightweight_test.hpp>
|
|
|
|
#if !defined(BOOST_HAS_THREADS) || defined(DONT_HAVE_BOOST) || defined(BOOST_DISABLE_THREADS)
|
|
static void skipped()
|
|
{
|
|
std::cout << "skipped\n";
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
skipped();
|
|
return 0;
|
|
}
|
|
#else
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static const unsigned long initial_test_size = 5000UL;
|
|
#if defined(_DEBUG) && (BOOST_MSVC >= 1400)
|
|
static const unsigned long maximum_test_size = 10000UL;
|
|
#else
|
|
static const unsigned long maximum_test_size = 1000000UL;
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef BOOST_SPIRIT_THREADSAFE
|
|
#define BOOST_SPIRIT_THREADSAFE
|
|
|
|
#include <boost/thread/thread.hpp>
|
|
#include <boost/spirit/home/classic/core/non_terminal/impl/object_with_id.ipp>
|
|
#include <boost/ref.hpp>
|
|
#include <boost/thread/xtime.hpp>
|
|
#include <boost/thread/mutex.hpp>
|
|
#include <boost/thread/lock_types.hpp>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <boost/detail/lightweight_test.hpp>
|
|
|
|
using BOOST_SPIRIT_CLASSIC_NS::impl::object_with_id;
|
|
|
|
struct tag1 {};
|
|
typedef object_with_id<tag1> class1;
|
|
|
|
unsigned long test_size = initial_test_size;
|
|
boost::xtime start_time;
|
|
|
|
template <typename ClassT>
|
|
struct test_task
|
|
{
|
|
test_task() : v(), m(), progress(0) {}
|
|
|
|
void operator ()()
|
|
{ // create lots of objects
|
|
unsigned long i = 0;
|
|
|
|
v.reserve(maximum_test_size);
|
|
do
|
|
{
|
|
for (; i<test_size; ++i)
|
|
v.push_back(new ClassT);
|
|
}
|
|
while ( i < increase_test_size(i) );
|
|
}
|
|
|
|
static unsigned long
|
|
increase_test_size(unsigned long size)
|
|
{
|
|
static boost::mutex m;
|
|
boost::unique_lock<boost::mutex> l(m);
|
|
|
|
if (size<test_size || test_size == maximum_test_size)
|
|
return test_size;
|
|
|
|
boost::xtime now;
|
|
boost::xtime_get(&now, boost::TIME_UTC_);
|
|
boost::xtime::xtime_sec_t seconds = now.sec - start_time.sec;
|
|
if (seconds < 4)
|
|
{
|
|
test_size *= 2;
|
|
if (test_size > maximum_test_size)
|
|
test_size = maximum_test_size;
|
|
}
|
|
|
|
return test_size;
|
|
}
|
|
|
|
std::vector<ClassT*> const &data() const
|
|
{
|
|
return v;
|
|
}
|
|
|
|
private:
|
|
std::vector<ClassT*> v;
|
|
boost::mutex m;
|
|
unsigned int progress;
|
|
};
|
|
|
|
template <typename T>
|
|
class callable_reference_wrapper
|
|
: public boost::reference_wrapper<T>
|
|
{
|
|
public:
|
|
explicit callable_reference_wrapper(T& t)
|
|
: boost::reference_wrapper<T>(t)
|
|
{}
|
|
inline void operator()() { this->get().operator()(); }
|
|
};
|
|
|
|
template <typename T>
|
|
callable_reference_wrapper<T>
|
|
callable_ref(T &t)
|
|
{
|
|
return callable_reference_wrapper<T>(t);
|
|
}
|
|
|
|
test_task<class1> test1;
|
|
test_task<class1> test2;
|
|
test_task<class1> test3;
|
|
|
|
|
|
template <typename ClassT>
|
|
void
|
|
check_ascending(test_task<ClassT> const &t)
|
|
{
|
|
typedef typename std::vector<ClassT*>::const_iterator iter;
|
|
iter p(t.data().begin());
|
|
iter const e(t.data().end());
|
|
iter n(p);
|
|
|
|
while (++n!=e)
|
|
{
|
|
if ((**n).get_object_id()<=(**p).get_object_id())
|
|
{
|
|
using namespace std;
|
|
throw std::runtime_error("object ids out of order");
|
|
}
|
|
p = n;
|
|
}
|
|
}
|
|
|
|
struct less1
|
|
{
|
|
bool operator()(class1 const *p, class1 const *q) const
|
|
{
|
|
return p->get_object_id() < q->get_object_id();
|
|
}
|
|
};
|
|
|
|
template <typename ClassT>
|
|
void
|
|
check_not_contained_in(
|
|
test_task<ClassT> const &candidate,
|
|
test_task<ClassT> const &in
|
|
)
|
|
{
|
|
typedef typename std::vector<ClassT*>::const_iterator iter;
|
|
iter p(candidate.data().begin());
|
|
iter const e(candidate.data().end());
|
|
|
|
while (p!=e)
|
|
{
|
|
iter found = std::lower_bound(in.data().begin(),in.data().end(),*p,less1());
|
|
if (found!=in.data().end() &&
|
|
(**found).get_object_id() == (**p).get_object_id())
|
|
{
|
|
using namespace std;
|
|
throw std::runtime_error("object ids not unqiue");
|
|
}
|
|
++p;
|
|
}
|
|
}
|
|
|
|
void concurrent_creation_of_objects()
|
|
{
|
|
{
|
|
boost::xtime_get(&start_time, boost::TIME_UTC_);
|
|
boost::thread thread1(callable_ref(test1));
|
|
boost::thread thread2(callable_ref(test2));
|
|
boost::thread thread3(callable_ref(test3));
|
|
|
|
thread1.join();
|
|
thread2.join();
|
|
thread3.join();
|
|
}
|
|
}
|
|
|
|
void local_uniqueness()
|
|
{
|
|
|
|
|
|
BOOST_TEST(test1.data().size()==test_size);
|
|
BOOST_TEST(test2.data().size()==test_size);
|
|
BOOST_TEST(test3.data().size()==test_size);
|
|
}
|
|
|
|
void local_ordering_and_uniqueness()
|
|
{
|
|
// now all objects should have unique ids,
|
|
// the ids must be ascending within each vector
|
|
// check for ascending ids
|
|
check_ascending(test1);
|
|
check_ascending(test2);
|
|
check_ascending(test3);
|
|
}
|
|
|
|
void global_uniqueness()
|
|
{
|
|
check_not_contained_in(test1,test3);
|
|
check_not_contained_in(test1,test2);
|
|
check_not_contained_in(test2,test1);
|
|
check_not_contained_in(test2,test3);
|
|
check_not_contained_in(test3,test2);
|
|
check_not_contained_in(test3,test1);
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
concurrent_creation_of_objects();
|
|
local_ordering_and_uniqueness();
|
|
global_uniqueness();
|
|
return boost::report_errors();
|
|
}
|
|
|
|
#endif // BOOST_HAS_THREADS
|
|
|