| /*------------------------------------------------------------------------- |
| * 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) |
| { |
| setStatus(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(); |
| setStatus(THREADSTATUS_READY); |
| } |
| catch (const tcu::NotSupportedError& e) |
| { |
| newMessage() << "tcu::NotSupportedError '" << e.what() << "'" << Message::End; |
| deinit(); |
| setStatus(initOk ? THREADSTATUS_NOT_SUPPORTED : THREADSTATUS_INIT_FAILED); |
| } |
| catch (const tcu::Exception& e) |
| { |
| newMessage() << "tcu::Exception '" << e.what() << "'" << Message::End; |
| deinit(); |
| setStatus(initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED); |
| } |
| catch (const std::exception& error) |
| { |
| newMessage() << "std::exception '" << error.what() << "'" << Message::End; |
| deinit(); |
| setStatus(initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED); |
| } |
| catch (...) |
| { |
| newMessage() << "Unkown exception" << Message::End; |
| deinit(); |
| setStatus(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 |