/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 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.
*
*//*!
* \file
* \brief Thread test utilities
*//*--------------------------------------------------------------------*/
#include "tcuThreadUtil.hpp"
#include "deClock.h"
#include "deMemory.h"
using std::vector;
using de::SharedPtr;
namespace tcu
{
namespace ThreadUtil
{
Event::Event (void)
: m_result (RESULT_NOT_READY)
, m_waiterCount (0)
, m_waiters (0, 0)
{
}
Event::~Event (void)
{
}
void Event::setResult (Result result)
{
m_lock.lock();
DE_ASSERT(m_result == RESULT_NOT_READY);
m_result = result;
m_lock.unlock();
for (int i = 0; i < m_waiterCount; i++)
m_waiters.increment();
}
Event::Result Event::waitReady (void)
{
m_lock.lock();
if (m_result == RESULT_NOT_READY)
m_waiterCount++;
else
{
m_lock.unlock();
return m_result;
}
m_lock.unlock();
m_waiters.decrement();
return m_result;
}
Object::Object (const char* type, SharedPtr<Event> e)
: m_type (type)
, m_modify (e)
{
}
Object::~Object (void)
{
}
void Object::read (SharedPtr<Event> event, std::vector<SharedPtr<Event> >& deps)
{
// Make call depend on last modifying call
deps.push_back(m_modify);
// Add read dependency
m_reads.push_back(event);
}
void Object::modify (SharedPtr<Event> event, std::vector<SharedPtr<Event> >& deps)
{
// Make call depend on all reads
for (int readNdx = 0; readNdx < (int)m_reads.size(); readNdx++)
{
deps.push_back(m_reads[readNdx]);
}
deps.push_back(m_modify);
// Update last modifying call
m_modify = event;
// Clear read dependencies of last "version" of this object
m_reads.clear();
}
Operation::Operation (const char* name)
: m_name (name)
, m_event (new Event)
{
}
Operation::~Operation (void)
{
}
void Operation::execute (Thread& thread)
{
bool success = true;
// Wait for dependencies and check that they succeeded
for (int depNdx = 0; depNdx < (int)m_deps.size(); depNdx++)
{
if (m_deps[depNdx]->waitReady() != Event::RESULT_OK)
success = false;
}
// Try execute operation
if (success)
{
try
{
exec(thread);
}
catch (...)
{
// Got exception event failed
m_event->setResult(Event::RESULT_FAILED);
throw;
}
m_event->setResult(Event::RESULT_OK);
}
else
// Some dependencies failed
m_event->setResult(Event::RESULT_FAILED);
// Release resources
m_deps.clear();
m_event = SharedPtr<Event>();
}
const MessageBuilder::EndToken Message::End = MessageBuilder::EndToken();
void MessageBuilder::operator<< (const EndToken&)
{
m_thread.pushMessage(m_stream.str());
}
Thread::Thread (deUint32 seed)
: m_random (seed)
, m_status (THREADSTATUS_NOT_STARTED)
{
}
Thread::~Thread (void)
{
for (int operationNdx = 0; operationNdx < (int)m_operations.size(); operationNdx++)
delete m_operations[operationNdx];
m_operations.clear();
}
deUint8* Thread::getDummyData (size_t size)
{
if (m_dummyData.size() < size)
{
m_dummyData.resize(size);
}
return &(m_dummyData[0]);
}
void Thread::addOperation (Operation* operation)
{
m_operations.push_back(operation);
}
void Thread::run (void)
{
m_status = THREADSTATUS_RUNNING;
bool initOk = false;
// Reserve at least two messages for each operation
m_messages.reserve(m_operations.size()*2);
try
{
init();
initOk = true;
for (int operationNdx = 0; operationNdx < (int)m_operations.size(); operationNdx++)
m_operations[operationNdx]->execute(*this);
deinit();
m_status = THREADSTATUS_READY;
}
catch (const tcu::NotSupportedError& e)
{
newMessage() << "tcu::NotSupportedError '" << e.what() << "'" << Message::End;
deinit();
m_status = (initOk ? THREADSTATUS_NOT_SUPPORTED : THREADSTATUS_INIT_FAILED);
}
catch (const tcu::Exception& e)
{
newMessage() << "tcu::Exception '" << e.what() << "'" << Message::End;
deinit();
m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
}
catch (const std::exception& error)
{
newMessage() << "std::exception '" << error.what() << "'" << Message::End;
deinit();
m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
}
catch (...)
{
newMessage() << "Unkown exception" << Message::End;
deinit();
m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
}
}
void Thread::exec (void)
{
start();
}
void Thread::pushMessage (const std::string& str)
{
de::ScopedLock lock(m_messageLock);
m_messages.push_back(Message(deGetMicroseconds(), str.c_str()));
}
int Thread::getMessageCount (void) const
{
de::ScopedLock lock(m_messageLock);
return (int)(m_messages.size());
}
Message Thread::getMessage (int index) const
{
de::ScopedLock lock(m_messageLock);
return m_messages[index];
}
DataBlock::DataBlock (SharedPtr<Event> event)
: Object("DataBlock", event)
{
}
void DataBlock::setData (size_t size, const void* data)
{
m_data = std::vector<deUint8>(size);
deMemcpy(&(m_data[0]), data, size);
}
CompareData::CompareData (SharedPtr<DataBlock> a, SharedPtr<DataBlock> b)
: Operation ("CompareData")
, m_a (a)
, m_b (b)
{
readObject(SharedPtr<Object>(a));
readObject(SharedPtr<Object>(b));
}
void CompareData::exec (Thread& thread)
{
bool result = true;
DE_ASSERT(m_a->getSize() == m_b->getSize());
thread.newMessage() << "Begin -- CompareData" << Message::End;
for (int byteNdx = 0; byteNdx < (int)m_a->getSize(); byteNdx++)
{
if (m_a->getData()[byteNdx] != m_b->getData()[byteNdx])
{
result = false;
thread.newMessage() << "CompareData failed at offset :" << byteNdx << Message::End;
break;
}
}
if (result)
thread.newMessage() << "CompareData passed" << Message::End;
else
TCU_FAIL("Data comparision failed");
thread.newMessage() << "End -- CompareData" << Message::End;
}
} // ThreadUtil
} // tcu