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