blob: f9572348e0f2f02c24903b779bf9982aef05d921 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#define LOG_TAG "AAudio"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <assert.h>
#include <new>
#include <stdint.h>
#include <utils/Mutex.h>
#include <aaudio/AAudio.h>
#include "HandleTracker.h"
using android::Mutex;
// Handle format is: tgggiiii
// where each letter is 4 bits, t=type, g=generation, i=index
#define TYPE_SIZE 4
#define GENERATION_SIZE 12
#define INDEX_SIZE 16
#define GENERATION_INVALID 0
#define GENERATION_SHIFT INDEX_SIZE
#define TYPE_MASK ((1 << TYPE_SIZE) - 1)
#define GENERATION_MASK ((1 << GENERATION_SIZE) - 1)
#define INDEX_MASK ((1 << INDEX_SIZE) - 1)
#define SLOT_UNAVAILABLE (-1)
// Error if handle is negative so type is limited to bottom half.
#define HANDLE_INVALID_TYPE TYPE_MASK
static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
"Mismatch between header and cpp.");
static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
"Mismatch between header and cpp.");
HandleTracker::HandleTracker(uint32_t maxHandles)
: mMaxHandleCount(maxHandles)
, mHandleHeaders(nullptr)
{
assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
// Allocate arrays to hold addresses and validation info.
mHandleAddresses = (handle_tracker_address_t *)
new(std::nothrow) handle_tracker_address_t[maxHandles];
if (mHandleAddresses != nullptr) {
mHandleHeaders = new(std::nothrow) handle_tracker_header_t[maxHandles];
if (mHandleHeaders != nullptr) {
handle_tracker_header_t initialHeader = buildHeader(0, 1);
// Initialize linked list of free nodes. nullptr terminated.
for (uint32_t i = 0; i < (maxHandles - 1); i++) {
mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
mHandleHeaders[i] = initialHeader;
}
mNextFreeAddress = &mHandleAddresses[0];
mHandleAddresses[maxHandles - 1] = nullptr;
mHandleHeaders[maxHandles - 1] = 0;
} else {
delete[] mHandleAddresses; // so the class appears uninitialized
mHandleAddresses = nullptr;
}
}
}
HandleTracker::~HandleTracker()
{
Mutex::Autolock _l(mLock);
delete[] mHandleAddresses;
delete[] mHandleHeaders;
mHandleAddresses = nullptr;
}
bool HandleTracker::isInitialized() const {
return mHandleAddresses != nullptr;
}
handle_tracker_slot_t HandleTracker::allocateSlot_l() {
void **allocated = mNextFreeAddress;
if (allocated == nullptr) {
return SLOT_UNAVAILABLE;
}
// Remove this slot from the head of the linked list.
mNextFreeAddress = (void **) *allocated;
return (allocated - mHandleAddresses);
}
handle_tracker_generation_t HandleTracker::nextGeneration_l(handle_tracker_slot_t index) {
handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
// Avoid generation zero so that 0x0 is not a valid handle.
if (generation == GENERATION_INVALID) {
generation++;
}
return generation;
}
aaudio_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
{
if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
return static_cast<aaudio_handle_t>(AAUDIO_ERROR_OUT_OF_RANGE);
}
if (!isInitialized()) {
return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_MEMORY);
}
Mutex::Autolock _l(mLock);
// Find an empty slot.
handle_tracker_slot_t index = allocateSlot_l();
if (index == SLOT_UNAVAILABLE) {
ALOGE("HandleTracker::put() no room for more handles");
return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_FREE_HANDLES);
}
// Cycle the generation counter so stale handles can be detected.
handle_tracker_generation_t generation = nextGeneration_l(index); // reads header table
handle_tracker_header_t inputHeader = buildHeader(type, generation);
// These two writes may need to be observed by other threads or cores during get().
mHandleHeaders[index] = inputHeader;
mHandleAddresses[index] = address;
// TODO use store release to enforce memory order with get()
// Generate a handle.
aaudio_handle_t handle = buildHandle(inputHeader, index);
ALOGV("HandleTracker::put(%p) returns 0x%08x", address, handle);
return handle;
}
handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
aaudio_handle_t handle) const
{
// Validate the handle.
handle_tracker_slot_t index = extractIndex(handle);
if (index >= mMaxHandleCount) {
ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
}
handle_tracker_generation_t handleGeneration = extractGeneration(handle);
handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
// We do not need to synchronize this access to mHandleHeaders because it is constant for
// the lifetime of the handle.
if (inputHeader != mHandleHeaders[index]) {
ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
inputHeader, index, mHandleHeaders[index]);
return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
}
return index;
}
handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, aaudio_handle_t handle) const
{
if (!isInitialized()) {
return nullptr;
}
handle_tracker_slot_t index = handleToIndex(type, handle);
if (index >= 0) {
// We do not need to synchronize this access to mHandleHeaders because this slot
// is allocated and, therefore, not part of the linked list of free slots.
return mHandleAddresses[index];
} else {
return nullptr;
}
}
handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, aaudio_handle_t handle) {
if (!isInitialized()) {
return nullptr;
}
Mutex::Autolock _l(mLock);
handle_tracker_slot_t index = handleToIndex(type,handle);
if (index >= 0) {
handle_tracker_address_t address = mHandleAddresses[index];
// Invalidate the header type but preserve the generation count.
handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
handle_tracker_header_t inputHeader = buildHeader(
(handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
mHandleHeaders[index] = inputHeader;
// Add this slot to the head of the linked list.
mHandleAddresses[index] = mNextFreeAddress;
mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
return address;
} else {
return nullptr;
}
}
aaudio_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
handle_tracker_slot_t index) {
return (aaudio_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
}
handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
handle_tracker_generation_t generation)
{
return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
| (generation & GENERATION_MASK));
}
handle_tracker_slot_t HandleTracker::extractIndex(aaudio_handle_t handle)
{
return handle & INDEX_MASK;
}
handle_tracker_generation_t HandleTracker::extractGeneration(aaudio_handle_t handle)
{
return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
}