blob: 23a73edbecdbed39dc8a71615859a772cdbfab97 [file] [log] [blame]
/*
* Copyright 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.
*/
#ifndef UTILITY_HANDLE_TRACKER_H
#define UTILITY_HANDLE_TRACKER_H
#include <stdint.h>
#include <utils/Mutex.h>
typedef int32_t aaudio_handle_t;
typedef int32_t handle_tracker_type_t; // what kind of handle
typedef int32_t handle_tracker_slot_t; // index in allocation table
typedef int32_t handle_tracker_generation_t; // incremented when slot used
typedef uint16_t handle_tracker_header_t; // combines type and generation
typedef void *handle_tracker_address_t; // address of something that is stored here
#define HANDLE_TRACKER_MAX_TYPES (1 << 3)
#define HANDLE_TRACKER_MAX_HANDLES (1 << 16)
/**
* Represent Objects using an integer handle that can be used with Java.
* This also makes the 'C' ABI more robust.
*
* Note that this should only be called from a single thread.
* If you call it from more than one thread then you need to use your own mutex.
*/
class HandleTracker {
public:
/**
* @param maxHandles cannot exceed HANDLE_TRACKER_MAX_HANDLES
*/
HandleTracker(uint32_t maxHandles = 256);
virtual ~HandleTracker();
/**
* Don't use if this returns false;
* @return true if the internal allocation succeeded
*/
bool isInitialized() const;
/**
* Store a pointer and return a handle that can be used to retrieve the pointer.
*
* It is safe to call put() or remove() from multiple threads.
*
* @param expectedType the type of the object to be tracked
* @param address pointer to be converted to a handle
* @return a valid handle or a negative error
*/
aaudio_handle_t put(handle_tracker_type_t expectedType, handle_tracker_address_t address);
/**
* Get the original pointer associated with the handle.
* The handle will be validated to prevent stale handles from being reused.
* Note that the validation is designed to prevent common coding errors and not
* to prevent deliberate hacking.
*
* @param expectedType shouldmatch the type we passed to put()
* @param handle to be converted to a pointer
* @return address associated with handle or nullptr
*/
handle_tracker_address_t get(handle_tracker_type_t expectedType, aaudio_handle_t handle) const;
/**
* Free up the storage associated with the handle.
* Subsequent attempts to use the handle will fail.
*
* Do NOT remove() a handle while get() is being called for the same handle from another thread.
*
* @param expectedType shouldmatch the type we passed to put()
* @param handle to be removed from tracking
* @return address associated with handle or nullptr if not found
*/
handle_tracker_address_t remove(handle_tracker_type_t expectedType, aaudio_handle_t handle);
private:
const int32_t mMaxHandleCount; // size of array
// This address is const after initialization.
handle_tracker_address_t * mHandleAddresses; // address of objects or a free linked list node
// This address is const after initialization.
handle_tracker_header_t * mHandleHeaders; // combination of type and generation
// head of the linked list of free nodes in mHandleAddresses
handle_tracker_address_t * mNextFreeAddress;
// This Mutex protects the linked list of free nodes.
// The list is managed using mHandleAddresses and mNextFreeAddress.
// The data in mHandleHeaders is only changed by put() and remove().
android::Mutex mLock;
/**
* Pull slot off of a list of empty slots.
* @return index or a negative error
*/
handle_tracker_slot_t allocateSlot_l();
/**
* Increment the generation for the slot, avoiding zero.
*/
handle_tracker_generation_t nextGeneration_l(handle_tracker_slot_t index);
/**
* Validate the handle and return the corresponding index.
* @return slot index or a negative error
*/
handle_tracker_slot_t handleToIndex(aaudio_handle_t handle, handle_tracker_type_t type) const;
/**
* Construct a handle from a header and an index.
* @param header combination of a type and a generation
* @param index slot index returned from allocateSlot
* @return handle or a negative error
*/
static aaudio_handle_t buildHandle(handle_tracker_header_t header, handle_tracker_slot_t index);
/**
* Combine a type and a generation field into a header.
*/
static handle_tracker_header_t buildHeader(handle_tracker_type_t type,
handle_tracker_generation_t generation);
/**
* Extract the index from a handle.
* Does not validate the handle.
* @return index associated with a handle
*/
static handle_tracker_slot_t extractIndex(aaudio_handle_t handle);
/**
* Extract the generation from a handle.
* Does not validate the handle.
* @return generation associated with a handle
*/
static handle_tracker_generation_t extractGeneration(aaudio_handle_t handle);
};
#endif //UTILITY_HANDLE_TRACKER_H