blob: bf5fb631ae8da89ad847cdf5c915077f9f9bdf07 [file] [log] [blame]
Phil Burke1ce4912016-11-21 10:40:25 -08001/*
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 "OboeAudio"
19//#define LOG_NDEBUG 0
20#include <utils/Log.h>
21
Phil Burkd8bdcab2017-01-03 17:20:30 -080022#include <new>
Phil Burke1ce4912016-11-21 10:40:25 -080023#include <stdint.h>
24#include <assert.h>
25
26#include <oboe/OboeDefinitions.h>
27#include "HandleTracker.h"
28
29// Handle format is: tgggiiii
30// where each letter is 4 bits, t=type, g=generation, i=index
31
32#define TYPE_SIZE 4
33#define GENERATION_SIZE 12
34#define INDEX_SIZE 16
35
36#define GENERATION_INVALID 0
37#define GENERATION_SHIFT INDEX_SIZE
38
39#define TYPE_MASK ((1 << TYPE_SIZE) - 1)
40#define GENERATION_MASK ((1 << GENERATION_SIZE) - 1)
41#define INDEX_MASK ((1 << INDEX_SIZE) - 1)
42
43#define SLOT_UNAVAILABLE (-1)
44
45// Error if handle is negative so type is limited to bottom half.
46#define HANDLE_INVALID_TYPE TYPE_MASK
47
48static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
49 "Mismatch between header and cpp.");
50static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
51 "Mismatch between header and cpp.");
52
53HandleTracker::HandleTracker(uint32_t maxHandles)
54 : mMaxHandleCount(maxHandles)
Phil Burke1ce4912016-11-21 10:40:25 -080055 , mHandleHeaders(nullptr)
56{
57 assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
58 // Allocate arrays to hold addresses and validation info.
Phil Burkd8bdcab2017-01-03 17:20:30 -080059 mHandleAddresses = (handle_tracker_address_t *)
60 new(std::nothrow) handle_tracker_address_t[maxHandles];
Phil Burke1ce4912016-11-21 10:40:25 -080061 if (mHandleAddresses != nullptr) {
Phil Burkd8bdcab2017-01-03 17:20:30 -080062 mHandleHeaders = new(std::nothrow) handle_tracker_header_t[maxHandles];
63
Phil Burke1ce4912016-11-21 10:40:25 -080064 if (mHandleHeaders != nullptr) {
Phil Burkd8bdcab2017-01-03 17:20:30 -080065 handle_tracker_header_t initialHeader = buildHeader(0, 1);
66 // Initialize linked list of free nodes. nullptr terminated.
Phil Burke1ce4912016-11-21 10:40:25 -080067 for (uint32_t i = 0; i < (maxHandles - 1); i++) {
68 mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
Phil Burkd8bdcab2017-01-03 17:20:30 -080069 mHandleHeaders[i] = initialHeader;
Phil Burke1ce4912016-11-21 10:40:25 -080070 }
71 mNextFreeAddress = &mHandleAddresses[0];
72 mHandleAddresses[maxHandles - 1] = nullptr;
73 mHandleHeaders[maxHandles - 1] = 0;
74 } else {
75 delete[] mHandleAddresses; // so the class appears uninitialized
Phil Burkd8bdcab2017-01-03 17:20:30 -080076 mHandleAddresses = nullptr;
Phil Burke1ce4912016-11-21 10:40:25 -080077 }
78 }
79}
80
81HandleTracker::~HandleTracker()
82{
83 delete[] mHandleAddresses;
84 delete[] mHandleHeaders;
85}
86
87bool HandleTracker::isInitialized() const {
88 return mHandleAddresses != nullptr;
89}
90
91handle_tracker_slot_t HandleTracker::allocateSlot() {
92 void **allocated = mNextFreeAddress;
93 if (allocated == nullptr) {
94 return SLOT_UNAVAILABLE;
95 }
96 // Remove this slot from the head of the linked list.
97 mNextFreeAddress = (void **) *allocated;
98 return (allocated - mHandleAddresses);
99}
100
101handle_tracker_generation_t HandleTracker::nextGeneration(handle_tracker_slot_t index) {
102 handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
103 // Avoid generation zero so that 0x0 is not a valid handle.
104 if (generation == GENERATION_INVALID) {
105 generation++;
106 }
107 return generation;
108}
109
110oboe_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
111{
112 if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
113 return static_cast<oboe_handle_t>(OBOE_ERROR_OUT_OF_RANGE);
114 }
115 if (!isInitialized()) {
116 return static_cast<oboe_handle_t>(OBOE_ERROR_NO_MEMORY);
117 }
118
119 // Find an empty slot.
120 handle_tracker_slot_t index = allocateSlot();
121 if (index == SLOT_UNAVAILABLE) {
122 ALOGE("HandleTracker::put() no room for more handles");
123 return static_cast<oboe_handle_t>(OBOE_ERROR_NO_FREE_HANDLES);
124 }
125
126 // Cycle the generation counter so stale handles can be detected.
127 handle_tracker_generation_t generation = nextGeneration(index); // reads header table
128 handle_tracker_header_t inputHeader = buildHeader(type, generation);
129
130 // These two writes may need to be observed by other threads or cores during get().
131 mHandleHeaders[index] = inputHeader;
132 mHandleAddresses[index] = address;
133 // TODO use store release to enforce memory order with get()
134
135 // Generate a handle.
136 oboe_handle_t handle = buildHandle(inputHeader, index);
137
Phil Burkd8bdcab2017-01-03 17:20:30 -0800138 ALOGV("HandleTracker::put(%p) returns 0x%08x", address, handle);
Phil Burke1ce4912016-11-21 10:40:25 -0800139 return handle;
140}
141
142handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
143 oboe_handle_t handle) const
144{
145 // Validate the handle.
146 handle_tracker_slot_t index = extractIndex(handle);
147 if (index >= mMaxHandleCount) {
148 ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
149 return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
150 }
151 handle_tracker_generation_t handleGeneration = extractGeneration(handle);
152 handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
153 if (inputHeader != mHandleHeaders[index]) {
154 ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
155 inputHeader, index, mHandleHeaders[index]);
156 return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
157 }
158 return index;
159}
160
161handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, oboe_handle_t handle) const
162{
163 if (!isInitialized()) {
164 return nullptr;
165 }
166 handle_tracker_slot_t index = handleToIndex(type, handle);
167 if (index >= 0) {
168 return mHandleAddresses[index];
169 } else {
170 return nullptr;
171 }
172}
173
174handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, oboe_handle_t handle) {
175 if (!isInitialized()) {
176 return nullptr;
177 }
178 handle_tracker_slot_t index = handleToIndex(type,handle);
179 if (index >= 0) {
180 handle_tracker_address_t address = mHandleAddresses[index];
181
182 // Invalidate the header type but preserve the generation count.
183 handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
184 handle_tracker_header_t inputHeader = buildHeader(
185 (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
186 mHandleHeaders[index] = inputHeader;
187
188 // Add this slot to the head of the linked list.
189 mHandleAddresses[index] = mNextFreeAddress;
190 mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
191 return address;
192 } else {
193 return nullptr;
194 }
195}
196
197oboe_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
198 handle_tracker_slot_t index) {
199 return (oboe_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
200}
201
202handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
203 handle_tracker_generation_t generation)
204{
205 return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
206 | (generation & GENERATION_MASK));
207}
208
209handle_tracker_slot_t HandleTracker::extractIndex(oboe_handle_t handle)
210{
211 return handle & INDEX_MASK;
212}
213
214handle_tracker_generation_t HandleTracker::extractGeneration(oboe_handle_t handle)
215{
216 return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
217}