262 lines
4.8 KiB
C++
262 lines
4.8 KiB
C++
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Test Executor
|
|
* ------------------------------------------
|
|
*
|
|
* 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 Cross-thread function call dispatcher.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "xeCallQueue.hpp"
|
|
#include "deInt32.h"
|
|
#include "deMemory.h"
|
|
|
|
using std::vector;
|
|
|
|
static inline int getNextQueueSize (int curSize, int minNewSize)
|
|
{
|
|
return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
|
|
}
|
|
|
|
namespace xe
|
|
{
|
|
|
|
// CallQueue
|
|
|
|
CallQueue::CallQueue (void)
|
|
: m_canceled (false)
|
|
, m_callSem (0)
|
|
, m_callQueue (64)
|
|
{
|
|
}
|
|
|
|
CallQueue::~CallQueue (void)
|
|
{
|
|
// Destroy all calls.
|
|
for (vector<Call*>::iterator i = m_calls.begin(); i != m_calls.end(); i++)
|
|
delete *i;
|
|
}
|
|
|
|
void CallQueue::cancel (void)
|
|
{
|
|
m_canceled = true;
|
|
m_callSem.increment();
|
|
}
|
|
|
|
void CallQueue::callNext (void)
|
|
{
|
|
Call* call = DE_NULL;
|
|
|
|
// Wait for a call.
|
|
m_callSem.decrement();
|
|
|
|
if (m_canceled)
|
|
return;
|
|
|
|
// Acquire call from buffer.
|
|
{
|
|
de::ScopedLock lock(m_lock);
|
|
call = m_callQueue.popBack();
|
|
}
|
|
|
|
try
|
|
{
|
|
// \note Enqueue lock is not held during call so it is possible to enqueue more work from dispatched call.
|
|
CallReader reader(call);
|
|
|
|
call->getFunction()(reader);
|
|
|
|
// check callee consumed all
|
|
DE_ASSERT(reader.isDataConsumed());
|
|
call->clear();
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
try
|
|
{
|
|
// Try to push call into free calls list.
|
|
de::ScopedLock lock(m_lock);
|
|
m_freeCalls.push_back(call);
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
// We can't do anything but ignore this.
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
// Push back to free calls list.
|
|
{
|
|
de::ScopedLock lock(m_lock);
|
|
m_freeCalls.push_back(call);
|
|
}
|
|
}
|
|
|
|
Call* CallQueue::getEmptyCall (void)
|
|
{
|
|
de::ScopedLock lock (m_lock);
|
|
Call* call = DE_NULL;
|
|
|
|
// Try to get from free calls list.
|
|
if (!m_freeCalls.empty())
|
|
{
|
|
call = m_freeCalls.back();
|
|
m_freeCalls.pop_back();
|
|
}
|
|
|
|
// If no free calls were available, create a new.
|
|
if (!call)
|
|
{
|
|
m_calls.reserve(m_calls.size()+1);
|
|
call = new Call();
|
|
m_calls.push_back(call);
|
|
}
|
|
|
|
return call;
|
|
}
|
|
|
|
void CallQueue::enqueue (Call* call)
|
|
{
|
|
de::ScopedLock lock(m_lock);
|
|
|
|
if (m_callQueue.getNumFree() == 0)
|
|
{
|
|
// Call queue must be grown.
|
|
m_callQueue.resize(getNextQueueSize(m_callQueue.getSize(), m_callQueue.getSize()+1));
|
|
}
|
|
|
|
m_callQueue.pushFront(call);
|
|
m_callSem.increment();
|
|
}
|
|
|
|
void CallQueue::freeCall (Call* call)
|
|
{
|
|
de::ScopedLock lock(m_lock);
|
|
m_freeCalls.push_back(call);
|
|
}
|
|
|
|
// Call
|
|
|
|
Call::Call (void)
|
|
: m_func(DE_NULL)
|
|
{
|
|
}
|
|
|
|
Call::~Call (void)
|
|
{
|
|
}
|
|
|
|
void Call::clear (void)
|
|
{
|
|
m_func = DE_NULL;
|
|
m_data.clear();
|
|
}
|
|
|
|
// CallReader
|
|
|
|
CallReader::CallReader (Call* call)
|
|
: m_call (call)
|
|
, m_curPos (0)
|
|
{
|
|
}
|
|
|
|
void CallReader::read (deUint8* bytes, size_t numBytes)
|
|
{
|
|
DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
|
|
deMemcpy(bytes, m_call->getData()+m_curPos, numBytes);
|
|
m_curPos += numBytes;
|
|
}
|
|
|
|
const deUint8* CallReader::getDataBlock (size_t numBytes)
|
|
{
|
|
DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
|
|
|
|
const deUint8* ptr = m_call->getData()+m_curPos;
|
|
m_curPos += numBytes;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
bool CallReader::isDataConsumed (void) const
|
|
{
|
|
return m_curPos == m_call->getDataSize();
|
|
}
|
|
|
|
CallReader& operator>> (CallReader& reader, std::string& value)
|
|
{
|
|
value.clear();
|
|
for (;;)
|
|
{
|
|
char c;
|
|
reader.read((deUint8*)&c, sizeof(char));
|
|
if (c != 0)
|
|
value.push_back(c);
|
|
else
|
|
break;
|
|
}
|
|
|
|
return reader;
|
|
}
|
|
|
|
// CallWriter
|
|
|
|
CallWriter::CallWriter (CallQueue* queue, Call::Function function)
|
|
: m_queue (queue)
|
|
, m_call (queue->getEmptyCall())
|
|
, m_enqueued (false)
|
|
{
|
|
m_call->setFunction(function);
|
|
}
|
|
|
|
CallWriter::~CallWriter (void)
|
|
{
|
|
if (!m_enqueued)
|
|
m_queue->freeCall(m_call);
|
|
}
|
|
|
|
void CallWriter::write (const deUint8* bytes, size_t numBytes)
|
|
{
|
|
DE_ASSERT(!m_enqueued);
|
|
size_t curPos = m_call->getDataSize();
|
|
m_call->setDataSize(curPos+numBytes);
|
|
deMemcpy(m_call->getData()+curPos, bytes, numBytes);
|
|
}
|
|
|
|
void CallWriter::enqueue (void)
|
|
{
|
|
DE_ASSERT(!m_enqueued);
|
|
m_queue->enqueue(m_call);
|
|
m_enqueued = true;
|
|
}
|
|
|
|
CallWriter& operator<< (CallWriter& writer, const char* str)
|
|
{
|
|
int pos = 0;
|
|
for (;;)
|
|
{
|
|
writer.write((const deUint8*)str + pos, sizeof(char));
|
|
if (str[pos] == 0)
|
|
break;
|
|
pos += 1;
|
|
}
|
|
|
|
return writer;
|
|
}
|
|
|
|
} // xe
|