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
28using std::vector;
29
30static inline int getNextQueueSize (int curSize, int minNewSize)
31{
32	return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
33}
34
35namespace xe
36{
37
38// CallQueue
39
40CallQueue::CallQueue (void)
41	: m_callSem		(0)
42	, m_callQueue	(64)
43{
44}
45
46CallQueue::~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
53void 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
95Call* 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
118void 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
132void CallQueue::freeCall (Call* call)
133{
134	de::ScopedLock lock(m_lock);
135	m_freeCalls.push_back(call);
136}
137
138// Call
139
140Call::Call (void)
141	: m_func(DE_NULL)
142{
143}
144
145Call::~Call (void)
146{
147}
148
149void Call::clear (void)
150{
151	m_func = DE_NULL;
152	m_data.clear();
153}
154
155// CallReader
156
157CallReader::CallReader (Call* call)
158	: m_call	(call)
159	, m_curPos	(0)
160{
161}
162
163void 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
170const 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
180CallReader& 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
198CallWriter::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
206CallWriter::~CallWriter (void)
207{
208	if (!m_enqueued)
209		m_queue->freeCall(m_call);
210}
211
212void 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
220void CallWriter::enqueue (void)
221{
222	DE_ASSERT(!m_enqueued);
223	m_queue->enqueue(m_call);
224	m_enqueued = true;
225}
226
227CallWriter& 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