mirror of https://gitee.com/openkylin/cwidget.git
246 lines
5.7 KiB
C++
246 lines
5.7 KiB
C++
|
// test_threads.cc
|
||
|
//
|
||
|
// Copyright (C) 2005-2006, 2009 Daniel Burrows
|
||
|
//
|
||
|
// This program is free software; you can redistribute it and/or
|
||
|
// modify it under the terms of the GNU General Public License as
|
||
|
// published by the Free Software Foundation; either version 2 of
|
||
|
// the License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
// General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with this program; see the file COPYING. If not, write to
|
||
|
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
|
// Boston, MA 02111-1307, USA.
|
||
|
|
||
|
// TODO: threads code lives in cwidget now, this test should move
|
||
|
// there.
|
||
|
|
||
|
#include <cppunit/extensions/HelperMacros.h>
|
||
|
|
||
|
#include <cwidget/generic/threads/event_queue.h>
|
||
|
#include <cwidget/generic/threads/threads.h>
|
||
|
#include <cwidget/generic/util/ssprintf.h>
|
||
|
|
||
|
#include <iostream>
|
||
|
|
||
|
#include <sys/time.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include <unistd.h>
|
||
|
|
||
|
namespace cw = cwidget;
|
||
|
|
||
|
class TestThreads : public CppUnit::TestFixture
|
||
|
{
|
||
|
CPPUNIT_TEST_SUITE(TestThreads);
|
||
|
|
||
|
CPPUNIT_TEST(testBox);
|
||
|
CPPUNIT_TEST(testTimedTake);
|
||
|
CPPUNIT_TEST(testEventQueue);
|
||
|
CPPUNIT_TEST(testAutoDetach);
|
||
|
|
||
|
CPPUNIT_TEST_SUITE_END();
|
||
|
|
||
|
public:
|
||
|
// Ye Olde Addition Thread
|
||
|
struct add_thread
|
||
|
{
|
||
|
int n;
|
||
|
cw::threads::box<int> &count;
|
||
|
public:
|
||
|
add_thread(int _n, cw::threads::box<int> &_count)
|
||
|
:n(_n), count(_count)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()() const
|
||
|
{
|
||
|
for(int i = 0; i < n; ++i)
|
||
|
count.put(count.take()+1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void do_testBox()
|
||
|
{
|
||
|
const int thread_count_max = 50;
|
||
|
const int thread_limit = 1000;
|
||
|
|
||
|
long real_threads_max = sysconf(_SC_THREAD_THREADS_MAX);
|
||
|
int thread_count;
|
||
|
if(real_threads_max == -1)
|
||
|
thread_count = thread_count_max; // No definite limit.
|
||
|
else
|
||
|
thread_count = std::min<int>((int)thread_count_max, (int)real_threads_max);
|
||
|
|
||
|
cw::threads::box<int> b(100);
|
||
|
|
||
|
CPPUNIT_ASSERT_EQUAL(100, b.take());
|
||
|
|
||
|
std::unique_ptr<cw::threads::thread> *writers =
|
||
|
new std::unique_ptr<cw::threads::thread>[thread_count];
|
||
|
|
||
|
try
|
||
|
{
|
||
|
for(int i = 0; i<thread_count; ++i)
|
||
|
writers[i] = std::unique_ptr<cw::threads::thread>(new cw::threads::thread(add_thread(thread_limit, b)));
|
||
|
|
||
|
int foo;
|
||
|
CPPUNIT_ASSERT(!b.try_take(foo));
|
||
|
|
||
|
CPPUNIT_ASSERT(b.try_put(-1));
|
||
|
|
||
|
for(int i = 0; i < thread_count; ++i)
|
||
|
writers[i]->join();
|
||
|
|
||
|
CPPUNIT_ASSERT_EQUAL(thread_count*thread_limit-1, b.take());
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
delete[] writers;
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
delete[] writers;
|
||
|
}
|
||
|
|
||
|
void testBox()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
do_testBox();
|
||
|
}
|
||
|
catch(const cw::util::Exception &e)
|
||
|
{
|
||
|
std::cerr << "Caught exception in testBox: " << e.errmsg() << std::endl;
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testTimedTake()
|
||
|
{
|
||
|
timeval now;
|
||
|
|
||
|
cw::threads::box<int> b;
|
||
|
|
||
|
gettimeofday(&now, NULL);
|
||
|
timespec timeout;
|
||
|
timeout.tv_sec = now.tv_sec + 2;
|
||
|
timeout.tv_nsec = now.tv_usec * 1000;
|
||
|
|
||
|
int dummy;
|
||
|
|
||
|
// Test that timing out trying to take values from an empty box works:
|
||
|
|
||
|
CPPUNIT_ASSERT(!b.timed_take(dummy, timeout));
|
||
|
|
||
|
timeval now2;
|
||
|
gettimeofday(&now2, NULL);
|
||
|
|
||
|
// pthread_cond_timedwait is allowed to return before the timeout
|
||
|
// expires, according to Bastian Blank <waldi@debian.org>, so this
|
||
|
// invariant is not an invariant at all.
|
||
|
//
|
||
|
//CPPUNIT_ASSERT(now2.tv_sec >= now.tv_sec + 2);
|
||
|
//CPPUNIT_ASSERT(now2.tv_sec > now.tv_sec + 2 || now2.tv_usec >= now.tv_usec);
|
||
|
|
||
|
// Test that we can retrieve a value from a full box:
|
||
|
|
||
|
timeout.tv_sec = now2.tv_sec + 2;
|
||
|
timeout.tv_nsec = now2.tv_usec * 1000;
|
||
|
b.put(5);
|
||
|
CPPUNIT_ASSERT(b.timed_take(dummy, timeout));
|
||
|
CPPUNIT_ASSERT_EQUAL(5, dummy);
|
||
|
}
|
||
|
|
||
|
class event_queue_write_thread
|
||
|
{
|
||
|
cw::threads::event_queue<std::pair<int, int> > &eq;
|
||
|
|
||
|
int id, n;
|
||
|
public:
|
||
|
event_queue_write_thread(cw::threads::event_queue<std::pair<int, int> > &_eq,
|
||
|
int _id, int _n)
|
||
|
:eq(_eq), id(_id), n(_n)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()() const
|
||
|
{
|
||
|
for(int i = 0; i < n; ++i)
|
||
|
eq.put(std::pair<int, int>(id, i));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void testEventQueue()
|
||
|
{
|
||
|
const int thread_count_max = 100;
|
||
|
const int thread_limit = 1000;
|
||
|
|
||
|
long real_threads_max = sysconf(_SC_THREAD_THREADS_MAX);
|
||
|
int thread_count;
|
||
|
if(real_threads_max == -1)
|
||
|
thread_count = thread_count_max; // No definite limit.
|
||
|
else
|
||
|
thread_count = std::min<int>((int)thread_count_max, (int)real_threads_max);
|
||
|
|
||
|
cw::threads::event_queue<std::pair<int, int> > eq;
|
||
|
|
||
|
std::unique_ptr<cw::threads::thread> *writers =
|
||
|
new std::unique_ptr<cw::threads::thread>[thread_count];
|
||
|
int *last_thread_msg =
|
||
|
new int[thread_count];
|
||
|
|
||
|
try
|
||
|
{
|
||
|
for(int i = 0; i < thread_count; ++i)
|
||
|
last_thread_msg[i] = -1;
|
||
|
|
||
|
for(int i = 0; i < thread_count; ++i)
|
||
|
writers[i] = std::unique_ptr<cw::threads::thread>(new cw::threads::thread(event_queue_write_thread(eq, i, thread_limit)));
|
||
|
|
||
|
for(int i = 0; i < thread_count * thread_limit; ++i)
|
||
|
{
|
||
|
std::pair<int, int> next = eq.get();
|
||
|
|
||
|
CPPUNIT_ASSERT_EQUAL(next.second-1, last_thread_msg[next.first]);
|
||
|
last_thread_msg[next.first] = next.second;
|
||
|
}
|
||
|
|
||
|
for(int i = 0; i < thread_count; ++i)
|
||
|
writers[i]->join();
|
||
|
|
||
|
CPPUNIT_ASSERT(eq.empty());
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
delete[] writers;
|
||
|
delete[] last_thread_msg;
|
||
|
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
delete[] writers;
|
||
|
delete[] last_thread_msg;
|
||
|
}
|
||
|
|
||
|
struct do_nothing
|
||
|
{
|
||
|
public:
|
||
|
void operator()() const
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void testAutoDetach()
|
||
|
{
|
||
|
cw::threads::thread t(do_nothing());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CPPUNIT_TEST_SUITE_REGISTRATION(TestThreads);
|