@ -12,13 +12,17 @@
typedef u32 Handle ;
typedef s32 Result ;
const Handle INVALID_HANDLE = 0 ;
namespace Kernel {
// From kernel.h. Declarations duplicated here to avoid a circular header dependency.
class Thread ;
Thread * GetCurrentThread ( ) ;
// TODO: Verify code
const ResultCode ERR_OUT_OF_HANDLES ( ErrorDescription : : OutOfMemory , ErrorModule : : Kernel ,
ErrorSummary : : OutOfResource , ErrorLevel : : Temporary ) ;
// TOOD: Verify code
const ResultCode ERR_INVALID_HANDLE = InvalidHandle ( ErrorModule : : Kernel ) ;
enum KernelHandle {
enum KernelHandle : Handle {
CurrentThread = 0xFFFF8000 ,
CurrentProcess = 0xFFFF8001 ,
} ;
@ -61,103 +65,127 @@ public:
LOG_ERROR ( Kernel , " (UNIMPLEMENTED) " ) ;
return UnimplementedFunction ( ErrorModule : : Kernel ) ;
}
} ;
class HandleTable : NonCopyable {
public :
HandleTable ( ) ;
~ HandleTable ( ) { }
// Allocates a handle within the range and inserts the object into the map.
Handle Create ( Object * obj , int range_bottom = INITIAL_NEXT_ID , int range_top = 0x7FFFFFFF ) ;
static Object * CreateByIDType ( int type ) ;
template < class T >
void Destroy ( Handle handle ) {
if ( Get < T > ( handle ) ) {
occupied [ handle - HANDLE_OFFSET ] = false ;
delete pool [ handle - HANDLE_OFFSET ] ;
}
}
bool IsValid ( Handle handle ) const ;
template < class T >
T * Get ( Handle handle ) {
if ( handle = = CurrentThread ) {
return reinterpret_cast < T * > ( GetCurrentThread ( ) ) ;
}
if ( handle < HANDLE_OFFSET | | handle > = HANDLE_OFFSET + MAX_COUNT | | ! occupied [ handle - HANDLE_OFFSET ] ) {
if ( handle ! = 0 ) {
LOG_ERROR ( Kernel , " Bad object handle %08x " , handle ) ;
}
return nullptr ;
} else {
Object * t = pool [ handle - HANDLE_OFFSET ] ;
if ( t - > GetHandleType ( ) ! = T : : HANDLE_TYPE ) {
LOG_ERROR ( Kernel , " Wrong object type for %08x " , handle ) ;
return nullptr ;
}
return static_cast < T * > ( t ) ;
}
}
// ONLY use this when you know the handle is valid.
template < class T >
T * GetFast ( Handle handle ) {
if ( handle = = CurrentThread ) {
return reinterpret_cast < T * > ( GetCurrentThread ( ) ) ;
}
const Handle realHandle = handle - HANDLE_OFFSET ;
_dbg_assert_ ( Kernel , realHandle > = 0 & & realHandle < MAX_COUNT & & occupied [ realHandle ] ) ;
return static_cast < T * > ( pool [ realHandle ] ) ;
}
template < class T , typename ArgT >
void Iterate ( bool func ( T * , ArgT ) , ArgT arg ) {
int type = T : : GetStaticIDType ( ) ;
for ( int i = 0 ; i < MAX_COUNT ; i + + )
{
if ( ! occupied [ i ] )
continue ;
T * t = static_cast < T * > ( pool [ i ] ) ;
if ( t - > GetIDType ( ) = = type ) {
if ( ! func ( t , arg ) )
break ;
}
}
}
bool GetIDType ( Handle handle , HandleType * type ) const {
if ( ( handle < HANDLE_OFFSET ) | | ( handle > = HANDLE_OFFSET + MAX_COUNT ) | |
! occupied [ handle - HANDLE_OFFSET ] ) {
LOG_ERROR ( Kernel , " Bad object handle %08X " , handle ) ;
return false ;
}
Object * t = pool [ handle - HANDLE_OFFSET ] ;
* type = t - > GetHandleType ( ) ;
return true ;
}
Object * & operator [ ] ( Handle handle ) ;
void List ( ) ;
void Clear ( ) ;
int GetCount ( ) const ;
private :
friend void intrusive_ptr_add_ref ( Object * ) ;
friend void intrusive_ptr_release ( Object * ) ;
enum {
MAX_COUNT = 0x1000 ,
HANDLE_OFFSET = 0x100 ,
INITIAL_NEXT_ID = 0x10 ,
} ;
unsigned int ref_count = 0 ;
} ;
std : : array < Object * , MAX_COUNT > pool ;
std : : array < bool , MAX_COUNT > occupied ;
int next_id ;
// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref ( Object * object ) {
+ + object - > ref_count ;
}
inline void intrusive_ptr_release ( Object * object ) {
if ( - - object - > ref_count = = 0 ) {
delete object ;
}
}
/**
* This class allows the creation of Handles , which are references to objects that can be tested
* for validity and looked up . Here they are used to pass references to kernel objects to / from the
* emulated process . it has been designed so that it follows the same handle format and has
* approximately the same restrictions as the handle manager in the CTR - OS .
*
* Handles contain two sub - fields : a slot index ( bits 31 : 15 ) and a generation value ( bits 14 : 0 ) .
* The slot index is used to index into the arrays in this class to access the data corresponding
* to the Handle .
*
* To prevent accidental use of a freed Handle whose slot has already been reused , a global counter
* is kept and incremented every time a Handle is created . This is the Handle ' s " generation " . The
* value of the counter is stored into the Handle as well as in the handle table ( in the
* " generations " array ) . When looking up a handle , the Handle ' s generation must match with the
* value stored on the class , otherwise the Handle is considered invalid .
*
* To find free slots when allocating a Handle without needing to scan the entire object array , the
* generations field of unallocated slots is re - purposed as a linked list of indices to free slots .
* When a Handle is created , an index is popped off the list and used for the new Handle . When it
* is destroyed , it is again pushed onto the list to be re - used by the next allocation . It is
* likely that this allocation strategy differs from the one used in CTR - OS , but this hasn ' t been
* verified and isn ' t likely to cause any problems .
*/
class HandleTable final : NonCopyable {
public :
HandleTable ( ) ;
/**
* Allocates a handle for the given object .
* @ return The created Handle or one of the following errors :
* - ` ERR_OUT_OF_HANDLES ` : the maximum number of handles has been exceeded .
*/
ResultVal < Handle > Create ( Object * obj ) ;
/**
* Returns a new handle that points to the same object as the passed in handle .
* @ return The duplicated Handle or one of the following errors :
* - ` ERR_INVALID_HANDLE ` : an invalid handle was passed in .
* - Any errors returned by ` Create ( ) ` .
*/
ResultVal < Handle > Duplicate ( Handle handle ) ;
/**
* Closes a handle , removing it from the table and decreasing the object ' s ref - count .
* @ return ` RESULT_SUCCESS ` or one of the following errors :
* - ` ERR_INVALID_HANDLE ` : an invalid handle was passed in .
*/
ResultCode Close ( Handle handle ) ;
/// Checks if a handle is valid and points to an existing object.
bool IsValid ( Handle handle ) const ;
/**
* Looks up a handle .
* @ returns Pointer to the looked - up object , or ` nullptr ` if the handle is not valid .
*/
Object * GetGeneric ( Handle handle ) const ;
/**
* Looks up a handle while verifying its type .
* @ returns Pointer to the looked - up object , or ` nullptr ` if the handle is not valid or its
* type differs from the handle type ` T : : HANDLE_TYPE ` .
*/
template < class T >
T * Get ( Handle handle ) const {
Object * object = GetGeneric ( handle ) ;
if ( object ! = nullptr & & object - > GetHandleType ( ) = = T : : HANDLE_TYPE ) {
return static_cast < T * > ( object ) ;
}
return nullptr ;
}
/// Closes all handles held in this table.
void Clear ( ) ;
private :
/**
* This is the maximum limit of handles allowed per process in CTR - OS . It can be further
* reduced by ExHeader values , but this is not emulated here .
*/
static const size_t MAX_COUNT = 4096 ;
static size_t GetSlot ( Handle handle ) { return handle > > 15 ; }
static u16 GetGeneration ( Handle handle ) { return handle & 0x7FFF ; }
/// Stores the Object referenced by the handle or null if the slot is empty.
std : : array < Object * , MAX_COUNT > objects ;
/**
* The value of ` next_generation ` when the handle was created , used to check for validity . For
* empty slots , contains the index of the next free slot in the list .
*/
std : : array < u16 , MAX_COUNT > generations ;
/**
* Global counter of the number of created handles . Stored in ` generations ` when a handle is
* created , and wraps around to 1 when it hits 0x8000 .
*/
u16 next_generation ;
/// Head of the free slots linked list.
u16 next_free_slot ;
} ;
extern HandleTable g_handle_table ;