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 
HandleTracker(uint32_t maxHandles)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 
~HandleTracker()84 HandleTracker::~HandleTracker()
85 {
86     Mutex::Autolock _l(mLock);
87     delete[] mHandleAddresses;
88     delete[] mHandleHeaders;
89     mHandleAddresses = nullptr;
90 }
91 
isInitialized() const92 bool HandleTracker::isInitialized() const {
93     return mHandleAddresses != nullptr;
94 }
95 
allocateSlot_l()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 
nextGeneration_l(handle_tracker_slot_t index)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 
put(handle_tracker_type_t type,void * address)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 
handleToIndex(handle_tracker_type_t type,aaudio_handle_t handle) const149 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 
get(handle_tracker_type_t type,aaudio_handle_t handle) const170 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 
remove(handle_tracker_type_t type,aaudio_handle_t handle)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 
buildHandle(handle_tracker_header_t typeGeneration,handle_tracker_slot_t index)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 
buildHeader(handle_tracker_type_t type,handle_tracker_generation_t generation)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 
extractIndex(aaudio_handle_t handle)223 handle_tracker_slot_t HandleTracker::extractIndex(aaudio_handle_t handle)
224 {
225     return handle & INDEX_MASK;
226 }
227 
extractGeneration(aaudio_handle_t handle)228 handle_tracker_generation_t HandleTracker::extractGeneration(aaudio_handle_t handle)
229 {
230     return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
231 }
232