blob: 706172cf3064c2826e4f27a2b4f43d2132ab10df [file] [log] [blame]
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed 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 "android/base/threads/Thread.h"
#include "android/base/synchronization/Lock.h"
#include "android/base/system/System.h"
#include <gtest/gtest.h>
#include <memory>
namespace android {
namespace base {
namespace {
using ::android::base::Thread;
// A simple thread instance that does nothing at all and exits immediately.
class EmptyThread : public Thread {
public:
intptr_t main() { return 42; }
};
// A thread that checks if onExit is called
class OnExitThread : public ::android::base::Thread {
public:
static bool onExitCalled;
static bool dtorCalled;
OnExitThread() { onExitCalled = dtorCalled = false; }
~OnExitThread() { dtorCalled = true; }
virtual intptr_t main() { return 42; }
virtual void onExit() {
onExitCalled = true;
delete this;
}
};
bool OnExitThread::onExitCalled = false;
bool OnExitThread::dtorCalled = false;
class CountingThread : public Thread {
public:
class State {
public:
State() : mLock(), mCount(0) {}
~State() {}
void increment() {
mLock.lock();
mCount++;
mLock.unlock();
}
int count() const {
int ret;
mLock.lock();
ret = mCount;
mLock.unlock();
return ret;
}
private:
mutable Lock mLock;
int mCount;
};
CountingThread(State* state) : Thread(), mState(state) {}
intptr_t main() {
mState->increment();
return 0;
}
private:
State* mState;
};
// A thread that blocks till it's instructed to continue.
class BlockingThread : public Thread {
public:
BlockingThread() : Thread(), mLock(), mSharedContinueFlag(new bool) {
*mSharedContinueFlag = false;
}
void unblock() {
mLock.lock();
*mSharedContinueFlag = true;
mLock.unlock();
}
intptr_t main() {
bool continueFlag;
do {
mLock.lock();
continueFlag = *mSharedContinueFlag;
mLock.unlock();
} while (!continueFlag);
return 42;
}
private:
mutable Lock mLock;
std::unique_ptr<bool> mSharedContinueFlag;
};
} // namespace
TEST(ThreadTest, SimpleThread) {
Thread* thread = new EmptyThread();
intptr_t status;
// Can't wait on threads before starting them.
EXPECT_FALSE(thread->wait(&status));
EXPECT_FALSE(thread->tryWait(&status));
EXPECT_TRUE(thread);
EXPECT_TRUE(thread->start());
status = 0;
EXPECT_TRUE(thread->wait(&status));
EXPECT_EQ(42, status);
// Thread objects are single use.
EXPECT_FALSE(thread->start());
}
TEST(ThreadTest, MultipleThreads) {
CountingThread::State state;
const size_t kMaxThreads = 100;
Thread* threads[kMaxThreads];
// Create all threads.
for (size_t n = 0; n < kMaxThreads; ++n) {
threads[n] = new CountingThread(&state);
EXPECT_TRUE(threads[n]) << "thread " << n;
}
// Start them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->start()) << "thread " << n;
}
// Wait for them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n;
}
// Check state.
EXPECT_EQ((int)kMaxThreads, state.count());
// Delete them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
delete threads[n];
}
}
TEST(ThreadTest, OnExit) {
OnExitThread* thread = new OnExitThread();
EXPECT_FALSE(OnExitThread::onExitCalled);
EXPECT_FALSE(OnExitThread::dtorCalled);
EXPECT_TRUE(thread->start());
EXPECT_TRUE(thread->wait(NULL));
EXPECT_TRUE(OnExitThread::onExitCalled);
EXPECT_TRUE(OnExitThread::dtorCalled);
}
TEST(ThreadTest, tryWait) {
BlockingThread thread;
intptr_t result = 0;
EXPECT_FALSE(thread.tryWait(&result));
EXPECT_TRUE(thread.start());
EXPECT_FALSE(thread.tryWait(&result));
thread.unblock();
// This has the potential to hang forever in case of a bug in the
// Thread under test. So, fail after a reaonsably long attempt.
static const unsigned kMaxWaitTimeMs = 5000; // 5 seconds.
static const unsigned kSleepTimeMs = 10;
static const unsigned kMaxNumChecks = kMaxWaitTimeMs / kSleepTimeMs;
unsigned numIters = 0;
result = 0;
while(numIters < kMaxNumChecks && !thread.tryWait(&result)) {
System::get()->sleepMs(kSleepTimeMs);
}
EXPECT_LT(numIters, kMaxNumChecks);
EXPECT_EQ(42, result);
}
class TidSetterThread : public Thread {
public:
intptr_t main() {
mTid = getCurrentThreadId();
return 0;
}
unsigned long mTid = 0;
};
TEST(ThreadTest, id) {
EXPECT_NE(0, getCurrentThreadId());
TidSetterThread thread1;
TidSetterThread thread2;
EXPECT_TRUE(thread1.start());
EXPECT_TRUE(thread2.start());
EXPECT_TRUE(thread1.wait());
EXPECT_TRUE(thread2.wait());
EXPECT_NE(0, thread1.mTid);
EXPECT_NE(0, thread2.mTid);
EXPECT_NE(thread1.mTid, thread2.mTid);
}
} // namespace base
} // namespace android