/*------------------------------------------------------------------------- * 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