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