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