Home | History | Annotate | Download | only in utility
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  *
     16  */
     17 
     18 #define LOG_TAG "AAudio"
     19 //#define LOG_NDEBUG 0
     20 #include <utils/Log.h>
     21 
     22 #include <assert.h>
     23 #include <new>
     24 #include <stdint.h>
     25 #include <utils/Mutex.h>
     26 
     27 #include <aaudio/AAudio.h>
     28 #include "HandleTracker.h"
     29 
     30 using android::Mutex;
     31 
     32 // Handle format is: tgggiiii
     33 // where each letter is 4 bits, t=type, g=generation, i=index
     34 
     35 #define TYPE_SIZE           4
     36 #define GENERATION_SIZE    12
     37 #define INDEX_SIZE         16
     38 
     39 #define GENERATION_INVALID  0
     40 #define GENERATION_SHIFT    INDEX_SIZE
     41 
     42 #define TYPE_MASK           ((1 << TYPE_SIZE) - 1)
     43 #define GENERATION_MASK     ((1 << GENERATION_SIZE) - 1)
     44 #define INDEX_MASK          ((1 << INDEX_SIZE) - 1)
     45 
     46 #define SLOT_UNAVAILABLE    (-1)
     47 
     48 // Error if handle is negative so type is limited to bottom half.
     49 #define HANDLE_INVALID_TYPE TYPE_MASK
     50 
     51 static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
     52     "Mismatch between header and cpp.");
     53 static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
     54     "Mismatch between header and cpp.");
     55 
     56 HandleTracker::HandleTracker(uint32_t maxHandles)
     57         : mMaxHandleCount(maxHandles)
     58         , mHandleHeaders(nullptr)
     59 {
     60     assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
     61     // Allocate arrays to hold addresses and validation info.
     62     mHandleAddresses = (handle_tracker_address_t *)
     63             new(std::nothrow) handle_tracker_address_t[maxHandles];
     64     if (mHandleAddresses != nullptr) {
     65         mHandleHeaders = new(std::nothrow) handle_tracker_header_t[maxHandles];
     66 
     67         if (mHandleHeaders != nullptr) {
     68             handle_tracker_header_t initialHeader = buildHeader(0, 1);
     69             // Initialize linked list of free nodes. nullptr terminated.
     70             for (uint32_t i = 0; i < (maxHandles - 1); i++) {
     71                 mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
     72                 mHandleHeaders[i] = initialHeader;
     73             }
     74             mNextFreeAddress = &mHandleAddresses[0];
     75             mHandleAddresses[maxHandles - 1] = nullptr;
     76             mHandleHeaders[maxHandles - 1] = 0;
     77         } else {
     78             delete[] mHandleAddresses; // so the class appears uninitialized
     79             mHandleAddresses = nullptr;
     80         }
     81     }
     82 }
     83 
     84 HandleTracker::~HandleTracker()
     85 {
     86     Mutex::Autolock _l(mLock);
     87     delete[] mHandleAddresses;
     88     delete[] mHandleHeaders;
     89     mHandleAddresses = nullptr;
     90 }
     91 
     92 bool HandleTracker::isInitialized() const {
     93     return mHandleAddresses != nullptr;
     94 }
     95 
     96 handle_tracker_slot_t HandleTracker::allocateSlot_l() {
     97     void **allocated = mNextFreeAddress;
     98     if (allocated == nullptr) {
     99         return SLOT_UNAVAILABLE;
    100     }
    101     // Remove this slot from the head of the linked list.
    102     mNextFreeAddress = (void **) *allocated;
    103     return (allocated - mHandleAddresses);
    104 }
    105 
    106 handle_tracker_generation_t HandleTracker::nextGeneration_l(handle_tracker_slot_t index) {
    107     handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
    108     // Avoid generation zero so that 0x0 is not a valid handle.
    109     if (generation == GENERATION_INVALID) {
    110         generation++;
    111     }
    112     return generation;
    113 }
    114 
    115 aaudio_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
    116 {
    117     if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
    118         return static_cast<aaudio_handle_t>(AAUDIO_ERROR_OUT_OF_RANGE);
    119     }
    120     if (!isInitialized()) {
    121         return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_MEMORY);
    122     }
    123 
    124     Mutex::Autolock _l(mLock);
    125 
    126     // Find an empty slot.
    127     handle_tracker_slot_t index = allocateSlot_l();
    128     if (index == SLOT_UNAVAILABLE) {
    129         ALOGE("HandleTracker::put() no room for more handles");
    130         return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_FREE_HANDLES);
    131     }
    132 
    133     // Cycle the generation counter so stale handles can be detected.
    134     handle_tracker_generation_t generation = nextGeneration_l(index); // reads header table
    135     handle_tracker_header_t inputHeader = buildHeader(type, generation);
    136 
    137     // These two writes may need to be observed by other threads or cores during get().
    138     mHandleHeaders[index] = inputHeader;
    139     mHandleAddresses[index] = address;
    140     // TODO use store release to enforce memory order with get()
    141 
    142     // Generate a handle.
    143     aaudio_handle_t handle = buildHandle(inputHeader, index);
    144 
    145     ALOGV("HandleTracker::put(%p) returns 0x%08x", address, handle);
    146     return handle;
    147 }
    148 
    149 handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
    150                                                    aaudio_handle_t handle) const
    151 {
    152     // Validate the handle.
    153     handle_tracker_slot_t index = extractIndex(handle);
    154     if (index >= mMaxHandleCount) {
    155         ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
    156         return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
    157     }
    158     handle_tracker_generation_t handleGeneration = extractGeneration(handle);
    159     handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
    160     // We do not need to synchronize this access to mHandleHeaders because it is constant for
    161     // the lifetime of the handle.
    162     if (inputHeader != mHandleHeaders[index]) {
    163         ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
    164              inputHeader, index, mHandleHeaders[index]);
    165         return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
    166     }
    167     return index;
    168 }
    169 
    170 handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, aaudio_handle_t handle) const
    171 {
    172     if (!isInitialized()) {
    173         return nullptr;
    174     }
    175     handle_tracker_slot_t index = handleToIndex(type, handle);
    176     if (index >= 0) {
    177         // We do not need to synchronize this access to mHandleHeaders because this slot
    178         // is allocated and, therefore, not part of the linked list of free slots.
    179         return mHandleAddresses[index];
    180     } else {
    181         return nullptr;
    182     }
    183 }
    184 
    185 handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, aaudio_handle_t handle) {
    186     if (!isInitialized()) {
    187         return nullptr;
    188     }
    189 
    190     Mutex::Autolock _l(mLock);
    191 
    192     handle_tracker_slot_t index = handleToIndex(type,handle);
    193     if (index >= 0) {
    194         handle_tracker_address_t address = mHandleAddresses[index];
    195 
    196         // Invalidate the header type but preserve the generation count.
    197         handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
    198         handle_tracker_header_t inputHeader = buildHeader(
    199                 (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
    200         mHandleHeaders[index] = inputHeader;
    201 
    202         // Add this slot to the head of the linked list.
    203         mHandleAddresses[index] = mNextFreeAddress;
    204         mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
    205         return address;
    206     } else {
    207         return nullptr;
    208     }
    209 }
    210 
    211 aaudio_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
    212                                          handle_tracker_slot_t index) {
    213     return (aaudio_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
    214 }
    215 
    216 handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
    217                                     handle_tracker_generation_t generation)
    218 {
    219     return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
    220         | (generation & GENERATION_MASK));
    221 }
    222 
    223 handle_tracker_slot_t HandleTracker::extractIndex(aaudio_handle_t handle)
    224 {
    225     return handle & INDEX_MASK;
    226 }
    227 
    228 handle_tracker_generation_t HandleTracker::extractGeneration(aaudio_handle_t handle)
    229 {
    230     return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
    231 }
    232