blob: 8ff4f1764fedb5bb83e21714952b9c5ee9f0a499 [file] [log] [blame]
/*
* 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 "ThreadTest.h"
#include <decaf/util/concurrent/Mutex.h>
#include <decaf/lang/System.h>
#include <decaf/util/ArrayList.h>
#include <decaf/util/Random.h>
#include <decaf/lang/exceptions/InterruptedException.h>
#include <decaf/lang/exceptions/RuntimeException.h>
#include <memory>
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::util;
using namespace decaf::util::concurrent;
////////////////////////////////////////////////////////////////////////////////
namespace decaf{
namespace lang{
class SimpleThread : public Runnable {
private:
int delay;
public:
decaf::util::concurrent::Mutex lock;
virtual void run() {
try {
synchronized( &lock ) {
lock.notify();
lock.wait( delay );
}
} catch( decaf::lang::exceptions::InterruptedException& e ) {
return;
}
}
SimpleThread( int d ) : delay(0), lock() {
if( d >= 0 ) {
delay = d;
}
}
virtual ~SimpleThread() {}
};
class ChildThread1 : public Thread {
private:
Thread* parent;
bool sync;
private:
ChildThread1(const ChildThread1&);
ChildThread1& operator= (const ChildThread1&);
public:
ChildThread1(Thread* parent, bool sync) : Thread(), parent(parent), sync(sync), lock() {}
virtual ~ChildThread1() {}
decaf::util::concurrent::Mutex lock;
virtual void run() {
if (sync) {
synchronized(&lock) {
lock.notify();
try {
lock.wait();
} catch(InterruptedException& e) {
}
}
}
parent->interrupt();
}
};
class SpinThread : public Runnable {
public:
SpinThread() : Runnable(), done(false) {}
virtual ~SpinThread() {}
bool done;
virtual void run() {
while (!Thread::currentThread()->isInterrupted());
while (!done);
}
};
class RunThread : public Runnable {
public:
RunThread() : Runnable(), didThreadRun(false) {}
virtual ~RunThread() {}
bool didThreadRun;
virtual void run() {
didThreadRun = true;
}
};
class YieldThread : public Runnable {
private:
volatile int delay;
public:
virtual void run() {
int x = 0;
while (true) {
++x;
}
}
YieldThread(int d) : delay(0) {
if (d >= 0) {
delay = d;
}
}
};
class Delegate : public Runnable{
private:
int stuff;
public:
Delegate() : stuff(0) {}
virtual ~Delegate(){}
int getStuff(){
return stuff;
}
virtual void run(){
stuff = 1;
}
};
class Derived : public Thread{
private:
int stuff;
public:
Derived() : stuff(0) {}
virtual ~Derived() {}
int getStuff() {
return stuff;
}
virtual void run() {
stuff = 1;
}
};
class JoinTest : public Thread{
public:
JoinTest() {}
virtual ~JoinTest() {}
virtual void run() {
Thread::sleep(1000);
}
};
class RandomSleepRun : public Thread{
private:
static Random rand;
public:
RandomSleepRun() {}
virtual ~RandomSleepRun(){}
virtual void run() {
// Sleep for Random time.
Thread::sleep(rand.nextInt(2000));
}
};
Random RandomSleepRun::rand( System::currentTimeMillis() );
class BadRunnable : public Runnable {
public:
BadRunnable(){}
virtual ~BadRunnable(){}
virtual void run() {
Thread::sleep(100);
throw RuntimeException(__FILE__, __LINE__, "Planned");
}
};
class InterruptibleSleeper : public Runnable {
private:
bool interrupted;
public:
InterruptibleSleeper() : Runnable(), interrupted(false) {}
virtual ~InterruptibleSleeper() {}
virtual void run() {
try {
Thread::sleep(10000);
} catch(InterruptedException& ex) {
interrupted = true;
}
}
bool wasInterrupted() const {
return interrupted;
}
};
class InterruptibleJoiner : public Runnable {
private:
bool interrupted;
Thread* parent;
private:
InterruptibleJoiner(const InterruptibleJoiner&);
InterruptibleJoiner& operator= (const InterruptibleJoiner&);
public:
InterruptibleJoiner(Thread* parent) : Runnable(), interrupted(false), parent(parent) {}
virtual ~InterruptibleJoiner() {}
virtual void run() {
try {
parent->join(10000);
} catch(InterruptedException& ex) {
interrupted = true;
}
}
bool wasInterrupted() const {
return interrupted;
}
};
class InterruptibleWaiter : public Runnable {
private:
bool interrupted;
public:
decaf::util::concurrent::Mutex lock;
InterruptibleWaiter() : Runnable(), interrupted(false), lock() {}
virtual ~InterruptibleWaiter(){}
virtual void run() {
synchronized(&lock) {
try {
lock.wait();
} catch(InterruptedException& e) {
interrupted = true;
}
}
}
bool wasInterrupted() const {
return interrupted;
}
};
class Handler : public Thread::UncaughtExceptionHandler {
public:
bool executed;
Handler() : executed(false) {}
virtual void uncaughtException(const Thread* t, const Throwable& error) {
this->executed = true;
}
};
}}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testConstructor() {
Thread ct;
ct.start();
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testConstructor_1() {
std::auto_ptr<Runnable> runnable( new SimpleThread( 10 ) );
Thread ct( runnable.get() );
ct.start();
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testConstructor_2() {
std::auto_ptr<Runnable> runnable( new SimpleThread( 10 ) );
Thread ct( runnable.get(), "SimpleThread_1" );
CPPUNIT_ASSERT( ct.getName() == "SimpleThread_1" );
ct.start();
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testConstructor_3() {
Thread ct( "SimpleThread_1" );
CPPUNIT_ASSERT( ct.getName() == "SimpleThread_1" );
ct.start();
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testRun() {
RunThread rt;
Thread t(&rt);
try {
t.start();
int count = 0;
while (!rt.didThreadRun && count < 20) {
Thread::sleep(100);
count++;
}
CPPUNIT_ASSERT_MESSAGE("Thread did not run", rt.didThreadRun);
t.join();
} catch(InterruptedException& e) {
CPPUNIT_FAIL("Joined thread was interrupted");
}
CPPUNIT_ASSERT_MESSAGE("Joined thread is still alive", !t.isAlive());
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testDelegate(){
Delegate test;
int initialValue = test.getStuff();
Thread thread( &test );
thread.start();
thread.join();
int finalValue = test.getStuff();
// The values should be different - this proves
// that the runnable was run.
CPPUNIT_ASSERT( finalValue != initialValue );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testDerived() {
Derived test;
int initialValue = test.getStuff();
test.start();
test.join();
int finalValue = test.getStuff();
// The values should be different - this proves
// that the runnable was run.
CPPUNIT_ASSERT( finalValue != initialValue );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testJoin1() {
JoinTest test;
// Joining a non-started thread should just return.
CPPUNIT_ASSERT_NO_THROW( test.join() );
CPPUNIT_ASSERT_NO_THROW( test.join( 10 ) );
CPPUNIT_ASSERT_NO_THROW( test.join( 10, 10 ) );
CPPUNIT_ASSERT_MESSAGE( "Thread is alive", !test.isAlive() );
time_t startTime = time( NULL );
test.start();
test.join();
time_t endTime = time( NULL );
CPPUNIT_ASSERT_MESSAGE( "Joined Thread is still alive", !test.isAlive() );
time_t delta = endTime - startTime;
// Should be about 2 seconds that elapsed.
CPPUNIT_ASSERT( delta >= 1 && delta <= 3 );
// Thread should be able to join itself, use a timeout so we don't freeze
Thread::currentThread()->join( 5 );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testJoin2() {
JoinTest test;
// Joining a non-started thread should just return.
CPPUNIT_ASSERT_NO_THROW( test.join() );
CPPUNIT_ASSERT_NO_THROW( test.join( 10 ) );
CPPUNIT_ASSERT_NO_THROW( test.join( 10, 10 ) );
CPPUNIT_ASSERT_MESSAGE( "Thread is alive", !test.isAlive() );
time_t startTime = time( NULL );
test.start();
test.join( 3500, 999999 );
time_t endTime = time( NULL );
CPPUNIT_ASSERT_MESSAGE( "Joined Thread is still alive", !test.isAlive() );
time_t delta = endTime - startTime;
// Should be about 2 seconds that elapsed.
CPPUNIT_ASSERT( delta >= 1 && delta <= 3 );
// Thread should be able to join itself, use a timeout so we don't freeze
Thread::currentThread()->join( 5 );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testJoin3() {
JoinTest test;
// Joining a non-started thread should just return.
CPPUNIT_ASSERT_NO_THROW( test.join() );
CPPUNIT_ASSERT_NO_THROW( test.join( 10 ) );
CPPUNIT_ASSERT_NO_THROW( test.join( 10, 10 ) );
CPPUNIT_ASSERT_MESSAGE( "Thread is alive", !test.isAlive() );
test.start();
test.join( 0, 999999 );
CPPUNIT_ASSERT_MESSAGE( "Joined Thread is not still alive", test.isAlive() );
test.join( 3500, 999999 );
CPPUNIT_ASSERT_MESSAGE( "Joined Thread is still alive", !test.isAlive() );
// Thread should be able to join itself, use a timeout so we don't freeze
Thread::currentThread()->join( 5 );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testJoin4() {
// Start all the threads.
const unsigned int numThreads = 200;
RandomSleepRun threads[numThreads];
for( unsigned int ix = 0; ix < numThreads; ++ix ){
threads[ix].start();
}
// Join them all to ensure they complete as expected
for( unsigned int ix = 0; ix < numThreads; ++ix ){
threads[ix].join();
}
// Thread should be able to join itself, use a timeout so we don't freeze
Thread::currentThread()->join( 5 );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testSetPriority() {
std::auto_ptr<Runnable> runnable( new SimpleThread( 10 ) );
Thread ct( runnable.get() );
CPPUNIT_ASSERT( ct.getPriority() == Thread::NORM_PRIORITY );
ct.setPriority( Thread::MAX_PRIORITY );
CPPUNIT_ASSERT( ct.getPriority() == Thread::MAX_PRIORITY );
ct.start();
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testIsAlive() {
std::auto_ptr<SimpleThread> runnable( new SimpleThread( 500 ) );
Thread ct( runnable.get() );
CPPUNIT_ASSERT_MESSAGE( "A thread that wasn't started is alive.", !ct.isAlive() );
synchronized( &( runnable->lock ) ) {
ct.start();
try {
runnable->lock.wait();
} catch( InterruptedException& e ) {
}
}
CPPUNIT_ASSERT_MESSAGE( "Started thread returned false", ct.isAlive() );
try {
ct.join();
} catch( InterruptedException& e ) {
CPPUNIT_FAIL( "Thread did not die" );
}
CPPUNIT_ASSERT_MESSAGE( "Stopped thread returned true", !ct.isAlive() );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testGetId() {
CPPUNIT_ASSERT( Thread::currentThread()->getId() > 0 );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testSleep() {
long long startTime = 0LL;
long long endTime = 0LL;
try {
startTime = System::currentTimeMillis();
Thread::sleep( 1000 );
endTime = System::currentTimeMillis();
} catch( InterruptedException& e ) {
CPPUNIT_FAIL( "Unexpected interrupt received" );
}
CPPUNIT_ASSERT_MESSAGE( "Failed to sleep long enough", ( endTime - startTime ) >= 800 );
CPPUNIT_ASSERT_THROW_MESSAGE(
"Should throw an IllegalArgumentException",
Thread::sleep( -1 ),
IllegalArgumentException );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testSleep2Arg() {
long long startTime = 0LL;
long long endTime = 0LL;
try {
startTime = System::currentTimeMillis();
Thread::sleep( 1000, 10 );
endTime = System::currentTimeMillis();
} catch( InterruptedException& e ) {
CPPUNIT_FAIL( "Unexpected interrupt received" );
}
CPPUNIT_ASSERT_MESSAGE( "Failed to sleep long enough", ( endTime - startTime ) >= 800 );
CPPUNIT_ASSERT_THROW_MESSAGE(
"Should throw an IllegalArgumentException",
Thread::sleep( -1, 0 ),
IllegalArgumentException );
CPPUNIT_ASSERT_THROW_MESSAGE(
"Should throw an IllegalArgumentException",
Thread::sleep( 1000, 10000000 ),
IllegalArgumentException );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testGetState() {
std::auto_ptr<SimpleThread> runnable( new SimpleThread( 1000 ) );
Thread ct( runnable.get() );
CPPUNIT_ASSERT_MESSAGE( "A thread that wasn't started is alive.", !ct.isAlive() );
synchronized( &( runnable->lock ) ) {
ct.start();
try {
runnable->lock.wait();
} catch( InterruptedException& e ) {
}
}
CPPUNIT_ASSERT_MESSAGE( "Started thread returned false", ct.isAlive() );
CPPUNIT_ASSERT( ct.getState() == Thread::TIMED_WAITING );
try {
ct.join();
} catch( InterruptedException& e ) {
CPPUNIT_FAIL( "Thread did not die" );
}
CPPUNIT_ASSERT_MESSAGE( "Stopped thread returned true", !ct.isAlive() );
CPPUNIT_ASSERT( ct.getState() == Thread::TERMINATED );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testUncaughtExceptionHandler() {
std::auto_ptr<BadRunnable> runnable( new BadRunnable() );
Thread t1( runnable.get() );
Handler myHandler;
t1.start();
t1.join();
Thread t2( runnable.get() );
CPPUNIT_ASSERT( myHandler.executed == false );
CPPUNIT_ASSERT( t2.getUncaughtExceptionHandler() == NULL );
t2.setUncaughtExceptionHandler( &myHandler );
CPPUNIT_ASSERT( t2.getUncaughtExceptionHandler() == &myHandler );
t2.start();
t2.join();
CPPUNIT_ASSERT( myHandler.executed == true );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testCurrentThread() {
CPPUNIT_ASSERT( Thread::currentThread() != NULL );
CPPUNIT_ASSERT( Thread::currentThread()->getName() != "" );
CPPUNIT_ASSERT( Thread::currentThread()->getPriority() == Thread::NORM_PRIORITY );
CPPUNIT_ASSERT( Thread::currentThread()->getState() == Thread::RUNNABLE );
CPPUNIT_ASSERT( Thread::currentThread() == Thread::currentThread() );
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testInterrupt() {
bool interrupted = false;
try {
ChildThread1 ct(Thread::currentThread(), false);
synchronized(&ct.lock) {
ct.start();
ct.lock.wait();
}
} catch(InterruptedException& e) {
interrupted = true;
} catch(Exception& ex) {
ex.printStackTrace();
CPPUNIT_FAIL("Caught unexpected message.");
}
CPPUNIT_ASSERT_MESSAGE("Failed to Interrupt thread1", interrupted);
interrupted = false;
try {
ChildThread1 ct(Thread::currentThread(), true);
synchronized(&ct.lock) {
ct.start();
ct.lock.wait();
ct.lock.notify();
}
Thread::sleep(20000);
} catch(InterruptedException& e) {
interrupted = true;
} catch(Exception& ex) {
ex.printStackTrace();
CPPUNIT_FAIL("Caught unexpected message.");
}
CPPUNIT_ASSERT_MESSAGE("Failed to Interrupt thread2", interrupted);
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testInterrupted() {
CPPUNIT_ASSERT_MESSAGE("Interrupted returned true for non-interrupted thread",
!Thread::interrupted());
Thread::currentThread()->interrupt();
CPPUNIT_ASSERT_MESSAGE("Interrupted returned true for non-interrupted thread", Thread::interrupted());
CPPUNIT_ASSERT_MESSAGE("Failed to clear interrupted flag", !Thread::interrupted());
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testIsInterrupted() {
SpinThread spin;
Thread spinner(&spin);
spinner.start();
Thread::sleep(100);
try {
CPPUNIT_ASSERT_MESSAGE("Non-Interrupted thread returned true", !spinner.isInterrupted());
spinner.interrupt();
CPPUNIT_ASSERT_MESSAGE("Interrupted thread returned false", spinner.isInterrupted());
spin.done = true;
} catch(...) {
spinner.interrupt();
spin.done = true;
}
spinner.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testSetName() {
JoinTest st;
st.setName("Bogus Name");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set thread name", std::string("Bogus Name"), st.getName());
st.setName("Another Bogus Name");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set thread name", std::string("Another Bogus Name"), st.getName());
st.start();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testInterruptSleep() {
std::auto_ptr<InterruptibleSleeper> runnable(new InterruptibleSleeper());
Thread ct( runnable.get() );
ct.start();
for(int i = 0; i < 10; ++i) {
if (ct.getState() == Thread::SLEEPING) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(ct.getState() == Thread::SLEEPING);
ct.interrupt();
for(int i = 0; i < 10; ++i) {
if (runnable->wasInterrupted()) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(runnable->wasInterrupted());
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testInterruptJoin() {
std::auto_ptr<InterruptibleJoiner> runnable(new InterruptibleJoiner(Thread::currentThread()));
Thread ct( runnable.get() );
ct.start();
for(int i = 0; i < 10; ++i) {
if (ct.getState() == Thread::SLEEPING) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(ct.getState() == Thread::SLEEPING);
ct.interrupt();
for(int i = 0; i < 10; ++i) {
if (runnable->wasInterrupted()) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(runnable->wasInterrupted());
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testInterruptWait() {
std::auto_ptr<InterruptibleWaiter> runnable(new InterruptibleWaiter());
Thread ct( runnable.get() );
ct.start();
for(int i = 0; i < 10; ++i) {
if (ct.getState() == Thread::WAITING) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(ct.getState() == Thread::WAITING);
ct.interrupt();
for(int i = 0; i < 10; ++i) {
if (runnable->wasInterrupted()) {
break;
} else {
Thread::sleep(10);
}
}
CPPUNIT_ASSERT(runnable->wasInterrupted());
ct.join();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testRapidCreateAndDestroy() {
for (int i = 0; i < 200; i++) {
JoinTest* st = new JoinTest;
st->start();
delete st;
}
}
////////////////////////////////////////////////////////////////////////////////
namespace {
class QuickThread : public Thread{
public:
QuickThread() {}
virtual ~QuickThread() {}
virtual void run() {
Thread::sleep(10);
}
};
class RapidCreateDestoryThread : public Thread{
public:
RapidCreateDestoryThread(){}
virtual ~RapidCreateDestoryThread(){}
virtual void run() {
for (int i = 0; i < 500; i++) {
QuickThread* t = new QuickThread;
t->start();
delete t;
}
}
};
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testConcurrentRapidCreateAndDestroy() {
ArrayList<Thread*> threads;
const int NUM_THREADS = 32;
for (int i = 0; i < NUM_THREADS; ++i) {
threads.add(new RapidCreateDestoryThread);
}
Pointer<Iterator<Thread*> > threadsIter(threads.iterator());
while (threadsIter->hasNext()) {
threadsIter->next()->start();
}
threadsIter.reset(threads.iterator());
while (threadsIter->hasNext()) {
threadsIter->next()->join();
}
threadsIter.reset(threads.iterator());
while (threadsIter->hasNext()) {
delete threadsIter->next();
}
threads.clear();
}
////////////////////////////////////////////////////////////////////////////////
void ThreadTest::testCreatedButNotStarted() {
RunThread runnable;
ArrayList<Thread*> threads;
const int NUM_THREADS = 32;
for (int i = 0; i < NUM_THREADS; ++i) {
threads.add(new Thread(&runnable));
}
Pointer<Iterator<Thread*> > threadsIter(threads.iterator());
while (threadsIter->hasNext()) {
delete threadsIter->next();
}
threads.clear();
}