Home | History | Annotate | Download | only in executor
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Test Executor
      3  * ------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Cross-thread function call dispatcher.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xeCallQueue.hpp"
     25 #include "deInt32.h"
     26 #include "deMemory.h"
     27 
     28 using std::vector;
     29 
     30 static inline int getNextQueueSize (int curSize, int minNewSize)
     31 {
     32 	return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
     33 }
     34 
     35 namespace xe
     36 {
     37 
     38 // CallQueue
     39 
     40 CallQueue::CallQueue (void)
     41 	: m_canceled	(false)
     42 	, m_callSem		(0)
     43 	, m_callQueue	(64)
     44 {
     45 }
     46 
     47 CallQueue::~CallQueue (void)
     48 {
     49 	// Destroy all calls.
     50 	for (vector<Call*>::iterator i = m_calls.begin(); i != m_calls.end(); i++)
     51 		delete *i;
     52 }
     53 
     54 void CallQueue::cancel (void)
     55 {
     56 	m_canceled = true;
     57 	m_callSem.increment();
     58 }
     59 
     60 void CallQueue::callNext (void)
     61 {
     62 	Call* call = DE_NULL;
     63 
     64 	// Wait for a call.
     65 	m_callSem.decrement();
     66 
     67 	if (m_canceled)
     68 		return;
     69 
     70 	// Acquire call from buffer.
     71 	{
     72 		de::ScopedLock lock(m_lock);
     73 		call = m_callQueue.popBack();
     74 	}
     75 
     76 	try
     77 	{
     78 		// \note Enqueue lock is not held during call so it is possible to enqueue more work from dispatched call.
     79 		CallReader reader(call);
     80 
     81 		call->getFunction()(reader);
     82 
     83 		// check callee consumed all
     84 		DE_ASSERT(reader.isDataConsumed());
     85 		call->clear();
     86 	}
     87 	catch (const std::exception&)
     88 	{
     89 		try
     90 		{
     91 			// Try to push call into free calls list.
     92 			de::ScopedLock lock(m_lock);
     93 			m_freeCalls.push_back(call);
     94 		}
     95 		catch (const std::exception&)
     96 		{
     97 			// We can't do anything but ignore this.
     98 		}
     99 
    100 		throw;
    101 	}
    102 
    103 	// Push back to free calls list.
    104 	{
    105 		de::ScopedLock lock(m_lock);
    106 		m_freeCalls.push_back(call);
    107 	}
    108 }
    109 
    110 Call* CallQueue::getEmptyCall (void)
    111 {
    112 	de::ScopedLock	lock	(m_lock);
    113 	Call*			call	= DE_NULL;
    114 
    115 	// Try to get from free calls list.
    116 	if (!m_freeCalls.empty())
    117 	{
    118 		call = m_freeCalls.back();
    119 		m_freeCalls.pop_back();
    120 	}
    121 
    122 	// If no free calls were available, create a new.
    123 	if (!call)
    124 	{
    125 		m_calls.reserve(m_calls.size()+1);
    126 		call = new Call();
    127 		m_calls.push_back(call);
    128 	}
    129 
    130 	return call;
    131 }
    132 
    133 void CallQueue::enqueue (Call* call)
    134 {
    135 	de::ScopedLock lock(m_lock);
    136 
    137 	if (m_callQueue.getNumFree() == 0)
    138 	{
    139 		// Call queue must be grown.
    140 		m_callQueue.resize(getNextQueueSize(m_callQueue.getSize(), m_callQueue.getSize()+1));
    141 	}
    142 
    143 	m_callQueue.pushFront(call);
    144 	m_callSem.increment();
    145 }
    146 
    147 void CallQueue::freeCall (Call* call)
    148 {
    149 	de::ScopedLock lock(m_lock);
    150 	m_freeCalls.push_back(call);
    151 }
    152 
    153 // Call
    154 
    155 Call::Call (void)
    156 	: m_func(DE_NULL)
    157 {
    158 }
    159 
    160 Call::~Call (void)
    161 {
    162 }
    163 
    164 void Call::clear (void)
    165 {
    166 	m_func = DE_NULL;
    167 	m_data.clear();
    168 }
    169 
    170 // CallReader
    171 
    172 CallReader::CallReader (Call* call)
    173 	: m_call	(call)
    174 	, m_curPos	(0)
    175 {
    176 }
    177 
    178 void CallReader::read (deUint8* bytes, size_t numBytes)
    179 {
    180 	DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
    181 	deMemcpy(bytes, m_call->getData()+m_curPos, numBytes);
    182 	m_curPos += numBytes;
    183 }
    184 
    185 const deUint8* CallReader::getDataBlock (size_t numBytes)
    186 {
    187 	DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
    188 
    189 	const deUint8* ptr = m_call->getData()+m_curPos;
    190 	m_curPos += numBytes;
    191 
    192 	return ptr;
    193 }
    194 
    195 bool CallReader::isDataConsumed (void) const
    196 {
    197 	return m_curPos == m_call->getDataSize();
    198 }
    199 
    200 CallReader& operator>> (CallReader& reader, std::string& value)
    201 {
    202 	value.clear();
    203 	for (;;)
    204 	{
    205 		char c;
    206 		reader.read((deUint8*)&c, sizeof(char));
    207 		if (c != 0)
    208 			value.push_back(c);
    209 		else
    210 			break;
    211 	}
    212 
    213 	return reader;
    214 }
    215 
    216 // CallWriter
    217 
    218 CallWriter::CallWriter (CallQueue* queue, Call::Function function)
    219 	: m_queue		(queue)
    220 	, m_call		(queue->getEmptyCall())
    221 	, m_enqueued	(false)
    222 {
    223 	m_call->setFunction(function);
    224 }
    225 
    226 CallWriter::~CallWriter (void)
    227 {
    228 	if (!m_enqueued)
    229 		m_queue->freeCall(m_call);
    230 }
    231 
    232 void CallWriter::write (const deUint8* bytes, size_t numBytes)
    233 {
    234 	DE_ASSERT(!m_enqueued);
    235 	size_t curPos = m_call->getDataSize();
    236 	m_call->setDataSize(curPos+numBytes);
    237 	deMemcpy(m_call->getData()+curPos, bytes, numBytes);
    238 }
    239 
    240 void CallWriter::enqueue (void)
    241 {
    242 	DE_ASSERT(!m_enqueued);
    243 	m_queue->enqueue(m_call);
    244 	m_enqueued = true;
    245 }
    246 
    247 CallWriter& operator<< (CallWriter& writer, const char* str)
    248 {
    249 	int pos = 0;
    250 	for (;;)
    251 	{
    252 		writer.write((const deUint8*)str + pos, sizeof(char));
    253 		if (str[pos] == 0)
    254 			break;
    255 		pos += 1;
    256 	}
    257 
    258 	return writer;
    259 }
    260 
    261 } // xe
    262