blob: cf042ae9672d98d2b0279b515f626f7d515c83c2 [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
#ifndef HIDL_MQ_H
#define HIDL_MQ_H
#include <atomic>
#include "MessageQueueDesc.h"
#define MINIMUM_GRANTOR_COUNT 3
namespace android {
namespace hardware {
template <typename T>
struct MessageQueue {
MessageQueue(const MQDescriptor& Desc);
~MessageQueue();
size_t availableToWrite() const;
size_t availableToRead() const;
size_t getQuantumSize() const;
size_t getQuantumCount() const;
bool isValid() const;
bool write(const T* data);
bool read(T* data);
bool write(const T* data, size_t count);
bool read(T* data, size_t count);
const MQDescriptor* getDesc() const { return &mDesc; }
private:
struct region {
uint8_t* address;
size_t length;
};
struct transaction {
region first;
region second;
};
size_t writeBytes(const uint8_t* data, size_t size);
transaction beginWrite(size_t nBytesDesired) const;
void commitWrite(size_t nBytesWritten);
size_t readBytes(uint8_t* data, size_t size);
transaction beginRead(size_t nBytesDesired) const;
void commitRead(size_t nBytesRead);
MessageQueue(const MessageQueue& other) = delete;
MessageQueue& operator=(const MessageQueue& other) = delete;
MessageQueue();
MQDescriptor mDesc;
uint8_t* mRing;
std::atomic<uint64_t>* mReadPtr;
std::atomic<uint64_t>* mWritePtr;
};
template <typename T>
MessageQueue<T>::MessageQueue(const MQDescriptor& Desc) : mDesc(Desc) {
/*
* Verify that the the Descriptor contains the minimum number of grantors
* the native_handle is valid and T matches quantum size.
*/
if ((Desc.getHandle() == nullptr) ||
(Desc.getGrantors().size() < MINIMUM_GRANTOR_COUNT) ||
(Desc.getQuantum() != sizeof(T)))
return;
mReadPtr = (std::atomic<uint64_t>*)mDesc.mapGrantorDescr(READPTRPOS);
mWritePtr = (std::atomic<uint64_t>*)mDesc.mapGrantorDescr(WRITEPTRPOS);
mReadPtr->store(0, std::memory_order_acquire);
mWritePtr->store(0, std::memory_order_acquire);
mRing = (uint8_t*)mDesc.mapGrantorDescr(DATAPTRPOS);
}
template <typename T>
MessageQueue<T>::~MessageQueue() {
if (mReadPtr) mDesc.unmapGrantorDescr(mReadPtr, READPTRPOS);
if (mWritePtr) mDesc.unmapGrantorDescr(mWritePtr, WRITEPTRPOS);
if (mRing) mDesc.unmapGrantorDescr(mRing, DATAPTRPOS);
}
template <typename T>
bool MessageQueue<T>::write(const T* data) {
return write(data, 1);
}
template <typename T>
bool MessageQueue<T>::read(T* data) {
return read(data, 1);
}
template <typename T>
bool MessageQueue<T>::write(const T* data, size_t count) {
if (availableToWrite() < sizeof(T) * count) {
return false;
}
return (writeBytes(reinterpret_cast<const uint8_t*>(data),
sizeof(T) * count) == sizeof(T) * count);
}
template <typename T>
bool MessageQueue<T>::read(T* data, size_t count) {
if (availableToRead() < sizeof(T) * count) {
return false;
}
return readBytes(reinterpret_cast<uint8_t*>(data), sizeof(T) * count) ==
sizeof(T) * count;
}
template <typename T>
size_t MessageQueue<T>::availableToWrite() const {
return mDesc.getSize() - availableToRead();
}
template <typename T>
size_t MessageQueue<T>::writeBytes(const uint8_t* data, size_t size) {
transaction tx = beginWrite(size);
memcpy(tx.first.address, data, tx.first.length);
memcpy(tx.second.address, data + tx.first.length, tx.second.length);
size_t result = tx.first.length + tx.second.length;
commitWrite(result);
return result;
}
/*
* The below method does not check for available space since it was already
* checked by write() API which invokes writeBytes() which in turn calls
* beginWrite().
*/
template <typename T>
typename MessageQueue<T>::transaction MessageQueue<T>::beginWrite(
size_t nBytesDesired) const {
transaction result;
auto readPtr = mReadPtr->load(std::memory_order_acquire);
auto writePtr = mWritePtr->load(std::memory_order_relaxed);
size_t writeOffset = writePtr % mDesc.getSize();
size_t contiguous = mDesc.getSize() - writeOffset;
if (contiguous < nBytesDesired) {
result = {{mRing + writeOffset, contiguous},
{mRing, nBytesDesired - contiguous}};
} else {
result = {
{mRing + writeOffset, nBytesDesired}, {0, 0},
};
}
return result;
}
template <typename T>
void MessageQueue<T>::commitWrite(size_t nBytesWritten) {
auto writePtr = mWritePtr->load(std::memory_order_relaxed);
writePtr += nBytesWritten;
mWritePtr->store(writePtr, std::memory_order_release);
}
template <typename T>
size_t MessageQueue<T>::availableToRead() const {
/*
* Doing relaxed loads here because these accesses don't carry dependencies.
* Dependent accesses won't happen until after a call to beginWrite or
* beginRead
* which do proper acquire/release.
*/
return mWritePtr->load(std::memory_order_relaxed) -
mReadPtr->load(std::memory_order_relaxed);
}
template <typename T>
size_t MessageQueue<T>::readBytes(uint8_t* data, size_t size) {
transaction tx = beginRead(size);
memcpy(data, tx.first.address, tx.first.length);
memcpy(data + tx.first.length, tx.second.address, tx.second.length);
size_t result = tx.first.length + tx.second.length;
commitRead(result);
return result;
}
/*
* The below method does not check whether nBytesDesired bytes are available
* to read because the check is performed in the read() method before
* readBytes() is invoked.
*/
template <typename T>
typename MessageQueue<T>::transaction MessageQueue<T>::beginRead(
size_t nBytesDesired) const {
transaction result;
auto writePtr = mWritePtr->load(std::memory_order_acquire);
auto readPtr = mReadPtr->load(std::memory_order_relaxed);
size_t readOffset = readPtr % mDesc.getSize();
size_t contiguous = mDesc.getSize() - readOffset;
if (contiguous < nBytesDesired) {
result = {{mRing + readOffset, contiguous},
{mRing, nBytesDesired - contiguous}};
} else {
result = {
{mRing + readOffset, nBytesDesired}, {0, 0},
};
}
return result;
}
template <typename T>
void MessageQueue<T>::commitRead(size_t nBytesRead) {
auto readPtr = mReadPtr->load(std::memory_order_relaxed);
readPtr += nBytesRead;
mReadPtr->store(readPtr, std::memory_order_release);
}
template <typename T>
size_t MessageQueue<T>::getQuantumSize() const {
return mDesc.getQuantum();
}
template <typename T>
size_t MessageQueue<T>::getQuantumCount() const {
return mDesc.getSize() / mDesc.getQuantum();
}
template <typename T>
bool MessageQueue<T>::isValid() const {
return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
}
} // namespace hardware
} // namespace android
#endif // HIDL_MQ_H