blob: e7b04cd2125f28ac575500c0f38b482b145a0be1 [file] [log] [blame] [edit]
#ifndef _TCUTHREADUTIL_HPP
#define _TCUTHREADUTIL_HPP
/*-------------------------------------------------------------------------
* 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 "tcuDefs.hpp"
#include "deSharedPtr.hpp"
#include "deMutex.hpp"
#include "deSemaphore.hpp"
#include "deThread.hpp"
#include "deRandom.hpp"
#include <vector>
#include <sstream>
namespace tcu
{
namespace ThreadUtil
{
// Event object for synchronizing threads
class Event
{
public:
enum Result
{
RESULT_NOT_READY = 0,
RESULT_OK,
RESULT_FAILED
};
Event (void);
~Event (void);
void setResult (Result result);
Result waitReady (void);
Result getResult (void) const { return m_result; }
private:
volatile Result m_result;
volatile int m_waiterCount;
de::Semaphore m_waiters;
de::Mutex m_lock;
// Disabled
Event (const Event&);
Event& operator= (const Event&);
};
// Base class for objects which modifications should be tracked between threads
class Object
{
public:
Object (const char* type, de::SharedPtr<Event> createEvent);
virtual ~Object (void);
const char* getType (void) const { return m_type; }
// Used by class Operation only
void read (de::SharedPtr<Event> event, std::vector<de::SharedPtr<Event> >& deps);
void modify (de::SharedPtr<Event> event, std::vector<de::SharedPtr<Event> >& deps);
private:
const char* m_type;
de::SharedPtr<Event> m_modify;
std::vector<de::SharedPtr<Event> > m_reads;
// Disabled
Object (const Object&);
Object& operator= (const Object&);
};
class Thread;
class MessageBuilder
{
public:
MessageBuilder (Thread& thread) : m_thread(thread) {}
MessageBuilder (const MessageBuilder& other) : m_thread(other.m_thread), m_stream(other.m_stream.str()) {}
template<class T>
MessageBuilder& operator<< (const T& t) { m_stream << t; return *this; }
class EndToken
{
public:
EndToken (void) {}
};
void operator<< (const EndToken&);
private:
Thread& m_thread;
std::stringstream m_stream;
};
class Message
{
public:
Message (deUint64 time, const char* message) : m_time(time), m_message(message) {}
deUint64 getTime (void) const { return m_time; }
const std::string& getMessage (void) const { return m_message; }
static const MessageBuilder::EndToken End;
private:
deUint64 m_time;
std::string m_message;
};
// Base class for operations executed by threads
class Operation
{
public:
Operation (const char* name);
virtual ~Operation (void);
const char* getName (void) const { return m_name; }
de::SharedPtr<Event> getEvent (void) { return m_event; }
void readObject (de::SharedPtr<Object> object) { object->read(m_event, m_deps); }
void modifyObject (de::SharedPtr<Object> object) { object->modify(m_event, m_deps); }
virtual void exec (Thread& thread) = 0; //!< Overwritten by inherited class to perform actual operation
virtual void execute (Thread& thread); //!< May Be overwritten by inherited class to change how syncronization is done
protected:
const char* m_name;
std::vector<de::SharedPtr<Event> > m_deps;
de::SharedPtr<Event> m_event;
Operation (const Operation&);
Operation& operator= (const Operation&);
};
class Thread : public de::Thread
{
public:
enum ThreadStatus
{
THREADSTATUS_NOT_STARTED = 0,
THREADSTATUS_INIT_FAILED,
THREADSTATUS_RUNNING,
THREADSTATUS_READY,
THREADSTATUS_FAILED,
THREADSTATUS_NOT_SUPPORTED
};
Thread (deUint32 seed);
~Thread (void);
virtual void init (void) {} //!< Called first before any Operation
// \todo [mika] Should the result of execution be passed to deinit?
virtual void deinit (void) {} //!< Called after after operation
void addOperation (Operation* operation);
void exec (void);
deUint8* getUnusedData (size_t size); //!< Return data pointer that contains at least size bytes. Valid until next call
ThreadStatus getStatus (void) const { de::ScopedLock lock(m_statusLock); return m_status; }
void setStatus (ThreadStatus status) { de::ScopedLock lock(m_statusLock); m_status = status; }
MessageBuilder newMessage (void) { return MessageBuilder(*this); }
de::Random& getRandom (void) { return m_random; }
// Used to by test case to read log messages
int getMessageCount (void) const;
Message getMessage (int index) const;
// Used by message builder
void pushMessage (const std::string& str);
private:
virtual void run (void);
std::vector<Operation*> m_operations;
de::Random m_random;
mutable de::Mutex m_messageLock;
std::vector<Message> m_messages;
mutable de::Mutex m_statusLock;
ThreadStatus m_status;
std::vector<deUint8> m_unusedData;
// Disabled
Thread (const Thread&);
Thread operator= (const Thread&);
};
class DataBlock : public Object
{
public:
DataBlock (de::SharedPtr<Event> event);
void setData (size_t size, const void* data);
const deUint8* getData (void) const { return &(m_data[0]); }
size_t getSize (void) const { return m_data.size(); }
private:
std::vector<deUint8> m_data;
};
class CompareData : public Operation
{
public:
CompareData (de::SharedPtr<DataBlock> a, de::SharedPtr<DataBlock> b);
void exec (Thread& thread);
private:
de::SharedPtr<DataBlock> m_a;
de::SharedPtr<DataBlock> m_b;
};
} // ThreadUtil
} // tcu
#endif // _TCUTHREADUTIL_HPP