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