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 
getNextQueueSize(int curSize,int minNewSize)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 
CallQueue(void)40 CallQueue::CallQueue (void)
41 	: m_callSem		(0)
42 	, m_callQueue	(64)
43 {
44 }
45 
~CallQueue(void)46 CallQueue::~CallQueue (void)
47 {
48 	// Destroy all calls.
49 	for (vector<Call*>::iterator i = m_calls.begin(); i != m_calls.end(); i++)
50 		delete *i;
51 }
52 
callNext(void)53 void CallQueue::callNext (void)
54 {
55 	Call* call = DE_NULL;
56 
57 	// Wait for a call.
58 	m_callSem.decrement();
59 
60 	// Acquire call from buffer.
61 	{
62 		de::ScopedLock lock(m_lock);
63 		call = m_callQueue.popBack();
64 	}
65 
66 	try
67 	{
68 		// \note Enqueue lock is not held during call so it is possible to enqueue more work from dispatched call.
69 		call->getFunction()(CallReader(call));
70 		call->clear();
71 	}
72 	catch (const std::exception&)
73 	{
74 		try
75 		{
76 			// Try to push call into free calls list.
77 			de::ScopedLock lock(m_lock);
78 			m_freeCalls.push_back(call);
79 		}
80 		catch (const std::exception&)
81 		{
82 			// We can't do anything but ignore this.
83 		}
84 
85 		throw;
86 	}
87 
88 	// Push back to free calls list.
89 	{
90 		de::ScopedLock lock(m_lock);
91 		m_freeCalls.push_back(call);
92 	}
93 }
94 
getEmptyCall(void)95 Call* CallQueue::getEmptyCall (void)
96 {
97 	de::ScopedLock	lock	(m_lock);
98 	Call*			call	= DE_NULL;
99 
100 	// Try to get from free calls list.
101 	if (!m_freeCalls.empty())
102 	{
103 		call = m_freeCalls.back();
104 		m_freeCalls.pop_back();
105 	}
106 
107 	// If no free calls were available, create a new.
108 	if (!call)
109 	{
110 		m_calls.reserve(m_calls.size()+1);
111 		call = new Call();
112 		m_calls.push_back(call);
113 	}
114 
115 	return call;
116 }
117 
enqueue(Call * call)118 void CallQueue::enqueue (Call* call)
119 {
120 	de::ScopedLock lock(m_lock);
121 
122 	if (m_callQueue.getNumFree() == 0)
123 	{
124 		// Call queue must be grown.
125 		m_callQueue.resize(getNextQueueSize(m_callQueue.getSize(), m_callQueue.getSize()+1));
126 	}
127 
128 	m_callQueue.pushFront(call);
129 	m_callSem.increment();
130 }
131 
freeCall(Call * call)132 void CallQueue::freeCall (Call* call)
133 {
134 	de::ScopedLock lock(m_lock);
135 	m_freeCalls.push_back(call);
136 }
137 
138 // Call
139 
Call(void)140 Call::Call (void)
141 	: m_func(DE_NULL)
142 {
143 }
144 
~Call(void)145 Call::~Call (void)
146 {
147 }
148 
clear(void)149 void Call::clear (void)
150 {
151 	m_func = DE_NULL;
152 	m_data.clear();
153 }
154 
155 // CallReader
156 
CallReader(Call * call)157 CallReader::CallReader (Call* call)
158 	: m_call	(call)
159 	, m_curPos	(0)
160 {
161 }
162 
read(deUint8 * bytes,int numBytes)163 void CallReader::read (deUint8* bytes, int numBytes)
164 {
165 	DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
166 	deMemcpy(bytes, m_call->getData()+m_curPos, numBytes);
167 	m_curPos += numBytes;
168 }
169 
getDataBlock(int numBytes)170 const deUint8* CallReader::getDataBlock (int numBytes)
171 {
172 	DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
173 
174 	const deUint8* ptr = m_call->getData()+m_curPos;
175 	m_curPos += numBytes;
176 
177 	return ptr;
178 }
179 
operator >>(CallReader & reader,std::string & value)180 CallReader& operator>> (CallReader& reader, std::string& value)
181 {
182 	value.clear();
183 	for (;;)
184 	{
185 		char c;
186 		reader.read((deUint8*)&c, sizeof(char));
187 		if (c != 0)
188 			value.push_back(c);
189 		else
190 			break;
191 	}
192 
193 	return reader;
194 }
195 
196 // CallWriter
197 
CallWriter(CallQueue * queue,Call::Function function)198 CallWriter::CallWriter (CallQueue* queue, Call::Function function)
199 	: m_queue		(queue)
200 	, m_call		(queue->getEmptyCall())
201 	, m_enqueued	(false)
202 {
203 	m_call->setFunction(function);
204 }
205 
~CallWriter(void)206 CallWriter::~CallWriter (void)
207 {
208 	if (!m_enqueued)
209 		m_queue->freeCall(m_call);
210 }
211 
write(const deUint8 * bytes,int numBytes)212 void CallWriter::write (const deUint8* bytes, int numBytes)
213 {
214 	DE_ASSERT(!m_enqueued);
215 	int curPos = m_call->getDataSize();
216 	m_call->setDataSize(curPos+numBytes);
217 	deMemcpy(m_call->getData()+curPos, bytes, numBytes);
218 }
219 
enqueue(void)220 void CallWriter::enqueue (void)
221 {
222 	DE_ASSERT(!m_enqueued);
223 	m_queue->enqueue(m_call);
224 	m_enqueued = true;
225 }
226 
operator <<(CallWriter & writer,const char * str)227 CallWriter& operator<< (CallWriter& writer, const char* str)
228 {
229 	int pos = 0;
230 	for (;;)
231 	{
232 		writer.write((const deUint8*)str + pos, sizeof(char));
233 		if (str[pos] == 0)
234 			break;
235 		pos += 1;
236 	}
237 
238 	return writer;
239 }
240 
241 } // xe
242