os: New utility for managing object handles

Some session interfaces use session-local handles for referring to
server-side objects, e.g., a file-system session hands out file handles
to the client. The new 'Handle_registry' class template can be used to
associate numeric handles with objects on the server side and thereby
simplifies the implementation of such servers.
This commit is contained in:
Norman Feske 2014-06-07 20:36:02 +02:00
parent 9b0c17730d
commit 8768a6dae2
1 changed files with 193 additions and 0 deletions

View File

@ -0,0 +1,193 @@
/*
* \brief Utility for managing object handles
* \author Norman Feske
* \date 2014-06-07
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__OS__HANDLE_REGISTRY_H_
#define _INCLUDE__OS__HANDLE_REGISTRY_H_
#include <util/avl_tree.h>
#include <base/weak_ptr.h>
#include <base/tslab.h>
namespace Genode {
template <typename T> class Handle;
template <typename HANDLE, typename OBJ> class Handle_registry;
}
/**
* Typed handle
*/
template <typename T>
class Genode::Handle
{
private:
static constexpr unsigned _invalid() { return ~0; }
unsigned _value = _invalid();
public:
typedef T Type;
Handle() { }
Handle(unsigned value) : _value(value) { }
unsigned value() const { return _value; }
bool valid() const { return _value != _invalid(); }
bool operator == (Handle const &other) const { return other.value() == _value; }
};
/**
* Registry of handles, which refer to objects
*
* \param HANDLE handle type
* \param OBJ type of context associated with a handle
*
* The constructor of the 'HANDLE' type must take an unsigned value as argument
* and have a 'value()' function that returns the same value.
*
* The 'OBJ' type must be inherited from 'Weak_object'.
*/
template <typename HANDLE, typename OBJ>
class Genode::Handle_registry
{
private:
/**
* Meta data that keeps the association of a handle with an object
*/
struct Element : public Avl_node<Element>,
public HANDLE,
public Weak_ptr<OBJ>
{
Element(Weak_ptr<OBJ> weak_ptr, unsigned id)
:
HANDLE(id), Weak_ptr<OBJ>(weak_ptr)
{ }
/**
* Avl_node interface
*/
bool higher(Element *e) {
return e->HANDLE::value() > HANDLE::value(); }
/**
* Traverse AVL tree to find handle by id
*/
Element *find_by_handle(HANDLE const &handle)
{
if (handle.value() == HANDLE::value()) return this;
Element * const e =
Avl_node<Element>::child(handle.value() > HANDLE::value());
return e ? e->find_by_handle(handle) : 0;
}
};
Tslab<Element, 4000> _slab;
unsigned _cnt = 0;
Avl_tree<Element> _elements;
Element &_lookup(HANDLE handle) const
{
Element *result = nullptr;
if (_elements.first())
result = _elements.first()->find_by_handle(handle);
if (result)
return *result;
throw Lookup_failed();
}
public:
/**
* Constructor
*
* \param alloc allocator used for allocating the meta data for the
* handles
*/
Handle_registry(Allocator &alloc) : _slab(&alloc) { }
~Handle_registry()
{
while (_elements.first())
free(*_elements.first());
}
/**
* Exception types
*/
class Lookup_failed { };
class Out_of_memory { };
/**
* Allocate handle for specified object
*
* \param handle designated handle to assign to the object. By
* default, a new handle gets allocated.
*
* \throw Out_of_memory
*/
HANDLE &alloc(OBJ &obj, HANDLE handle = HANDLE())
{
/* disassociate original object from supplied handle */
try {
if (handle.valid())
free(handle);
} catch (Lookup_failed) { }
try {
Element *e = new (_slab)
Element(obj.weak_ptr(),
handle.valid() ? handle.value() : ++_cnt);
_elements.insert(e);
return *e;
}
catch (Allocator::Out_of_memory) {
throw Out_of_memory(); }
}
/**
* Release handle
*
* \throw Lookup_failed
*/
void free(HANDLE handle)
{
Element &e = _lookup(handle);
_elements.remove(&e);
destroy(_slab, &e);
}
/**
* Lookup pointer to OBJ by a given handle
*
* \throw Lookup_failed
*/
Weak_ptr<OBJ> &lookup(HANDLE handle) const { return _lookup(handle); }
};
#endif /* _INCLUDE__OS__HANDLE_REGISTRY_H_ */