Snap for 8426163 from de9cf48bf6458044373845968d18637b4ce3175f to mainline-tzdata2-release
Change-Id: I83a1c4c1b406da373608c4a59dd91be709912f77
diff --git a/Android.bp b/Android.bp
index 5889a9c..e2cb961 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libfmq",
shared_libs: [
@@ -35,12 +31,6 @@
],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
- header_libs: [
- "libfmq-base",
- ],
- export_header_lib_headers: [
- "libfmq-base",
- ],
srcs: [
"EventFlag.cpp",
"FmqInternal.cpp",
@@ -50,7 +40,6 @@
"-Werror",
],
vendor_available: true,
- product_available: true,
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
vndk: {
@@ -58,21 +47,4 @@
},
double_loadable: true,
min_sdk_version: "29",
- host_supported: true,
-}
-
-// Header only lib to share type between HIDL and AIDL MQDescriptor
-cc_library_headers {
- name: "libfmq-base",
- host_supported: true,
- vendor_available: true,
- product_available: true,
- native_bridge_supported: true,
- recovery_available: true,
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
- export_include_dirs: ["base"],
- min_sdk_version: "29",
}
diff --git a/EventFlag.cpp b/EventFlag.cpp
index 96f9519..cb101bd 100644
--- a/EventFlag.cpp
+++ b/EventFlag.cpp
@@ -22,7 +22,6 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include <limits>
#include <new>
#include <fmq/EventFlag.h>
@@ -121,9 +120,9 @@
* No need to call FUTEX_WAKE_BITSET if there were deferred wakes
* already available for all set bits from bitmask.
*/
- constexpr size_t kIntMax = std::numeric_limits<int>::max();
if ((~old & bitmask) != 0) {
- int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET, kIntMax, NULL, NULL, bitmask);
+ int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
+ INT_MAX, NULL, NULL, bitmask);
if (ret == -1) {
status = -errno;
ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
diff --git a/FmqInternal.cpp b/FmqInternal.cpp
index 886d155..6b95def 100644
--- a/FmqInternal.cpp
+++ b/FmqInternal.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "FMQ"
#include <android-base/logging.h>
-#include <utils/Log.h>
namespace android {
namespace hardware {
@@ -30,10 +29,6 @@
LOG(ERROR) << message;
}
-void errorWriteLog(int tag, const char* info) {
- android_errorWriteLog(tag, info);
-}
-
} // namespace details
} // namespace hardware
} // namespace android
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 18b0a1b..213c93a 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,4 +3,3 @@
[Builtin Hooks]
clang_format = true
-bpfmt = true
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index dfa7071..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "presubmit": [
- {
- "name": "fmq_unit_tests"
- },
- {
- "name": "fmq_test"
- }
- ]
-}
diff --git a/base/fmq/MQDescriptorBase.h b/base/fmq/MQDescriptorBase.h
deleted file mode 100644
index 0d18d4c..0000000
--- a/base/fmq/MQDescriptorBase.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <assert.h>
-#include <string>
-
-namespace android {
-namespace hardware {
-
-enum MQFlavor : uint32_t {
- /*
- * kSynchronizedReadWrite represents the wait-free synchronized flavor of the
- * FMQ. It is intended to be have a single reader and single writer.
- * Attempts to overflow/underflow returns a failure.
- */
- kSynchronizedReadWrite = 0x01,
- /*
- * kUnsynchronizedWrite represents the flavor of FMQ where writes always
- * succeed. This flavor allows one writer and many readers. A read operation
- * can detect an overwrite and reset the read counter.
- */
- kUnsynchronizedWrite = 0x02
-};
-
-struct GrantorDescriptor {
- uint32_t flags __attribute__((aligned(4)));
- uint32_t fdIndex __attribute__((aligned(4)));
- uint32_t offset __attribute__((aligned(4)));
- uint64_t extent __attribute__((aligned(8)));
-};
-
-static_assert(offsetof(GrantorDescriptor, flags) == 0, "wrong offset");
-static_assert(offsetof(GrantorDescriptor, fdIndex) == 4, "wrong offset");
-static_assert(offsetof(GrantorDescriptor, offset) == 8, "wrong offset");
-static_assert(offsetof(GrantorDescriptor, extent) == 16, "wrong offset");
-static_assert(sizeof(GrantorDescriptor) == 24, "wrong size");
-static_assert(__alignof(GrantorDescriptor) == 8, "wrong alignment");
-
-namespace details {
-
-void logError(const std::string& message);
-void errorWriteLog(int tag, const char* message);
-
-typedef uint64_t RingBufferPosition;
-enum GrantorType : int { READPTRPOS = 0, WRITEPTRPOS, DATAPTRPOS, EVFLAGWORDPOS };
-/*
- * There should at least be GrantorDescriptors for the read counter, write
- * counter and data buffer. A GrantorDescriptor for an EventFlag word is
- * not required if there is no need for blocking FMQ operations.
- */
-static constexpr int32_t kMinGrantorCount = DATAPTRPOS + 1;
-
-/*
- * Minimum number of GrantorDescriptors required if EventFlag support is
- * needed for blocking FMQ operations.
- */
-static constexpr int32_t kMinGrantorCountForEvFlagSupport = EVFLAGWORDPOS + 1;
-
-static inline size_t alignToWordBoundary(size_t length) {
- constexpr size_t kAlignmentSize = 64;
- if (kAlignmentSize % __WORDSIZE != 0) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "Incompatible word size");
-#endif
- }
-
- /*
- * Check if alignment to word boundary would cause an overflow.
- */
- if (length > SIZE_MAX - kAlignmentSize / 8 + 1) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "Queue size too large");
-#endif
- }
-
- return (length + kAlignmentSize / 8 - 1) & ~(kAlignmentSize / 8 - 1U);
-}
-
-static inline size_t isAlignedToWordBoundary(size_t offset) {
- constexpr size_t kAlignmentSize = 64;
- return (offset & (kAlignmentSize / 8 - 1)) == 0;
-}
-
-} // namespace details
-} // namespace hardware
-} // namespace android
diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk
index 3cae117..d82c0a3 100644
--- a/benchmarks/Android.mk
+++ b/benchmarks/Android.mk
@@ -31,11 +31,10 @@
LOCAL_REQUIRED_MODULES := android.hardware.tests.msgq@1.0-impl
ifneq ($(TARGET_2ND_ARCH),)
-LOCAL_REQUIRED_MODULES += android.hardware.tests.msgq@1.0-impl:32
+LOCAL_REQUIRED_MODULES += android.hardware.tests.msgq@1.0-impl$(TARGET_2ND_ARCH_MODULE_SUFFIX)
endif
LOCAL_SHARED_LIBRARIES += android.hardware.tests.msgq@1.0 libfmq
LOCAL_MODULE := mq_benchmark_client
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
include $(BUILD_NATIVE_TEST)
+
diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp
deleted file mode 100644
index 6b41e3f..0000000
--- a/fuzzer/Android.bp
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2021 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.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_fuzz {
- name: "fmq_fuzzer",
-
- defaults: [
- "libbinder_ndk_host_user",
- ],
-
- srcs: [
- "fmq_fuzzer.cpp",
- ],
-
- static_libs: [
- "libfmq",
- "android.hardware.common.fmq-V1-ndk_platform",
- ],
-
- shared_libs: [
- "libbase",
- "libcutils",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
-
- fuzz_config: {
- cc: [
- "devinmoore@google.com",
- "smoreland@google.com",
- ],
- componentid: 655781,
- libfuzzer_options: [
- "max_len=50000",
- ],
- },
-
- host_supported: true,
-
- sanitize: {
- hwaddress: true,
- scs: true,
- cfi: true,
- memtag_heap: true,
- // undefined behavior is expected
- all_undefined: false,
- // integer overflow is expected
- integer_overflow: false,
- },
-}
diff --git a/fuzzer/fmq_fuzzer.cpp b/fuzzer/fmq_fuzzer.cpp
deleted file mode 100644
index 844188f..0000000
--- a/fuzzer/fmq_fuzzer.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <iostream>
-#include <limits>
-#include <thread>
-
-#include <android-base/logging.h>
-#include <fmq/AidlMessageQueue.h>
-#include <fmq/ConvertMQDescriptors.h>
-#include <fmq/EventFlag.h>
-#include <fmq/MessageQueue.h>
-
-#include "fuzzer/FuzzedDataProvider.h"
-
-using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using android::hardware::kSynchronizedReadWrite;
-using android::hardware::kUnsynchronizedWrite;
-
-typedef int32_t payload_t;
-
-/*
- * MessageQueueBase.h contains asserts when memory allocation fails. So we need
- * to set a reasonable limit if we want to avoid those asserts.
- */
-static constexpr size_t kAlignment = 8;
-static constexpr size_t kMaxNumElements = PAGE_SIZE * 10 / sizeof(payload_t) - kAlignment + 1;
-
-/*
- * The read counter can be found in the shared memory 16 bytes before the start
- * of the ring buffer.
- */
-static constexpr int kReadCounterOffsetBytes = 16;
-/*
- * The write counter can be found in the shared memory 8 bytes before the start
- * of the ring buffer.
- */
-static constexpr int kWriteCounterOffsetBytes = 8;
-
-static constexpr int kMaxNumSyncReaders = 1;
-static constexpr int kMaxNumUnsyncReaders = 5;
-
-typedef android::AidlMessageQueue<payload_t, SynchronizedReadWrite> AidlMessageQueueSync;
-typedef android::AidlMessageQueue<payload_t, UnsynchronizedWrite> AidlMessageQueueUnsync;
-typedef android::hardware::MessageQueue<payload_t, kSynchronizedReadWrite> MessageQueueSync;
-typedef android::hardware::MessageQueue<payload_t, kUnsynchronizedWrite> MessageQueueUnsync;
-typedef aidl::android::hardware::common::fmq::MQDescriptor<payload_t, SynchronizedReadWrite>
- AidlMQDescSync;
-typedef aidl::android::hardware::common::fmq::MQDescriptor<payload_t, UnsynchronizedWrite>
- AidlMQDescUnsync;
-typedef android::hardware::MQDescriptorSync<payload_t> MQDescSync;
-typedef android::hardware::MQDescriptorUnsync<payload_t> MQDescUnsync;
-
-template <typename Queue, typename Desc>
-void reader(const Desc& desc, std::vector<uint8_t> readerData) {
- Queue readMq(desc);
- if (!readMq.isValid()) {
- LOG(ERROR) << "read mq invalid";
- return;
- }
- FuzzedDataProvider fdp(&readerData[0], readerData.size());
- while (fdp.remaining_bytes()) {
- typename Queue::MemTransaction tx;
- size_t numElements = fdp.ConsumeIntegralInRange<size_t>(0, kMaxNumElements);
- if (!readMq.beginRead(numElements, &tx)) {
- continue;
- }
- const auto& region = tx.getFirstRegion();
- payload_t* firstStart = region.getAddress();
-
- // TODO add the debug function to get pointer to the ring buffer
- uint64_t* writeCounter = reinterpret_cast<uint64_t*>(
- reinterpret_cast<uint8_t*>(firstStart) - kWriteCounterOffsetBytes);
- *writeCounter = fdp.ConsumeIntegral<uint64_t>();
-
- (void)std::to_string(*firstStart);
-
- readMq.commitRead(numElements);
- }
-}
-
-template <typename Queue>
-void writer(Queue& writeMq, FuzzedDataProvider& fdp) {
- while (fdp.remaining_bytes()) {
- typename Queue::MemTransaction tx;
- size_t numElements = 1;
- if (!writeMq.beginWrite(numElements, &tx)) {
- // need to consume something so we don't end up looping forever
- fdp.ConsumeIntegral<uint8_t>();
- continue;
- }
-
- const auto& region = tx.getFirstRegion();
- payload_t* firstStart = region.getAddress();
-
- // TODO add the debug function to get pointer to the ring buffer
- uint64_t* readCounter = reinterpret_cast<uint64_t*>(reinterpret_cast<uint8_t*>(firstStart) -
- kReadCounterOffsetBytes);
- *readCounter = fdp.ConsumeIntegral<uint64_t>();
-
- *firstStart = fdp.ConsumeIntegral<payload_t>();
-
- writeMq.commitWrite(numElements);
- }
-}
-
-template <typename Queue, typename Desc>
-void fuzzAidlWithReaders(std::vector<uint8_t>& writerData,
- std::vector<std::vector<uint8_t>>& readerData) {
- FuzzedDataProvider fdp(&writerData[0], writerData.size());
- Queue writeMq(fdp.ConsumeIntegralInRange<size_t>(1, kMaxNumElements), fdp.ConsumeBool());
- if (!writeMq.isValid()) {
- LOG(ERROR) << "AIDL write mq invalid";
- return;
- }
- const auto desc = writeMq.dupeDesc();
- CHECK(desc.handle.fds[0].get() != -1);
-
- std::vector<std::thread> clients;
- for (int i = 0; i < readerData.size(); i++) {
- clients.emplace_back(reader<Queue, Desc>, std::ref(desc), std::ref(readerData[i]));
- }
-
- writer<Queue>(writeMq, fdp);
-
- for (auto& client : clients) {
- client.join();
- }
-}
-
-template <typename Queue, typename Desc>
-void fuzzHidlWithReaders(std::vector<uint8_t>& writerData,
- std::vector<std::vector<uint8_t>>& readerData) {
- FuzzedDataProvider fdp(&writerData[0], writerData.size());
- Queue writeMq(fdp.ConsumeIntegralInRange<size_t>(1, kMaxNumElements), fdp.ConsumeBool());
- if (!writeMq.isValid()) {
- LOG(ERROR) << "HIDL write mq invalid";
- return;
- }
- const auto desc = writeMq.getDesc();
- CHECK(desc->isHandleValid());
-
- std::vector<std::thread> clients;
- for (int i = 0; i < readerData.size(); i++) {
- clients.emplace_back(reader<Queue, Desc>, std::ref(*desc), std::ref(readerData[i]));
- }
-
- writer<Queue>(writeMq, fdp);
-
- for (auto& client : clients) {
- client.join();
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < 1 || size > 50000) {
- return 0;
- }
- FuzzedDataProvider fdp(data, size);
-
- bool fuzzSync = fdp.ConsumeBool();
- std::vector<std::vector<uint8_t>> readerData;
- uint8_t numReaders = fuzzSync ? fdp.ConsumeIntegralInRange<uint8_t>(0, kMaxNumSyncReaders)
- : fdp.ConsumeIntegralInRange<uint8_t>(0, kMaxNumUnsyncReaders);
- for (int i = 0; i < numReaders; i++) {
- readerData.emplace_back(fdp.ConsumeBytes<uint8_t>(5));
- }
- std::vector<uint8_t> writerData = fdp.ConsumeRemainingBytes<uint8_t>();
-
- if (fuzzSync) {
- fuzzHidlWithReaders<MessageQueueSync, MQDescSync>(writerData, readerData);
- fuzzAidlWithReaders<AidlMessageQueueSync, AidlMQDescSync>(writerData, readerData);
- } else {
- fuzzHidlWithReaders<MessageQueueUnsync, MQDescUnsync>(writerData, readerData);
- fuzzAidlWithReaders<AidlMessageQueueUnsync, AidlMQDescUnsync>(writerData, readerData);
- }
-
- return 0;
-}
diff --git a/include/fmq/AidlMQDescriptorShim.h b/include/fmq/AidlMQDescriptorShim.h
deleted file mode 100644
index e3d3cb9..0000000
--- a/include/fmq/AidlMQDescriptorShim.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
-#include <cutils/native_handle.h>
-#include <fmq/MQDescriptorBase.h>
-#include <limits>
-#include <type_traits>
-
-namespace android {
-namespace details {
-
-using aidl::android::hardware::common::fmq::GrantorDescriptor;
-using aidl::android::hardware::common::fmq::MQDescriptor;
-using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using android::hardware::MQFlavor;
-
-template <typename T, MQFlavor flavor>
-struct AidlMQDescriptorShim {
- // Takes ownership of handle
- AidlMQDescriptorShim(const std::vector<android::hardware::GrantorDescriptor>& grantors,
- native_handle_t* nHandle, size_t size);
-
- // Takes ownership of handle
- AidlMQDescriptorShim(
- const MQDescriptor<
- T, typename std::conditional<flavor == hardware::kSynchronizedReadWrite,
- SynchronizedReadWrite, UnsynchronizedWrite>::type>&
- desc);
-
- // Takes ownership of handle
- AidlMQDescriptorShim(size_t bufferSize, native_handle_t* nHandle, size_t messageSize,
- bool configureEventFlag = false);
-
- explicit AidlMQDescriptorShim(const AidlMQDescriptorShim& other)
- : AidlMQDescriptorShim(0, nullptr, 0) {
- *this = other;
- }
- AidlMQDescriptorShim& operator=(const AidlMQDescriptorShim& other);
-
- ~AidlMQDescriptorShim();
-
- size_t getSize() const;
-
- size_t getQuantum() const;
-
- uint32_t getFlags() const;
-
- bool isHandleValid() const { return mHandle != nullptr; }
- size_t countGrantors() const { return mGrantors.size(); }
-
- inline const std::vector<android::hardware::GrantorDescriptor>& grantors() const {
- return mGrantors;
- }
-
- inline const ::native_handle_t* handle() const { return mHandle; }
-
- inline ::native_handle_t* handle() { return mHandle; }
-
- static const size_t kOffsetOfGrantors;
- static const size_t kOffsetOfHandle;
-
- private:
- std::vector<android::hardware::GrantorDescriptor> mGrantors;
- native_handle_t* mHandle = nullptr;
- uint32_t mQuantum = 0;
- uint32_t mFlags = 0;
-};
-
-template <typename T, MQFlavor flavor>
-AidlMQDescriptorShim<T, flavor>::AidlMQDescriptorShim(
- const MQDescriptor<T, typename std::conditional<flavor == hardware::kSynchronizedReadWrite,
- SynchronizedReadWrite,
- UnsynchronizedWrite>::type>& desc)
- : mQuantum(desc.quantum), mFlags(desc.flags) {
- if (desc.quantum < 0 || desc.flags < 0) {
- // MQDescriptor uses signed integers, but the values must be positive.
- hardware::details::logError("Invalid MQDescriptor. Values must be positive. quantum: " +
- std::to_string(desc.quantum) +
- ". flags: " + std::to_string(desc.flags));
- return;
- }
-
- mGrantors.resize(desc.grantors.size());
- for (size_t i = 0; i < desc.grantors.size(); ++i) {
- if (desc.grantors[i].offset < 0 || desc.grantors[i].extent < 0 ||
- desc.grantors[i].fdIndex < 0) {
- // GrantorDescriptor uses signed integers, but the values must be positive.
- // Return before setting up the native_handle to make this invalid.
- hardware::details::logError(
- "Invalid MQDescriptor grantors. Values must be positive. Grantor index: " +
- std::to_string(i) + ". offset: " + std::to_string(desc.grantors[i].offset) +
- ". extent: " + std::to_string(desc.grantors[i].extent));
- return;
- }
- mGrantors[i].flags = 0;
- mGrantors[i].fdIndex = desc.grantors[i].fdIndex;
- mGrantors[i].offset = desc.grantors[i].offset;
- mGrantors[i].extent = desc.grantors[i].extent;
- }
-
- mHandle = native_handle_create(desc.handle.fds.size() /* num fds */,
- desc.handle.ints.size() /* num ints */);
- if (mHandle == nullptr) {
- hardware::details::logError("Null native_handle_t");
- return;
- }
- int data_index = 0;
- for (const auto& fd : desc.handle.fds) {
- mHandle->data[data_index] = dup(fd.get());
- data_index++;
- }
- for (const auto& data_int : desc.handle.ints) {
- mHandle->data[data_index] = data_int;
- data_index++;
- }
-}
-
-template <typename T, MQFlavor flavor>
-AidlMQDescriptorShim<T, flavor>::AidlMQDescriptorShim(
- const std::vector<android::hardware::GrantorDescriptor>& grantors, native_handle_t* nhandle,
- size_t size)
- : mGrantors(grantors),
- mHandle(nhandle),
- mQuantum(static_cast<uint32_t>(size)),
- mFlags(flavor) {}
-
-template <typename T, MQFlavor flavor>
-AidlMQDescriptorShim<T, flavor>& AidlMQDescriptorShim<T, flavor>::operator=(
- const AidlMQDescriptorShim& other) {
- mGrantors = other.mGrantors;
- if (mHandle != nullptr) {
- native_handle_close(mHandle);
- native_handle_delete(mHandle);
- mHandle = nullptr;
- }
- mQuantum = other.mQuantum;
- mFlags = other.mFlags;
-
- if (other.mHandle != nullptr) {
- mHandle = native_handle_create(other.mHandle->numFds, other.mHandle->numInts);
-
- for (int i = 0; i < other.mHandle->numFds; ++i) {
- mHandle->data[i] = dup(other.mHandle->data[i]);
- }
-
- memcpy(&mHandle->data[other.mHandle->numFds], &other.mHandle->data[other.mHandle->numFds],
- static_cast<size_t>(other.mHandle->numInts) * sizeof(int));
- }
-
- return *this;
-}
-
-template <typename T, MQFlavor flavor>
-AidlMQDescriptorShim<T, flavor>::AidlMQDescriptorShim(size_t bufferSize, native_handle_t* nHandle,
- size_t messageSize, bool configureEventFlag)
- : mHandle(nHandle), mQuantum(messageSize), mFlags(flavor) {
- /*
- * TODO(b/165674950) Since AIDL does not support unsigned integers, it can only support
- * The offset of EventFlag word needs to fit into an int32_t in MQDescriptor. This word comes
- * after the readPtr, writePtr, and dataBuffer.
- */
- bool overflow = bufferSize > std::numeric_limits<uint64_t>::max() -
- (sizeof(hardware::details::RingBufferPosition) +
- sizeof(hardware::details::RingBufferPosition));
- uint64_t largestOffset = hardware::details::alignToWordBoundary(
- sizeof(hardware::details::RingBufferPosition) +
- sizeof(hardware::details::RingBufferPosition) + bufferSize);
- if (overflow || largestOffset > std::numeric_limits<int32_t>::max() ||
- messageSize > std::numeric_limits<int32_t>::max()) {
- hardware::details::logError(
- "Queue size is too large. Message size: " + std::to_string(messageSize) +
- " bytes. Data buffer size: " + std::to_string(bufferSize) + " bytes. Max size: " +
- std::to_string(std::numeric_limits<int32_t>::max()) + " bytes.");
- return;
- }
-
- /*
- * If configureEventFlag is true, allocate an additional spot in mGrantor
- * for containing the fd and offset for mmapping the EventFlag word.
- */
- mGrantors.resize(configureEventFlag ? hardware::details::kMinGrantorCountForEvFlagSupport
- : hardware::details::kMinGrantorCount);
-
- size_t memSize[] = {
- sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for read
- * pointer counter
- */
- sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for write
- pointer counter */
- bufferSize, /* memory to be allocated for data buffer */
- sizeof(std::atomic<uint32_t>) /* memory to be allocated for EventFlag word */
- };
-
- /*
- * Create a default grantor descriptor for read, write pointers and
- * the data buffer. fdIndex parameter is set to 0 by default and
- * the offset for each grantor is contiguous.
- */
- for (size_t grantorPos = 0, offset = 0; grantorPos < mGrantors.size();
- offset += memSize[grantorPos++]) {
- mGrantors[grantorPos] = {
- 0 /* grantor flags */, 0 /* fdIndex */,
- static_cast<uint32_t>(hardware::details::alignToWordBoundary(offset)),
- memSize[grantorPos]};
- }
-}
-
-template <typename T, MQFlavor flavor>
-AidlMQDescriptorShim<T, flavor>::~AidlMQDescriptorShim() {
- if (mHandle != nullptr) {
- native_handle_close(mHandle);
- native_handle_delete(mHandle);
- }
-}
-
-template <typename T, MQFlavor flavor>
-size_t AidlMQDescriptorShim<T, flavor>::getSize() const {
- return mGrantors[hardware::details::DATAPTRPOS].extent;
-}
-
-template <typename T, MQFlavor flavor>
-size_t AidlMQDescriptorShim<T, flavor>::getQuantum() const {
- return mQuantum;
-}
-
-template <typename T, MQFlavor flavor>
-uint32_t AidlMQDescriptorShim<T, flavor>::getFlags() const {
- return mFlags;
-}
-
-template <typename T, MQFlavor flavor>
-std::string toString(const AidlMQDescriptorShim<T, flavor>& q) {
- std::string os;
- if (flavor & hardware::kSynchronizedReadWrite) {
- os += "fmq_sync";
- }
- if (flavor & hardware::kUnsynchronizedWrite) {
- os += "fmq_unsync";
- }
- os += " {" + toString(q.grantors().size()) + " grantor(s), " +
- "size = " + toString(q.getSize()) + ", .handle = " + toString(q.handle()) +
- ", .quantum = " + toString(q.getQuantum()) + "}";
- return os;
-}
-
-} // namespace details
-} // namespace android
diff --git a/include/fmq/AidlMessageQueue.h b/include/fmq/AidlMessageQueue.h
deleted file mode 100644
index 0536d6a..0000000
--- a/include/fmq/AidlMessageQueue.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
-#include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h>
-#include <aidl/android/hardware/common/fmq/UnsynchronizedWrite.h>
-#include <cutils/native_handle.h>
-#include <fmq/AidlMQDescriptorShim.h>
-#include <fmq/MessageQueueBase.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-namespace android {
-
-using aidl::android::hardware::common::fmq::MQDescriptor;
-using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using android::details::AidlMQDescriptorShim;
-using android::hardware::MQFlavor;
-
-template <typename T>
-struct FlavorTypeToValue;
-
-template <>
-struct FlavorTypeToValue<SynchronizedReadWrite> {
- static constexpr MQFlavor value = hardware::kSynchronizedReadWrite;
-};
-
-template <>
-struct FlavorTypeToValue<UnsynchronizedWrite> {
- static constexpr MQFlavor value = hardware::kUnsynchronizedWrite;
-};
-
-typedef uint64_t RingBufferPosition;
-
-/*
- * AIDL parcelables will have the typedef fixed_size. It is std::true_type when the
- * parcelable is annotated with @FixedSize, and std::false_type when not. Other types
- * should not have the fixed_size typedef, so they will always resolve to std::false_type.
- */
-template <typename T, typename = void>
-struct has_typedef_fixed_size : std::false_type {};
-
-template <typename T>
-struct has_typedef_fixed_size<T, std::void_t<typename T::fixed_size>> : T::fixed_size {};
-
-#define STATIC_AIDL_TYPE_CHECK(T) \
- static_assert(has_typedef_fixed_size<T>::value == true || std::is_fundamental<T>::value || \
- std::is_enum<T>::value, \
- "Only fundamental types, enums, and AIDL parcelables annotated with @FixedSize " \
- "and built for the NDK backend are supported as payload types(T).");
-
-template <typename T, typename U>
-struct AidlMessageQueue final
- : public MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value> {
- STATIC_AIDL_TYPE_CHECK(T);
- typedef AidlMQDescriptorShim<T, FlavorTypeToValue<U>::value> Descriptor;
- /**
- * This constructor uses the external descriptor used with AIDL interfaces.
- * It will create an FMQ based on the descriptor that was obtained from
- * another FMQ instance for communication.
- *
- * @param desc Descriptor from another FMQ that contains all of the
- * information required to create a new instance of that queue.
- * @param resetPointers Boolean indicating whether the read/write pointers
- * should be reset or not.
- */
- AidlMessageQueue(const MQDescriptor<T, U>& desc, bool resetPointers = true);
- ~AidlMessageQueue() = default;
-
- /**
- * This constructor uses Ashmem shared memory to create an FMQ
- * that can contain a maximum of 'numElementsInQueue' elements of type T.
- *
- * @param numElementsInQueue Capacity of the AidlMessageQueue in terms of T.
- * @param configureEventFlagWord Boolean that specifies if memory should
- * also be allocated and mapped for an EventFlag word.
- * @param bufferFd User-supplied file descriptor to map the memory for the ringbuffer
- * By default, bufferFd=-1 means library will allocate ashmem region for ringbuffer.
- * MessageQueue takes ownership of the file descriptor.
- * @param bufferSize size of buffer in bytes that bufferFd represents. This
- * size must be larger than or equal to (numElementsInQueue * sizeof(T)).
- * Otherwise, operations will cause out-of-bounds memory access.
- */
- AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord,
- android::base::unique_fd bufferFd, size_t bufferSize);
-
- AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord = false)
- : AidlMessageQueue(numElementsInQueue, configureEventFlagWord, android::base::unique_fd(),
- 0) {}
-
- MQDescriptor<T, U> dupeDesc();
-
- private:
- AidlMessageQueue(const AidlMessageQueue& other) = delete;
- AidlMessageQueue& operator=(const AidlMessageQueue& other) = delete;
- AidlMessageQueue() = delete;
-};
-
-template <typename T, typename U>
-AidlMessageQueue<T, U>::AidlMessageQueue(const MQDescriptor<T, U>& desc, bool resetPointers)
- : MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>(Descriptor(desc),
- resetPointers) {}
-
-template <typename T, typename U>
-AidlMessageQueue<T, U>::AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord,
- android::base::unique_fd bufferFd, size_t bufferSize)
- : MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>(
- numElementsInQueue, configureEventFlagWord, std::move(bufferFd), bufferSize) {}
-
-template <typename T, typename U>
-MQDescriptor<T, U> AidlMessageQueue<T, U>::dupeDesc() {
- auto* shim = MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>::getDesc();
- if (shim) {
- std::vector<aidl::android::hardware::common::fmq::GrantorDescriptor> grantors;
- for (const auto& grantor : shim->grantors()) {
- grantors.push_back(aidl::android::hardware::common::fmq::GrantorDescriptor{
- .fdIndex = static_cast<int32_t>(grantor.fdIndex),
- .offset = static_cast<int32_t>(grantor.offset),
- .extent = static_cast<int64_t>(grantor.extent)});
- }
- std::vector<ndk::ScopedFileDescriptor> fds;
- std::vector<int> ints;
- int data_index = 0;
- for (; data_index < shim->handle()->numFds; data_index++) {
- fds.push_back(ndk::ScopedFileDescriptor(dup(shim->handle()->data[data_index])));
- }
- for (; data_index < shim->handle()->numFds + shim->handle()->numInts; data_index++) {
- ints.push_back(shim->handle()->data[data_index]);
- }
- return MQDescriptor<T, U>{
- .quantum = static_cast<int32_t>(shim->getQuantum()),
- .grantors = grantors,
- .flags = static_cast<int32_t>(shim->getFlags()),
- .handle = {std::move(fds), std::move(ints)},
- };
- } else {
- return MQDescriptor<T, U>();
- }
-}
-
-} // namespace android
diff --git a/include/fmq/ConvertMQDescriptors.h b/include/fmq/ConvertMQDescriptors.h
deleted file mode 100644
index 6534ebe..0000000
--- a/include/fmq/ConvertMQDescriptors.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-#pragma once
-
-#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
-#include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h>
-#include <aidl/android/hardware/common/fmq/UnsynchronizedWrite.h>
-#include <cutils/native_handle.h>
-#include <fmq/AidlMessageQueue.h>
-#include <hidl/MQDescriptor.h>
-
-namespace android {
-using aidl::android::hardware::common::fmq::GrantorDescriptor;
-using aidl::android::hardware::common::fmq::MQDescriptor;
-using hardware::details::logError;
-
-/**
- * This function converts a HIDL hardware::MQDescriptor to an AIDL
- * aidl::android::hardware::common::fmq::MQDescriptor for Fast
- * Message Queue.
- *
- * This is considered UNSAFE because it is not checking the offsets of each of the
- * paylod types' fields. In order for these objects to be passed through shared memory safely,
- * they must have the exact same memory layout. Same size, same alignment, and same
- * offsets for each field. Make sure this is the case before using this!
- * Same sized C++ fundamental types and enums with same sized backing types are OK.
- * Ex 1: uint64_t is compatible with int64_t
- * Ex 2:
- * @FixedSize parcelable Foo {
- * int a;
- * long b;
- * MyEnum c; // backed by int32_t
- * }
- * struct Bar {
- * int a;
- * long b;
- * YourEnum c; // backed by uint32_t
- * }
- * The two types above are compatible with each other as long as the fields have
- * the same offsets.
- *
- * Template params:
- * HidlPayload - the type of the payload used for the HIDL MessageQueue
- * AidlPayload - the type of the payload used for the AIDL AidlMessageQueue
- * AidlFlavor - the flavor of the queues. Either SynchronizedReadWrite,
- * or UnsynchronizedWrite
- * Function params:
- * hidlDesc - reference to the HIDL MQDescriptor to be copied from
- * aidlDesc - pointer to the AIDL MQDescriptor to be copied to
- */
-template <typename HidlPayload, typename AidlPayload, typename AidlFlavor>
-bool unsafeHidlToAidlMQDescriptor(
- const hardware::MQDescriptor<HidlPayload, FlavorTypeToValue<AidlFlavor>::value>& hidlDesc,
- MQDescriptor<AidlPayload, AidlFlavor>* aidlDesc) {
- static_assert(sizeof(HidlPayload) == sizeof(AidlPayload),
- "Payload types are definitely incompatible");
- static_assert(alignof(HidlPayload) == alignof(AidlPayload),
- "Payload types are definitely incompatible");
- STATIC_AIDL_TYPE_CHECK(AidlPayload);
- if (!aidlDesc->grantors.empty()) {
- logError("Destination AIDL MQDescriptor should be empty, but already contains grantors.");
- return false;
- }
-
- for (const auto& grantor : hidlDesc.grantors()) {
- if (static_cast<int32_t>(grantor.offset) < 0 || static_cast<int64_t>(grantor.extent) < 0 ||
- static_cast<int64_t>(grantor.fdIndex) < 0) {
- logError(
- "Unsafe static_cast of grantor fields. Either the hardware::MQDescriptor is "
- "invalid, or the MessageQueue is too large to be described by AIDL.");
- return false;
- }
- aidlDesc->grantors.push_back(
- GrantorDescriptor{.fdIndex = static_cast<int32_t>(grantor.fdIndex),
- .offset = static_cast<int32_t>(grantor.offset),
- .extent = static_cast<int64_t>(grantor.extent)});
- }
-
- std::vector<ndk::ScopedFileDescriptor> fds;
- std::vector<int> ints;
- int data_index = 0;
- for (; data_index < hidlDesc.handle()->numFds; data_index++) {
- fds.push_back(ndk::ScopedFileDescriptor(dup(hidlDesc.handle()->data[data_index])));
- }
- for (; data_index < hidlDesc.handle()->numFds + hidlDesc.handle()->numInts; data_index++) {
- ints.push_back(hidlDesc.handle()->data[data_index]);
- }
-
- aidlDesc->handle = {std::move(fds), std::move(ints)};
- if (static_cast<int32_t>(hidlDesc.getQuantum()) < 0 ||
- static_cast<int32_t>(hidlDesc.getFlags()) < 0) {
- logError(
- "Unsafe static_cast of quantum or flags. Either the hardware::MQDescriptor is "
- "invalid, or the MessageQueue is too large to be described by AIDL.");
- return false;
- }
- if (hidlDesc.getFlags() != FlavorTypeToValue<AidlFlavor>::value) {
- logError("hardware::MQDescriptor hidlDesc is invalid. Unexpected getFlags() value: " +
- std::to_string(hidlDesc.getFlags()) +
- ". Expected value: " + std::to_string(FlavorTypeToValue<AidlFlavor>::value));
- return false;
- }
- aidlDesc->quantum = static_cast<int32_t>(hidlDesc.getQuantum());
- aidlDesc->flags = static_cast<int32_t>(hidlDesc.getFlags());
- return true;
-}
-
-} // namespace android
diff --git a/include/fmq/MessageQueue.h b/include/fmq/MessageQueue.h
index 8ff0739..97fee0e 100644
--- a/include/fmq/MessageQueue.h
+++ b/include/fmq/MessageQueue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,20 +14,38 @@
* limitations under the License.
*/
-#pragma once
+#ifndef HIDL_MQ_H
+#define HIDL_MQ_H
+#include <atomic>
+#include <cutils/ashmem.h>
+#include <fmq/EventFlag.h>
#include <hidl/MQDescriptor.h>
-#include "MessageQueueBase.h"
+#include <new>
+#include <sys/mman.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
namespace android {
namespace hardware {
+namespace details {
+void check(bool exp);
+void logError(const std::string &message);
+} // namespace details
+
template <typename T, MQFlavor flavor>
-struct MessageQueue final : public MessageQueueBase<MQDescriptor, T, flavor> {
+struct MessageQueue {
typedef MQDescriptor<T, flavor> Descriptor;
- MessageQueue(const Descriptor& Desc, bool resetPointers = true)
- : MessageQueueBase<MQDescriptor, T, flavor>(Desc, resetPointers) {}
- ~MessageQueue() = default;
+
+ /**
+ * @param Desc MQDescriptor describing the FMQ.
+ * @param resetPointers bool indicating whether the read/write pointers
+ * should be reset or not.
+ */
+ MessageQueue(const Descriptor& Desc, bool resetPointers = true);
+
+ ~MessageQueue();
/**
* This constructor uses Ashmem shared memory to create an FMQ
@@ -36,27 +54,1177 @@
* @param numElementsInQueue Capacity of the MessageQueue in terms of T.
* @param configureEventFlagWord Boolean that specifies if memory should
* also be allocated and mapped for an EventFlag word.
- * @param bufferFd User-supplied file descriptor to map the memory for the ringbuffer
- * By default, bufferFd=-1 means library will allocate ashmem region for ringbuffer.
- * MessageQueue takes ownership of the file descriptor.
- * @param bufferSize size of buffer in bytes that bufferFd represents. This
- * size must be larger than or equal to (numElementsInQueue * sizeof(T)).
- * Otherwise, operations will cause out-of-bounds memory access.
*/
- MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord,
- android::base::unique_fd bufferFd, size_t bufferSize)
- : MessageQueueBase<MQDescriptor, T, flavor>(numElementsInQueue, configureEventFlagWord,
- std::move(bufferFd), bufferSize) {}
+ MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord = false);
- MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord = false)
- : MessageQueueBase<MQDescriptor, T, flavor>(numElementsInQueue, configureEventFlagWord,
- android::base::unique_fd(), 0) {}
+ /**
+ * @return Number of items of type T that can be written into the FMQ
+ * without a read.
+ */
+ size_t availableToWrite() const;
- private:
+ /**
+ * @return Number of items of type T that are waiting to be read from the
+ * FMQ.
+ */
+ size_t availableToRead() const;
+
+ /**
+ * Returns the size of type T in bytes.
+ *
+ * @param Size of T.
+ */
+ size_t getQuantumSize() const;
+
+ /**
+ * Returns the size of the FMQ in terms of the size of type T.
+ *
+ * @return Number of items of type T that will fit in the FMQ.
+ */
+ size_t getQuantumCount() const;
+
+ /**
+ * @return Whether the FMQ is configured correctly.
+ */
+ bool isValid() const;
+
+ /**
+ * Non-blocking write to FMQ.
+ *
+ * @param data Pointer to the object of type T to be written into the FMQ.
+ *
+ * @return Whether the write was successful.
+ */
+ bool write(const T* data);
+
+ /**
+ * Non-blocking read from FMQ.
+ *
+ * @param data Pointer to the memory where the object read from the FMQ is
+ * copied to.
+ *
+ * @return Whether the read was successful.
+ */
+ bool read(T* data);
+
+ /**
+ * Write some data into the FMQ without blocking.
+ *
+ * @param data Pointer to the array of items of type T.
+ * @param count Number of items in array.
+ *
+ * @return Whether the write was successful.
+ */
+ __attribute__((noinline)) bool write(const T* data, size_t count);
+
+ /**
+ * Perform a blocking write of 'count' items into the FMQ using EventFlags.
+ * Does not support partial writes.
+ *
+ * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
+ * associated with the FMQ and it is used in that case.
+ *
+ * The application code must ensure that 'evFlag' used by the
+ * reader(s)/writer is based upon the same EventFlag word.
+ *
+ * The method will return false without blocking if any of the following
+ * conditions are true:
+ * - If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
+ * - If the 'readNotification' bit mask is zero.
+ * - If 'count' is greater than the FMQ size.
+ *
+ * If the there is insufficient space available to write into it, the
+ * EventFlag bit mask 'readNotification' is is waited upon.
+ *
+ * This method should only be used with a MessageQueue of the flavor
+ * 'kSynchronizedReadWrite'.
+ *
+ * Upon a successful write, wake is called on 'writeNotification' (if
+ * non-zero).
+ *
+ * @param data Pointer to the array of items of type T.
+ * @param count Number of items in array.
+ * @param readNotification The EventFlag bit mask to wait on if there is not
+ * enough space in FMQ to write 'count' items.
+ * @param writeNotification The EventFlag bit mask to call wake on
+ * a successful write. No wake is called if 'writeNotification' is zero.
+ * @param timeOutNanos Number of nanoseconds after which the blocking
+ * write attempt is aborted.
+ * @param evFlag The EventFlag object to be used for blocking. If nullptr,
+ * it is checked whether the FMQ owns an EventFlag object and that is used
+ * for blocking instead.
+ *
+ * @return Whether the write was successful.
+ */
+ bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
+ uint32_t writeNotification, int64_t timeOutNanos = 0,
+ android::hardware::EventFlag* evFlag = nullptr);
+
+ bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);
+
+ /**
+ * Read some data from the FMQ without blocking.
+ *
+ * @param data Pointer to the array to which read data is to be written.
+ * @param count Number of items to be read.
+ *
+ * @return Whether the read was successful.
+ */
+ __attribute__((noinline)) bool read(T* data, size_t count);
+
+ /**
+ * Perform a blocking read operation of 'count' items from the FMQ. Does not
+ * perform a partial read.
+ *
+ * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
+ * associated with the FMQ and it is used in that case.
+ *
+ * The application code must ensure that 'evFlag' used by the
+ * reader(s)/writer is based upon the same EventFlag word.
+ *
+ * The method will return false without blocking if any of the following
+ * conditions are true:
+ * -If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
+ * -If the 'writeNotification' bit mask is zero.
+ * -If 'count' is greater than the FMQ size.
+ *
+ * This method should only be used with a MessageQueue of the flavor
+ * 'kSynchronizedReadWrite'.
+
+ * If FMQ does not contain 'count' items, the eventFlag bit mask
+ * 'writeNotification' is waited upon. Upon a successful read from the FMQ,
+ * wake is called on 'readNotification' (if non-zero).
+ *
+ * @param data Pointer to the array to which read data is to be written.
+ * @param count Number of items to be read.
+ * @param readNotification The EventFlag bit mask to call wake on after
+ * a successful read. No wake is called if 'readNotification' is zero.
+ * @param writeNotification The EventFlag bit mask to call a wait on
+ * if there is insufficient data in the FMQ to be read.
+ * @param timeOutNanos Number of nanoseconds after which the blocking
+ * read attempt is aborted.
+ * @param evFlag The EventFlag object to be used for blocking.
+ *
+ * @return Whether the read was successful.
+ */
+ bool readBlocking(T* data, size_t count, uint32_t readNotification,
+ uint32_t writeNotification, int64_t timeOutNanos = 0,
+ android::hardware::EventFlag* evFlag = nullptr);
+
+ bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);
+
+ /**
+ * Get a pointer to the MQDescriptor object that describes this FMQ.
+ *
+ * @return Pointer to the MQDescriptor associated with the FMQ.
+ */
+ const Descriptor* getDesc() const { return mDesc.get(); }
+
+ /**
+ * Get a pointer to the EventFlag word if there is one associated with this FMQ.
+ *
+ * @return Pointer to an EventFlag word, will return nullptr if not
+ * configured. This method does not transfer ownership. The EventFlag
+ * word will be unmapped by the MessageQueue destructor.
+ */
+ std::atomic<uint32_t>* getEventFlagWord() const { return mEvFlagWord; }
+
+ /**
+ * Describes a memory region in the FMQ.
+ */
+ struct MemRegion {
+ MemRegion() : MemRegion(nullptr, 0) {}
+
+ MemRegion(T* base, size_t size) : address(base), length(size) {}
+
+ MemRegion& operator=(const MemRegion &other) {
+ address = other.address;
+ length = other.length;
+ return *this;
+ }
+
+ /**
+ * Gets a pointer to the base address of the MemRegion.
+ */
+ inline T* getAddress() const { return address; }
+
+ /**
+ * Gets the length of the MemRegion. This would equal to the number
+ * of items of type T that can be read from/written into the MemRegion.
+ */
+ inline size_t getLength() const { return length; }
+
+ /**
+ * Gets the length of the MemRegion in bytes.
+ */
+ inline size_t getLengthInBytes() const { return length * sizeof(T); }
+
+ private:
+ /* Base address */
+ T* address;
+
+ /*
+ * Number of items of type T that can be written to/read from the base
+ * address.
+ */
+ size_t length;
+ };
+
+ /**
+ * Describes the memory regions to be used for a read or write.
+ * The struct contains two MemRegion objects since the FMQ is a ring
+ * buffer and a read or write operation can wrap around. A single message
+ * of type T will never be broken between the two MemRegions.
+ */
+ struct MemTransaction {
+ MemTransaction() : MemTransaction(MemRegion(), MemRegion()) {}
+
+ MemTransaction(const MemRegion& regionFirst, const MemRegion& regionSecond) :
+ first(regionFirst), second(regionSecond) {}
+
+ MemTransaction& operator=(const MemTransaction &other) {
+ first = other.first;
+ second = other.second;
+ return *this;
+ }
+
+ /**
+ * Helper method to calculate the address for a particular index for
+ * the MemTransaction object.
+ *
+ * @param idx Index of the slot to be read/written. If the
+ * MemTransaction object is representing the memory region to read/write
+ * N items of type T, the valid range of idx is between 0 and N-1.
+ *
+ * @return Pointer to the slot idx. Will be nullptr for an invalid idx.
+ */
+ T* getSlot(size_t idx);
+
+ /**
+ * Helper method to write 'nMessages' items of type T into the memory
+ * regions described by the object starting from 'startIdx'. This method
+ * uses memcpy() and is not to meant to be used for a zero copy operation.
+ * Partial writes are not supported.
+ *
+ * @param data Pointer to the source buffer.
+ * @param nMessages Number of items of type T.
+ * @param startIdx The slot number to begin the write from. If the
+ * MemTransaction object is representing the memory region to read/write
+ * N items of type T, the valid range of startIdx is between 0 and N-1;
+ *
+ * @return Whether the write operation of size 'nMessages' succeeded.
+ */
+ bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
+
+ /*
+ * Helper method to read 'nMessages' items of type T from the memory
+ * regions described by the object starting from 'startIdx'. This method uses
+ * memcpy() and is not meant to be used for a zero copy operation. Partial reads
+ * are not supported.
+ *
+ * @param data Pointer to the destination buffer.
+ * @param nMessages Number of items of type T.
+ * @param startIdx The slot number to begin the read from. If the
+ * MemTransaction object is representing the memory region to read/write
+ * N items of type T, the valid range of startIdx is between 0 and N-1.
+ *
+ * @return Whether the read operation of size 'nMessages' succeeded.
+ */
+ bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
+
+ /**
+ * Returns a const reference to the first MemRegion in the
+ * MemTransaction object.
+ */
+ inline const MemRegion& getFirstRegion() const { return first; }
+
+ /**
+ * Returns a const reference to the second MemRegion in the
+ * MemTransaction object.
+ */
+ inline const MemRegion& getSecondRegion() const { return second; }
+
+ private:
+ /*
+ * Given a start index and the number of messages to be
+ * read/written, this helper method calculates the
+ * number of messages that should should be written to both the first
+ * and second MemRegions and the base addresses to be used for
+ * the read/write operation.
+ *
+ * Returns false if the 'startIdx' and 'nMessages' is
+ * invalid for the MemTransaction object.
+ */
+ bool inline getMemRegionInfo(size_t idx,
+ size_t nMessages,
+ size_t& firstCount,
+ size_t& secondCount,
+ T** firstBaseAddress,
+ T** secondBaseAddress);
+ MemRegion first;
+ MemRegion second;
+ };
+
+ /**
+ * Get a MemTransaction object to write 'nMessages' items of type T.
+ * Once the write is performed using the information from MemTransaction,
+ * the write operation is to be committed using a call to commitWrite().
+ *
+ * @param nMessages Number of messages of type T.
+ * @param Pointer to MemTransaction struct that describes memory to write 'nMessages'
+ * items of type T. If a write of size 'nMessages' is not possible, the base
+ * addresses in the MemTransaction object would be set to nullptr.
+ *
+ * @return Whether it is possible to write 'nMessages' items of type T
+ * into the FMQ.
+ */
+ __attribute__((always_inline)) bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
+
+ /**
+ * Commit a write of size 'nMessages'. To be only used after a call to beginWrite().
+ *
+ * @param nMessages number of messages of type T to be written.
+ *
+ * @return Whether the write operation of size 'nMessages' succeeded.
+ */
+ bool commitWrite(size_t nMessages);
+
+ /**
+ * Get a MemTransaction object to read 'nMessages' items of type T.
+ * Once the read is performed using the information from MemTransaction,
+ * the read operation is to be committed using a call to commitRead().
+ *
+ * @param nMessages Number of messages of type T.
+ * @param pointer to MemTransaction struct that describes memory to read 'nMessages'
+ * items of type T. If a read of size 'nMessages' is not possible, the base
+ * pointers in the MemTransaction object returned will be set to nullptr.
+ *
+ * @return bool Whether it is possible to read 'nMessages' items of type T
+ * from the FMQ.
+ */
+ __attribute__((always_inline)) bool beginRead(size_t nMessages, MemTransaction* memTx) const;
+
+ /**
+ * Commit a read of size 'nMessages'. To be only used after a call to beginRead().
+ * For the unsynchronized flavor of FMQ, this method will return a failure
+ * if a write overflow happened after beginRead() was invoked.
+ *
+ * @param nMessages number of messages of type T to be read.
+ *
+ * @return bool Whether the read operation of size 'nMessages' succeeded.
+ */
+ bool commitRead(size_t nMessages);
+
+private:
+
+ size_t availableToWriteBytes() const;
+ size_t availableToReadBytes() const;
+
MessageQueue(const MessageQueue& other) = delete;
MessageQueue& operator=(const MessageQueue& other) = delete;
- MessageQueue() = delete;
+ MessageQueue();
+
+ void* mapGrantorDescr(uint32_t grantorIdx);
+ void unmapGrantorDescr(void* address, uint32_t grantorIdx);
+ void initMemory(bool resetPointers);
+
+ enum DefaultEventNotification : uint32_t {
+ /*
+ * These are only used internally by the readBlocking()/writeBlocking()
+ * methods and hence once other bit combinations are not required.
+ */
+ FMQ_NOT_FULL = 0x01,
+ FMQ_NOT_EMPTY = 0x02
+ };
+
+ std::unique_ptr<Descriptor> mDesc;
+ uint8_t* mRing = nullptr;
+ /*
+ * TODO(b/31550092): Change to 32 bit read and write pointer counters.
+ */
+ std::atomic<uint64_t>* mReadPtr = nullptr;
+ std::atomic<uint64_t>* mWritePtr = nullptr;
+
+ std::atomic<uint32_t>* mEvFlagWord = nullptr;
+
+ /*
+ * This EventFlag object will be owned by the FMQ and will have the same
+ * lifetime.
+ */
+ android::hardware::EventFlag* mEventFlag = nullptr;
};
+template <typename T, MQFlavor flavor>
+T* MessageQueue<T, flavor>::MemTransaction::getSlot(size_t idx) {
+ size_t firstRegionLength = first.getLength();
+ size_t secondRegionLength = second.getLength();
+
+ if (idx > firstRegionLength + secondRegionLength) {
+ return nullptr;
+ }
+
+ if (idx < firstRegionLength) {
+ return first.getAddress() + idx;
+ }
+
+ return second.getAddress() + idx - firstRegionLength;
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::MemTransaction::getMemRegionInfo(size_t startIdx,
+ size_t nMessages,
+ size_t& firstCount,
+ size_t& secondCount,
+ T** firstBaseAddress,
+ T** secondBaseAddress) {
+ size_t firstRegionLength = first.getLength();
+ size_t secondRegionLength = second.getLength();
+
+ if (startIdx + nMessages > firstRegionLength + secondRegionLength) {
+ /*
+ * Return false if 'nMessages' starting at 'startIdx' cannot be
+ * accomodated by the MemTransaction object.
+ */
+ return false;
+ }
+
+ /* Number of messages to be read/written to the first MemRegion. */
+ firstCount = startIdx < firstRegionLength ?
+ std::min(nMessages, firstRegionLength - startIdx) : 0;
+
+ /* Number of messages to be read/written to the second MemRegion. */
+ secondCount = nMessages - firstCount;
+
+ if (firstCount != 0) {
+ *firstBaseAddress = first.getAddress() + startIdx;
+ }
+
+ if (secondCount != 0) {
+ size_t secondStartIdx = startIdx > firstRegionLength ? startIdx - firstRegionLength : 0;
+ *secondBaseAddress = second.getAddress() + secondStartIdx;
+ }
+
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::MemTransaction::copyFrom(T* data, size_t startIdx, size_t nMessages) {
+ if (data == nullptr) {
+ return false;
+ }
+
+ size_t firstReadCount = 0, secondReadCount = 0;
+ T* firstBaseAddress = nullptr, * secondBaseAddress = nullptr;
+
+ if (getMemRegionInfo(startIdx,
+ nMessages,
+ firstReadCount,
+ secondReadCount,
+ &firstBaseAddress,
+ &secondBaseAddress) == false) {
+ /*
+ * Returns false if 'startIdx' and 'nMessages' are invalid for this
+ * MemTransaction object.
+ */
+ return false;
+ }
+
+ if (firstReadCount != 0) {
+ memcpy(data, firstBaseAddress, firstReadCount * sizeof(T));
+ }
+
+ if (secondReadCount != 0) {
+ memcpy(data + firstReadCount,
+ secondBaseAddress,
+ secondReadCount * sizeof(T));
+ }
+
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::MemTransaction::copyTo(const T* data,
+ size_t startIdx,
+ size_t nMessages) {
+ if (data == nullptr) {
+ return false;
+ }
+
+ size_t firstWriteCount = 0, secondWriteCount = 0;
+ T * firstBaseAddress = nullptr, * secondBaseAddress = nullptr;
+
+ if (getMemRegionInfo(startIdx,
+ nMessages,
+ firstWriteCount,
+ secondWriteCount,
+ &firstBaseAddress,
+ &secondBaseAddress) == false) {
+ /*
+ * Returns false if 'startIdx' and 'nMessages' are invalid for this
+ * MemTransaction object.
+ */
+ return false;
+ }
+
+ if (firstWriteCount != 0) {
+ memcpy(firstBaseAddress, data, firstWriteCount * sizeof(T));
+ }
+
+ if (secondWriteCount != 0) {
+ memcpy(secondBaseAddress,
+ data + firstWriteCount,
+ secondWriteCount * sizeof(T));
+ }
+
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+void MessageQueue<T, flavor>::initMemory(bool resetPointers) {
+ /*
+ * Verify that the the Descriptor contains the minimum number of grantors
+ * the native_handle is valid and T matches quantum size.
+ */
+ if ((mDesc == nullptr) || !mDesc->isHandleValid() ||
+ (mDesc->countGrantors() < Descriptor::kMinGrantorCount) ||
+ (mDesc->getQuantum() != sizeof(T))) {
+ return;
+ }
+
+ if (flavor == kSynchronizedReadWrite) {
+ mReadPtr = reinterpret_cast<std::atomic<uint64_t>*>(
+ mapGrantorDescr(Descriptor::READPTRPOS));
+ } else {
+ /*
+ * The unsynchronized write flavor of the FMQ may have multiple readers
+ * and each reader would have their own read pointer counter.
+ */
+ mReadPtr = new (std::nothrow) std::atomic<uint64_t>;
+ }
+
+ details::check(mReadPtr != nullptr);
+
+ mWritePtr =
+ reinterpret_cast<std::atomic<uint64_t>*>(mapGrantorDescr(Descriptor::WRITEPTRPOS));
+ details::check(mWritePtr != nullptr);
+
+ if (resetPointers) {
+ mReadPtr->store(0, std::memory_order_release);
+ mWritePtr->store(0, std::memory_order_release);
+ } else if (flavor != kSynchronizedReadWrite) {
+ // Always reset the read pointer.
+ mReadPtr->store(0, std::memory_order_release);
+ }
+
+ mRing = reinterpret_cast<uint8_t*>(mapGrantorDescr(Descriptor::DATAPTRPOS));
+ details::check(mRing != nullptr);
+
+ mEvFlagWord = static_cast<std::atomic<uint32_t>*>(mapGrantorDescr(Descriptor::EVFLAGWORDPOS));
+ if (mEvFlagWord != nullptr) {
+ android::hardware::EventFlag::createEventFlag(mEvFlagWord, &mEventFlag);
+ }
+}
+
+template <typename T, MQFlavor flavor>
+MessageQueue<T, flavor>::MessageQueue(const Descriptor& Desc, bool resetPointers) {
+ mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(Desc));
+ if (mDesc == nullptr) {
+ return;
+ }
+
+ initMemory(resetPointers);
+}
+
+template <typename T, MQFlavor flavor>
+MessageQueue<T, flavor>::MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord) {
+
+ // Check if the buffer size would not overflow size_t
+ if (numElementsInQueue > SIZE_MAX / sizeof(T)) {
+ return;
+ }
+ /*
+ * The FMQ needs to allocate memory for the ringbuffer as well as for the
+ * read and write pointer counters. If an EventFlag word is to be configured,
+ * we also need to allocate memory for the same/
+ */
+ size_t kQueueSizeBytes = numElementsInQueue * sizeof(T);
+ size_t kMetaDataSize = 2 * sizeof(android::hardware::RingBufferPosition);
+
+ if (configureEventFlagWord) {
+ kMetaDataSize+= sizeof(std::atomic<uint32_t>);
+ }
+
+ /*
+ * Ashmem memory region size needs to be specified in page-aligned bytes.
+ * kQueueSizeBytes needs to be aligned to word boundary so that all offsets
+ * in the grantorDescriptor will be word aligned.
+ */
+ size_t kAshmemSizePageAligned =
+ (Descriptor::alignToWordBoundary(kQueueSizeBytes) + kMetaDataSize + PAGE_SIZE - 1) &
+ ~(PAGE_SIZE - 1);
+
+ /*
+ * Create an ashmem region to map the memory for the ringbuffer,
+ * read counter and write counter.
+ */
+ int ashmemFd = ashmem_create_region("MessageQueue", kAshmemSizePageAligned);
+ ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
+
+ /*
+ * The native handle will contain the fds to be mapped.
+ */
+ native_handle_t* mqHandle =
+ native_handle_create(1 /* numFds */, 0 /* numInts */);
+ if (mqHandle == nullptr) {
+ return;
+ }
+
+ mqHandle->data[0] = ashmemFd;
+ mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(kQueueSizeBytes,
+ mqHandle,
+ sizeof(T),
+ configureEventFlagWord));
+ if (mDesc == nullptr) {
+ return;
+ }
+ initMemory(true);
+}
+
+template <typename T, MQFlavor flavor>
+MessageQueue<T, flavor>::~MessageQueue() {
+ if (flavor == kUnsynchronizedWrite) {
+ delete mReadPtr;
+ } else {
+ unmapGrantorDescr(mReadPtr, Descriptor::READPTRPOS);
+ }
+ if (mWritePtr != nullptr) {
+ unmapGrantorDescr(mWritePtr, Descriptor::WRITEPTRPOS);
+ }
+ if (mRing != nullptr) {
+ unmapGrantorDescr(mRing, Descriptor::DATAPTRPOS);
+ }
+ if (mEvFlagWord != nullptr) {
+ unmapGrantorDescr(mEvFlagWord, Descriptor::EVFLAGWORDPOS);
+ android::hardware::EventFlag::deleteEventFlag(&mEventFlag);
+ }
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::write(const T* data) {
+ return write(data, 1);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::read(T* data) {
+ return read(data, 1);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::write(const T* data, size_t nMessages) {
+ MemTransaction tx;
+ return beginWrite(nMessages, &tx) &&
+ tx.copyTo(data, 0 /* startIdx */, nMessages) &&
+ commitWrite(nMessages);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::writeBlocking(const T* data,
+ size_t count,
+ uint32_t readNotification,
+ uint32_t writeNotification,
+ int64_t timeOutNanos,
+ android::hardware::EventFlag* evFlag) {
+ static_assert(flavor == kSynchronizedReadWrite,
+ "writeBlocking can only be used with the "
+ "kSynchronizedReadWrite flavor.");
+ /*
+ * If evFlag is null and the FMQ does not have its own EventFlag object
+ * return false;
+ * If the flavor is kSynchronizedReadWrite and the readNotification
+ * bit mask is zero return false;
+ * If the count is greater than queue size, return false
+ * to prevent blocking until timeOut.
+ */
+ if (evFlag == nullptr) {
+ evFlag = mEventFlag;
+ if (evFlag == nullptr) {
+ details::logError(
+ "writeBlocking failed: called on MessageQueue with no Eventflag"
+ "configured or provided");
+ return false;
+ }
+ }
+
+ if (readNotification == 0 || (count > getQuantumCount())) {
+ return false;
+ }
+
+ /*
+ * There is no need to wait for a readNotification if there is sufficient
+ * space to write is already present in the FMQ. The latter would be the case when
+ * read operations read more number of messages than write operations write.
+ * In other words, a single large read may clear the FMQ after multiple small
+ * writes. This would fail to clear a pending readNotification bit since
+ * EventFlag bits can only be cleared by a wait() call, however the bit would
+ * be correctly cleared by the next writeBlocking() call.
+ */
+
+ bool result = write(data, count);
+ if (result) {
+ if (writeNotification) {
+ evFlag->wake(writeNotification);
+ }
+ return result;
+ }
+
+ bool shouldTimeOut = timeOutNanos != 0;
+ int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
+
+ while (true) {
+ /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
+ if (shouldTimeOut) {
+ /*
+ * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
+ * to Nanoseconds)
+ */
+ int64_t currentTimeNs = android::elapsedRealtimeNano();
+ /*
+ * Decrement 'timeOutNanos' to account for the time taken to complete the last
+ * iteration of the while loop.
+ */
+ timeOutNanos -= currentTimeNs - prevTimeNanos;
+ prevTimeNanos = currentTimeNs;
+
+ if (timeOutNanos <= 0) {
+ /*
+ * Attempt write in case a context switch happened outside of
+ * evFlag->wait().
+ */
+ result = write(data, count);
+ break;
+ }
+ }
+
+ /*
+ * wait() will return immediately if there was a pending read
+ * notification.
+ */
+ uint32_t efState = 0;
+ status_t status = evFlag->wait(readNotification,
+ &efState,
+ timeOutNanos,
+ true /* retry on spurious wake */);
+
+ if (status != android::TIMED_OUT && status != android::NO_ERROR) {
+ details::logError("Unexpected error code from EventFlag Wait status " + std::to_string(status));
+ break;
+ }
+
+ if (status == android::TIMED_OUT) {
+ break;
+ }
+
+ /*
+ * If there is still insufficient space to write to the FMQ,
+ * keep waiting for another readNotification.
+ */
+ if ((efState & readNotification) && write(data, count)) {
+ result = true;
+ break;
+ }
+ }
+
+ if (result && writeNotification != 0) {
+ evFlag->wake(writeNotification);
+ }
+
+ return result;
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::writeBlocking(const T* data,
+ size_t count,
+ int64_t timeOutNanos) {
+ return writeBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::readBlocking(T* data,
+ size_t count,
+ uint32_t readNotification,
+ uint32_t writeNotification,
+ int64_t timeOutNanos,
+ android::hardware::EventFlag* evFlag) {
+ static_assert(flavor == kSynchronizedReadWrite,
+ "readBlocking can only be used with the "
+ "kSynchronizedReadWrite flavor.");
+
+ /*
+ * If evFlag is null and the FMQ does not own its own EventFlag object
+ * return false;
+ * If the writeNotification bit mask is zero return false;
+ * If the count is greater than queue size, return false to prevent
+ * blocking until timeOut.
+ */
+ if (evFlag == nullptr) {
+ evFlag = mEventFlag;
+ if (evFlag == nullptr) {
+ details::logError(
+ "readBlocking failed: called on MessageQueue with no Eventflag"
+ "configured or provided");
+ return false;
+ }
+ }
+
+ if (writeNotification == 0 || count > getQuantumCount()) {
+ return false;
+ }
+
+ /*
+ * There is no need to wait for a write notification if sufficient
+ * data to read is already present in the FMQ. This would be the
+ * case when read operations read lesser number of messages than
+ * a write operation and multiple reads would be required to clear the queue
+ * after a single write operation. This check would fail to clear a pending
+ * writeNotification bit since EventFlag bits can only be cleared
+ * by a wait() call, however the bit would be correctly cleared by the next
+ * readBlocking() call.
+ */
+
+ bool result = read(data, count);
+ if (result) {
+ if (readNotification) {
+ evFlag->wake(readNotification);
+ }
+ return result;
+ }
+
+ bool shouldTimeOut = timeOutNanos != 0;
+ int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
+
+ while (true) {
+ /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
+ if (shouldTimeOut) {
+ /*
+ * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
+ * to Nanoseconds)
+ */
+ int64_t currentTimeNs = android::elapsedRealtimeNano();
+ /*
+ * Decrement 'timeOutNanos' to account for the time taken to complete the last
+ * iteration of the while loop.
+ */
+ timeOutNanos -= currentTimeNs - prevTimeNanos;
+ prevTimeNanos = currentTimeNs;
+
+ if (timeOutNanos <= 0) {
+ /*
+ * Attempt read in case a context switch happened outside of
+ * evFlag->wait().
+ */
+ result = read(data, count);
+ break;
+ }
+ }
+
+ /*
+ * wait() will return immediately if there was a pending write
+ * notification.
+ */
+ uint32_t efState = 0;
+ status_t status = evFlag->wait(writeNotification,
+ &efState,
+ timeOutNanos,
+ true /* retry on spurious wake */);
+
+ if (status != android::TIMED_OUT && status != android::NO_ERROR) {
+ details::logError("Unexpected error code from EventFlag Wait status " + std::to_string(status));
+ break;
+ }
+
+ if (status == android::TIMED_OUT) {
+ break;
+ }
+
+ /*
+ * If the data in FMQ is still insufficient, go back to waiting
+ * for another write notification.
+ */
+ if ((efState & writeNotification) && read(data, count)) {
+ result = true;
+ break;
+ }
+ }
+
+ if (result && readNotification != 0) {
+ evFlag->wake(readNotification);
+ }
+ return result;
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::readBlocking(T* data, size_t count, int64_t timeOutNanos) {
+ return readBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::availableToWriteBytes() const {
+ return mDesc->getSize() - availableToReadBytes();
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::availableToWrite() const {
+ return availableToWriteBytes() / sizeof(T);
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::availableToRead() const {
+ return availableToReadBytes() / sizeof(T);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::beginWrite(size_t nMessages, MemTransaction* result) const {
+ /*
+ * If nMessages is greater than size of FMQ or in case of the synchronized
+ * FMQ flavor, if there is not enough space to write nMessages, then return
+ * result with null addresses.
+ */
+ if ((flavor == kSynchronizedReadWrite && (availableToWrite() < nMessages)) ||
+ nMessages > getQuantumCount()) {
+ *result = MemTransaction();
+ return false;
+ }
+
+ auto writePtr = mWritePtr->load(std::memory_order_relaxed);
+ if (writePtr % sizeof(T) != 0) {
+ hardware::details::logError(
+ "The write pointer has become misaligned. Writing to the queue is no longer "
+ "possible.");
+ return false;
+ }
+ size_t writeOffset = writePtr % mDesc->getSize();
+
+ /*
+ * From writeOffset, the number of messages that can be written
+ * contiguously without wrapping around the ring buffer are calculated.
+ */
+ size_t contiguousMessages = (mDesc->getSize() - writeOffset) / sizeof(T);
+
+ if (contiguousMessages < nMessages) {
+ /*
+ * Wrap around is required. Both result.first and result.second are
+ * populated.
+ */
+ *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + writeOffset),
+ contiguousMessages),
+ MemRegion(reinterpret_cast<T*>(mRing),
+ nMessages - contiguousMessages));
+ } else {
+ /*
+ * A wrap around is not required to write nMessages. Only result.first
+ * is populated.
+ */
+ *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + writeOffset), nMessages),
+ MemRegion());
+ }
+
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+/*
+ * Disable integer sanitization since integer overflow here is allowed
+ * and legal.
+ */
+__attribute__((no_sanitize("integer")))
+bool MessageQueue<T, flavor>::commitWrite(size_t nMessages) {
+ size_t nBytesWritten = nMessages * sizeof(T);
+ auto writePtr = mWritePtr->load(std::memory_order_relaxed);
+ writePtr += nBytesWritten;
+ mWritePtr->store(writePtr, std::memory_order_release);
+ /*
+ * This method cannot fail now since we are only incrementing the writePtr
+ * counter.
+ */
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::availableToReadBytes() const {
+ /*
+ * This method is invoked by implementations of both read() and write() and
+ * hence requries a memory_order_acquired load for both mReadPtr and
+ * mWritePtr.
+ */
+ return mWritePtr->load(std::memory_order_acquire) -
+ mReadPtr->load(std::memory_order_acquire);
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::read(T* data, size_t nMessages) {
+ MemTransaction tx;
+ return beginRead(nMessages, &tx) &&
+ tx.copyFrom(data, 0 /* startIdx */, nMessages) &&
+ commitRead(nMessages);
+}
+
+template <typename T, MQFlavor flavor>
+/*
+ * Disable integer sanitization since integer overflow here is allowed
+ * and legal.
+ */
+__attribute__((no_sanitize("integer")))
+bool MessageQueue<T, flavor>::beginRead(size_t nMessages, MemTransaction* result) const {
+ *result = MemTransaction();
+ /*
+ * If it is detected that the data in the queue was overwritten
+ * due to the reader process being too slow, the read pointer counter
+ * is set to the same as the write pointer counter to indicate error
+ * and the read returns false;
+ * Need acquire/release memory ordering for mWritePtr.
+ */
+ auto writePtr = mWritePtr->load(std::memory_order_acquire);
+ /*
+ * A relaxed load is sufficient for mReadPtr since there will be no
+ * stores to mReadPtr from a different thread.
+ */
+ auto readPtr = mReadPtr->load(std::memory_order_relaxed);
+ if (writePtr % sizeof(T) != 0 || readPtr % sizeof(T) != 0) {
+ hardware::details::logError(
+ "The write or read pointer has become misaligned. Reading from the queue is no "
+ "longer possible.");
+ return false;
+ }
+
+ if (writePtr - readPtr > mDesc->getSize()) {
+ mReadPtr->store(writePtr, std::memory_order_release);
+ return false;
+ }
+
+ size_t nBytesDesired = nMessages * sizeof(T);
+ /*
+ * Return if insufficient data to read in FMQ.
+ */
+ if (writePtr - readPtr < nBytesDesired) {
+ return false;
+ }
+
+ size_t readOffset = readPtr % mDesc->getSize();
+ /*
+ * From readOffset, the number of messages that can be read contiguously
+ * without wrapping around the ring buffer are calculated.
+ */
+ size_t contiguousMessages = (mDesc->getSize() - readOffset) / sizeof(T);
+
+ if (contiguousMessages < nMessages) {
+ /*
+ * A wrap around is required. Both result.first and result.second
+ * are populated.
+ */
+ *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + readOffset),
+ contiguousMessages),
+ MemRegion(reinterpret_cast<T*>(mRing),
+ nMessages - contiguousMessages));
+ } else {
+ /*
+ * A wrap around is not required. Only result.first need to be
+ * populated.
+ */
+ *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + readOffset), nMessages),
+ MemRegion());
+ }
+
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+/*
+ * Disable integer sanitization since integer overflow here is allowed
+ * and legal.
+ */
+__attribute__((no_sanitize("integer")))
+bool MessageQueue<T, flavor>::commitRead(size_t nMessages) {
+ // TODO: Use a local copy of readPtr to avoid relazed mReadPtr loads.
+ auto readPtr = mReadPtr->load(std::memory_order_relaxed);
+ auto writePtr = mWritePtr->load(std::memory_order_acquire);
+ /*
+ * If the flavor is unsynchronized, it is possible that a write overflow may
+ * have occured between beginRead() and commitRead().
+ */
+ if (writePtr - readPtr > mDesc->getSize()) {
+ mReadPtr->store(writePtr, std::memory_order_release);
+ return false;
+ }
+
+ size_t nBytesRead = nMessages * sizeof(T);
+ readPtr += nBytesRead;
+ mReadPtr->store(readPtr, std::memory_order_release);
+ return true;
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::getQuantumSize() const {
+ return mDesc->getQuantum();
+}
+
+template <typename T, MQFlavor flavor>
+size_t MessageQueue<T, flavor>::getQuantumCount() const {
+ return mDesc->getSize() / mDesc->getQuantum();
+}
+
+template <typename T, MQFlavor flavor>
+bool MessageQueue<T, flavor>::isValid() const {
+ return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
+}
+
+template <typename T, MQFlavor flavor>
+void* MessageQueue<T, flavor>::mapGrantorDescr(uint32_t grantorIdx) {
+ const native_handle_t* handle = mDesc->handle();
+ auto grantors = mDesc->grantors();
+ if (handle == nullptr) {
+ details::logError("mDesc->handle is null");
+ return nullptr;
+ }
+
+ if (grantorIdx >= grantors.size()) {
+ details::logError(std::string("grantorIdx must be less than ") +
+ std::to_string(grantors.size()));
+ return nullptr;
+ }
+
+ int fdIndex = grantors[grantorIdx].fdIndex;
+ /*
+ * Offset for mmap must be a multiple of PAGE_SIZE.
+ */
+ int mapOffset = (grantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
+ int mapLength =
+ grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;
+
+ void* address = mmap(0, mapLength, PROT_READ | PROT_WRITE, MAP_SHARED,
+ handle->data[fdIndex], mapOffset);
+ if (address == MAP_FAILED) {
+ details::logError(std::string("mmap failed: ") + std::to_string(errno));
+ return nullptr;
+ }
+ return reinterpret_cast<uint8_t*>(address) + (grantors[grantorIdx].offset - mapOffset);
+}
+
+template <typename T, MQFlavor flavor>
+void MessageQueue<T, flavor>::unmapGrantorDescr(void* address,
+ uint32_t grantorIdx) {
+ auto grantors = mDesc->grantors();
+ if ((address == nullptr) || (grantorIdx >= grantors.size())) {
+ return;
+ }
+
+ int mapOffset = (grantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
+ int mapLength =
+ grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;
+ void* baseAddress = reinterpret_cast<uint8_t*>(address) -
+ (grantors[grantorIdx].offset - mapOffset);
+ if (baseAddress) munmap(baseAddress, mapLength);
+}
+
} // namespace hardware
} // namespace android
+#endif // HIDL_MQ_H
diff --git a/include/fmq/MessageQueueBase.h b/include/fmq/MessageQueueBase.h
deleted file mode 100644
index b932317..0000000
--- a/include/fmq/MessageQueueBase.h
+++ /dev/null
@@ -1,1300 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <android-base/unique_fd.h>
-#include <cutils/ashmem.h>
-#include <fmq/EventFlag.h>
-#include <sys/mman.h>
-#include <sys/user.h>
-#include <utils/Log.h>
-#include <utils/SystemClock.h>
-#include <atomic>
-#include <new>
-
-using android::hardware::kSynchronizedReadWrite;
-using android::hardware::kUnsynchronizedWrite;
-using android::hardware::MQFlavor;
-
-namespace android {
-
-template <template <typename, MQFlavor> class MQDescriptorType, typename T, MQFlavor flavor>
-struct MessageQueueBase {
- typedef MQDescriptorType<T, flavor> Descriptor;
-
- /**
- * @param Desc MQDescriptor describing the FMQ.
- * @param resetPointers bool indicating whether the read/write pointers
- * should be reset or not.
- */
- MessageQueueBase(const Descriptor& Desc, bool resetPointers = true);
-
- ~MessageQueueBase();
-
- /**
- * This constructor uses Ashmem shared memory to create an FMQ
- * that can contain a maximum of 'numElementsInQueue' elements of type T.
- *
- * @param numElementsInQueue Capacity of the MessageQueue in terms of T.
- * @param configureEventFlagWord Boolean that specifies if memory should
- * also be allocated and mapped for an EventFlag word.
- * @param bufferFd User-supplied file descriptor to map the memory for the ringbuffer
- * By default, bufferFd=-1 means library will allocate ashmem region for ringbuffer.
- * MessageQueue takes ownership of the file descriptor.
- * @param bufferSize size of buffer in bytes that bufferFd represents. This
- * size must be larger than or equal to (numElementsInQueue * sizeof(T)).
- * Otherwise, operations will cause out-of-bounds memory access.
- */
-
- MessageQueueBase(size_t numElementsInQueue, bool configureEventFlagWord,
- android::base::unique_fd bufferFd, size_t bufferSize);
-
- MessageQueueBase(size_t numElementsInQueue, bool configureEventFlagWord = false)
- : MessageQueueBase(numElementsInQueue, configureEventFlagWord, android::base::unique_fd(),
- 0) {}
-
- /**
- * @return Number of items of type T that can be written into the FMQ
- * without a read.
- */
- size_t availableToWrite() const;
-
- /**
- * @return Number of items of type T that are waiting to be read from the
- * FMQ.
- */
- size_t availableToRead() const;
-
- /**
- * Returns the size of type T in bytes.
- *
- * @param Size of T.
- */
- size_t getQuantumSize() const;
-
- /**
- * Returns the size of the FMQ in terms of the size of type T.
- *
- * @return Number of items of type T that will fit in the FMQ.
- */
- size_t getQuantumCount() const;
-
- /**
- * @return Whether the FMQ is configured correctly.
- */
- bool isValid() const;
-
- /**
- * Non-blocking write to FMQ.
- *
- * @param data Pointer to the object of type T to be written into the FMQ.
- *
- * @return Whether the write was successful.
- */
- bool write(const T* data);
-
- /**
- * Non-blocking read from FMQ.
- *
- * @param data Pointer to the memory where the object read from the FMQ is
- * copied to.
- *
- * @return Whether the read was successful.
- */
- bool read(T* data);
-
- /**
- * Write some data into the FMQ without blocking.
- *
- * @param data Pointer to the array of items of type T.
- * @param count Number of items in array.
- *
- * @return Whether the write was successful.
- */
- bool write(const T* data, size_t count);
-
- /**
- * Perform a blocking write of 'count' items into the FMQ using EventFlags.
- * Does not support partial writes.
- *
- * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
- * associated with the FMQ and it is used in that case.
- *
- * The application code must ensure that 'evFlag' used by the
- * reader(s)/writer is based upon the same EventFlag word.
- *
- * The method will return false without blocking if any of the following
- * conditions are true:
- * - If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
- * - If the 'readNotification' bit mask is zero.
- * - If 'count' is greater than the FMQ size.
- *
- * If the there is insufficient space available to write into it, the
- * EventFlag bit mask 'readNotification' is is waited upon.
- *
- * This method should only be used with a MessageQueue of the flavor
- * 'kSynchronizedReadWrite'.
- *
- * Upon a successful write, wake is called on 'writeNotification' (if
- * non-zero).
- *
- * @param data Pointer to the array of items of type T.
- * @param count Number of items in array.
- * @param readNotification The EventFlag bit mask to wait on if there is not
- * enough space in FMQ to write 'count' items.
- * @param writeNotification The EventFlag bit mask to call wake on
- * a successful write. No wake is called if 'writeNotification' is zero.
- * @param timeOutNanos Number of nanoseconds after which the blocking
- * write attempt is aborted.
- * @param evFlag The EventFlag object to be used for blocking. If nullptr,
- * it is checked whether the FMQ owns an EventFlag object and that is used
- * for blocking instead.
- *
- * @return Whether the write was successful.
- */
- bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
- uint32_t writeNotification, int64_t timeOutNanos = 0,
- android::hardware::EventFlag* evFlag = nullptr);
-
- bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);
-
- /**
- * Read some data from the FMQ without blocking.
- *
- * @param data Pointer to the array to which read data is to be written.
- * @param count Number of items to be read.
- *
- * @return Whether the read was successful.
- */
- bool read(T* data, size_t count);
-
- /**
- * Perform a blocking read operation of 'count' items from the FMQ. Does not
- * perform a partial read.
- *
- * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
- * associated with the FMQ and it is used in that case.
- *
- * The application code must ensure that 'evFlag' used by the
- * reader(s)/writer is based upon the same EventFlag word.
- *
- * The method will return false without blocking if any of the following
- * conditions are true:
- * -If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
- * -If the 'writeNotification' bit mask is zero.
- * -If 'count' is greater than the FMQ size.
- *
- * This method should only be used with a MessageQueue of the flavor
- * 'kSynchronizedReadWrite'.
-
- * If FMQ does not contain 'count' items, the eventFlag bit mask
- * 'writeNotification' is waited upon. Upon a successful read from the FMQ,
- * wake is called on 'readNotification' (if non-zero).
- *
- * @param data Pointer to the array to which read data is to be written.
- * @param count Number of items to be read.
- * @param readNotification The EventFlag bit mask to call wake on after
- * a successful read. No wake is called if 'readNotification' is zero.
- * @param writeNotification The EventFlag bit mask to call a wait on
- * if there is insufficient data in the FMQ to be read.
- * @param timeOutNanos Number of nanoseconds after which the blocking
- * read attempt is aborted.
- * @param evFlag The EventFlag object to be used for blocking.
- *
- * @return Whether the read was successful.
- */
- bool readBlocking(T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
- int64_t timeOutNanos = 0, android::hardware::EventFlag* evFlag = nullptr);
-
- bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);
-
- /**
- * Get a pointer to the MQDescriptor object that describes this FMQ.
- *
- * @return Pointer to the MQDescriptor associated with the FMQ.
- */
- const Descriptor* getDesc() const { return mDesc.get(); }
-
- /**
- * Get a pointer to the EventFlag word if there is one associated with this FMQ.
- *
- * @return Pointer to an EventFlag word, will return nullptr if not
- * configured. This method does not transfer ownership. The EventFlag
- * word will be unmapped by the MessageQueue destructor.
- */
- std::atomic<uint32_t>* getEventFlagWord() const { return mEvFlagWord; }
-
- /**
- * Describes a memory region in the FMQ.
- */
- struct MemRegion {
- MemRegion() : MemRegion(nullptr, 0) {}
-
- MemRegion(T* base, size_t size) : address(base), length(size) {}
-
- MemRegion& operator=(const MemRegion& other) {
- address = other.address;
- length = other.length;
- return *this;
- }
-
- /**
- * Gets a pointer to the base address of the MemRegion.
- */
- inline T* getAddress() const { return address; }
-
- /**
- * Gets the length of the MemRegion. This would equal to the number
- * of items of type T that can be read from/written into the MemRegion.
- */
- inline size_t getLength() const { return length; }
-
- /**
- * Gets the length of the MemRegion in bytes.
- */
- inline size_t getLengthInBytes() const { return length * sizeof(T); }
-
- private:
- /* Base address */
- T* address;
-
- /*
- * Number of items of type T that can be written to/read from the base
- * address.
- */
- size_t length;
- };
-
- /**
- * Describes the memory regions to be used for a read or write.
- * The struct contains two MemRegion objects since the FMQ is a ring
- * buffer and a read or write operation can wrap around. A single message
- * of type T will never be broken between the two MemRegions.
- */
- struct MemTransaction {
- MemTransaction() : MemTransaction(MemRegion(), MemRegion()) {}
-
- MemTransaction(const MemRegion& regionFirst, const MemRegion& regionSecond)
- : first(regionFirst), second(regionSecond) {}
-
- MemTransaction& operator=(const MemTransaction& other) {
- first = other.first;
- second = other.second;
- return *this;
- }
-
- /**
- * Helper method to calculate the address for a particular index for
- * the MemTransaction object.
- *
- * @param idx Index of the slot to be read/written. If the
- * MemTransaction object is representing the memory region to read/write
- * N items of type T, the valid range of idx is between 0 and N-1.
- *
- * @return Pointer to the slot idx. Will be nullptr for an invalid idx.
- */
- T* getSlot(size_t idx);
-
- /**
- * Helper method to write 'nMessages' items of type T into the memory
- * regions described by the object starting from 'startIdx'. This method
- * uses memcpy() and is not to meant to be used for a zero copy operation.
- * Partial writes are not supported.
- *
- * @param data Pointer to the source buffer.
- * @param nMessages Number of items of type T.
- * @param startIdx The slot number to begin the write from. If the
- * MemTransaction object is representing the memory region to read/write
- * N items of type T, the valid range of startIdx is between 0 and N-1;
- *
- * @return Whether the write operation of size 'nMessages' succeeded.
- */
- bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
-
- /*
- * Helper method to read 'nMessages' items of type T from the memory
- * regions described by the object starting from 'startIdx'. This method uses
- * memcpy() and is not meant to be used for a zero copy operation. Partial reads
- * are not supported.
- *
- * @param data Pointer to the destination buffer.
- * @param nMessages Number of items of type T.
- * @param startIdx The slot number to begin the read from. If the
- * MemTransaction object is representing the memory region to read/write
- * N items of type T, the valid range of startIdx is between 0 and N-1.
- *
- * @return Whether the read operation of size 'nMessages' succeeded.
- */
- bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
-
- /**
- * Returns a const reference to the first MemRegion in the
- * MemTransaction object.
- */
- inline const MemRegion& getFirstRegion() const { return first; }
-
- /**
- * Returns a const reference to the second MemRegion in the
- * MemTransaction object.
- */
- inline const MemRegion& getSecondRegion() const { return second; }
-
- private:
- /*
- * Given a start index and the number of messages to be
- * read/written, this helper method calculates the
- * number of messages that should should be written to both the first
- * and second MemRegions and the base addresses to be used for
- * the read/write operation.
- *
- * Returns false if the 'startIdx' and 'nMessages' is
- * invalid for the MemTransaction object.
- */
- bool inline getMemRegionInfo(size_t idx, size_t nMessages, size_t& firstCount,
- size_t& secondCount, T** firstBaseAddress,
- T** secondBaseAddress);
- MemRegion first;
- MemRegion second;
- };
-
- /**
- * Get a MemTransaction object to write 'nMessages' items of type T.
- * Once the write is performed using the information from MemTransaction,
- * the write operation is to be committed using a call to commitWrite().
- *
- * @param nMessages Number of messages of type T.
- * @param Pointer to MemTransaction struct that describes memory to write 'nMessages'
- * items of type T. If a write of size 'nMessages' is not possible, the base
- * addresses in the MemTransaction object would be set to nullptr.
- *
- * @return Whether it is possible to write 'nMessages' items of type T
- * into the FMQ.
- */
- bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
-
- /**
- * Commit a write of size 'nMessages'. To be only used after a call to beginWrite().
- *
- * @param nMessages number of messages of type T to be written.
- *
- * @return Whether the write operation of size 'nMessages' succeeded.
- */
- bool commitWrite(size_t nMessages);
-
- /**
- * Get a MemTransaction object to read 'nMessages' items of type T.
- * Once the read is performed using the information from MemTransaction,
- * the read operation is to be committed using a call to commitRead().
- *
- * @param nMessages Number of messages of type T.
- * @param pointer to MemTransaction struct that describes memory to read 'nMessages'
- * items of type T. If a read of size 'nMessages' is not possible, the base
- * pointers in the MemTransaction object returned will be set to nullptr.
- *
- * @return bool Whether it is possible to read 'nMessages' items of type T
- * from the FMQ.
- */
- bool beginRead(size_t nMessages, MemTransaction* memTx) const;
-
- /**
- * Commit a read of size 'nMessages'. To be only used after a call to beginRead().
- * For the unsynchronized flavor of FMQ, this method will return a failure
- * if a write overflow happened after beginRead() was invoked.
- *
- * @param nMessages number of messages of type T to be read.
- *
- * @return bool Whether the read operation of size 'nMessages' succeeded.
- */
- bool commitRead(size_t nMessages);
-
- private:
- size_t availableToWriteBytes() const;
- size_t availableToReadBytes() const;
-
- MessageQueueBase(const MessageQueueBase& other) = delete;
- MessageQueueBase& operator=(const MessageQueueBase& other) = delete;
-
- void* mapGrantorDescr(uint32_t grantorIdx);
- void unmapGrantorDescr(void* address, uint32_t grantorIdx);
- void initMemory(bool resetPointers);
-
- enum DefaultEventNotification : uint32_t {
- /*
- * These are only used internally by the readBlocking()/writeBlocking()
- * methods and hence once other bit combinations are not required.
- */
- FMQ_NOT_FULL = 0x01,
- FMQ_NOT_EMPTY = 0x02
- };
- std::unique_ptr<Descriptor> mDesc;
- uint8_t* mRing = nullptr;
- /*
- * TODO(b/31550092): Change to 32 bit read and write pointer counters.
- */
- std::atomic<uint64_t>* mReadPtr = nullptr;
- std::atomic<uint64_t>* mWritePtr = nullptr;
-
- std::atomic<uint32_t>* mEvFlagWord = nullptr;
-
- /*
- * This EventFlag object will be owned by the FMQ and will have the same
- * lifetime.
- */
- android::hardware::EventFlag* mEventFlag = nullptr;
-};
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-T* MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::getSlot(size_t idx) {
- size_t firstRegionLength = first.getLength();
- size_t secondRegionLength = second.getLength();
-
- if (idx > firstRegionLength + secondRegionLength) {
- return nullptr;
- }
-
- if (idx < firstRegionLength) {
- return first.getAddress() + idx;
- }
-
- return second.getAddress() + idx - firstRegionLength;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::getMemRegionInfo(
- size_t startIdx, size_t nMessages, size_t& firstCount, size_t& secondCount,
- T** firstBaseAddress, T** secondBaseAddress) {
- size_t firstRegionLength = first.getLength();
- size_t secondRegionLength = second.getLength();
-
- if (startIdx + nMessages > firstRegionLength + secondRegionLength) {
- /*
- * Return false if 'nMessages' starting at 'startIdx' cannot be
- * accommodated by the MemTransaction object.
- */
- return false;
- }
-
- /* Number of messages to be read/written to the first MemRegion. */
- firstCount =
- startIdx < firstRegionLength ? std::min(nMessages, firstRegionLength - startIdx) : 0;
-
- /* Number of messages to be read/written to the second MemRegion. */
- secondCount = nMessages - firstCount;
-
- if (firstCount != 0) {
- *firstBaseAddress = first.getAddress() + startIdx;
- }
-
- if (secondCount != 0) {
- size_t secondStartIdx = startIdx > firstRegionLength ? startIdx - firstRegionLength : 0;
- *secondBaseAddress = second.getAddress() + secondStartIdx;
- }
-
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyFrom(T* data,
- size_t startIdx,
- size_t nMessages) {
- if (data == nullptr) {
- return false;
- }
-
- size_t firstReadCount = 0, secondReadCount = 0;
- T *firstBaseAddress = nullptr, *secondBaseAddress = nullptr;
-
- if (getMemRegionInfo(startIdx, nMessages, firstReadCount, secondReadCount, &firstBaseAddress,
- &secondBaseAddress) == false) {
- /*
- * Returns false if 'startIdx' and 'nMessages' are invalid for this
- * MemTransaction object.
- */
- return false;
- }
-
- if (firstReadCount != 0) {
- memcpy(data, firstBaseAddress, firstReadCount * sizeof(T));
- }
-
- if (secondReadCount != 0) {
- memcpy(data + firstReadCount, secondBaseAddress, secondReadCount * sizeof(T));
- }
-
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyTo(const T* data,
- size_t startIdx,
- size_t nMessages) {
- if (data == nullptr) {
- return false;
- }
-
- size_t firstWriteCount = 0, secondWriteCount = 0;
- T *firstBaseAddress = nullptr, *secondBaseAddress = nullptr;
-
- if (getMemRegionInfo(startIdx, nMessages, firstWriteCount, secondWriteCount, &firstBaseAddress,
- &secondBaseAddress) == false) {
- /*
- * Returns false if 'startIdx' and 'nMessages' are invalid for this
- * MemTransaction object.
- */
- return false;
- }
-
- if (firstWriteCount != 0) {
- memcpy(firstBaseAddress, data, firstWriteCount * sizeof(T));
- }
-
- if (secondWriteCount != 0) {
- memcpy(secondBaseAddress, data + firstWriteCount, secondWriteCount * sizeof(T));
- }
-
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-void MessageQueueBase<MQDescriptorType, T, flavor>::initMemory(bool resetPointers) {
- /*
- * Verify that the Descriptor contains the minimum number of grantors
- * the native_handle is valid and T matches quantum size.
- */
- if ((mDesc == nullptr) || !mDesc->isHandleValid() ||
- (mDesc->countGrantors() < hardware::details::kMinGrantorCount)) {
- return;
- }
- if (mDesc->getQuantum() != sizeof(T)) {
- hardware::details::logError(
- "Payload size differs between the queue instantiation and the "
- "MQDescriptor.");
- return;
- }
-
- const auto& grantors = mDesc->grantors();
- for (const auto& grantor : grantors) {
- if (hardware::details::isAlignedToWordBoundary(grantor.offset) == false) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "Grantor offsets need to be aligned");
-#endif
- }
- }
-
- if (flavor == kSynchronizedReadWrite) {
- mReadPtr = reinterpret_cast<std::atomic<uint64_t>*>(
- mapGrantorDescr(hardware::details::READPTRPOS));
- } else {
- /*
- * The unsynchronized write flavor of the FMQ may have multiple readers
- * and each reader would have their own read pointer counter.
- */
- mReadPtr = new (std::nothrow) std::atomic<uint64_t>;
- }
- if (mReadPtr == nullptr) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "mReadPtr is null");
-#endif
- }
-
- mWritePtr = reinterpret_cast<std::atomic<uint64_t>*>(
- mapGrantorDescr(hardware::details::WRITEPTRPOS));
- if (mWritePtr == nullptr) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "mWritePtr is null");
-#endif
- }
-
- if (resetPointers) {
- mReadPtr->store(0, std::memory_order_release);
- mWritePtr->store(0, std::memory_order_release);
- } else if (flavor != kSynchronizedReadWrite) {
- // Always reset the read pointer.
- mReadPtr->store(0, std::memory_order_release);
- }
-
- mRing = reinterpret_cast<uint8_t*>(mapGrantorDescr(hardware::details::DATAPTRPOS));
- if (mRing == nullptr) {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "mRing is null");
-#endif
- }
-
- if (mDesc->countGrantors() > hardware::details::EVFLAGWORDPOS) {
- mEvFlagWord = static_cast<std::atomic<uint32_t>*>(
- mapGrantorDescr(hardware::details::EVFLAGWORDPOS));
- if (mEvFlagWord != nullptr) {
- android::hardware::EventFlag::createEventFlag(mEvFlagWord, &mEventFlag);
- } else {
-#ifdef __BIONIC__
- __assert(__FILE__, __LINE__, "mEvFlagWord is null");
-#endif
- }
- }
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-MessageQueueBase<MQDescriptorType, T, flavor>::MessageQueueBase(const Descriptor& Desc,
- bool resetPointers) {
- mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(Desc));
- if (mDesc == nullptr) {
- return;
- }
-
- initMemory(resetPointers);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-MessageQueueBase<MQDescriptorType, T, flavor>::MessageQueueBase(size_t numElementsInQueue,
- bool configureEventFlagWord,
- android::base::unique_fd bufferFd,
- size_t bufferSize) {
- // Check if the buffer size would not overflow size_t
- if (numElementsInQueue > SIZE_MAX / sizeof(T)) {
- hardware::details::logError("Requested message queue size too large. Size of elements: " +
- std::to_string(sizeof(T)) +
- ". Number of elements: " + std::to_string(numElementsInQueue));
- return;
- }
- if (bufferFd != -1 && numElementsInQueue * sizeof(T) > bufferSize) {
- hardware::details::logError("The supplied buffer size(" + std::to_string(bufferSize) +
- ") is smaller than the required size(" +
- std::to_string(numElementsInQueue * sizeof(T)) + ").");
- return;
- }
- /*
- * The FMQ needs to allocate memory for the ringbuffer as well as for the
- * read and write pointer counters. If an EventFlag word is to be configured,
- * we also need to allocate memory for the same/
- */
- size_t kQueueSizeBytes = numElementsInQueue * sizeof(T);
- size_t kMetaDataSize = 2 * sizeof(android::hardware::details::RingBufferPosition);
-
- if (configureEventFlagWord) {
- kMetaDataSize += sizeof(std::atomic<uint32_t>);
- }
-
- /*
- * Ashmem memory region size needs to be specified in page-aligned bytes.
- * kQueueSizeBytes needs to be aligned to word boundary so that all offsets
- * in the grantorDescriptor will be word aligned.
- */
- size_t kAshmemSizePageAligned;
- if (bufferFd != -1) {
- // Allocate read counter and write counter only. User-supplied memory will be used for the
- // ringbuffer.
- kAshmemSizePageAligned = (kMetaDataSize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
- } else {
- // Allocate ringbuffer, read counter and write counter.
- kAshmemSizePageAligned = (hardware::details::alignToWordBoundary(kQueueSizeBytes) +
- kMetaDataSize + PAGE_SIZE - 1) &
- ~(PAGE_SIZE - 1);
- }
-
- /*
- * The native handle will contain the fds to be mapped.
- */
- int numFds = (bufferFd != -1) ? 2 : 1;
- native_handle_t* mqHandle = native_handle_create(numFds, 0 /* numInts */);
- if (mqHandle == nullptr) {
- return;
- }
-
- /*
- * Create an ashmem region to map the memory.
- */
- int ashmemFd = ashmem_create_region("MessageQueue", kAshmemSizePageAligned);
- ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
- mqHandle->data[0] = ashmemFd;
-
- if (bufferFd != -1) {
- // Use user-supplied file descriptor for fdIndex 1
- mqHandle->data[1] = bufferFd.get();
- // release ownership of fd. mqHandle owns it now.
- if (bufferFd.release() < 0) {
- hardware::details::logError("Error releasing supplied bufferFd");
- }
-
- std::vector<android::hardware::GrantorDescriptor> grantors;
- grantors.resize(configureEventFlagWord ? hardware::details::kMinGrantorCountForEvFlagSupport
- : hardware::details::kMinGrantorCount);
-
- size_t memSize[] = {
- sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for read
- pointer counter */
- sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for write
- pointer counter */
- kQueueSizeBytes, /* memory to be allocated for data buffer */
- sizeof(std::atomic<uint32_t>) /* memory to be allocated for EventFlag word */
- };
-
- for (size_t grantorPos = 0, offset = 0; grantorPos < grantors.size(); grantorPos++) {
- uint32_t grantorFdIndex;
- size_t grantorOffset;
- if (grantorPos == hardware::details::DATAPTRPOS) {
- grantorFdIndex = 1;
- grantorOffset = 0;
- } else {
- grantorFdIndex = 0;
- grantorOffset = offset;
- offset += memSize[grantorPos];
- }
- grantors[grantorPos] = {
- 0 /* grantor flags */, grantorFdIndex,
- static_cast<uint32_t>(hardware::details::alignToWordBoundary(grantorOffset)),
- memSize[grantorPos]};
- }
-
- mDesc = std::unique_ptr<Descriptor>(new (std::nothrow)
- Descriptor(grantors, mqHandle, sizeof(T)));
- } else {
- mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(
- kQueueSizeBytes, mqHandle, sizeof(T), configureEventFlagWord));
- }
- if (mDesc == nullptr) {
- native_handle_close(mqHandle);
- native_handle_delete(mqHandle);
- return;
- }
- initMemory(true);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-MessageQueueBase<MQDescriptorType, T, flavor>::~MessageQueueBase() {
- if (flavor == kUnsynchronizedWrite && mReadPtr != nullptr) {
- delete mReadPtr;
- } else if (mReadPtr != nullptr) {
- unmapGrantorDescr(mReadPtr, hardware::details::READPTRPOS);
- }
- if (mWritePtr != nullptr) {
- unmapGrantorDescr(mWritePtr, hardware::details::WRITEPTRPOS);
- }
- if (mRing != nullptr) {
- unmapGrantorDescr(mRing, hardware::details::DATAPTRPOS);
- }
- if (mEvFlagWord != nullptr) {
- unmapGrantorDescr(mEvFlagWord, hardware::details::EVFLAGWORDPOS);
- android::hardware::EventFlag::deleteEventFlag(&mEventFlag);
- }
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::write(const T* data) {
- return write(data, 1);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::read(T* data) {
- return read(data, 1);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::write(const T* data, size_t nMessages) {
- MemTransaction tx;
- return beginWrite(nMessages, &tx) && tx.copyTo(data, 0 /* startIdx */, nMessages) &&
- commitWrite(nMessages);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::writeBlocking(
- const T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
- int64_t timeOutNanos, android::hardware::EventFlag* evFlag) {
- static_assert(flavor == kSynchronizedReadWrite,
- "writeBlocking can only be used with the "
- "kSynchronizedReadWrite flavor.");
- /*
- * If evFlag is null and the FMQ does not have its own EventFlag object
- * return false;
- * If the flavor is kSynchronizedReadWrite and the readNotification
- * bit mask is zero return false;
- * If the count is greater than queue size, return false
- * to prevent blocking until timeOut.
- */
- if (evFlag == nullptr) {
- evFlag = mEventFlag;
- if (evFlag == nullptr) {
- hardware::details::logError(
- "writeBlocking failed: called on MessageQueue with no Eventflag"
- "configured or provided");
- return false;
- }
- }
-
- if (readNotification == 0 || (count > getQuantumCount())) {
- return false;
- }
-
- /*
- * There is no need to wait for a readNotification if there is sufficient
- * space to write is already present in the FMQ. The latter would be the case when
- * read operations read more number of messages than write operations write.
- * In other words, a single large read may clear the FMQ after multiple small
- * writes. This would fail to clear a pending readNotification bit since
- * EventFlag bits can only be cleared by a wait() call, however the bit would
- * be correctly cleared by the next writeBlocking() call.
- */
-
- bool result = write(data, count);
- if (result) {
- if (writeNotification) {
- evFlag->wake(writeNotification);
- }
- return result;
- }
-
- bool shouldTimeOut = timeOutNanos != 0;
- int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
-
- while (true) {
- /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
- if (shouldTimeOut) {
- /*
- * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
- * to Nanoseconds)
- */
- int64_t currentTimeNs = android::elapsedRealtimeNano();
- /*
- * Decrement 'timeOutNanos' to account for the time taken to complete the last
- * iteration of the while loop.
- */
- timeOutNanos -= currentTimeNs - prevTimeNanos;
- prevTimeNanos = currentTimeNs;
-
- if (timeOutNanos <= 0) {
- /*
- * Attempt write in case a context switch happened outside of
- * evFlag->wait().
- */
- result = write(data, count);
- break;
- }
- }
-
- /*
- * wait() will return immediately if there was a pending read
- * notification.
- */
- uint32_t efState = 0;
- status_t status = evFlag->wait(readNotification, &efState, timeOutNanos,
- true /* retry on spurious wake */);
-
- if (status != android::TIMED_OUT && status != android::NO_ERROR) {
- hardware::details::logError("Unexpected error code from EventFlag Wait status " +
- std::to_string(status));
- break;
- }
-
- if (status == android::TIMED_OUT) {
- break;
- }
-
- /*
- * If there is still insufficient space to write to the FMQ,
- * keep waiting for another readNotification.
- */
- if ((efState & readNotification) && write(data, count)) {
- result = true;
- break;
- }
- }
-
- if (result && writeNotification != 0) {
- evFlag->wake(writeNotification);
- }
-
- return result;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::writeBlocking(const T* data, size_t count,
- int64_t timeOutNanos) {
- return writeBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::readBlocking(
- T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
- int64_t timeOutNanos, android::hardware::EventFlag* evFlag) {
- static_assert(flavor == kSynchronizedReadWrite,
- "readBlocking can only be used with the "
- "kSynchronizedReadWrite flavor.");
-
- /*
- * If evFlag is null and the FMQ does not own its own EventFlag object
- * return false;
- * If the writeNotification bit mask is zero return false;
- * If the count is greater than queue size, return false to prevent
- * blocking until timeOut.
- */
- if (evFlag == nullptr) {
- evFlag = mEventFlag;
- if (evFlag == nullptr) {
- hardware::details::logError(
- "readBlocking failed: called on MessageQueue with no Eventflag"
- "configured or provided");
- return false;
- }
- }
-
- if (writeNotification == 0 || count > getQuantumCount()) {
- return false;
- }
-
- /*
- * There is no need to wait for a write notification if sufficient
- * data to read is already present in the FMQ. This would be the
- * case when read operations read lesser number of messages than
- * a write operation and multiple reads would be required to clear the queue
- * after a single write operation. This check would fail to clear a pending
- * writeNotification bit since EventFlag bits can only be cleared
- * by a wait() call, however the bit would be correctly cleared by the next
- * readBlocking() call.
- */
-
- bool result = read(data, count);
- if (result) {
- if (readNotification) {
- evFlag->wake(readNotification);
- }
- return result;
- }
-
- bool shouldTimeOut = timeOutNanos != 0;
- int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
-
- while (true) {
- /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
- if (shouldTimeOut) {
- /*
- * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
- * to Nanoseconds)
- */
- int64_t currentTimeNs = android::elapsedRealtimeNano();
- /*
- * Decrement 'timeOutNanos' to account for the time taken to complete the last
- * iteration of the while loop.
- */
- timeOutNanos -= currentTimeNs - prevTimeNanos;
- prevTimeNanos = currentTimeNs;
-
- if (timeOutNanos <= 0) {
- /*
- * Attempt read in case a context switch happened outside of
- * evFlag->wait().
- */
- result = read(data, count);
- break;
- }
- }
-
- /*
- * wait() will return immediately if there was a pending write
- * notification.
- */
- uint32_t efState = 0;
- status_t status = evFlag->wait(writeNotification, &efState, timeOutNanos,
- true /* retry on spurious wake */);
-
- if (status != android::TIMED_OUT && status != android::NO_ERROR) {
- hardware::details::logError("Unexpected error code from EventFlag Wait status " +
- std::to_string(status));
- break;
- }
-
- if (status == android::TIMED_OUT) {
- break;
- }
-
- /*
- * If the data in FMQ is still insufficient, go back to waiting
- * for another write notification.
- */
- if ((efState & writeNotification) && read(data, count)) {
- result = true;
- break;
- }
- }
-
- if (result && readNotification != 0) {
- evFlag->wake(readNotification);
- }
- return result;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::readBlocking(T* data, size_t count,
- int64_t timeOutNanos) {
- return readBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToWriteBytes() const {
- return mDesc->getSize() - availableToReadBytes();
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToWrite() const {
- return availableToWriteBytes() / sizeof(T);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToRead() const {
- return availableToReadBytes() / sizeof(T);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::beginWrite(size_t nMessages,
- MemTransaction* result) const {
- /*
- * If nMessages is greater than size of FMQ or in case of the synchronized
- * FMQ flavor, if there is not enough space to write nMessages, then return
- * result with null addresses.
- */
- if ((flavor == kSynchronizedReadWrite && (availableToWrite() < nMessages)) ||
- nMessages > getQuantumCount()) {
- *result = MemTransaction();
- return false;
- }
-
- auto writePtr = mWritePtr->load(std::memory_order_relaxed);
- if (writePtr % sizeof(T) != 0) {
- hardware::details::logError(
- "The write pointer has become misaligned. Writing to the queue is no longer "
- "possible.");
- hardware::details::errorWriteLog(0x534e4554, "184963385");
- return false;
- }
- size_t writeOffset = writePtr % mDesc->getSize();
-
- /*
- * From writeOffset, the number of messages that can be written
- * contiguously without wrapping around the ring buffer are calculated.
- */
- size_t contiguousMessages = (mDesc->getSize() - writeOffset) / sizeof(T);
-
- if (contiguousMessages < nMessages) {
- /*
- * Wrap around is required. Both result.first and result.second are
- * populated.
- */
- *result = MemTransaction(
- MemRegion(reinterpret_cast<T*>(mRing + writeOffset), contiguousMessages),
- MemRegion(reinterpret_cast<T*>(mRing), nMessages - contiguousMessages));
- } else {
- /*
- * A wrap around is not required to write nMessages. Only result.first
- * is populated.
- */
- *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + writeOffset), nMessages),
- MemRegion());
- }
-
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-/*
- * Disable integer sanitization since integer overflow here is allowed
- * and legal.
- */
-__attribute__((no_sanitize("integer"))) bool
-MessageQueueBase<MQDescriptorType, T, flavor>::commitWrite(size_t nMessages) {
- size_t nBytesWritten = nMessages * sizeof(T);
- auto writePtr = mWritePtr->load(std::memory_order_relaxed);
- writePtr += nBytesWritten;
- mWritePtr->store(writePtr, std::memory_order_release);
- /*
- * This method cannot fail now since we are only incrementing the writePtr
- * counter.
- */
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToReadBytes() const {
- /*
- * This method is invoked by implementations of both read() and write() and
- * hence requires a memory_order_acquired load for both mReadPtr and
- * mWritePtr.
- */
- return mWritePtr->load(std::memory_order_acquire) - mReadPtr->load(std::memory_order_acquire);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::read(T* data, size_t nMessages) {
- MemTransaction tx;
- return beginRead(nMessages, &tx) && tx.copyFrom(data, 0 /* startIdx */, nMessages) &&
- commitRead(nMessages);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-/*
- * Disable integer sanitization since integer overflow here is allowed
- * and legal.
- */
-__attribute__((no_sanitize("integer"))) bool
-MessageQueueBase<MQDescriptorType, T, flavor>::beginRead(size_t nMessages,
- MemTransaction* result) const {
- *result = MemTransaction();
- /*
- * If it is detected that the data in the queue was overwritten
- * due to the reader process being too slow, the read pointer counter
- * is set to the same as the write pointer counter to indicate error
- * and the read returns false;
- * Need acquire/release memory ordering for mWritePtr.
- */
- auto writePtr = mWritePtr->load(std::memory_order_acquire);
- /*
- * A relaxed load is sufficient for mReadPtr since there will be no
- * stores to mReadPtr from a different thread.
- */
- auto readPtr = mReadPtr->load(std::memory_order_relaxed);
- if (writePtr % sizeof(T) != 0 || readPtr % sizeof(T) != 0) {
- hardware::details::logError(
- "The write or read pointer has become misaligned. Reading from the queue is no "
- "longer possible.");
- hardware::details::errorWriteLog(0x534e4554, "184963385");
- return false;
- }
-
- if (writePtr - readPtr > mDesc->getSize()) {
- mReadPtr->store(writePtr, std::memory_order_release);
- return false;
- }
-
- size_t nBytesDesired = nMessages * sizeof(T);
- /*
- * Return if insufficient data to read in FMQ.
- */
- if (writePtr - readPtr < nBytesDesired) {
- return false;
- }
-
- size_t readOffset = readPtr % mDesc->getSize();
- /*
- * From readOffset, the number of messages that can be read contiguously
- * without wrapping around the ring buffer are calculated.
- */
- size_t contiguousMessages = (mDesc->getSize() - readOffset) / sizeof(T);
-
- if (contiguousMessages < nMessages) {
- /*
- * A wrap around is required. Both result.first and result.second
- * are populated.
- */
- *result = MemTransaction(
- MemRegion(reinterpret_cast<T*>(mRing + readOffset), contiguousMessages),
- MemRegion(reinterpret_cast<T*>(mRing), nMessages - contiguousMessages));
- } else {
- /*
- * A wrap around is not required. Only result.first need to be
- * populated.
- */
- *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + readOffset), nMessages),
- MemRegion());
- }
-
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-/*
- * Disable integer sanitization since integer overflow here is allowed
- * and legal.
- */
-__attribute__((no_sanitize("integer"))) bool
-MessageQueueBase<MQDescriptorType, T, flavor>::commitRead(size_t nMessages) {
- // TODO: Use a local copy of readPtr to avoid relazed mReadPtr loads.
- auto readPtr = mReadPtr->load(std::memory_order_relaxed);
- auto writePtr = mWritePtr->load(std::memory_order_acquire);
- /*
- * If the flavor is unsynchronized, it is possible that a write overflow may
- * have occurred between beginRead() and commitRead().
- */
- if (writePtr - readPtr > mDesc->getSize()) {
- mReadPtr->store(writePtr, std::memory_order_release);
- return false;
- }
-
- size_t nBytesRead = nMessages * sizeof(T);
- readPtr += nBytesRead;
- mReadPtr->store(readPtr, std::memory_order_release);
- return true;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::getQuantumSize() const {
- return mDesc->getQuantum();
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-size_t MessageQueueBase<MQDescriptorType, T, flavor>::getQuantumCount() const {
- return mDesc->getSize() / mDesc->getQuantum();
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-bool MessageQueueBase<MQDescriptorType, T, flavor>::isValid() const {
- return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-void* MessageQueueBase<MQDescriptorType, T, flavor>::mapGrantorDescr(uint32_t grantorIdx) {
- const native_handle_t* handle = mDesc->handle();
- auto grantors = mDesc->grantors();
- if (handle == nullptr) {
- hardware::details::logError("mDesc->handle is null");
- return nullptr;
- }
-
- if (grantorIdx >= grantors.size()) {
- hardware::details::logError(std::string("grantorIdx must be less than ") +
- std::to_string(grantors.size()));
- return nullptr;
- }
-
- int fdIndex = grantors[grantorIdx].fdIndex;
- /*
- * Offset for mmap must be a multiple of PAGE_SIZE.
- */
- int mapOffset = (grantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
- int mapLength = grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;
-
- void* address = mmap(0, mapLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->data[fdIndex],
- mapOffset);
- if (address == MAP_FAILED) {
- hardware::details::logError(std::string("mmap failed: ") + std::to_string(errno));
- return nullptr;
- }
- return reinterpret_cast<uint8_t*>(address) + (grantors[grantorIdx].offset - mapOffset);
-}
-
-template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
-void MessageQueueBase<MQDescriptorType, T, flavor>::unmapGrantorDescr(void* address,
- uint32_t grantorIdx) {
- auto grantors = mDesc->grantors();
- if ((address == nullptr) || (grantorIdx >= grantors.size())) {
- return;
- }
-
- int mapOffset = (grantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
- int mapLength = grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;
- void* baseAddress =
- reinterpret_cast<uint8_t*>(address) - (grantors[grantorIdx].offset - mapOffset);
- if (baseAddress) munmap(baseAddress, mapLength);
-}
-
-} // namespace hardware
diff --git a/tests/Android.bp b/tests/Android.bp
index ec38cea..b85aa16 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -14,36 +14,14 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-python_test_host {
- name: "fmq_test",
- main: "fmq_test.py",
- srcs: ["fmq_test.py"],
- test_config: "fmq_test.xml",
- target_required: [
- "android.hardware.tests.msgq@1.0-service-test",
- "fmq_test_client",
- ],
- test_suites: ["general-tests", "vts"],
- test_options: {
- unit_test: false,
- },
-}
-
cc_test {
- name: "fmq_test_client",
+ name: "mq_test_client",
srcs: ["msgq_test_client.cpp"],
cflags: [
"-Wall",
"-Werror",
],
- sanitize: {
- misc_undefined: ["integer"],
- },
shared_libs: [
"libbase",
@@ -52,39 +30,21 @@
"libhidlbase",
"liblog",
"libutils",
- "libbinder_ndk",
],
+ // Allow dlsym'ing self for statically linked passthrough implementations
+ ldflags: ["-rdynamic"],
+
// These are static libs only for testing purposes and portability. Shared
// libs should be used on device.
- static_libs: [
- "android.hardware.common-V2-ndk_platform",
- "android.hardware.common.fmq-V1-ndk_platform",
- "android.hardware.tests.msgq@1.0",
- "android.fmq.test-ndk_platform",
- ],
- whole_static_libs: [
- "android.hardware.tests.msgq@1.0-impl",
- "android.fmq.test-impl"
- ],
-
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
- test_suites: ["general-tests", "vts"],
- auto_gen_config: false,
+ static_libs: ["android.hardware.tests.msgq@1.0"],
+ whole_static_libs: ["android.hardware.tests.msgq@1.0-impl"],
}
cc_test {
- name: "fmq_unit_tests",
+ name: "mq_test",
- srcs: ["fmq_unit_tests.cpp"],
+ srcs: ["mq_test.cpp"],
shared_libs: [
"libbase",
"libcutils",
@@ -93,17 +53,9 @@
"liblog",
"libutils",
],
- static_libs: [
- "android.hardware.common.fmq-V1-ndk_platform",
- ],
cflags: [
"-Wall",
"-Werror",
],
- sanitize: {
- misc_undefined: ["integer"],
- },
- test_suites: ["general-tests"],
- auto_gen_config: true,
}
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..6efe7c2
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := fmq_test
+LOCAL_MODULE_CLASS := NATIVE_TESTS
+LOCAL_SRC_FILES := fmq_test
+LOCAL_REQUIRED_MODULES := \
+ mq_test_client \
+ android.hardware.tests.msgq@1.0-service-test \
+ hidl_test_helper
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest64
+
+ifneq ($(TARGET_2ND_ARCH),)
+LOCAL_REQUIRED_MODULES += android.hardware.tests.msgq@1.0-service-test$(TARGET_2ND_ARCH_MODULE_SUFFIX)
+LOCAL_REQUIRED_MODULES += mq_test_client$(TARGET_2ND_ARCH_MODULE_SUFFIX)
+endif
+
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsFmqUnitTests
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..9e5df01
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for VTS FMQ unit tests">
+ <option name="config-descriptor:metadata" key="plan" value="vts-library" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="push-group" value="HostDrivenTest.push" />
+ <option name="cleanup" value="true" />
+ <option name="push" value="DATA/nativetest/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test->/data/nativetest/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" />
+ <option name="push" value="DATA/nativetest64/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test->/data/nativetest64/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" />
+ <option name="push" value="DATA/nativetest/mq_test_client/mq_test_client->/data/nativetest/mq_test_client/mq_test_client" />
+ <option name="push" value="DATA/nativetest64/mq_test_client/mq_test_client->/data/nativetest64/mq_test_client/mq_test_client" />
+ <option name="push" value="DATA/nativetest64/hidl_test_helper->/data/nativetest64/hidl_test_helper" />
+ </target_preparer>
+ <multi_target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsFmqUnitTests" />
+ <option name="binary-test-type" value="binary_test" />
+ <option name="binary-test-source" value="DATA/nativetest64/fmq_test->/data/nativetest64/fmq_test" />
+ <option name="test-timeout" value="1m"/>
+ </test>
+</configuration>
diff --git a/tests/aidl/Android.bp b/tests/aidl/Android.bp
deleted file mode 100644
index e210fda..0000000
--- a/tests/aidl/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-aidl_interface {
- name: "android.fmq.test",
- host_supported: true,
- vendor_available: true,
- unstable: true,
- srcs: [
- "android/fmq/test/*.aidl",
- ],
- imports: [
- "android.hardware.common-V2",
- "android.hardware.common.fmq-V1",
- ],
- gen_trace: true,
- backend: {
- java: {
- enabled: false,
- },
- ndk: {
- enabled: true,
- },
- cpp: {
- enabled: false,
- }
- },
-}
diff --git a/tests/aidl/android/fmq/test/EventFlagBits.aidl b/tests/aidl/android/fmq/test/EventFlagBits.aidl
deleted file mode 100644
index 202a67c..0000000
--- a/tests/aidl/android/fmq/test/EventFlagBits.aidl
+++ /dev/null
@@ -1,9 +0,0 @@
-// FIXME: license file if you have one
-
-package android.fmq.test;
-
-@Backing(type="int")
-enum EventFlagBits {
- FMQ_NOT_EMPTY = 1 << 0,
- FMQ_NOT_FULL = 1 << 1,
-}
diff --git a/tests/aidl/android/fmq/test/FixedParcelable.aidl b/tests/aidl/android/fmq/test/FixedParcelable.aidl
deleted file mode 100644
index acb54f2..0000000
--- a/tests/aidl/android/fmq/test/FixedParcelable.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-package android.fmq.test;
-
-import android.fmq.test.EventFlagBits;
-
-@FixedSize
-parcelable FixedParcelable {
- int a;
- EventFlagBits b;
-}
diff --git a/tests/aidl/android/fmq/test/ITestAidlMsgQ.aidl b/tests/aidl/android/fmq/test/ITestAidlMsgQ.aidl
deleted file mode 100644
index 7f1f8b7..0000000
--- a/tests/aidl/android/fmq/test/ITestAidlMsgQ.aidl
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-package android.fmq.test;
-
-import android.hardware.common.fmq.MQDescriptor;
-import android.hardware.common.fmq.SynchronizedReadWrite;
-import android.hardware.common.fmq.UnsynchronizedWrite;
-
-/**
- * Test interface for MQDescriptor.
- */
-interface ITestAidlMsgQ {
- /**
- * This method requests the service to set up a synchronous read/write
- * wait-free FMQ using the input descriptor with the client as reader.
- *
- * @param mqDesc This structure describes the FMQ that was set up by the
- * client. Server uses this descriptor to set up a FMQ object at its end.
- *
- * @return True if the setup is successful.
- */
- boolean configureFmqSyncReadWrite(in MQDescriptor<int, SynchronizedReadWrite> mqDesc);
-
- /**
- * This method requests the service to return an MQDescriptor to
- * an unsynchronized FMQ set up by the server. If 'configureFmq' is
- * true, then the server sets up a new unsynchronized FMQ. This
- * method is to be used to test multiple reader processes.
- *
- * @param configureFmq The server sets up a new unsynchronized FMQ if
- * this parameter is true.
- * @param userFd True to initialize the message queue with a user supplied
- * file descriptor for the ring buffer.
- * False to let the message queue use a single FD for everything.
- *
- * @param out ret True if successful.
- * @param out mqDesc This structure describes the unsynchronized FMQ that was
- * set up by the service. Client can use it to set up the FMQ at its end.
- */
- boolean getFmqUnsyncWrite(in boolean configureFmq, in boolean userFd,
- out MQDescriptor<int, UnsynchronizedWrite> mqDesc);
-
- /**
- * This method requests the service to trigger a blocking read.
- *
- * @param count Number of messages to read.
- *
- */
- oneway void requestBlockingRead(in int count);
-
- /**
- * This method requests the service to trigger a blocking read using
- * default Event Flag notification bits defined by the MessageQueue class.
- *
- * @param count Number of messages to read.
- *
- */
- oneway void requestBlockingReadDefaultEventFlagBits(in int count);
-
- /**
- * This method requests the service to repeatedly trigger blocking reads.
- *
- * @param count Number of messages to read in a single blocking read.
- * @param numIter Number of blocking reads to trigger.
- *
- */
- oneway void requestBlockingReadRepeat(in int count, in int numIter);
-
- /**
- * This method request the service to read from the synchronized read/write
- * FMQ.
- *
- * @param count Number to messages to read.
- *
- * @return True if the read operation was successful.
- */
- boolean requestReadFmqSync(in int count);
-
- /**
- * This method request the service to read from the unsynchronized flavor of
- * FMQ.
- *
- * @param count Number to messages to read.
- *
- * @return Will be True if the read operation was successful.
- */
- boolean requestReadFmqUnsync(in int count);
-
- /**
- * This method request the service to write into the synchronized read/write
- * flavor of the FMQ.
- *
- * @param count Number to messages to write.
- *
- * @return True if the write operation was successful.
- */
- boolean requestWriteFmqSync(in int count);
-
- /**
- * This method request the service to write into the unsynchronized flavor
- * of FMQ.
- *
- * @param count Number to messages to write.
- *
- * @return True if the write operation was successful.
- */
- boolean requestWriteFmqUnsync(in int count);
-}
diff --git a/tests/aidl/default/Android.bp b/tests/aidl/default/Android.bp
deleted file mode 100644
index 71a5533..0000000
--- a/tests/aidl/default/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
- name: "android.fmq.test-impl",
- shared_libs: [
- "libbase",
- "libbinder_ndk",
- "libfmq",
- ],
- static_libs: [
- "android.fmq.test-ndk_platform",
- ],
- export_include_dirs: ["."],
- srcs: [
- "TestAidlMsgQ.cpp",
- ],
-}
diff --git a/tests/aidl/default/TestAidlMsgQ.cpp b/tests/aidl/default/TestAidlMsgQ.cpp
deleted file mode 100644
index 485e75b..0000000
--- a/tests/aidl/default/TestAidlMsgQ.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include "TestAidlMsgQ.h"
-
-namespace aidl {
-namespace android {
-namespace fmq {
-namespace test {
-
-// Methods from ::aidl::android::fmq::test::ITestAidlMsgQ follow.
-ndk::ScopedAStatus TestAidlMsgQ::configureFmqSyncReadWrite(
- const MQDescriptor<int32_t, SynchronizedReadWrite>& mqDesc, bool* _aidl_return) {
- mFmqSynchronized.reset(new (std::nothrow) TestAidlMsgQ::MessageQueueSync(mqDesc));
- if ((mFmqSynchronized == nullptr) || (mFmqSynchronized->isValid() == false)) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
- /*
- * Initialize the EventFlag word with bit FMQ_NOT_FULL.
- */
- auto evFlagWordPtr = mFmqSynchronized->getEventFlagWord();
- if (evFlagWordPtr != nullptr) {
- std::atomic_init(evFlagWordPtr, static_cast<uint32_t>(EventFlagBits::FMQ_NOT_FULL));
- }
- *_aidl_return = true;
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::getFmqUnsyncWrite(
- bool configureFmq, bool userFd, MQDescriptor<int32_t, UnsynchronizedWrite>* mqDesc,
- bool* _aidl_return) {
- if (configureFmq) {
- static constexpr size_t kNumElementsInQueue = 1024;
- static constexpr size_t kElementSizeBytes = sizeof(int32_t);
- ::android::base::unique_fd ringbufferFd;
- if (userFd) {
- ringbufferFd.reset(
- ::ashmem_create_region("UnsyncWrite", kNumElementsInQueue * kElementSizeBytes));
- }
- mFmqUnsynchronized.reset(new (std::nothrow) TestAidlMsgQ::MessageQueueUnsync(
- kNumElementsInQueue, false, std::move(ringbufferFd),
- kNumElementsInQueue * kElementSizeBytes));
- }
-
- if ((mFmqUnsynchronized == nullptr) || (mFmqUnsynchronized->isValid() == false) ||
- (mqDesc == nullptr)) {
- *_aidl_return = false;
- } else {
- *mqDesc = std::move(mFmqUnsynchronized->dupeDesc());
- *_aidl_return = true;
- }
-
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestBlockingRead(int32_t count) {
- std::vector<int32_t> data(count);
- bool result = mFmqSynchronized->readBlocking(
- &data[0], count, static_cast<uint32_t>(EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(EventFlagBits::FMQ_NOT_EMPTY), 5000000000 /* timeOutNanos */);
-
- if (result == false) {
- ALOGE("Blocking read fails");
- }
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestBlockingReadDefaultEventFlagBits(int32_t count) {
- std::vector<int32_t> data(count);
- bool result = mFmqSynchronized->readBlocking(&data[0], count);
-
- if (result == false) {
- ALOGE("Blocking read fails");
- }
-
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestBlockingReadRepeat(int32_t count, int32_t numIter) {
- std::vector<int32_t> data(count);
- for (int i = 0; i < numIter; i++) {
- bool result = mFmqSynchronized->readBlocking(
- &data[0], count, static_cast<uint32_t>(EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(EventFlagBits::FMQ_NOT_EMPTY), 5000000000 /* timeOutNanos */);
-
- if (result == false) {
- ALOGE("Blocking read fails");
- break;
- }
- }
-
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestReadFmqSync(int32_t count, bool* _aidl_return) {
- std::vector<int32_t> data(count);
- bool result = mFmqSynchronized->read(&data[0], count) && verifyData(&data[0], count);
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestReadFmqUnsync(int32_t count, bool* _aidl_return) {
- std::vector<int32_t> data(count);
- bool result = mFmqUnsynchronized->read(&data[0], count) && verifyData(&data[0], count);
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestWriteFmqSync(int32_t count, bool* _aidl_return) {
- std::vector<int32_t> data(count);
- for (int i = 0; i < count; i++) {
- data[i] = i;
- }
- bool result = mFmqSynchronized->write(&data[0], count);
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus TestAidlMsgQ::requestWriteFmqUnsync(int32_t count, bool* _aidl_return) {
- std::vector<int32_t> data(count);
- for (int i = 0; i < count; i++) {
- data[i] = i;
- }
- if (!mFmqUnsynchronized) {
- ALOGE("Unsynchronized queue is not configured.");
- *_aidl_return = false;
- return ndk::ScopedAStatus::ok();
- }
- bool result = mFmqUnsynchronized->write(&data[0], count);
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
-}
-
-} // namespace test
-} // namespace fmq
-} // namespace android
-} // namespace aidl
diff --git a/tests/aidl/default/TestAidlMsgQ.h b/tests/aidl/default/TestAidlMsgQ.h
deleted file mode 100644
index 6e7c859..0000000
--- a/tests/aidl/default/TestAidlMsgQ.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <aidl/android/fmq/test/BnTestAidlMsgQ.h>
-#include <aidl/android/fmq/test/EventFlagBits.h>
-#include <fmq/AidlMessageQueue.h>
-#include <fmq/EventFlag.h>
-
-namespace aidl {
-namespace android {
-namespace fmq {
-namespace test {
-
-using ::aidl::android::fmq::test::EventFlagBits;
-using ::aidl::android::fmq::test::ITestAidlMsgQ;
-
-using ::aidl::android::hardware::common::fmq::MQDescriptor;
-using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using ::aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using ::android::hardware::kSynchronizedReadWrite;
-using ::android::hardware::kUnsynchronizedWrite;
-using ::android::hardware::MQFlavor;
-
-using ::android::AidlMessageQueue;
-
-struct TestAidlMsgQ : public BnTestAidlMsgQ {
- typedef AidlMessageQueue<int32_t, SynchronizedReadWrite> MessageQueueSync;
- typedef AidlMessageQueue<int32_t, UnsynchronizedWrite> MessageQueueUnsync;
-
- TestAidlMsgQ() : mFmqSynchronized(nullptr), mFmqUnsynchronized(nullptr) {}
-
- // Methods from ::aidl::android::fmq::test::ITestAidlMsgQ follow.
- ndk::ScopedAStatus configureFmqSyncReadWrite(
- const MQDescriptor<int32_t, SynchronizedReadWrite>& mqDesc,
- bool* _aidl_return) override;
- ndk::ScopedAStatus getFmqUnsyncWrite(bool configureFmq, bool userFd,
- MQDescriptor<int32_t, UnsynchronizedWrite>* mqDesc,
- bool* _aidl_return) override;
- ndk::ScopedAStatus requestBlockingRead(int32_t count) override;
- ndk::ScopedAStatus requestBlockingReadDefaultEventFlagBits(int32_t count) override;
- ndk::ScopedAStatus requestBlockingReadRepeat(int32_t count, int32_t numIter) override;
- ndk::ScopedAStatus requestReadFmqSync(int32_t count, bool* _aidl_return) override;
- ndk::ScopedAStatus requestReadFmqUnsync(int32_t count, bool* _aidl_return) override;
- ndk::ScopedAStatus requestWriteFmqSync(int32_t count, bool* _aidl_return) override;
- ndk::ScopedAStatus requestWriteFmqUnsync(int32_t count, bool* _aidl_return) override;
-
- private:
- std::unique_ptr<MessageQueueSync> mFmqSynchronized;
- std::unique_ptr<MessageQueueUnsync> mFmqUnsynchronized;
-
- /*
- * Utility function to verify data read from the fast message queue.
- */
- bool verifyData(int32_t* data, int count) {
- for (int i = 0; i < count; i++) {
- if (data[i] != i) return false;
- }
- return true;
- }
-};
-
-} // namespace test
-} // namespace fmq
-} // namespace android
-} // namespace aidl
diff --git a/tests/fmq_test b/tests/fmq_test
new file mode 100644
index 0000000..02683c9
--- /dev/null
+++ b/tests/fmq_test
@@ -0,0 +1,9 @@
+source /data/nativetest64/hidl_test_helper
+
+TREBLE_TESTING_OVERRIDE=true run_all_tests \
+ "/data/nativetest/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" \
+ "/data/nativetest64/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" \
+ "/data/nativetest/mq_test_client/mq_test_client" \
+ "/data/nativetest64/mq_test_client/mq_test_client" \
+ "fmq_test" \
+ "$@"
diff --git a/tests/fmq_test.py b/tests/fmq_test.py
deleted file mode 100644
index 68114ad..0000000
--- a/tests/fmq_test.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 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.
-#
-
-import subprocess
-import unittest
-
-def run_cmd(cmd, ignore_error=False):
- print("Running command:", cmd)
- p = subprocess.Popen(cmd, shell=True)
- p.communicate()
- if not ignore_error and p.returncode:
- raise subprocess.CalledProcessError(p.returncode, cmd)
- return p.returncode
-
-class TestFmq(unittest.TestCase):
- pass
-
-def make_test(client, server):
- def test(self):
- try:
- run_cmd("adb shell killall %s >/dev/null 2>&1" % client, ignore_error=True)
- run_cmd("adb shell killall %s >/dev/null 2>&1" % server, ignore_error=True)
- run_cmd("adb shell \"( %s ) </dev/null >/dev/null 2>&1 &\"" % server)
- run_cmd("adb shell %s" % client)
- finally:
- run_cmd("adb shell killall %s >/dev/null 2>&1" % client, ignore_error=True)
- run_cmd("adb shell killall %s >/dev/null 2>&1" % server, ignore_error=True)
- return test
-
-def has_bitness(bitness):
- return 0 == run_cmd("echo '[[ \"$(getprop ro.product.cpu.abilist%s)\" != \"\" ]]' | adb shell sh" % bitness, ignore_error=True)
-
-if __name__ == '__main__':
- clients = []
- servers = []
-
- if has_bitness(32):
- clients += ["/data/nativetest/fmq_test_client/fmq_test_client"]
- servers += ["/data/nativetest/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test"]
-
- if has_bitness(64):
- clients += ["/data/nativetest64/fmq_test_client/fmq_test_client"]
- servers += ["/data/nativetest64/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test"]
-
- assert len(clients) > 0
- assert len(servers) > 0
-
- def short_name(binary):
- if "64" in binary:
- return "64"
- return "32"
-
- for client in clients:
- for server in servers:
- test_name = 'test_%s_to_%s' % (short_name(client), short_name(server))
- test = make_test(client, server)
- setattr(TestFmq, test_name, test)
-
- suite = unittest.TestLoader().loadTestsFromTestCase(TestFmq)
- unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/fmq_test.xml b/tests/fmq_test.xml
deleted file mode 100644
index f7b1f16..0000000
--- a/tests/fmq_test.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs FMQ on-device integration tests.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <!-- We would like to abort, but currently there is not a simple way to specify installation of both bitnesses of targets. -->
- <option name="abort-on-push-failure" value="false"/>
-
- <option name="push" value="fmq_test_client32->/data/nativetest/fmq_test_client/fmq_test_client" />
- <option name="push" value="fmq_test_client64->/data/nativetest64/fmq_test_client/fmq_test_client" />
-
- <option name="push" value="android.hardware.tests.msgq@1.0-service-test32->/data/nativetest/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" />
- <option name="push" value="android.hardware.tests.msgq@1.0-service-test64->/data/nativetest64/android.hardware.tests.msgq@1.0-service-test/android.hardware.tests.msgq@1.0-service-test" />
-
- <option name="cleanup" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
- <option name="par-file-name" value="fmq_test" />
- <option name="test-timeout" value="2m" />
- </test>
-</configuration>
-
diff --git a/tests/fmq_unit_tests.cpp b/tests/fmq_unit_tests.cpp
deleted file mode 100644
index be866ec..0000000
--- a/tests/fmq_unit_tests.cpp
+++ /dev/null
@@ -1,1147 +0,0 @@
-/*
- * 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.
- */
-
-#include <asm-generic/mman.h>
-#include <fmq/AidlMessageQueue.h>
-#include <fmq/ConvertMQDescriptors.h>
-#include <fmq/EventFlag.h>
-#include <fmq/MessageQueue.h>
-#include <gtest/gtest-death-test.h>
-#include <gtest/gtest.h>
-#include <sys/resource.h>
-#include <atomic>
-#include <cstdlib>
-#include <sstream>
-#include <thread>
-
-using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using android::hardware::kSynchronizedReadWrite;
-using android::hardware::kUnsynchronizedWrite;
-
-enum EventFlagBits : uint32_t {
- kFmqNotEmpty = 1 << 0,
- kFmqNotFull = 1 << 1,
-};
-
-typedef android::AidlMessageQueue<uint8_t, SynchronizedReadWrite> AidlMessageQueueSync;
-typedef android::AidlMessageQueue<uint8_t, UnsynchronizedWrite> AidlMessageQueueUnsync;
-typedef android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite> MessageQueueSync;
-typedef android::hardware::MessageQueue<uint8_t, kUnsynchronizedWrite> MessageQueueUnsync;
-typedef android::AidlMessageQueue<uint16_t, SynchronizedReadWrite> AidlMessageQueueSync16;
-typedef android::hardware::MessageQueue<uint16_t, kSynchronizedReadWrite> MessageQueueSync16;
-
-typedef android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite> MessageQueueSync8;
-typedef android::hardware::MQDescriptor<uint8_t, kSynchronizedReadWrite> HidlMQDescSync8;
-typedef android::AidlMessageQueue<int8_t, SynchronizedReadWrite> AidlMessageQueueSync8;
-typedef aidl::android::hardware::common::fmq::MQDescriptor<int8_t, SynchronizedReadWrite>
- AidlMQDescSync8;
-typedef android::hardware::MessageQueue<uint8_t, kUnsynchronizedWrite> MessageQueueUnsync8;
-typedef android::hardware::MQDescriptor<uint8_t, kUnsynchronizedWrite> HidlMQDescUnsync8;
-typedef android::AidlMessageQueue<int8_t, UnsynchronizedWrite> AidlMessageQueueUnsync8;
-typedef aidl::android::hardware::common::fmq::MQDescriptor<int8_t, UnsynchronizedWrite>
- AidlMQDescUnsync8;
-
-enum class SetupType {
- SINGLE_FD,
- DOUBLE_FD,
-};
-
-template <typename T, SetupType setupType>
-class TestParamTypes {
- public:
- typedef T MQType;
- static constexpr SetupType Setup = setupType;
-};
-
-// Run everything on both the AIDL and HIDL versions with one and two FDs
-typedef ::testing::Types<TestParamTypes<AidlMessageQueueSync, SetupType::SINGLE_FD>,
- TestParamTypes<MessageQueueSync, SetupType::SINGLE_FD>,
- TestParamTypes<AidlMessageQueueSync, SetupType::DOUBLE_FD>,
- TestParamTypes<MessageQueueSync, SetupType::DOUBLE_FD>>
- SyncTypes;
-typedef ::testing::Types<TestParamTypes<AidlMessageQueueUnsync, SetupType::SINGLE_FD>,
- TestParamTypes<MessageQueueUnsync, SetupType::SINGLE_FD>,
- TestParamTypes<AidlMessageQueueUnsync, SetupType::DOUBLE_FD>,
- TestParamTypes<MessageQueueUnsync, SetupType::DOUBLE_FD>>
- UnsyncTypes;
-typedef ::testing::Types<TestParamTypes<AidlMessageQueueSync16, SetupType::SINGLE_FD>,
- TestParamTypes<MessageQueueSync16, SetupType::SINGLE_FD>,
- TestParamTypes<AidlMessageQueueSync16, SetupType::DOUBLE_FD>,
- TestParamTypes<MessageQueueSync16, SetupType::DOUBLE_FD>>
- BadConfigTypes;
-
-template <typename T>
-class TestBase : public ::testing::Test {
- public:
- static void ReaderThreadBlocking(typename T::MQType* fmq, std::atomic<uint32_t>* fwAddr);
- static void ReaderThreadBlocking2(typename T::MQType* fmq, std::atomic<uint32_t>* fwAddr);
-};
-
-TYPED_TEST_CASE(SynchronizedReadWrites, SyncTypes);
-
-template <typename T>
-class SynchronizedReadWrites : public TestBase<T> {
- protected:
- virtual void TearDown() {
- delete mQueue;
- }
-
- virtual void SetUp() {
- static constexpr size_t kNumElementsInQueue = 2048;
- static constexpr size_t kPayloadSizeBytes = 1;
- if (T::Setup == SetupType::SINGLE_FD) {
- mQueue = new (std::nothrow) typename T::MQType(kNumElementsInQueue);
- } else {
- android::base::unique_fd ringbufferFd(::ashmem_create_region(
- "SyncReadWrite", kNumElementsInQueue * kPayloadSizeBytes));
- mQueue = new (std::nothrow)
- typename T::MQType(kNumElementsInQueue, false, std::move(ringbufferFd),
- kNumElementsInQueue * kPayloadSizeBytes);
- }
- ASSERT_NE(nullptr, mQueue);
- ASSERT_TRUE(mQueue->isValid());
- mNumMessagesMax = mQueue->getQuantumCount();
- ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
- }
-
- typename T::MQType* mQueue = nullptr;
- size_t mNumMessagesMax = 0;
-};
-
-TYPED_TEST_CASE(UnsynchronizedWriteTest, UnsyncTypes);
-
-template <typename T>
-class UnsynchronizedWriteTest : public TestBase<T> {
- protected:
- virtual void TearDown() {
- delete mQueue;
- }
-
- virtual void SetUp() {
- static constexpr size_t kNumElementsInQueue = 2048;
- static constexpr size_t kPayloadSizeBytes = 1;
- if (T::Setup == SetupType::SINGLE_FD) {
- mQueue = new (std::nothrow) typename T::MQType(kNumElementsInQueue);
- } else {
- android::base::unique_fd ringbufferFd(
- ::ashmem_create_region("UnsyncWrite", kNumElementsInQueue * kPayloadSizeBytes));
- mQueue = new (std::nothrow)
- typename T::MQType(kNumElementsInQueue, false, std::move(ringbufferFd),
- kNumElementsInQueue * kPayloadSizeBytes);
- }
- ASSERT_NE(nullptr, mQueue);
- ASSERT_TRUE(mQueue->isValid());
- mNumMessagesMax = mQueue->getQuantumCount();
- ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
- }
-
- typename T::MQType* mQueue = nullptr;
- size_t mNumMessagesMax = 0;
-};
-
-TYPED_TEST_CASE(BlockingReadWrites, SyncTypes);
-
-template <typename T>
-class BlockingReadWrites : public TestBase<T> {
- protected:
- virtual void TearDown() {
- delete mQueue;
- }
- virtual void SetUp() {
- static constexpr size_t kNumElementsInQueue = 2048;
- static constexpr size_t kPayloadSizeBytes = 1;
- if (T::Setup == SetupType::SINGLE_FD) {
- mQueue = new (std::nothrow) typename T::MQType(kNumElementsInQueue);
- } else {
- android::base::unique_fd ringbufferFd(::ashmem_create_region(
- "SyncBlockingReadWrite", kNumElementsInQueue * kPayloadSizeBytes));
- mQueue = new (std::nothrow)
- typename T::MQType(kNumElementsInQueue, false, std::move(ringbufferFd),
- kNumElementsInQueue * kPayloadSizeBytes);
- }
- ASSERT_NE(nullptr, mQueue);
- ASSERT_TRUE(mQueue->isValid());
- mNumMessagesMax = mQueue->getQuantumCount();
- ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
- /*
- * Initialize the EventFlag word to indicate Queue is not full.
- */
- std::atomic_init(&mFw, static_cast<uint32_t>(kFmqNotFull));
- }
-
- typename T::MQType* mQueue;
- std::atomic<uint32_t> mFw;
- size_t mNumMessagesMax = 0;
-};
-
-TYPED_TEST_CASE(QueueSizeOdd, SyncTypes);
-
-template <typename T>
-class QueueSizeOdd : public TestBase<T> {
- protected:
- virtual void TearDown() { delete mQueue; }
- virtual void SetUp() {
- static constexpr size_t kNumElementsInQueue = 2049;
- static constexpr size_t kPayloadSizeBytes = 1;
- if (T::Setup == SetupType::SINGLE_FD) {
- mQueue = new (std::nothrow)
- typename T::MQType(kNumElementsInQueue, true /* configureEventFlagWord */);
- } else {
- android::base::unique_fd ringbufferFd(
- ::ashmem_create_region("SyncSizeOdd", kNumElementsInQueue * kPayloadSizeBytes));
- mQueue = new (std::nothrow) typename T::MQType(
- kNumElementsInQueue, true /* configureEventFlagWord */, std::move(ringbufferFd),
- kNumElementsInQueue * kPayloadSizeBytes);
- }
- ASSERT_NE(nullptr, mQueue);
- ASSERT_TRUE(mQueue->isValid());
- mNumMessagesMax = mQueue->getQuantumCount();
- ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
- auto evFlagWordPtr = mQueue->getEventFlagWord();
- ASSERT_NE(nullptr, evFlagWordPtr);
- /*
- * Initialize the EventFlag word to indicate Queue is not full.
- */
- std::atomic_init(evFlagWordPtr, static_cast<uint32_t>(kFmqNotFull));
- }
-
- typename T::MQType* mQueue;
- size_t mNumMessagesMax = 0;
-};
-
-TYPED_TEST_CASE(BadQueueConfig, BadConfigTypes);
-
-template <typename T>
-class BadQueueConfig : public TestBase<T> {};
-
-class AidlOnlyBadQueueConfig : public ::testing::Test {};
-class Hidl2AidlOperation : public ::testing::Test {};
-class DoubleFdFailures : public ::testing::Test {};
-
-/*
- * Utility function to initialize data to be written to the FMQ
- */
-inline void initData(uint8_t* data, size_t count) {
- for (size_t i = 0; i < count; i++) {
- data[i] = i & 0xFF;
- }
-}
-
-/*
- * This thread will attempt to read and block. When wait returns
- * it checks if the kFmqNotEmpty bit is actually set.
- * If the read is succesful, it signals Wake to kFmqNotFull.
- */
-template <typename T>
-void TestBase<T>::ReaderThreadBlocking(typename T::MQType* fmq, std::atomic<uint32_t>* fwAddr) {
- const size_t dataLen = 64;
- uint8_t data[dataLen];
- android::hardware::EventFlag* efGroup = nullptr;
- android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
- ASSERT_NE(nullptr, efGroup);
-
- while (true) {
- uint32_t efState = 0;
- android::status_t ret = efGroup->wait(kFmqNotEmpty,
- &efState,
- 5000000000 /* timeoutNanoSeconds */);
- /*
- * Wait should not time out here after 5s
- */
- ASSERT_NE(android::TIMED_OUT, ret);
-
- if ((efState & kFmqNotEmpty) && fmq->read(data, dataLen)) {
- efGroup->wake(kFmqNotFull);
- break;
- }
- }
-
- status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
-}
-
-/*
- * This thread will attempt to read and block using the readBlocking() API and
- * passes in a pointer to an EventFlag object.
- */
-template <typename T>
-void TestBase<T>::ReaderThreadBlocking2(typename T::MQType* fmq, std::atomic<uint32_t>* fwAddr) {
- const size_t dataLen = 64;
- uint8_t data[dataLen];
- android::hardware::EventFlag* efGroup = nullptr;
- android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
- ASSERT_NE(nullptr, efGroup);
- bool ret = fmq->readBlocking(data,
- dataLen,
- static_cast<uint32_t>(kFmqNotFull),
- static_cast<uint32_t>(kFmqNotEmpty),
- 5000000000 /* timeOutNanos */,
- efGroup);
- ASSERT_TRUE(ret);
- status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
-}
-
-TYPED_TEST(BadQueueConfig, QueueSizeTooLarge) {
- size_t numElementsInQueue = SIZE_MAX / sizeof(uint16_t) + 1;
- typename TypeParam::MQType* fmq =
- new (std::nothrow) typename TypeParam::MQType(numElementsInQueue);
- ASSERT_NE(nullptr, fmq);
- /*
- * Should fail due to size being too large to fit into size_t.
- */
- ASSERT_FALSE(fmq->isValid());
-}
-
-// If this test fails and we do leak FDs, the next test will cause a crash
-TEST_F(AidlOnlyBadQueueConfig, LookForLeakedFds) {
- size_t numElementsInQueue = SIZE_MAX / sizeof(uint32_t) - PAGE_SIZE - 1;
- struct rlimit rlim;
- ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &rlim), 0);
- for (int i = 0; i <= rlim.rlim_cur + 1; i++) {
- android::AidlMessageQueue<uint32_t, SynchronizedReadWrite> fmq(numElementsInQueue);
- ASSERT_FALSE(fmq.isValid());
- }
- // try to get another FD
- int fd = ashmem_create_region("test", 100);
- ASSERT_NE(fd, -1);
- close(fd);
-}
-
-TEST_F(Hidl2AidlOperation, ConvertDescriptorsSync) {
- size_t numElementsInQueue = 64;
-
- // Create HIDL side and get MQDescriptor
- MessageQueueSync8* fmq = new (std::nothrow) MessageQueueSync8(numElementsInQueue);
- ASSERT_NE(nullptr, fmq);
- ASSERT_TRUE(fmq->isValid());
- const HidlMQDescSync8* hidlDesc = fmq->getDesc();
- ASSERT_NE(nullptr, hidlDesc);
-
- // Create AIDL MQDescriptor to send to another process based off the HIDL MQDescriptor
- AidlMQDescSync8 aidlDesc;
- android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(*hidlDesc,
- &aidlDesc);
-
- // Other process will create the other side of the queue using the AIDL MQDescriptor
- AidlMessageQueueSync8* aidlFmq = new (std::nothrow) AidlMessageQueueSync8(aidlDesc);
- ASSERT_NE(nullptr, aidlFmq);
- ASSERT_TRUE(aidlFmq->isValid());
-
- // Make sure a write to the HIDL side, will show up for the AIDL side
- constexpr size_t dataLen = 4;
- uint8_t data[dataLen] = {12, 11, 10, 9};
- fmq->write(data, dataLen);
-
- int8_t readData[dataLen];
- ASSERT_TRUE(aidlFmq->read(readData, dataLen));
-
- ASSERT_EQ(data[0], readData[0]);
- ASSERT_EQ(data[1], readData[1]);
- ASSERT_EQ(data[2], readData[2]);
- ASSERT_EQ(data[3], readData[3]);
-}
-
-TEST_F(Hidl2AidlOperation, ConvertDescriptorsUnsync) {
- size_t numElementsInQueue = 64;
-
- // Create HIDL side and get MQDescriptor
- MessageQueueUnsync8* fmq = new (std::nothrow) MessageQueueUnsync8(numElementsInQueue);
- ASSERT_NE(nullptr, fmq);
- ASSERT_TRUE(fmq->isValid());
- const HidlMQDescUnsync8* hidlDesc = fmq->getDesc();
- ASSERT_NE(nullptr, hidlDesc);
-
- // Create AIDL MQDescriptor to send to another process based off the HIDL MQDescriptor
- AidlMQDescUnsync8 aidlDesc;
- android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(*hidlDesc,
- &aidlDesc);
-
- // Other process will create the other side of the queue using the AIDL MQDescriptor
- AidlMessageQueueUnsync8* aidlFmq = new (std::nothrow) AidlMessageQueueUnsync8(aidlDesc);
- ASSERT_NE(nullptr, aidlFmq);
- ASSERT_TRUE(aidlFmq->isValid());
-
- // Can have multiple readers with unsync flavor
- AidlMessageQueueUnsync8* aidlFmq2 = new (std::nothrow) AidlMessageQueueUnsync8(aidlDesc);
- ASSERT_NE(nullptr, aidlFmq2);
- ASSERT_TRUE(aidlFmq2->isValid());
-
- // Make sure a write to the HIDL side, will show up for the AIDL side
- constexpr size_t dataLen = 4;
- uint8_t data[dataLen] = {12, 11, 10, 9};
- fmq->write(data, dataLen);
-
- int8_t readData[dataLen];
- ASSERT_TRUE(aidlFmq->read(readData, dataLen));
- int8_t readData2[dataLen];
- ASSERT_TRUE(aidlFmq2->read(readData2, dataLen));
-
- ASSERT_EQ(data[0], readData[0]);
- ASSERT_EQ(data[1], readData[1]);
- ASSERT_EQ(data[2], readData[2]);
- ASSERT_EQ(data[3], readData[3]);
- ASSERT_EQ(data[0], readData2[0]);
- ASSERT_EQ(data[1], readData2[1]);
- ASSERT_EQ(data[2], readData2[2]);
- ASSERT_EQ(data[3], readData2[3]);
-}
-
-TEST_F(Hidl2AidlOperation, ConvertFdIndex1) {
- native_handle_t* mqHandle = native_handle_create(2 /* numFds */, 0 /* numInts */);
- if (mqHandle == nullptr) {
- return;
- }
- mqHandle->data[0] = 12;
- mqHandle->data[1] = 5;
- ::android::hardware::hidl_vec<android::hardware::GrantorDescriptor> grantors;
- grantors.resize(3);
- grantors[0] = {0, 1 /* fdIndex */, 16, 16};
- grantors[1] = {0, 1 /* fdIndex */, 16, 16};
- grantors[2] = {0, 1 /* fdIndex */, 16, 16};
-
- HidlMQDescUnsync8* hidlDesc = new (std::nothrow) HidlMQDescUnsync8(grantors, mqHandle, 10);
- ASSERT_TRUE(hidlDesc->isHandleValid());
-
- AidlMQDescUnsync8 aidlDesc;
- bool ret = android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(
- *hidlDesc, &aidlDesc);
- ASSERT_TRUE(ret);
-}
-
-TEST_F(Hidl2AidlOperation, ConvertMultipleFds) {
- native_handle_t* mqHandle = native_handle_create(2 /* numFds */, 0 /* numInts */);
- if (mqHandle == nullptr) {
- return;
- }
- mqHandle->data[0] = ::ashmem_create_region("ConvertMultipleFds", 8);
- mqHandle->data[1] = ::ashmem_create_region("ConvertMultipleFds2", 8);
- ::android::hardware::hidl_vec<android::hardware::GrantorDescriptor> grantors;
- grantors.resize(3);
- grantors[0] = {0, 1 /* fdIndex */, 16, 16};
- grantors[1] = {0, 1 /* fdIndex */, 16, 16};
- grantors[2] = {0, 0 /* fdIndex */, 16, 16};
-
- HidlMQDescUnsync8* hidlDesc = new (std::nothrow) HidlMQDescUnsync8(grantors, mqHandle, 10);
- ASSERT_TRUE(hidlDesc->isHandleValid());
-
- AidlMQDescUnsync8 aidlDesc;
- bool ret = android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(
- *hidlDesc, &aidlDesc);
- ASSERT_TRUE(ret);
- EXPECT_EQ(aidlDesc.handle.fds.size(), 2);
- native_handle_close(mqHandle);
- native_handle_delete(mqHandle);
-}
-
-// TODO(b/165674950) Since AIDL does not support unsigned integers, it can only support
-// 1/2 the queue size of HIDL. Once support is added to AIDL, this restriction can be
-// lifted. Until then, check against SSIZE_MAX instead of SIZE_MAX.
-TEST_F(AidlOnlyBadQueueConfig, QueueSizeTooLargeForAidl) {
- size_t numElementsInQueue = SSIZE_MAX / sizeof(uint16_t) + 1;
- AidlMessageQueueSync16* fmq = new (std::nothrow) AidlMessageQueueSync16(numElementsInQueue);
- ASSERT_NE(nullptr, fmq);
- /*
- * Should fail due to size being too large to fit into size_t.
- */
- ASSERT_FALSE(fmq->isValid());
-}
-
-TEST_F(AidlOnlyBadQueueConfig, NegativeAidlDescriptor) {
- aidl::android::hardware::common::fmq::MQDescriptor<uint16_t, SynchronizedReadWrite> desc;
- desc.quantum = -10;
- AidlMessageQueueSync16* fmq = new (std::nothrow) AidlMessageQueueSync16(desc);
- ASSERT_NE(nullptr, fmq);
- /*
- * Should fail due to quantum being negative.
- */
- ASSERT_FALSE(fmq->isValid());
-}
-
-TEST_F(AidlOnlyBadQueueConfig, NegativeAidlDescriptorGrantor) {
- aidl::android::hardware::common::fmq::MQDescriptor<uint16_t, SynchronizedReadWrite> desc;
- desc.quantum = 2;
- desc.flags = 0;
- desc.grantors.push_back(
- aidl::android::hardware::common::fmq::GrantorDescriptor{.offset = 12, .extent = -10});
- AidlMessageQueueSync16* fmq = new (std::nothrow) AidlMessageQueueSync16(desc);
- ASSERT_NE(nullptr, fmq);
- /*
- * Should fail due to grantor having negative extent.
- */
- ASSERT_FALSE(fmq->isValid());
-}
-
-/*
- * Test creating a new queue from a modified MQDescriptor of another queue.
- * If MQDescriptor.quantum doesn't match the size of the payload(T), the queue
- * should be invalid.
- */
-TEST_F(AidlOnlyBadQueueConfig, MismatchedPayloadSize) {
- AidlMessageQueueSync16 fmq = AidlMessageQueueSync16(64);
- aidl::android::hardware::common::fmq::MQDescriptor<uint16_t, SynchronizedReadWrite> desc =
- fmq.dupeDesc();
- // This should work fine with the unmodified MQDescriptor
- AidlMessageQueueSync16 fmq2 = AidlMessageQueueSync16(desc);
- ASSERT_TRUE(fmq2.isValid());
-
- // Simulate a difference in payload size between processes handling the queue
- desc.quantum = 8;
- AidlMessageQueueSync16 fmq3 = AidlMessageQueueSync16(desc);
-
- // Should fail due to the quantum not matching the sizeof(uint16_t)
- ASSERT_FALSE(fmq3.isValid());
-}
-
-/*
- * Test creating a new queue with an invalid fd. This should assert with message
- * "mRing is null".
- */
-TEST_F(DoubleFdFailures, InvalidFd) {
- EXPECT_DEATH_IF_SUPPORTED(AidlMessageQueueSync(64, false, android::base::unique_fd(3000), 64),
- "mRing is null");
-}
-
-/*
- * Test creating a new queue with a buffer fd and bufferSize smaller than the
- * requested queue. This should fail to create a valid message queue.
- */
-TEST_F(DoubleFdFailures, InvalidFdSize) {
- constexpr size_t kNumElementsInQueue = 1024;
- constexpr size_t kRequiredDataBufferSize = kNumElementsInQueue * sizeof(uint16_t);
- android::base::unique_fd ringbufferFd(
- ::ashmem_create_region("SyncReadWrite", kRequiredDataBufferSize - 8));
- AidlMessageQueueSync16 fmq = AidlMessageQueueSync16(
- kNumElementsInQueue, false, std::move(ringbufferFd), kRequiredDataBufferSize - 8);
- EXPECT_FALSE(fmq.isValid());
-}
-
-/*
- * Test creating a new queue with a buffer fd and bufferSize larger than the
- * requested queue. The message queue should be valid.
- */
-TEST_F(DoubleFdFailures, LargerFdSize) {
- constexpr size_t kNumElementsInQueue = 1024;
- constexpr size_t kRequiredDataBufferSize = kNumElementsInQueue * sizeof(uint16_t);
- android::base::unique_fd ringbufferFd(
- ::ashmem_create_region("SyncReadWrite", kRequiredDataBufferSize + 8));
- AidlMessageQueueSync16 fmq = AidlMessageQueueSync16(
- kNumElementsInQueue, false, std::move(ringbufferFd), kRequiredDataBufferSize + 8);
- EXPECT_TRUE(fmq.isValid());
-}
-
-/*
- * Test that basic blocking works. This test uses the non-blocking read()/write()
- * APIs.
- */
-TYPED_TEST(BlockingReadWrites, SmallInputTest1) {
- const size_t dataLen = 64;
- uint8_t data[dataLen] = {0};
-
- android::hardware::EventFlag* efGroup = nullptr;
- android::status_t status = android::hardware::EventFlag::createEventFlag(&this->mFw, &efGroup);
-
- ASSERT_EQ(android::NO_ERROR, status);
- ASSERT_NE(nullptr, efGroup);
-
- /*
- * Start a thread that will try to read and block on kFmqNotEmpty.
- */
- std::thread Reader(BlockingReadWrites<TypeParam>::ReaderThreadBlocking, this->mQueue,
- &this->mFw);
- struct timespec waitTime = {0, 100 * 1000000};
- ASSERT_EQ(0, nanosleep(&waitTime, NULL));
-
- /*
- * After waiting for some time write into the FMQ
- * and call Wake on kFmqNotEmpty.
- */
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- status = efGroup->wake(kFmqNotEmpty);
- ASSERT_EQ(android::NO_ERROR, status);
-
- ASSERT_EQ(0, nanosleep(&waitTime, NULL));
- Reader.join();
-
- status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
-}
-
-/*
- * Test that basic blocking works. This test uses the
- * writeBlocking()/readBlocking() APIs.
- */
-TYPED_TEST(BlockingReadWrites, SmallInputTest2) {
- const size_t dataLen = 64;
- uint8_t data[dataLen] = {0};
-
- android::hardware::EventFlag* efGroup = nullptr;
- android::status_t status = android::hardware::EventFlag::createEventFlag(&this->mFw, &efGroup);
-
- ASSERT_EQ(android::NO_ERROR, status);
- ASSERT_NE(nullptr, efGroup);
-
- /*
- * Start a thread that will try to read and block on kFmqNotEmpty. It will
- * call wake() on kFmqNotFull when the read is successful.
- */
- std::thread Reader(BlockingReadWrites<TypeParam>::ReaderThreadBlocking2, this->mQueue,
- &this->mFw);
- bool ret = this->mQueue->writeBlocking(data, dataLen, static_cast<uint32_t>(kFmqNotFull),
- static_cast<uint32_t>(kFmqNotEmpty),
- 5000000000 /* timeOutNanos */, efGroup);
- ASSERT_TRUE(ret);
- Reader.join();
-
- status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
-}
-
-/*
- * Test that basic blocking times out as intended.
- */
-TYPED_TEST(BlockingReadWrites, BlockingTimeOutTest) {
- android::hardware::EventFlag* efGroup = nullptr;
- android::status_t status = android::hardware::EventFlag::createEventFlag(&this->mFw, &efGroup);
-
- ASSERT_EQ(android::NO_ERROR, status);
- ASSERT_NE(nullptr, efGroup);
-
- /* Block on an EventFlag bit that no one will wake and time out in 1s */
- uint32_t efState = 0;
- android::status_t ret = efGroup->wait(kFmqNotEmpty,
- &efState,
- 1000000000 /* timeoutNanoSeconds */);
- /*
- * Wait should time out in a second.
- */
- EXPECT_EQ(android::TIMED_OUT, ret);
-
- status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
- ASSERT_EQ(android::NO_ERROR, status);
-}
-
-/*
- * Test that odd queue sizes do not cause unaligned error
- * on access to EventFlag object.
- */
-TYPED_TEST(QueueSizeOdd, EventFlagTest) {
- const size_t dataLen = 64;
- uint8_t data[dataLen] = {0};
-
- bool ret = this->mQueue->writeBlocking(data, dataLen, static_cast<uint32_t>(kFmqNotFull),
- static_cast<uint32_t>(kFmqNotEmpty),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
-}
-
-/*
- * Verify that a few bytes of data can be successfully written and read.
- */
-TYPED_TEST(SynchronizedReadWrites, SmallInputTest1) {
- const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
-
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- uint8_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
- ASSERT_EQ(0, memcmp(data, readData, dataLen));
-}
-
-/*
- * Verify that a few bytes of data can be successfully written and read using
- * beginRead/beginWrite/CommitRead/CommitWrite
- */
-TYPED_TEST(SynchronizedReadWrites, SmallInputTest2) {
- const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
-
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(dataLen, &tx));
-
- ASSERT_TRUE(tx.copyTo(data, 0 /* startIdx */, dataLen));
-
- ASSERT_TRUE(this->mQueue->commitWrite(dataLen));
-
- uint8_t readData[dataLen] = {};
-
- ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
-
- ASSERT_TRUE(tx.copyFrom(readData, 0 /* startIdx */, dataLen));
-
- ASSERT_TRUE(this->mQueue->commitRead(dataLen));
-
- ASSERT_EQ(0, memcmp(data, readData, dataLen));
-}
-
-/*
- * Verify that a few bytes of data can be successfully written and read using
- * beginRead/beginWrite/CommitRead/CommitWrite as well as getSlot().
- */
-TYPED_TEST(SynchronizedReadWrites, SmallInputTest3) {
- const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(dataLen, &tx));
-
- auto first = tx.getFirstRegion();
- auto second = tx.getSecondRegion();
-
- ASSERT_EQ(first.getLength() + second.getLength(), dataLen);
- for (size_t i = 0; i < dataLen; i++) {
- uint8_t* ptr = tx.getSlot(i);
- *ptr = data[i];
- }
-
- ASSERT_TRUE(this->mQueue->commitWrite(dataLen));
-
- uint8_t readData[dataLen] = {};
-
- ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
-
- first = tx.getFirstRegion();
- second = tx.getSecondRegion();
-
- ASSERT_EQ(first.getLength() + second.getLength(), dataLen);
-
- for (size_t i = 0; i < dataLen; i++) {
- uint8_t* ptr = tx.getSlot(i);
- readData[i] = *ptr;
- }
-
- ASSERT_TRUE(this->mQueue->commitRead(dataLen));
-
- ASSERT_EQ(0, memcmp(data, readData, dataLen));
-}
-
-/*
- * Verify that read() returns false when trying to read from an empty queue.
- */
-TYPED_TEST(SynchronizedReadWrites, ReadWhenEmpty1) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 2;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t readData[dataLen];
- ASSERT_FALSE(this->mQueue->read(readData, dataLen));
-}
-
-/*
- * Verify that beginRead() returns a MemTransaction object with null pointers when trying
- * to read from an empty queue.
- */
-TYPED_TEST(SynchronizedReadWrites, ReadWhenEmpty2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 2;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
-
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_FALSE(this->mQueue->beginRead(dataLen, &tx));
-
- auto first = tx.getFirstRegion();
- auto second = tx.getSecondRegion();
-
- ASSERT_EQ(nullptr, first.getAddress());
- ASSERT_EQ(nullptr, second.getAddress());
-}
-
-/*
- * Write the queue until full. Verify that another write is unsuccessful.
- * Verify that availableToWrite() returns 0 as expected.
- */
-TYPED_TEST(SynchronizedReadWrites, WriteWhenFull1) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- std::vector<uint8_t> data(this->mNumMessagesMax);
-
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_FALSE(this->mQueue->write(&data[0], 1));
-
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Write the queue until full. Verify that beginWrite() returns
- * a MemTransaction object with null base pointers.
- */
-TYPED_TEST(SynchronizedReadWrites, WriteWhenFull2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- std::vector<uint8_t> data(this->mNumMessagesMax);
-
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
-
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_FALSE(this->mQueue->beginWrite(1, &tx));
-
- auto first = tx.getFirstRegion();
- auto second = tx.getSecondRegion();
-
- ASSERT_EQ(nullptr, first.getAddress());
- ASSERT_EQ(nullptr, second.getAddress());
-}
-
-/*
- * Write a chunk of data equal to the queue size.
- * Verify that the write is successful and the subsequent read
- * returns the expected data.
- */
-TYPED_TEST(SynchronizedReadWrites, LargeInputTest1) {
- std::vector<uint8_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Attempt to write a chunk of data larger than the queue size.
- * Verify that it fails. Verify that a subsequent read fails and
- * the queue is still empty.
- */
-TYPED_TEST(SynchronizedReadWrites, LargeInputTest2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 4096;
- ASSERT_GT(dataLen, this->mNumMessagesMax);
- std::vector<uint8_t> data(dataLen);
-
- initData(&data[0], dataLen);
- ASSERT_FALSE(this->mQueue->write(&data[0], dataLen));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_NE(data, readData);
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
-}
-
-/*
- * After the queue is full, try to write more data. Verify that
- * the attempt returns false. Verify that the attempt did not
- * affect the pre-existing data in the queue.
- */
-TYPED_TEST(SynchronizedReadWrites, LargeInputTest3) {
- std::vector<uint8_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_FALSE(this->mQueue->write(&data[0], 1));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Verify that beginWrite() returns a MemTransaction with
- * null base pointers when attempting to write data larger
- * than the queue size.
- */
-TYPED_TEST(SynchronizedReadWrites, LargeInputTest4) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 4096;
- ASSERT_GT(dataLen, this->mNumMessagesMax);
-
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_FALSE(this->mQueue->beginWrite(dataLen, &tx));
-
- auto first = tx.getFirstRegion();
- auto second = tx.getSecondRegion();
-
- ASSERT_EQ(nullptr, first.getAddress());
- ASSERT_EQ(nullptr, second.getAddress());
-}
-
-/*
- * Verify that multiple reads one after the other return expected data.
- */
-TYPED_TEST(SynchronizedReadWrites, MultipleRead) {
- const size_t chunkSize = 100;
- const size_t chunkNum = 5;
- const size_t dataLen = chunkSize * chunkNum;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- uint8_t readData[dataLen] = {};
- for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
- }
- ASSERT_EQ(0, memcmp(readData, data, dataLen));
-}
-
-/*
- * Verify that multiple writes one after the other happens correctly.
- */
-TYPED_TEST(SynchronizedReadWrites, MultipleWrite) {
- const int chunkSize = 100;
- const int chunkNum = 5;
- const size_t dataLen = chunkSize * chunkNum;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
- for (unsigned int i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->write(data + i * chunkSize, chunkSize));
- }
- uint8_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
- ASSERT_EQ(0, memcmp(readData, data, dataLen));
-}
-
-/*
- * Write enough messages into the FMQ to fill half of it
- * and read back the same.
- * Write this->mNumMessagesMax messages into the queue. This will cause a
- * wrap around. Read and verify the data.
- */
-TYPED_TEST(SynchronizedReadWrites, ReadWriteWrapAround1) {
- size_t numMessages = this->mNumMessagesMax - 1;
- std::vector<uint8_t> data(this->mNumMessagesMax);
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
- ASSERT_TRUE(this->mQueue->read(&readData[0], numMessages));
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Use beginRead/CommitRead/beginWrite/commitWrite APIs
- * to test wrap arounds are handled correctly.
- * Write enough messages into the FMQ to fill half of it
- * and read back the same.
- * Write mNumMessagesMax messages into the queue. This will cause a
- * wrap around. Read and verify the data.
- */
-TYPED_TEST(SynchronizedReadWrites, ReadWriteWrapAround2) {
- size_t dataLen = this->mNumMessagesMax - 1;
- std::vector<uint8_t> data(this->mNumMessagesMax);
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], dataLen));
- ASSERT_TRUE(this->mQueue->read(&readData[0], dataLen));
-
- /*
- * The next write and read will have to deal with with wrap arounds.
- */
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(this->mNumMessagesMax, &tx));
-
- auto first = tx.getFirstRegion();
- auto second = tx.getSecondRegion();
-
- ASSERT_EQ(first.getLength() + second.getLength(), this->mNumMessagesMax);
-
- ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */, this->mNumMessagesMax));
-
- ASSERT_TRUE(this->mQueue->commitWrite(this->mNumMessagesMax));
-
- ASSERT_TRUE(this->mQueue->beginRead(this->mNumMessagesMax, &tx));
-
- first = tx.getFirstRegion();
- second = tx.getSecondRegion();
-
- ASSERT_EQ(first.getLength() + second.getLength(), this->mNumMessagesMax);
-
- ASSERT_TRUE(tx.copyFrom(&readData[0], 0 /* startIdx */, this->mNumMessagesMax));
- ASSERT_TRUE(this->mQueue->commitRead(this->mNumMessagesMax));
-
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Verify that a few bytes of data can be successfully written and read.
- */
-TYPED_TEST(UnsynchronizedWriteTest, SmallInputTest1) {
- const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- uint8_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
- ASSERT_EQ(0, memcmp(data, readData, dataLen));
-}
-
-/*
- * Verify that read() returns false when trying to read from an empty queue.
- */
-TYPED_TEST(UnsynchronizedWriteTest, ReadWhenEmpty) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 2;
- ASSERT_TRUE(dataLen < this->mNumMessagesMax);
- uint8_t readData[dataLen];
- ASSERT_FALSE(this->mQueue->read(readData, dataLen));
-}
-
-/*
- * Write the queue when full. Verify that a subsequent writes is succesful.
- * Verify that availableToWrite() returns 0 as expected.
- */
-TYPED_TEST(UnsynchronizedWriteTest, WriteWhenFull1) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- std::vector<uint8_t> data(this->mNumMessagesMax);
-
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_TRUE(this->mQueue->write(&data[0], 1));
-
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
-}
-
-/*
- * Write the queue when full. Verify that a subsequent writes
- * using beginRead()/commitRead() is succesful.
- * Verify that the next read fails as expected for unsynchronized flavor.
- */
-TYPED_TEST(UnsynchronizedWriteTest, WriteWhenFull2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- std::vector<uint8_t> data(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
-
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(1, &tx));
-
- ASSERT_EQ(tx.getFirstRegion().getLength(), 1U);
-
- ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */));
-
- ASSERT_TRUE(this->mQueue->commitWrite(1));
-
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
-}
-
-/*
- * Write a chunk of data equal to the queue size.
- * Verify that the write is successful and the subsequent read
- * returns the expected data.
- */
-TYPED_TEST(UnsynchronizedWriteTest, LargeInputTest1) {
- std::vector<uint8_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
-
-/*
- * Attempt to write a chunk of data larger than the queue size.
- * Verify that it fails. Verify that a subsequent read fails and
- * the queue is still empty.
- */
-TYPED_TEST(UnsynchronizedWriteTest, LargeInputTest2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t dataLen = 4096;
- ASSERT_GT(dataLen, this->mNumMessagesMax);
- std::vector<uint8_t> data(dataLen);
- initData(&data[0], dataLen);
- ASSERT_FALSE(this->mQueue->write(&data[0], dataLen));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_NE(data, readData);
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
-}
-
-/*
- * After the queue is full, try to write more data. Verify that
- * the attempt is succesful. Verify that the read fails
- * as expected.
- */
-TYPED_TEST(UnsynchronizedWriteTest, LargeInputTest3) {
- std::vector<uint8_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_TRUE(this->mQueue->write(&data[0], 1));
- std::vector<uint8_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
-}
-
-/*
- * Verify that multiple reads one after the other return expected data.
- */
-TYPED_TEST(UnsynchronizedWriteTest, MultipleRead) {
- const size_t chunkSize = 100;
- const size_t chunkNum = 5;
- const size_t dataLen = chunkSize * chunkNum;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
- initData(data, dataLen);
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- uint8_t readData[dataLen] = {};
- for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
- }
- ASSERT_EQ(0, memcmp(readData, data, dataLen));
-}
-
-/*
- * Verify that multiple writes one after the other happens correctly.
- */
-TYPED_TEST(UnsynchronizedWriteTest, MultipleWrite) {
- const size_t chunkSize = 100;
- const size_t chunkNum = 5;
- const size_t dataLen = chunkSize * chunkNum;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- uint8_t data[dataLen];
-
- initData(data, dataLen);
- for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->write(data + i * chunkSize, chunkSize));
- }
-
- uint8_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
- ASSERT_EQ(0, memcmp(readData, data, dataLen));
-}
-
-/*
- * Write enough messages into the FMQ to fill half of it
- * and read back the same.
- * Write mNumMessagesMax messages into the queue. This will cause a
- * wrap around. Read and verify the data.
- */
-TYPED_TEST(UnsynchronizedWriteTest, ReadWriteWrapAround) {
- size_t numMessages = this->mNumMessagesMax - 1;
- std::vector<uint8_t> data(this->mNumMessagesMax);
- std::vector<uint8_t> readData(this->mNumMessagesMax);
-
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
- ASSERT_TRUE(this->mQueue->read(&readData[0], numMessages));
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_TRUE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
- ASSERT_EQ(data, readData);
-}
diff --git a/tests/mq_test.cpp b/tests/mq_test.cpp
new file mode 100644
index 0000000..96528d3
--- /dev/null
+++ b/tests/mq_test.cpp
@@ -0,0 +1,809 @@
+/*
+ * 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.
+ */
+
+#include <asm-generic/mman.h>
+#include <gtest/gtest.h>
+#include <atomic>
+#include <cstdlib>
+#include <sstream>
+#include <thread>
+#include <fmq/MessageQueue.h>
+#include <fmq/EventFlag.h>
+
+enum EventFlagBits : uint32_t {
+ kFmqNotEmpty = 1 << 0,
+ kFmqNotFull = 1 << 1,
+};
+
+typedef android::hardware::MessageQueue<uint8_t, android::hardware::kSynchronizedReadWrite>
+ MessageQueueSync;
+typedef android::hardware::MessageQueue<uint8_t, android::hardware::kUnsynchronizedWrite>
+ MessageQueueUnsync;
+
+class SynchronizedReadWrites : public ::testing::Test {
+protected:
+ virtual void TearDown() {
+ delete mQueue;
+ }
+
+ virtual void SetUp() {
+ static constexpr size_t kNumElementsInQueue = 2048;
+ mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue);
+ ASSERT_NE(nullptr, mQueue);
+ ASSERT_TRUE(mQueue->isValid());
+ mNumMessagesMax = mQueue->getQuantumCount();
+ ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
+ }
+
+ MessageQueueSync* mQueue = nullptr;
+ size_t mNumMessagesMax = 0;
+};
+
+class UnsynchronizedWrite : public ::testing::Test {
+protected:
+ virtual void TearDown() {
+ delete mQueue;
+ }
+
+ virtual void SetUp() {
+ static constexpr size_t kNumElementsInQueue = 2048;
+ mQueue = new (std::nothrow) MessageQueueUnsync(kNumElementsInQueue);
+ ASSERT_NE(nullptr, mQueue);
+ ASSERT_TRUE(mQueue->isValid());
+ mNumMessagesMax = mQueue->getQuantumCount();
+ ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
+ }
+
+ MessageQueueUnsync* mQueue = nullptr;
+ size_t mNumMessagesMax = 0;
+};
+
+class BlockingReadWrites : public ::testing::Test {
+protected:
+ virtual void TearDown() {
+ delete mQueue;
+ }
+ virtual void SetUp() {
+ static constexpr size_t kNumElementsInQueue = 2048;
+ mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue);
+ ASSERT_NE(nullptr, mQueue);
+ ASSERT_TRUE(mQueue->isValid());
+ mNumMessagesMax = mQueue->getQuantumCount();
+ ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
+ /*
+ * Initialize the EventFlag word to indicate Queue is not full.
+ */
+ std::atomic_init(&mFw, static_cast<uint32_t>(kFmqNotFull));
+ }
+
+ MessageQueueSync* mQueue;
+ std::atomic<uint32_t> mFw;
+ size_t mNumMessagesMax = 0;
+};
+
+class QueueSizeOdd : public ::testing::Test {
+protected:
+ virtual void TearDown() {
+ delete mQueue;
+ }
+ virtual void SetUp() {
+ static constexpr size_t kNumElementsInQueue = 2049;
+ mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue,
+ true /* configureEventFlagWord */);
+ ASSERT_NE(nullptr, mQueue);
+ ASSERT_TRUE(mQueue->isValid());
+ mNumMessagesMax = mQueue->getQuantumCount();
+ ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
+ auto evFlagWordPtr = mQueue->getEventFlagWord();
+ ASSERT_NE(nullptr, evFlagWordPtr);
+ /*
+ * Initialize the EventFlag word to indicate Queue is not full.
+ */
+ std::atomic_init(evFlagWordPtr, static_cast<uint32_t>(kFmqNotFull));
+ }
+
+ MessageQueueSync* mQueue;
+ size_t mNumMessagesMax = 0;
+};
+
+class BadQueueConfig: public ::testing::Test {
+};
+
+/*
+ * Utility function to initialize data to be written to the FMQ
+ */
+inline void initData(uint8_t* data, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ data[i] = i & 0xFF;
+ }
+}
+
+/*
+ * This thread will attempt to read and block. When wait returns
+ * it checks if the kFmqNotEmpty bit is actually set.
+ * If the read is succesful, it signals Wake to kFmqNotFull.
+ */
+void ReaderThreadBlocking(
+ android::hardware::MessageQueue<uint8_t,
+ android::hardware::kSynchronizedReadWrite>* fmq,
+ std::atomic<uint32_t>* fwAddr) {
+ const size_t dataLen = 64;
+ uint8_t data[dataLen];
+ android::hardware::EventFlag* efGroup = nullptr;
+ android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+ ASSERT_NE(nullptr, efGroup);
+
+ while (true) {
+ uint32_t efState = 0;
+ android::status_t ret = efGroup->wait(kFmqNotEmpty,
+ &efState,
+ 5000000000 /* timeoutNanoSeconds */);
+ /*
+ * Wait should not time out here after 5s
+ */
+ ASSERT_NE(android::TIMED_OUT, ret);
+
+ if ((efState & kFmqNotEmpty) && fmq->read(data, dataLen)) {
+ efGroup->wake(kFmqNotFull);
+ break;
+ }
+ }
+
+ status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+}
+
+/*
+ * This thread will attempt to read and block using the readBlocking() API and
+ * passes in a pointer to an EventFlag object.
+ */
+void ReaderThreadBlocking2(
+ android::hardware::MessageQueue<uint8_t,
+ android::hardware::kSynchronizedReadWrite>* fmq,
+ std::atomic<uint32_t>* fwAddr) {
+ const size_t dataLen = 64;
+ uint8_t data[dataLen];
+ android::hardware::EventFlag* efGroup = nullptr;
+ android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+ ASSERT_NE(nullptr, efGroup);
+ bool ret = fmq->readBlocking(data,
+ dataLen,
+ static_cast<uint32_t>(kFmqNotFull),
+ static_cast<uint32_t>(kFmqNotEmpty),
+ 5000000000 /* timeOutNanos */,
+ efGroup);
+ ASSERT_TRUE(ret);
+ status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+}
+
+
+TEST_F(BadQueueConfig, QueueSizeTooLarge) {
+ typedef android::hardware::MessageQueue<uint16_t, android::hardware::kSynchronizedReadWrite>
+ MessageQueueSync16;
+ size_t numElementsInQueue = SIZE_MAX / sizeof(uint16_t) + 1;
+ MessageQueueSync16 * fmq = new (std::nothrow) MessageQueueSync16(numElementsInQueue);
+ ASSERT_NE(nullptr, fmq);
+ /*
+ * Should fail due to size being too large to fit into size_t.
+ */
+ ASSERT_FALSE(fmq->isValid());
+}
+
+/*
+ * Test that basic blocking works. This test uses the non-blocking read()/write()
+ * APIs.
+ */
+TEST_F(BlockingReadWrites, SmallInputTest1) {
+ const size_t dataLen = 64;
+ uint8_t data[dataLen] = {0};
+
+ android::hardware::EventFlag* efGroup = nullptr;
+ android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup);
+
+ ASSERT_EQ(android::NO_ERROR, status);
+ ASSERT_NE(nullptr, efGroup);
+
+ /*
+ * Start a thread that will try to read and block on kFmqNotEmpty.
+ */
+ std::thread Reader(ReaderThreadBlocking, mQueue, &mFw);
+ struct timespec waitTime = {0, 100 * 1000000};
+ ASSERT_EQ(0, nanosleep(&waitTime, NULL));
+
+ /*
+ * After waiting for some time write into the FMQ
+ * and call Wake on kFmqNotEmpty.
+ */
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ status = efGroup->wake(kFmqNotEmpty);
+ ASSERT_EQ(android::NO_ERROR, status);
+
+ ASSERT_EQ(0, nanosleep(&waitTime, NULL));
+ Reader.join();
+
+ status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+}
+
+/*
+ * Test that basic blocking works. This test uses the
+ * writeBlocking()/readBlocking() APIs.
+ */
+TEST_F(BlockingReadWrites, SmallInputTest2) {
+ const size_t dataLen = 64;
+ uint8_t data[dataLen] = {0};
+
+ android::hardware::EventFlag* efGroup = nullptr;
+ android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup);
+
+ ASSERT_EQ(android::NO_ERROR, status);
+ ASSERT_NE(nullptr, efGroup);
+
+ /*
+ * Start a thread that will try to read and block on kFmqNotEmpty. It will
+ * call wake() on kFmqNotFull when the read is successful.
+ */
+ std::thread Reader(ReaderThreadBlocking2, mQueue, &mFw);
+ bool ret = mQueue->writeBlocking(data,
+ dataLen,
+ static_cast<uint32_t>(kFmqNotFull),
+ static_cast<uint32_t>(kFmqNotEmpty),
+ 5000000000 /* timeOutNanos */,
+ efGroup);
+ ASSERT_TRUE(ret);
+ Reader.join();
+
+ status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+}
+
+/*
+ * Test that basic blocking times out as intended.
+ */
+TEST_F(BlockingReadWrites, BlockingTimeOutTest) {
+ android::hardware::EventFlag* efGroup = nullptr;
+ android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup);
+
+ ASSERT_EQ(android::NO_ERROR, status);
+ ASSERT_NE(nullptr, efGroup);
+
+ /* Block on an EventFlag bit that no one will wake and time out in 1s */
+ uint32_t efState = 0;
+ android::status_t ret = efGroup->wait(kFmqNotEmpty,
+ &efState,
+ 1000000000 /* timeoutNanoSeconds */);
+ /*
+ * Wait should time out in a second.
+ */
+ EXPECT_EQ(android::TIMED_OUT, ret);
+
+ status = android::hardware::EventFlag::deleteEventFlag(&efGroup);
+ ASSERT_EQ(android::NO_ERROR, status);
+}
+
+/*
+ * Test that odd queue sizes do not cause unaligned error
+ * on access to EventFlag object.
+ */
+TEST_F(QueueSizeOdd, EventFlagTest) {
+ const size_t dataLen = 64;
+ uint8_t data[dataLen] = {0};
+
+ bool ret = mQueue->writeBlocking(data,
+ dataLen,
+ static_cast<uint32_t>(kFmqNotFull),
+ static_cast<uint32_t>(kFmqNotEmpty),
+ 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
+}
+
+/*
+ * Verify that a few bytes of data can be successfully written and read.
+ */
+TEST_F(SynchronizedReadWrites, SmallInputTest1) {
+ const size_t dataLen = 16;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ uint8_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
+ ASSERT_EQ(0, memcmp(data, readData, dataLen));
+}
+
+/*
+ * Verify that a few bytes of data can be successfully written and read using
+ * beginRead/beginWrite/CommitRead/CommitWrite
+ */
+TEST_F(SynchronizedReadWrites, SmallInputTest2) {
+ const size_t dataLen = 16;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(dataLen, &tx));
+
+ ASSERT_TRUE(tx.copyTo(data, 0 /* startIdx */, dataLen));
+
+ ASSERT_TRUE(mQueue->commitWrite(dataLen));
+
+ uint8_t readData[dataLen] = {};
+
+ ASSERT_TRUE(mQueue->beginRead(dataLen, &tx));
+
+ ASSERT_TRUE(tx.copyFrom(readData, 0 /* startIdx */, dataLen));
+
+ ASSERT_TRUE(mQueue->commitRead(dataLen));
+
+ ASSERT_EQ(0, memcmp(data, readData, dataLen));
+}
+
+/*
+ * Verify that a few bytes of data can be successfully written and read using
+ * beginRead/beginWrite/CommitRead/CommitWrite as well as getSlot().
+ */
+TEST_F(SynchronizedReadWrites, SmallInputTest3) {
+ const size_t dataLen = 16;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(dataLen, &tx));
+
+ auto first = tx.getFirstRegion();
+ auto second = tx.getSecondRegion();
+
+ ASSERT_EQ(first.getLength() + second.getLength(), dataLen);
+ for (size_t i = 0; i < dataLen; i++) {
+ uint8_t* ptr = tx.getSlot(i);
+ *ptr = data[i];
+ }
+
+ ASSERT_TRUE(mQueue->commitWrite(dataLen));
+
+ uint8_t readData[dataLen] = {};
+
+ ASSERT_TRUE(mQueue->beginRead(dataLen, &tx));
+
+ first = tx.getFirstRegion();
+ second = tx.getSecondRegion();
+
+ ASSERT_EQ(first.getLength() + second.getLength(), dataLen);
+
+ for (size_t i = 0; i < dataLen; i++) {
+ uint8_t* ptr = tx.getSlot(i);
+ readData[i] = *ptr;
+ }
+
+ ASSERT_TRUE(mQueue->commitRead(dataLen));
+
+ ASSERT_EQ(0, memcmp(data, readData, dataLen));
+}
+
+/*
+ * Verify that read() returns false when trying to read from an empty queue.
+ */
+TEST_F(SynchronizedReadWrites, ReadWhenEmpty1) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 2;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t readData[dataLen];
+ ASSERT_FALSE(mQueue->read(readData, dataLen));
+}
+
+/*
+ * Verify that beginRead() returns a MemTransaction object with null pointers when trying
+ * to read from an empty queue.
+ */
+TEST_F(SynchronizedReadWrites, ReadWhenEmpty2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 2;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_FALSE(mQueue->beginRead(dataLen, &tx));
+
+ auto first = tx.getFirstRegion();
+ auto second = tx.getSecondRegion();
+
+ ASSERT_EQ(nullptr, first.getAddress());
+ ASSERT_EQ(nullptr, second.getAddress());
+}
+
+/*
+ * Write the queue until full. Verify that another write is unsuccessful.
+ * Verify that availableToWrite() returns 0 as expected.
+ */
+TEST_F(SynchronizedReadWrites, WriteWhenFull1) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ std::vector<uint8_t> data(mNumMessagesMax);
+
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_FALSE(mQueue->write(&data[0], 1));
+
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Write the queue until full. Verify that beginWrite() returns
+ * a MemTransaction object with null base pointers.
+ */
+TEST_F(SynchronizedReadWrites, WriteWhenFull2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ std::vector<uint8_t> data(mNumMessagesMax);
+
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_FALSE(mQueue->beginWrite(1, &tx));
+
+ auto first = tx.getFirstRegion();
+ auto second = tx.getSecondRegion();
+
+ ASSERT_EQ(nullptr, first.getAddress());
+ ASSERT_EQ(nullptr, second.getAddress());
+}
+
+/*
+ * Write a chunk of data equal to the queue size.
+ * Verify that the write is successful and the subsequent read
+ * returns the expected data.
+ */
+TEST_F(SynchronizedReadWrites, LargeInputTest1) {
+ std::vector<uint8_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Attempt to write a chunk of data larger than the queue size.
+ * Verify that it fails. Verify that a subsequent read fails and
+ * the queue is still empty.
+ */
+TEST_F(SynchronizedReadWrites, LargeInputTest2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 4096;
+ ASSERT_GT(dataLen, mNumMessagesMax);
+ std::vector<uint8_t> data(dataLen);
+
+ initData(&data[0], dataLen);
+ ASSERT_FALSE(mQueue->write(&data[0], dataLen));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_NE(data, readData);
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+}
+
+/*
+ * After the queue is full, try to write more data. Verify that
+ * the attempt returns false. Verify that the attempt did not
+ * affect the pre-existing data in the queue.
+ */
+TEST_F(SynchronizedReadWrites, LargeInputTest3) {
+ std::vector<uint8_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_FALSE(mQueue->write(&data[0], 1));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Verify that beginWrite() returns a MemTransaction with
+ * null base pointers when attempting to write data larger
+ * than the queue size.
+ */
+TEST_F(SynchronizedReadWrites, LargeInputTest4) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 4096;
+ ASSERT_GT(dataLen, mNumMessagesMax);
+
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_FALSE(mQueue->beginWrite(dataLen, &tx));
+
+ auto first = tx.getFirstRegion();
+ auto second = tx.getSecondRegion();
+
+ ASSERT_EQ(nullptr, first.getAddress());
+ ASSERT_EQ(nullptr, second.getAddress());
+}
+
+/*
+ * Verify that multiple reads one after the other return expected data.
+ */
+TEST_F(SynchronizedReadWrites, MultipleRead) {
+ const size_t chunkSize = 100;
+ const size_t chunkNum = 5;
+ const size_t dataLen = chunkSize * chunkNum;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ uint8_t readData[dataLen] = {};
+ for (size_t i = 0; i < chunkNum; i++) {
+ ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize));
+ }
+ ASSERT_EQ(0, memcmp(readData, data, dataLen));
+}
+
+/*
+ * Verify that multiple writes one after the other happens correctly.
+ */
+TEST_F(SynchronizedReadWrites, MultipleWrite) {
+ const int chunkSize = 100;
+ const int chunkNum = 5;
+ const size_t dataLen = chunkSize * chunkNum;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+ for (unsigned int i = 0; i < chunkNum; i++) {
+ ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize));
+ }
+ uint8_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
+ ASSERT_EQ(0, memcmp(readData, data, dataLen));
+}
+
+/*
+ * Write enough messages into the FMQ to fill half of it
+ * and read back the same.
+ * Write mNumMessagesMax messages into the queue. This will cause a
+ * wrap around. Read and verify the data.
+ */
+TEST_F(SynchronizedReadWrites, ReadWriteWrapAround1) {
+ size_t numMessages = mNumMessagesMax - 1;
+ std::vector<uint8_t> data(mNumMessagesMax);
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], numMessages));
+ ASSERT_TRUE(mQueue->read(&readData[0], numMessages));
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Use beginRead/CommitRead/beginWrite/commitWrite APIs
+ * to test wrap arounds are handled correctly.
+ * Write enough messages into the FMQ to fill half of it
+ * and read back the same.
+ * Write mNumMessagesMax messages into the queue. This will cause a
+ * wrap around. Read and verify the data.
+ */
+TEST_F(SynchronizedReadWrites, ReadWriteWrapAround2) {
+ size_t dataLen = mNumMessagesMax - 1;
+ std::vector<uint8_t> data(mNumMessagesMax);
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], dataLen));
+ ASSERT_TRUE(mQueue->read(&readData[0], dataLen));
+
+ /*
+ * The next write and read will have to deal with with wrap arounds.
+ */
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(mNumMessagesMax, &tx));
+
+ auto first = tx.getFirstRegion();
+ auto second = tx.getSecondRegion();
+
+ ASSERT_EQ(first.getLength() + second.getLength(), mNumMessagesMax);
+
+ ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */, mNumMessagesMax));
+
+ ASSERT_TRUE(mQueue->commitWrite(mNumMessagesMax));
+
+ ASSERT_TRUE(mQueue->beginRead(mNumMessagesMax, &tx));
+
+ first = tx.getFirstRegion();
+ second = tx.getSecondRegion();
+
+ ASSERT_EQ(first.getLength() + second.getLength(), mNumMessagesMax);
+
+ ASSERT_TRUE(tx.copyFrom(&readData[0], 0 /* startIdx */, mNumMessagesMax));
+ ASSERT_TRUE(mQueue->commitRead(mNumMessagesMax));
+
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Verify that a few bytes of data can be successfully written and read.
+ */
+TEST_F(UnsynchronizedWrite, SmallInputTest1) {
+ const size_t dataLen = 16;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ uint8_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
+ ASSERT_EQ(0, memcmp(data, readData, dataLen));
+}
+
+/*
+ * Verify that read() returns false when trying to read from an empty queue.
+ */
+TEST_F(UnsynchronizedWrite, ReadWhenEmpty) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 2;
+ ASSERT_TRUE(dataLen < mNumMessagesMax);
+ uint8_t readData[dataLen];
+ ASSERT_FALSE(mQueue->read(readData, dataLen));
+}
+
+/*
+ * Write the queue when full. Verify that a subsequent writes is succesful.
+ * Verify that availableToWrite() returns 0 as expected.
+ */
+TEST_F(UnsynchronizedWrite, WriteWhenFull1) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ std::vector<uint8_t> data(mNumMessagesMax);
+
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_TRUE(mQueue->write(&data[0], 1));
+
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
+}
+
+/*
+ * Write the queue when full. Verify that a subsequent writes
+ * using beginRead()/commitRead() is succesful.
+ * Verify that the next read fails as expected for unsynchronized flavor.
+ */
+TEST_F(UnsynchronizedWrite, WriteWhenFull2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ std::vector<uint8_t> data(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+
+ MessageQueueUnsync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(1, &tx));
+
+ ASSERT_EQ(tx.getFirstRegion().getLength(), 1U);
+
+ ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */));
+
+ ASSERT_TRUE(mQueue->commitWrite(1));
+
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
+}
+
+/*
+ * Write a chunk of data equal to the queue size.
+ * Verify that the write is successful and the subsequent read
+ * returns the expected data.
+ */
+TEST_F(UnsynchronizedWrite, LargeInputTest1) {
+ std::vector<uint8_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
+
+/*
+ * Attempt to write a chunk of data larger than the queue size.
+ * Verify that it fails. Verify that a subsequent read fails and
+ * the queue is still empty.
+ */
+TEST_F(UnsynchronizedWrite, LargeInputTest2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t dataLen = 4096;
+ ASSERT_GT(dataLen, mNumMessagesMax);
+ std::vector<uint8_t> data(dataLen);
+ initData(&data[0], dataLen);
+ ASSERT_FALSE(mQueue->write(&data[0], dataLen));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_NE(data, readData);
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+}
+
+/*
+ * After the queue is full, try to write more data. Verify that
+ * the attempt is succesful. Verify that the read fails
+ * as expected.
+ */
+TEST_F(UnsynchronizedWrite, LargeInputTest3) {
+ std::vector<uint8_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_TRUE(mQueue->write(&data[0], 1));
+ std::vector<uint8_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
+}
+
+/*
+ * Verify that multiple reads one after the other return expected data.
+ */
+TEST_F(UnsynchronizedWrite, MultipleRead) {
+ const size_t chunkSize = 100;
+ const size_t chunkNum = 5;
+ const size_t dataLen = chunkSize * chunkNum;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+ initData(data, dataLen);
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ uint8_t readData[dataLen] = {};
+ for (size_t i = 0; i < chunkNum; i++) {
+ ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize));
+ }
+ ASSERT_EQ(0, memcmp(readData, data, dataLen));
+}
+
+/*
+ * Verify that multiple writes one after the other happens correctly.
+ */
+TEST_F(UnsynchronizedWrite, MultipleWrite) {
+ const size_t chunkSize = 100;
+ const size_t chunkNum = 5;
+ const size_t dataLen = chunkSize * chunkNum;
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint8_t data[dataLen];
+
+ initData(data, dataLen);
+ for (size_t i = 0; i < chunkNum; i++) {
+ ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize));
+ }
+
+ uint8_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
+ ASSERT_EQ(0, memcmp(readData, data, dataLen));
+}
+
+/*
+ * Write enough messages into the FMQ to fill half of it
+ * and read back the same.
+ * Write mNumMessagesMax messages into the queue. This will cause a
+ * wrap around. Read and verify the data.
+ */
+TEST_F(UnsynchronizedWrite, ReadWriteWrapAround) {
+ size_t numMessages = mNumMessagesMax - 1;
+ std::vector<uint8_t> data(mNumMessagesMax);
+ std::vector<uint8_t> readData(mNumMessagesMax);
+
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], numMessages));
+ ASSERT_TRUE(mQueue->read(&readData[0], numMessages));
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_EQ(data, readData);
+}
diff --git a/tests/msgq_test_client.cpp b/tests/msgq_test_client.cpp
index a6f1ccc..271586a 100644
--- a/tests/msgq_test_client.cpp
+++ b/tests/msgq_test_client.cpp
@@ -19,13 +19,7 @@
#error "GTest did not detect pthread library."
#endif
-#include <aidl/android/fmq/test/FixedParcelable.h>
-#include <aidl/android/fmq/test/ITestAidlMsgQ.h>
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
#include <android/hardware/tests/msgq/1.0/ITestMsgQ.h>
-#include <fmq/AidlMessageQueue.h>
#include <fmq/EventFlag.h>
#include <fmq/MessageQueue.h>
#include <hidl/ServiceManagement.h>
@@ -36,9 +30,6 @@
using android::status_t;
// generated
-using ::aidl::android::fmq::test::EventFlagBits;
-using ::aidl::android::fmq::test::FixedParcelable;
-using ::aidl::android::fmq::test::ITestAidlMsgQ;
using android::hardware::tests::msgq::V1_0::ITestMsgQ;
// libhidl
@@ -49,258 +40,87 @@
using android::hardware::MQDescriptorUnsync;
using android::hardware::details::waitForHwService;
-using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
-using android::hardware::kSynchronizedReadWrite;
-using android::hardware::kUnsynchronizedWrite;
+typedef MessageQueue<uint16_t, kSynchronizedReadWrite> MessageQueueSync;
+typedef MessageQueue<uint16_t, kUnsynchronizedWrite> MessageQueueUnsync;
-typedef android::AidlMessageQueue<int32_t, SynchronizedReadWrite> AidlMessageQueueSync;
-typedef android::AidlMessageQueue<int32_t, UnsynchronizedWrite> AidlMessageQueueUnsync;
-typedef android::hardware::MessageQueue<int32_t, kSynchronizedReadWrite> MessageQueueSync;
-typedef android::hardware::MessageQueue<int32_t, kUnsynchronizedWrite> MessageQueueUnsync;
-static const std::string kServiceName = "BnTestAidlMsgQ";
-static constexpr size_t kNumElementsInSyncQueue = (PAGE_SIZE - 16) / sizeof(int32_t);
+static sp<ITestMsgQ> waitGetTestService() {
+ // waitForHwService is required because ITestMsgQ is not in manifest.xml.
+ // "Real" HALs shouldn't be doing this.
+ waitForHwService(ITestMsgQ::descriptor, "default");
+ return ITestMsgQ::getService();
+}
-enum class SetupType {
- SINGLE_FD,
- DOUBLE_FD,
+class UnsynchronizedWriteClientMultiProcess : public ::testing::Test {
+ protected:
+ void getQueue(MessageQueueUnsync** fmq, sp<ITestMsgQ>* service, bool setupQueue) {
+ *service = waitGetTestService();
+ *fmq = nullptr;
+ if (*service == nullptr) return;
+ if (!(*service)->isRemote()) return;
+ (*service)->getFmqUnsyncWrite(setupQueue,
+ [fmq](bool ret, const MQDescriptorUnsync<uint16_t>& in) {
+ ASSERT_TRUE(ret);
+ *fmq = new (std::nothrow) MessageQueueUnsync(in);
+ });
+ }
};
-template <typename T, SetupType setupType>
-class TestParamTypes {
- public:
- typedef T MQType;
- static constexpr bool UserFd = setupType == SetupType::DOUBLE_FD;
-};
-
-// Run everything on both the AIDL and HIDL versions with one and two FDs
-typedef ::testing::Types<TestParamTypes<AidlMessageQueueSync, SetupType::SINGLE_FD>,
- TestParamTypes<MessageQueueSync, SetupType::SINGLE_FD>,
- TestParamTypes<AidlMessageQueueSync, SetupType::DOUBLE_FD>,
- TestParamTypes<MessageQueueSync, SetupType::DOUBLE_FD>>
- SyncTypes;
-typedef ::testing::Types<TestParamTypes<AidlMessageQueueUnsync, SetupType::SINGLE_FD>,
- TestParamTypes<MessageQueueUnsync, SetupType::SINGLE_FD>,
- TestParamTypes<AidlMessageQueueUnsync, SetupType::DOUBLE_FD>,
- TestParamTypes<MessageQueueUnsync, SetupType::DOUBLE_FD>>
- UnsyncTypes;
-
-template <typename T>
-class ClientSyncTestBase : public ::testing::Test {};
-
-// Specialize for AIDL
-template <>
-class ClientSyncTestBase<AidlMessageQueueSync> : public ::testing::Test {
- protected:
- static std::shared_ptr<ITestAidlMsgQ> waitGetTestService() {
- const std::string instance = std::string() + ITestAidlMsgQ::descriptor + "/default";
- ndk::SpAIBinder binder(AServiceManager_getService(instance.c_str()));
- return ITestAidlMsgQ::fromBinder(binder);
- }
- bool configureFmqSyncReadWrite(AidlMessageQueueSync* mq) {
- bool result = false;
- auto ret = mService->configureFmqSyncReadWrite(mq->dupeDesc(), &result);
- return result && ret.isOk();
- }
- bool requestReadFmqSync(size_t dataLen) {
- bool result = false;
- auto ret = mService->requestReadFmqSync(dataLen, &result);
- return result && ret.isOk();
- }
- bool requestWriteFmqSync(size_t dataLen) {
- bool result = false;
- auto ret = mService->requestWriteFmqSync(dataLen, &result);
- return result && ret.isOk();
- }
-
- std::shared_ptr<ITestAidlMsgQ> mService;
-};
-
-// Specialize for HIDL
-template <>
-class ClientSyncTestBase<MessageQueueSync> : public ::testing::Test {
- protected:
- static sp<ITestMsgQ> waitGetTestService() {
- android::hardware::details::setTrebleTestingOverride(true);
- // waitForHwService is required because ITestMsgQ is not in manifest.xml.
- // "Real" HALs shouldn't be doing this.
- waitForHwService(ITestMsgQ::descriptor, "default");
- return ITestMsgQ::getService();
- }
- bool configureFmqSyncReadWrite(MessageQueueSync* mq) {
- auto ret = mService->configureFmqSyncReadWrite(*mq->getDesc());
- return ret && ret.isOk();
- }
- bool requestReadFmqSync(size_t dataLen) {
- auto ret = mService->requestReadFmqSync(dataLen);
- return ret && ret.isOk();
- }
- bool requestWriteFmqSync(size_t dataLen) {
- auto ret = mService->requestWriteFmqSync(dataLen);
- return ret && ret.isOk();
- }
-
- sp<ITestMsgQ> mService;
-};
-
-template <typename T>
-class ClientUnsyncTestBase : public ::testing::Test {};
-
-// Specialize for AIDL
-template <>
-class ClientUnsyncTestBase<AidlMessageQueueUnsync> : public ::testing::Test {
- protected:
- static std::shared_ptr<ITestAidlMsgQ> waitGetTestService() {
- const std::string instance = std::string() + ITestAidlMsgQ::descriptor + "/default";
- ndk::SpAIBinder binder(AServiceManager_getService(instance.c_str()));
- return ITestAidlMsgQ::fromBinder(binder);
- }
- bool getFmqUnsyncWrite(bool configureFmq, bool userFd, std::shared_ptr<ITestAidlMsgQ> service,
- AidlMessageQueueUnsync** queue) {
- bool result = false;
- aidl::android::hardware::common::fmq::MQDescriptor<int32_t, UnsynchronizedWrite> desc;
- auto ret = service->getFmqUnsyncWrite(configureFmq, userFd, &desc, &result);
- *queue = new (std::nothrow) AidlMessageQueueUnsync(desc);
- return result && ret.isOk();
- }
-
- std::shared_ptr<ITestAidlMsgQ> getQueue(AidlMessageQueueUnsync** fmq, bool setupQueue,
- bool userFd) {
- std::shared_ptr<ITestAidlMsgQ> service = waitGetTestService();
- if (service == nullptr) return nullptr;
- getFmqUnsyncWrite(setupQueue, userFd, service, fmq);
- return service;
- }
-
- bool requestReadFmqUnsync(size_t dataLen, std::shared_ptr<ITestAidlMsgQ> service) {
- bool result = false;
- auto ret = service->requestReadFmqUnsync(dataLen, &result);
- return result && ret.isOk();
- }
- bool requestWriteFmqUnsync(size_t dataLen, std::shared_ptr<ITestAidlMsgQ> service) {
- bool result = false;
- auto ret = service->requestWriteFmqUnsync(dataLen, &result);
- return result && ret.isOk();
- }
- AidlMessageQueueUnsync* newQueue() {
- if (mQueue->isValid())
- return new (std::nothrow) AidlMessageQueueUnsync(mQueue->dupeDesc());
- else
- return nullptr;
- }
-
- std::shared_ptr<ITestAidlMsgQ> mService;
- AidlMessageQueueUnsync* mQueue = nullptr;
-};
-
-// Specialize for HIDL
-template <>
-class ClientUnsyncTestBase<MessageQueueUnsync> : public ::testing::Test {
- protected:
- static sp<ITestMsgQ> waitGetTestService() {
- android::hardware::details::setTrebleTestingOverride(true);
- // waitForHwService is required because ITestMsgQ is not in manifest.xml.
- // "Real" HALs shouldn't be doing this.
- waitForHwService(ITestMsgQ::descriptor, "default");
- return ITestMsgQ::getService();
- }
- bool getFmqUnsyncWrite(bool configureFmq, bool userFd, sp<ITestMsgQ> service,
- MessageQueueUnsync** queue) {
- if (!service) {
- return false;
- }
- service->getFmqUnsyncWrite(configureFmq, userFd,
- [queue](bool ret, const MQDescriptorUnsync<int32_t>& in) {
- ASSERT_TRUE(ret);
- *queue = new (std::nothrow) MessageQueueUnsync(in);
- });
- return true;
- }
-
- sp<ITestMsgQ> getQueue(MessageQueueUnsync** fmq, bool setupQueue, bool userFd) {
- sp<ITestMsgQ> service = waitGetTestService();
- if (service == nullptr) return nullptr;
- getFmqUnsyncWrite(setupQueue, userFd, service, fmq);
- return service;
- }
-
- bool requestReadFmqUnsync(size_t dataLen, sp<ITestMsgQ> service) {
- auto ret = service->requestReadFmqUnsync(dataLen);
- return ret && ret.isOk();
- }
- bool requestWriteFmqUnsync(size_t dataLen, sp<ITestMsgQ> service) {
- auto ret = service->requestWriteFmqUnsync(dataLen);
- return ret && ret.isOk();
- }
-
- MessageQueueUnsync* newQueue() {
- return new (std::nothrow) MessageQueueUnsync(*mQueue->getDesc());
- }
-
- sp<ITestMsgQ> mService;
- MessageQueueUnsync* mQueue = nullptr;
-};
-
-TYPED_TEST_CASE(UnsynchronizedWriteClientMultiProcess, UnsyncTypes);
-template <typename T>
-class UnsynchronizedWriteClientMultiProcess : public ClientUnsyncTestBase<typename T::MQType> {};
-
-TYPED_TEST_CASE(SynchronizedReadWriteClient, SyncTypes);
-template <typename T>
-class SynchronizedReadWriteClient : public ClientSyncTestBase<typename T::MQType> {
- protected:
+class SynchronizedReadWriteClient : public ::testing::Test {
+protected:
virtual void TearDown() {
delete mQueue;
}
virtual void SetUp() {
- this->mService = this->waitGetTestService();
- ASSERT_NE(this->mService, nullptr);
- ASSERT_TRUE(this->mService->isRemote());
- static constexpr size_t kSyncElementSizeBytes = sizeof(int32_t);
- android::base::unique_fd ringbufferFd;
- if (T::UserFd) {
- ringbufferFd.reset(::ashmem_create_region(
- "SyncReadWrite", kNumElementsInSyncQueue * kSyncElementSizeBytes));
- }
+ static constexpr size_t kNumElementsInQueue = (PAGE_SIZE - 16) / sizeof(uint16_t);
+ mService = waitGetTestService();
+ ASSERT_NE(mService, nullptr);
+ ASSERT_TRUE(mService->isRemote());
// create a queue on the client side
- mQueue = new (std::nothrow) typename T::MQType(
- kNumElementsInSyncQueue, true /* configure event flag word */,
- std::move(ringbufferFd), kNumElementsInSyncQueue * kSyncElementSizeBytes);
+ mQueue = new (std::nothrow)
+ MessageQueueSync(kNumElementsInQueue, true /* configure event flag word */);
ASSERT_NE(nullptr, mQueue);
ASSERT_TRUE(mQueue->isValid());
- ASSERT_EQ(mQueue->getQuantumCount(), kNumElementsInSyncQueue);
+ mNumMessagesMax = mQueue->getQuantumCount();
// tell server to set up the queue on its end
- ASSERT_TRUE(this->configureFmqSyncReadWrite(mQueue));
+ ASSERT_TRUE(mService->configureFmqSyncReadWrite(*mQueue->getDesc()));
}
- typename T::MQType* mQueue = nullptr;
+ sp<ITestMsgQ> mService;
+ MessageQueueSync* mQueue = nullptr;
+ size_t mNumMessagesMax = 0;
};
-TYPED_TEST_CASE(UnsynchronizedWriteClient, UnsyncTypes);
-template <typename T>
-class UnsynchronizedWriteClient : public ClientUnsyncTestBase<typename T::MQType> {
- protected:
- virtual void TearDown() { delete this->mQueue; }
-
- virtual void SetUp() {
- this->mService = this->waitGetTestService();
- ASSERT_NE(this->mService, nullptr);
- ASSERT_TRUE(this->mService->isRemote());
- this->getFmqUnsyncWrite(true, false, this->mService, &this->mQueue);
- ASSERT_NE(nullptr, this->mQueue);
- ASSERT_TRUE(this->mQueue->isValid());
- mNumMessagesMax = this->mQueue->getQuantumCount();
+class UnsynchronizedWriteClient : public ::testing::Test {
+protected:
+ virtual void TearDown() {
+ delete mQueue;
}
+ virtual void SetUp() {
+ mService = waitGetTestService();
+ ASSERT_NE(mService, nullptr);
+ ASSERT_TRUE(mService->isRemote());
+ mService->getFmqUnsyncWrite(true /* configureFmq */,
+ [this](bool ret, const MQDescriptorUnsync<uint16_t>& in) {
+ ASSERT_TRUE(ret);
+ mQueue = new (std::nothrow) MessageQueueUnsync(in);
+ });
+ ASSERT_NE(nullptr, mQueue);
+ ASSERT_TRUE(mQueue->isValid());
+ mNumMessagesMax = mQueue->getQuantumCount();
+ }
+
+ sp<ITestMsgQ> mService;
+ MessageQueueUnsync* mQueue = nullptr;
size_t mNumMessagesMax = 0;
};
/*
* Utility function to verify data read from the fast message queue.
*/
-bool verifyData(int32_t* data, size_t count) {
+bool verifyData(uint16_t* data, size_t count) {
for (size_t i = 0; i < count; i++) {
if (data[i] != i) return false;
}
@@ -310,7 +130,7 @@
/*
* Utility function to initialize data to be written to the FMQ
*/
-inline void initData(int32_t* data, size_t count) {
+inline void initData(uint16_t* data, size_t count) {
for (size_t i = 0; i < count; i++) {
data[i] = i;
}
@@ -320,31 +140,33 @@
* Verify that for an unsynchronized flavor of FMQ, multiple readers
* can recover from a write overflow condition.
*/
-TYPED_TEST(UnsynchronizedWriteClientMultiProcess, MultipleReadersAfterOverflow) {
+TEST_F(UnsynchronizedWriteClientMultiProcess, MultipleReadersAfterOverflow) {
const size_t dataLen = 16;
pid_t pid;
/* creating first reader process */
if ((pid = fork()) == 0) {
- typename TypeParam::MQType* queue = nullptr;
- auto service =
- this->getQueue(&queue, true /* setupQueue */, TypeParam::UserFd /* userFd */);
- ASSERT_NE(service, nullptr);
- ASSERT_TRUE(service->isRemote());
+ sp<ITestMsgQ> testService;
+ MessageQueueUnsync* queue = nullptr;
+ getQueue(&queue, &testService, true /* setupQueue */);
+ ASSERT_NE(testService, nullptr);
+ ASSERT_TRUE(testService->isRemote());
ASSERT_NE(queue, nullptr);
ASSERT_TRUE(queue->isValid());
size_t numMessagesMax = queue->getQuantumCount();
// The following two writes will cause a write overflow.
- auto ret = this->requestWriteFmqUnsync(numMessagesMax, service);
+ auto ret = testService->requestWriteFmqUnsync(numMessagesMax);
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
- ret = this->requestWriteFmqUnsync(1, service);
+ ret = testService->requestWriteFmqUnsync(1);
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
// The following read should fail due to the overflow.
- std::vector<int32_t> readData(numMessagesMax);
+ std::vector<uint16_t> readData(numMessagesMax);
ASSERT_FALSE(queue->read(&readData[0], numMessagesMax));
/*
@@ -352,7 +174,8 @@
* overflow condition.
*/
ASSERT_LT(dataLen, numMessagesMax);
- ret = this->requestWriteFmqUnsync(dataLen, service);
+ ret = testService->requestWriteFmqUnsync(dataLen);
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
// Verify that the read is successful.
@@ -371,22 +194,25 @@
// creating second reader process.
if ((pid = fork()) == 0) {
- typename TypeParam::MQType* queue = nullptr;
- auto service = this->getQueue(&queue, false /* setupQueue */, false /* userFd */);
- ASSERT_NE(service, nullptr);
- ASSERT_TRUE(service->isRemote());
+ sp<ITestMsgQ> testService;
+ MessageQueueUnsync* queue = nullptr;
+
+ getQueue(&queue, &testService, false /* setupQueue */);
+ ASSERT_NE(testService, nullptr);
+ ASSERT_TRUE(testService->isRemote());
ASSERT_NE(queue, nullptr);
ASSERT_TRUE(queue->isValid());
// This read should fail due to the write overflow.
- std::vector<int32_t> readData(dataLen);
+ std::vector<uint16_t> readData(dataLen);
ASSERT_FALSE(queue->read(&readData[0], dataLen));
/*
* Request another write to verify that the process that recover from
* the overflow condition.
*/
- auto ret = this->requestWriteFmqUnsync(dataLen, service);
+ auto ret = testService->requestWriteFmqUnsync(dataLen);
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
// verify that the read is successful.
@@ -405,32 +231,26 @@
* Test that basic blocking works using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ.
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWrite1) {
+TEST_F(SynchronizedReadWriteClient, BlockingReadWrite1) {
const size_t dataLen = 64;
- bool ret = false;
+ uint16_t data[dataLen] = {0};
+
/*
* Request service to perform a blocking read. This call is oneway and will
* return immediately.
*/
- this->mService->requestBlockingRead(dataLen);
- {
- std::array<int32_t, dataLen> data = {0};
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
- {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
+ mService->requestBlockingRead(dataLen);
+ bool ret = mQueue->writeBlocking(data,
+ dataLen,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
+ ret = mQueue->writeBlocking(data, mNumMessagesMax,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
}
/*
@@ -438,34 +258,30 @@
* using the EventFlag object owned by FMQ and using the default EventFlag
* notification bit mask.
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWrite2) {
+TEST_F(SynchronizedReadWriteClient, BlockingReadWrite2) {
const size_t dataLen = 64;
- bool ret = false;
+ std::vector<uint16_t> data(mNumMessagesMax);
/*
* Request service to perform a blocking read using default EventFlag
* notification bit mask. This call is oneway and will
* return immediately.
*/
- this->mService->requestBlockingReadDefaultEventFlagBits(dataLen);
+ mService->requestBlockingReadDefaultEventFlagBits(dataLen);
/* Cause a context switch to allow service to block */
sched_yield();
- {
- std::array<int32_t, dataLen> data = {0};
- ret = this->mQueue->writeBlocking(data.data(), data.size());
- ASSERT_TRUE(ret);
- }
+
+ bool ret = mQueue->writeBlocking(&data[0],
+ dataLen);
+ ASSERT_TRUE(ret);
/*
* If the blocking read was successful, another write of size
- * kNumElementsInSyncQueue will succeed.
+ * mNumMessagesMax will succeed.
*/
- {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- ret = this->mQueue->writeBlocking(data.data(), data.size(), 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
+ ret = mQueue->writeBlocking(&data[0], mNumMessagesMax, 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
}
/*
@@ -474,44 +290,32 @@
* Each write operation writes the same amount of data as a single read
* operation.
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat1) {
+TEST_F(SynchronizedReadWriteClient, BlockingReadWriteRepeat1) {
const size_t dataLen = 64;
+ uint16_t data[dataLen] = {0};
bool ret = false;
/*
- * Request service to perform a blocking read of 64 elements. This call is
- * oneway and will return immediately.
+ * Request service to perform a blocking read. This call is oneway and will
+ * return immediately.
*/
- const size_t writeCount = kNumElementsInSyncQueue;
- this->mService->requestBlockingReadRepeat(dataLen, writeCount);
- /*
- * Write 64 elements into the queue for the service to consume
- */
- {
- std::array<int32_t, dataLen> data = {0};
- for (size_t i = 0; i < writeCount; i++) {
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
- }
- /*
- * The queue should be totally empty now, so filling it up entirely with one
- * blocking write should be successful.
- */
- {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
+ const size_t writeCount = 1024;
+ mService->requestBlockingReadRepeat(dataLen, writeCount);
+ for (size_t i = 0; i < writeCount; i++) {
+ ret = mQueue->writeBlocking(data, dataLen,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
+
+ ret = mQueue->writeBlocking(data, mNumMessagesMax,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
+
+ ASSERT_TRUE(ret);
}
/*
@@ -520,43 +324,31 @@
* amount of data as a single write.
*
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat2) {
+TEST_F(SynchronizedReadWriteClient, BlockingReadWriteRepeat2) {
const size_t dataLen = 64;
+ uint16_t data[dataLen] = {0};
bool ret = false;
+
/*
- * Request service to perform a repeated blocking read. This call is oneway
- * and will return immediately. It will read 64 * 2 elements with each
- * blocking read, for a total of writeCount / 2 calls.
+ * Request service to perform a blocking read. This call is oneway and will
+ * return immediately.
*/
- const size_t writeCount = kNumElementsInSyncQueue;
- this->mService->requestBlockingReadRepeat(dataLen * 2, writeCount / 2);
- /*
- * Write 64 elements into the queue writeCount times
- */
- {
- std::array<int32_t, dataLen> data = {0};
- for (size_t i = 0; i < writeCount; i++) {
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
- }
- /*
- * The queue should be totally empty now, so filling it up entirely with one
- * blocking write should be successful.
- */
- {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
+ const size_t writeCount = 1024;
+ mService->requestBlockingReadRepeat(dataLen*2, writeCount/2);
+
+ for (size_t i = 0; i < writeCount; i++) {
+ ret = mQueue->writeBlocking(data, dataLen,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
+
+ ret = mQueue->writeBlocking(data, mNumMessagesMax,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
}
/*
@@ -564,67 +356,56 @@
* using the EventFlag object owned by FMQ. Each write operation writes twice
* the amount of data as a single read.
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat3) {
+TEST_F(SynchronizedReadWriteClient, BlockingReadWriteRepeat3) {
const size_t dataLen = 64;
+ uint16_t data[dataLen] = {0};
bool ret = false;
/*
- * Request service to perform a repeated blocking read. This call is oneway
- * and will return immediately. It will read 64 / 2 elements with each
- * blocking read, for a total of writeCount * 2 calls.
+ * Request service to perform a blocking read. This call is oneway and will
+ * return immediately.
*/
size_t writeCount = 1024;
- this->mService->requestBlockingReadRepeat(dataLen / 2, writeCount * 2);
- /*
- * Write 64 elements into the queue writeCount times
- */
- {
- std::array<int32_t, dataLen> data = {0};
- for (size_t i = 0; i < writeCount; i++) {
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
- ASSERT_TRUE(ret);
- }
- }
- /*
- * The queue should be totally empty now, so filling it up entirely with one
- * blocking write should be successful.
- */
- {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- ret = this->mQueue->writeBlocking(
- data.data(), data.size(),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
- 5000000000 /* timeOutNanos */);
+ mService->requestBlockingReadRepeat(dataLen/2, writeCount*2);
+
+ for (size_t i = 0; i < writeCount; i++) {
+ ret = mQueue->writeBlocking(data, dataLen,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
+ ret = mQueue->writeBlocking(data, mNumMessagesMax,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
+ 5000000000 /* timeOutNanos */);
+ ASSERT_TRUE(ret);
}
/*
* Test that writeBlocking()/readBlocking() APIs do not block on
* attempts to write/read 0 messages and return true.
*/
-TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteZeroMessages) {
- int32_t data = 0;
+TEST_F(SynchronizedReadWriteClient, BlockingReadWriteZeroMessages) {
+ uint16_t data = 0;
/*
* Trigger a blocking write for zero messages with no timeout.
*/
- bool ret = this->mQueue->writeBlocking(
- &data, 0, static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ bool ret = mQueue->writeBlocking(
+ &data,
+ 0,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY));
ASSERT_TRUE(ret);
/*
* Trigger a blocking read for zero messages with no timeout.
*/
- ret = this->mQueue->readBlocking(
- &data, 0, static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
- static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY));
+ ret = mQueue->readBlocking(&data,
+ 0,
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
+ static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY));
ASSERT_TRUE(ret);
}
@@ -632,13 +413,13 @@
* Request mService to write a small number of messages
* to the FMQ. Read and verify data.
*/
-TYPED_TEST(SynchronizedReadWriteClient, SmallInputReaderTest1) {
+TEST_F(SynchronizedReadWriteClient, SmallInputReaderTest1) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, kNumElementsInSyncQueue);
- bool ret = this->requestWriteFmqSync(dataLen);
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ bool ret = mService->requestWriteFmqSync(dataLen);
ASSERT_TRUE(ret);
- int32_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
+ uint16_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
@@ -648,22 +429,16 @@
* counter to the last byte in the ring buffer. Request another write from
* mService. The write should fail because the write address is misaligned.
*/
-TYPED_TEST(SynchronizedReadWriteClient, MisalignedWriteCounter) {
- if (TypeParam::UserFd) {
- // When using the second FD for the ring buffer, we can't get to the read/write
- // counters from a pointer to the ring buffer, so no sense in testing.
- GTEST_SKIP();
- }
+TEST_F(SynchronizedReadWriteClient, MisalignedWriteCounter) {
const size_t dataLen = 1;
- ASSERT_LE(dataLen, kNumElementsInSyncQueue);
- bool ret = this->requestWriteFmqSync(dataLen);
+ bool ret = mService->requestWriteFmqSync(dataLen);
ASSERT_TRUE(ret);
// begin read and get a MemTransaction object for the first object in the queue
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginRead(dataLen, &tx));
// get a pointer to the beginning of the ring buffer
const auto& region = tx.getFirstRegion();
- int32_t* firstStart = region.getAddress();
+ uint16_t* firstStart = region.getAddress();
// because this is the first location in the ring buffer, we can get
// access to the read and write pointer stored in the fd. 8 bytes back for the
@@ -671,15 +446,15 @@
uint64_t* writeCntr = (uint64_t*)((uint8_t*)firstStart - 8);
// set it to point to the very last byte in the ring buffer
- *(writeCntr) = this->mQueue->getQuantumCount() * this->mQueue->getQuantumSize() - 1;
- ASSERT_TRUE(*writeCntr % sizeof(int32_t) != 0);
+ *(writeCntr) = mQueue->getQuantumCount() * mQueue->getQuantumSize() - 1;
+ ASSERT_TRUE(*writeCntr % sizeof(uint16_t) != 0);
// this is not actually necessary, but it's the expected the pattern.
- this->mQueue->commitRead(dataLen);
+ mQueue->commitRead(dataLen);
// This next write will be misaligned and will overlap outside of the ring buffer.
// The write should fail.
- ret = this->requestWriteFmqSync(dataLen);
+ ret = mService->requestWriteFmqSync(dataLen);
EXPECT_FALSE(ret);
}
@@ -688,14 +463,16 @@
* to the FMQ. Read and verify each message using
* beginRead/Commit read APIs.
*/
-TYPED_TEST(SynchronizedReadWriteClient, SmallInputReaderTest2) {
+TEST_F(SynchronizedReadWriteClient, SmallInputReaderTest2) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, kNumElementsInSyncQueue);
- auto ret = this->requestWriteFmqSync(dataLen);
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ auto ret = mService->requestWriteFmqSync(dataLen);
+
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginRead(dataLen, &tx));
auto first = tx.getFirstRegion();
auto second = tx.getSecondRegion();
@@ -709,46 +486,46 @@
}
}
- ASSERT_TRUE(this->mQueue->commitRead(dataLen));
+ ASSERT_TRUE(mQueue->commitRead(dataLen));
}
/*
* Write a small number of messages to FMQ. Request
- * mService to read and verify that the write was successful.
+ * mService to read and verify that the write was succesful.
*/
-TYPED_TEST(SynchronizedReadWriteClient, SmallInputWriterTest1) {
+TEST_F(SynchronizedReadWriteClient, SmallInputWriterTest1) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, kNumElementsInSyncQueue);
- size_t originalCount = this->mQueue->availableToWrite();
- int32_t data[dataLen];
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ size_t originalCount = mQueue->availableToWrite();
+ uint16_t data[dataLen];
initData(data, dataLen);
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- bool ret = this->requestReadFmqSync(dataLen);
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ bool ret = mService->requestReadFmqSync(dataLen);
ASSERT_TRUE(ret);
- size_t availableCount = this->mQueue->availableToWrite();
+ size_t availableCount = mQueue->availableToWrite();
ASSERT_EQ(originalCount, availableCount);
}
/*
* Write a small number of messages to FMQ using the beginWrite()/CommitWrite()
- * APIs. Request mService to read and verify that the write was successful.
+ * APIs. Request mService to read and verify that the write was succesful.
*/
-TYPED_TEST(SynchronizedReadWriteClient, SmallInputWriterTest2) {
+TEST_F(SynchronizedReadWriteClient, SmallInputWriterTest2) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, kNumElementsInSyncQueue);
- size_t originalCount = this->mQueue->availableToWrite();
- int32_t data[dataLen];
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ size_t originalCount = mQueue->availableToWrite();
+ uint16_t data[dataLen];
initData(data, dataLen);
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(dataLen, &tx));
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(dataLen, &tx));
auto first = tx.getFirstRegion();
auto second = tx.getSecondRegion();
size_t firstRegionLength = first.getLength();
- int32_t* firstBaseAddress = first.getAddress();
- int32_t* secondBaseAddress = second.getAddress();
+ uint16_t* firstBaseAddress = first.getAddress();
+ uint16_t* secondBaseAddress = second.getAddress();
for (size_t i = 0; i < dataLen; i++) {
if (i < firstRegionLength) {
@@ -758,24 +535,24 @@
}
}
- ASSERT_TRUE(this->mQueue->commitWrite(dataLen));
+ ASSERT_TRUE(mQueue->commitWrite(dataLen));
- auto ret = this->requestReadFmqSync(dataLen);
- // ASSERT_TRUE(ret.isOk());
+ auto ret = mService->requestReadFmqSync(dataLen);
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
- size_t availableCount = this->mQueue->availableToWrite();
+ size_t availableCount = mQueue->availableToWrite();
ASSERT_EQ(originalCount, availableCount);
}
/*
* Verify that the FMQ is empty and read fails when it is empty.
*/
-TYPED_TEST(SynchronizedReadWriteClient, ReadWhenEmpty) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
+TEST_F(SynchronizedReadWriteClient, ReadWhenEmpty) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
const size_t numMessages = 2;
- ASSERT_LE(numMessages, kNumElementsInSyncQueue);
- int32_t readData[numMessages];
- ASSERT_FALSE(this->mQueue->read(readData, numMessages));
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ uint16_t readData[numMessages];
+ ASSERT_FALSE(mQueue->read(readData, numMessages));
}
/*
@@ -783,16 +560,17 @@
* Write enough messages to fill it.
* Verify availableToWrite() method returns is zero.
* Try writing another message and verify that
- * the attempted write was unsuccessful. Request mService
+ * the attempted write was unsuccesful. Request mService
* to read and verify the messages in the FMQ.
*/
-TYPED_TEST(SynchronizedReadWriteClient, WriteWhenFull) {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- initData(data.data(), data.size());
- ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_FALSE(this->mQueue->write(&data[0], 1));
- bool ret = this->requestReadFmqSync(data.size());
+
+TEST_F(SynchronizedReadWriteClient, WriteWhenFull) {
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_FALSE(mQueue->write(&data[0], 1));
+ bool ret = mService->requestReadFmqSync(mNumMessagesMax);
ASSERT_TRUE(ret);
}
@@ -801,12 +579,12 @@
* Request mService to write data equal to queue size.
* Read and verify data in mQueue.
*/
-TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest1) {
- bool ret = this->requestWriteFmqSync(kNumElementsInSyncQueue);
+TEST_F(SynchronizedReadWriteClient, LargeInputTest1) {
+ bool ret = mService->requestWriteFmqSync(mNumMessagesMax);
ASSERT_TRUE(ret);
- std::vector<int32_t> readData(kNumElementsInSyncQueue);
- ASSERT_TRUE(this->mQueue->read(&readData[0], kNumElementsInSyncQueue));
- ASSERT_TRUE(verifyData(&readData[0], kNumElementsInSyncQueue));
+ std::vector<uint16_t> readData(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax));
+ ASSERT_TRUE(verifyData(&readData[0], mNumMessagesMax));
}
/*
@@ -814,15 +592,15 @@
* Verify that the write fails. Verify that availableToRead() method
* still returns 0 and verify that attempt to read fails.
*/
-TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
+TEST_F(SynchronizedReadWriteClient, LargeInputTest2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
const size_t numMessages = 2048;
- ASSERT_GT(numMessages, kNumElementsInSyncQueue);
- bool ret = this->requestWriteFmqSync(numMessages);
+ ASSERT_GT(numMessages, mNumMessagesMax);
+ bool ret = mService->requestWriteFmqSync(numMessages);
ASSERT_FALSE(ret);
- int32_t readData;
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- ASSERT_FALSE(this->mQueue->read(&readData, 1));
+ uint16_t readData;
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ ASSERT_FALSE(mQueue->read(&readData, 1));
}
/*
@@ -833,14 +611,14 @@
* Request mService to read. Verify read count.
*/
-TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest3) {
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- initData(data.data(), data.size());
- ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_FALSE(this->mQueue->write(data.data(), 1));
+TEST_F(SynchronizedReadWriteClient, LargeInputTest3) {
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_FALSE(mQueue->write(&data[0], 1));
- bool ret = this->requestReadFmqSync(data.size());
+ bool ret = mService->requestReadFmqSync(mNumMessagesMax);
ASSERT_TRUE(ret);
}
@@ -848,19 +626,19 @@
* Confirm that the FMQ is empty. Request mService to write to FMQ.
* Do multiple reads to empty FMQ and verify data.
*/
-TYPED_TEST(SynchronizedReadWriteClient, MultipleRead) {
+TEST_F(SynchronizedReadWriteClient, MultipleRead) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
- ASSERT_LE(numMessages, kNumElementsInSyncQueue);
- size_t availableToRead = this->mQueue->availableToRead();
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ size_t availableToRead = mQueue->availableToRead();
size_t expectedCount = 0;
ASSERT_EQ(expectedCount, availableToRead);
- bool ret = this->requestWriteFmqSync(numMessages);
+ bool ret = mService->requestWriteFmqSync(numMessages);
ASSERT_TRUE(ret);
- int32_t readData[numMessages] = {};
+ uint16_t readData[numMessages] = {};
for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
+ ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize));
}
ASSERT_TRUE(verifyData(readData, numMessages));
}
@@ -869,18 +647,18 @@
* Write to FMQ in bursts.
* Request mService to read data. Verify the read was successful.
*/
-TYPED_TEST(SynchronizedReadWriteClient, MultipleWrite) {
+TEST_F(SynchronizedReadWriteClient, MultipleWrite) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
- ASSERT_LE(numMessages, kNumElementsInSyncQueue);
- int32_t data[numMessages];
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ uint16_t data[numMessages];
initData(&data[0], numMessages);
for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->write(data + i * chunkSize, chunkSize));
+ ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize));
}
- bool ret = this->requestReadFmqSync(numMessages);
+ bool ret = mService->requestReadFmqSync(numMessages);
ASSERT_TRUE(ret);
}
@@ -890,15 +668,15 @@
* Write mNumMessagesMax messages into the queue. This should cause a
* wrap around. Request mService to read and verify the data.
*/
-TYPED_TEST(SynchronizedReadWriteClient, ReadWriteWrapAround) {
- size_t numMessages = kNumElementsInSyncQueue / 2;
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- initData(data.data(), data.size());
- ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
- bool ret = this->requestReadFmqSync(numMessages);
+TEST_F(SynchronizedReadWriteClient, ReadWriteWrapAround) {
+ size_t numMessages = mNumMessagesMax / 2;
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], numMessages));
+ bool ret = mService->requestReadFmqSync(numMessages);
ASSERT_TRUE(ret);
- ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
- ret = this->requestReadFmqSync(data.size());
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ret = mService->requestReadFmqSync(mNumMessagesMax);
ASSERT_TRUE(ret);
}
@@ -910,70 +688,74 @@
* Write mNumMessagesMax messages into the queue. This will cause a
* wrap around. Read and verify the data.
*/
-TYPED_TEST(SynchronizedReadWriteClient, ReadWriteWrapAround2) {
- size_t numMessages = kNumElementsInSyncQueue / 2;
- std::array<int32_t, kNumElementsInSyncQueue> data = {0};
- initData(data.data(), data.size());
- ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
- auto ret = this->requestReadFmqSync(numMessages);
+TEST_F(SynchronizedReadWriteClient, ReadWriteWrapAround2) {
+ size_t numMessages = mNumMessagesMax / 2;
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], numMessages));
+ auto ret = mService->requestReadFmqSync(numMessages);
+
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
/*
* The next write and read will have to deal with with wrap arounds.
*/
- typename TypeParam::MQType::MemTransaction tx;
- ASSERT_TRUE(this->mQueue->beginWrite(data.size(), &tx));
+ MessageQueueSync::MemTransaction tx;
+ ASSERT_TRUE(mQueue->beginWrite(mNumMessagesMax, &tx));
- ASSERT_EQ(tx.getFirstRegion().getLength() + tx.getSecondRegion().getLength(), data.size());
+ ASSERT_EQ(tx.getFirstRegion().getLength() + tx.getSecondRegion().getLength(), mNumMessagesMax);
- for (size_t i = 0; i < data.size(); i++) {
- int32_t* ptr = tx.getSlot(i);
+ for (size_t i = 0; i < mNumMessagesMax; i++) {
+ uint16_t* ptr = tx.getSlot(i);
*ptr = data[i];
}
- ASSERT_TRUE(this->mQueue->commitWrite(data.size()));
+ ASSERT_TRUE(mQueue->commitWrite(mNumMessagesMax));
- ret = this->requestReadFmqSync(data.size());
+ ret = mService->requestReadFmqSync(mNumMessagesMax);
+
+ ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
}
/*
- * Request this->mService to write a small number of messages
+ * Request mService to write a small number of messages
* to the FMQ. Read and verify data.
*/
-TYPED_TEST(UnsynchronizedWriteClient, SmallInputReaderTest1) {
+TEST_F(UnsynchronizedWriteClient, SmallInputReaderTest1) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- bool ret = this->requestWriteFmqUnsync(dataLen, this->mService);
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ bool ret = mService->requestWriteFmqUnsync(dataLen);
ASSERT_TRUE(ret);
- int32_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
+ uint16_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
/*
* Write a small number of messages to FMQ. Request
- * this->mService to read and verify that the write was successful.
+ * mService to read and verify that the write was succesful.
*/
-TYPED_TEST(UnsynchronizedWriteClient, SmallInputWriterTest1) {
+TEST_F(UnsynchronizedWriteClient, SmallInputWriterTest1) {
const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
- int32_t data[dataLen];
+ ASSERT_LE(dataLen, mNumMessagesMax);
+ uint16_t data[dataLen];
initData(data, dataLen);
- ASSERT_TRUE(this->mQueue->write(data, dataLen));
- bool ret = this->requestReadFmqUnsync(dataLen, this->mService);
+ ASSERT_TRUE(mQueue->write(data, dataLen));
+ bool ret = mService->requestReadFmqUnsync(dataLen);
ASSERT_TRUE(ret);
}
/*
* Verify that the FMQ is empty and read fails when it is empty.
*/
-TYPED_TEST(UnsynchronizedWriteClient, ReadWhenEmpty) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
+TEST_F(UnsynchronizedWriteClient, ReadWhenEmpty) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
const size_t numMessages = 2;
- ASSERT_LE(numMessages, this->mNumMessagesMax);
- int32_t readData[numMessages];
- ASSERT_FALSE(this->mQueue->read(readData, numMessages));
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ uint16_t readData[numMessages];
+ ASSERT_FALSE(mQueue->read(readData, numMessages));
}
/*
@@ -981,210 +763,187 @@
* Write enough messages to fill it.
* Verify availableToWrite() method returns is zero.
* Try writing another message and verify that
- * the attempted write was successful. Request this->mService
- * to read the messages in the FMQ and verify that it is unsuccessful.
+ * the attempted write was successful. Request mService
+ * to read the messages in the FMQ and verify that it is unsuccesful.
*/
-TYPED_TEST(UnsynchronizedWriteClient, WriteWhenFull) {
- std::vector<int32_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_TRUE(this->mQueue->write(&data[0], 1));
- bool ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
+TEST_F(UnsynchronizedWriteClient, WriteWhenFull) {
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_TRUE(mQueue->write(&data[0], 1));
+ bool ret = mService->requestReadFmqUnsync(mNumMessagesMax);
ASSERT_FALSE(ret);
}
/*
* Verify FMQ is empty.
- * Request this->mService to write data equal to queue size.
- * Read and verify data in this->mQueue.
+ * Request mService to write data equal to queue size.
+ * Read and verify data in mQueue.
*/
-TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest1) {
- bool ret = this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService);
+TEST_F(UnsynchronizedWriteClient, LargeInputTest1) {
+ bool ret = mService->requestWriteFmqUnsync(mNumMessagesMax);
ASSERT_TRUE(ret);
- std::vector<int32_t> data(this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->read(&data[0], this->mNumMessagesMax));
- ASSERT_TRUE(verifyData(&data[0], this->mNumMessagesMax));
+ std::vector<uint16_t> data(mNumMessagesMax);
+ ASSERT_TRUE(mQueue->read(&data[0], mNumMessagesMax));
+ ASSERT_TRUE(verifyData(&data[0], mNumMessagesMax));
}
/*
- * Request this->mService to write more than maximum number of messages to the FMQ.
+ * Request mService to write more than maximum number of messages to the FMQ.
* Verify that the write fails. Verify that availableToRead() method
* still returns 0 and verify that attempt to read fails.
*/
-TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest2) {
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- const size_t numMessages = this->mNumMessagesMax + 1;
- bool ret = this->requestWriteFmqUnsync(numMessages, this->mService);
+TEST_F(UnsynchronizedWriteClient, LargeInputTest2) {
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ const size_t numMessages = mNumMessagesMax + 1;
+ bool ret = mService->requestWriteFmqUnsync(numMessages);
ASSERT_FALSE(ret);
- int32_t readData;
- ASSERT_EQ(0UL, this->mQueue->availableToRead());
- ASSERT_FALSE(this->mQueue->read(&readData, 1));
+ uint16_t readData;
+ ASSERT_EQ(0UL, mQueue->availableToRead());
+ ASSERT_FALSE(mQueue->read(&readData, 1));
}
/*
* Write until FMQ is full.
* Verify that the number of messages available to write
- * is equal to this->mNumMessagesMax.
- * Verify that another write attempt is successful.
- * Request this->mService to read. Verify that read is unsuccessful.
- * Perform another write and verify that the read is successful
+ * is equal to mNumMessagesMax.
+ * Verify that another write attempt is succesful.
+ * Request mService to read. Verify that read is unsuccessful.
+ * Perform another write and verify that the read is succesful
* to check if the reader process can recover from the error condition.
*/
-TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest3) {
- std::vector<int32_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ASSERT_EQ(0UL, this->mQueue->availableToWrite());
- ASSERT_TRUE(this->mQueue->write(&data[0], 1));
+TEST_F(UnsynchronizedWriteClient, LargeInputTest3) {
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ASSERT_EQ(0UL, mQueue->availableToWrite());
+ ASSERT_TRUE(mQueue->write(&data[0], 1));
- bool ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
+ bool ret = mService->requestReadFmqUnsync(mNumMessagesMax);
ASSERT_FALSE(ret);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
- ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
+ ret = mService->requestReadFmqUnsync(mNumMessagesMax);
ASSERT_TRUE(ret);
}
/*
- * Confirm that the FMQ is empty. Request this->mService to write to FMQ.
+ * Confirm that the FMQ is empty. Request mService to write to FMQ.
* Do multiple reads to empty FMQ and verify data.
*/
-TYPED_TEST(UnsynchronizedWriteClient, MultipleRead) {
+TEST_F(UnsynchronizedWriteClient, MultipleRead) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
- ASSERT_LE(numMessages, this->mNumMessagesMax);
- size_t availableToRead = this->mQueue->availableToRead();
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ size_t availableToRead = mQueue->availableToRead();
size_t expectedCount = 0;
ASSERT_EQ(expectedCount, availableToRead);
- bool ret = this->requestWriteFmqUnsync(numMessages, this->mService);
+ bool ret = mService->requestWriteFmqUnsync(numMessages);
ASSERT_TRUE(ret);
- int32_t readData[numMessages] = {};
+ uint16_t readData[numMessages] = {};
for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
+ ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize));
}
ASSERT_TRUE(verifyData(readData, numMessages));
}
/*
* Write to FMQ in bursts.
- * Request this->mService to read data, verify that it was successful.
+ * Request mService to read data, verify that it was successful.
*/
-TYPED_TEST(UnsynchronizedWriteClient, MultipleWrite) {
+TEST_F(UnsynchronizedWriteClient, MultipleWrite) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
- ASSERT_LE(numMessages, this->mNumMessagesMax);
- int32_t data[numMessages];
+ ASSERT_LE(numMessages, mNumMessagesMax);
+ uint16_t data[numMessages];
initData(data, numMessages);
for (size_t i = 0; i < chunkNum; i++) {
- ASSERT_TRUE(this->mQueue->write(data + i * chunkSize, chunkSize));
+ ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize));
}
- bool ret = this->requestReadFmqUnsync(numMessages, this->mService);
+ bool ret = mService->requestReadFmqUnsync(numMessages);
ASSERT_TRUE(ret);
}
/*
* Write enough messages into the FMQ to fill half of it.
- * Request this->mService to read back the same.
- * Write this->mNumMessagesMax messages into the queue. This should cause a
- * wrap around. Request this->mService to read and verify the data.
+ * Request mService to read back the same.
+ * Write mNumMessagesMax messages into the queue. This should cause a
+ * wrap around. Request mService to read and verify the data.
*/
-TYPED_TEST(UnsynchronizedWriteClient, ReadWriteWrapAround) {
- size_t numMessages = this->mNumMessagesMax / 2;
- std::vector<int32_t> data(this->mNumMessagesMax);
- initData(&data[0], this->mNumMessagesMax);
- ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
- bool ret = this->requestReadFmqUnsync(numMessages, this->mService);
+TEST_F(UnsynchronizedWriteClient, ReadWriteWrapAround) {
+ size_t numMessages = mNumMessagesMax / 2;
+ std::vector<uint16_t> data(mNumMessagesMax);
+ initData(&data[0], mNumMessagesMax);
+ ASSERT_TRUE(mQueue->write(&data[0], numMessages));
+ bool ret = mService->requestReadFmqUnsync(numMessages);
ASSERT_TRUE(ret);
- ASSERT_TRUE(this->mQueue->write(&data[0], this->mNumMessagesMax));
- ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
+ ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax));
+ ret = mService->requestReadFmqUnsync(mNumMessagesMax);
ASSERT_TRUE(ret);
}
/*
- * Request this->mService to write a small number of messages
+ * Request mService to write a small number of messages
* to the FMQ. Read and verify data from two threads configured
* as readers to the FMQ.
*/
-TYPED_TEST(UnsynchronizedWriteClient, SmallInputMultipleReaderTest) {
- typename TypeParam::MQType* mQueue2 = this->newQueue();
-
- ASSERT_NE(nullptr, mQueue2);
+TEST_F(UnsynchronizedWriteClient, SmallInputMultipleReaderTest) {
+ auto desc = mQueue->getDesc();
+ std::unique_ptr<MessageQueue<uint16_t, kUnsynchronizedWrite>> mQueue2(
+ new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>(*desc));
+ ASSERT_NE(nullptr, mQueue2.get());
const size_t dataLen = 16;
- ASSERT_LE(dataLen, this->mNumMessagesMax);
+ ASSERT_LE(dataLen, mNumMessagesMax);
- bool ret = this->requestWriteFmqUnsync(dataLen, this->mService);
+ bool ret = mService->requestWriteFmqUnsync(dataLen);
ASSERT_TRUE(ret);
pid_t pid;
if ((pid = fork()) == 0) {
/* child process */
- int32_t readData[dataLen] = {};
+ uint16_t readData[dataLen] = {};
ASSERT_TRUE(mQueue2->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
exit(0);
} else {
ASSERT_GT(pid,
0 /* parent should see PID greater than 0 for a good fork */);
- int32_t readData[dataLen] = {};
- ASSERT_TRUE(this->mQueue->read(readData, dataLen));
+ uint16_t readData[dataLen] = {};
+ ASSERT_TRUE(mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
}
/*
- * Request this->mService to write into the FMQ until it is full.
- * Request this->mService to do another write and verify it is successful.
+ * Request mService to write into the FMQ until it is full.
+ * Request mService to do another write and verify it is successful.
* Use two reader processes to read and verify that both fail.
*/
-TYPED_TEST(UnsynchronizedWriteClient, OverflowNotificationTest) {
- typename TypeParam::MQType* mQueue2 = this->newQueue();
- ASSERT_NE(nullptr, mQueue2);
+TEST_F(UnsynchronizedWriteClient, OverflowNotificationTest) {
+ auto desc = mQueue->getDesc();
+ std::unique_ptr<MessageQueue<uint16_t, kUnsynchronizedWrite>> mQueue2(
+ new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>(*desc));
+ ASSERT_NE(nullptr, mQueue2.get());
- bool ret = this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService);
+ bool ret = mService->requestWriteFmqUnsync(mNumMessagesMax);
ASSERT_TRUE(ret);
- ret = this->requestWriteFmqUnsync(1, this->mService);
+ ret = mService->requestWriteFmqUnsync(1);
ASSERT_TRUE(ret);
pid_t pid;
if ((pid = fork()) == 0) {
/* child process */
- std::vector<int32_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(mQueue2->read(&readData[0], this->mNumMessagesMax));
+ std::vector<uint16_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue2->read(&readData[0], mNumMessagesMax));
exit(0);
} else {
ASSERT_GT(pid, 0/* parent should see PID greater than 0 for a good fork */);
- std::vector<int32_t> readData(this->mNumMessagesMax);
- ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
+ std::vector<uint16_t> readData(mNumMessagesMax);
+ ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax));
}
}
-
-/*
- * Make sure a valid queue can be created with different supported types.
- * All fundamental or native types should work. An AIDL parcelable that is
- * annotated with @FixedSize is supported. A parcelable without it, will cause
- * a compilation error.
- */
-typedef ::testing::Types<FixedParcelable, EventFlagBits, bool, int8_t, char, char16_t, int32_t,
- int64_t, float, double>
- AidlTypeCheckTypes;
-
-template <typename T>
-class AidlTypeChecks : public ::testing::Test {};
-
-TYPED_TEST_CASE(AidlTypeChecks, AidlTypeCheckTypes);
-
-TYPED_TEST(AidlTypeChecks, FixedSizeParcelableTest) {
- android::AidlMessageQueue<TypeParam, UnsynchronizedWrite> queue =
- android::AidlMessageQueue<TypeParam, UnsynchronizedWrite>(64);
- ASSERT_TRUE(queue.isValid());
- // Make sure we can do a simple write/read of any value.
- TypeParam writeData[1];
- TypeParam readData[1];
- EXPECT_TRUE(queue.write(writeData, 1));
- EXPECT_TRUE(queue.read(readData, 1));
-}