blob: 2f8456e9adc8b09c217863b2e4c292f0f65b9be1 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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(uint8_t *bytes, size_t numBytes)
{
DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
deMemcpy(bytes, m_call->getData() + m_curPos, numBytes);
m_curPos += numBytes;
}
const uint8_t *CallReader::getDataBlock(size_t numBytes)
{
DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
const uint8_t *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((uint8_t *)&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 uint8_t *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 uint8_t *)str + pos, sizeof(char));
if (str[pos] == 0)
break;
pos += 1;
}
return writer;
}
} // namespace xe