/* * Copyright © 2006-2016 SplinterGU (Fenix/Bennugd) * Copyright © 2002-2006 Fenix Team (Fenix) * Copyright © 1999-2002 José Luis Cebrián Pagüe (Fenix) * * This file is part of Bennu - Game Development * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * */ #include #include #include #ifdef TARGET_BEOS #include #else #include #endif #include "offsets.h" #include "bgdrtm.h" #include "sysprocs_p.h" #include "instance.h" #include "xstrings.h" #undef STACK_SIZE #define STACK_SIZE 4096 /* ---------------------------------------------------------------------- */ #define INSTANCE_MIN_PRIORITY -32768 #define INSTANCE_MAX_PRIORITY 32767 #define INSTANCE_NORMALIZE_PRIORITY 32768 /* ---------------------------------------------------------------------- */ /* Módulo de gestión de instancias, con las funciones de incialización y */ /* destrucción, duplicado, etc. */ /* ---------------------------------------------------------------------- */ #define HASH(id) (unsigned int)((id)&0x0000ffff) #define HASH_PRIORITY(id) (unsigned int)(((id) + INSTANCE_NORMALIZE_PRIORITY) & 0x0000ffff) #define HASH_INSTANCE(id) (unsigned int)(((( uint32_t )(id)) >> 2 ) & 0x0000ffff) #define HASH_SIZE 65536 INSTANCE ** hashed_by_id = NULL; INSTANCE ** hashed_by_instance = NULL; INSTANCE ** hashed_by_type = NULL; INSTANCE ** hashed_by_priority = NULL; INSTANCE * first_instance = NULL ; /* Priority lists */ static INSTANCE * iterator_by_priority = NULL ; static int iterator_pos = HASH_SIZE; static int instance_maxid = FIRST_INSTANCE_ID ; static int instance_min_actual_prio = INSTANCE_MAX_PRIORITY ; static int instance_max_actual_prio = INSTANCE_MIN_PRIORITY ; /* ---------------------------------------------------------------------- */ /* By id */ /* ---------------------------------------------------------------------- */ void instance_add_to_list_by_id( INSTANCE * r, uint32_t id ) { if ( !hashed_by_id ) hashed_by_id = calloc( HASH_SIZE, sizeof( INSTANCE * ) ); hashed_by_id[HASH( id )] = r; } /* ---------------------------------------------------------------------- */ void instance_remove_from_list_by_id( INSTANCE * r, uint32_t id ) { hashed_by_id[HASH( id )] = NULL; } /* ---------------------------------------------------------------------- */ /* By instance */ /* ---------------------------------------------------------------------- */ void instance_add_to_list_by_instance( INSTANCE * r ) { unsigned int hash = HASH_INSTANCE( r ); if ( !hashed_by_instance ) hashed_by_instance = calloc( HASH_SIZE, sizeof( INSTANCE * ) ); r->prev_by_instance = NULL ; r->next_by_instance = hashed_by_instance[hash]; if ( r->next_by_instance ) r->next_by_instance->prev_by_instance = r ; hashed_by_instance[hash] = r; } /* ---------------------------------------------------------------------- */ void instance_remove_from_list_by_instance( INSTANCE * r ) { unsigned int hash = HASH_INSTANCE( r ); if ( r->prev_by_instance ) r->prev_by_instance->next_by_instance = r->next_by_instance ; if ( r->next_by_instance ) r->next_by_instance->prev_by_instance = r->prev_by_instance ; if ( hashed_by_instance[hash] == r ) hashed_by_instance[hash] = r->next_by_instance ; } /* ---------------------------------------------------------------------- */ /* By type */ /* ---------------------------------------------------------------------- */ void instance_add_to_list_by_type( INSTANCE * r, uint32_t type ) { unsigned int hash = HASH( type ); if ( !hashed_by_type ) hashed_by_type = calloc( HASH_SIZE, sizeof( INSTANCE * ) ); r->prev_by_type = NULL ; r->next_by_type = hashed_by_type[hash]; if ( r->next_by_type ) r->next_by_type->prev_by_type = r ; hashed_by_type[hash] = r; } /* ---------------------------------------------------------------------- */ void instance_remove_from_list_by_type( INSTANCE * r, uint32_t type ) { unsigned int hash = HASH( type ); if ( r->prev_by_type ) r->prev_by_type->next_by_type = r->next_by_type ; if ( r->next_by_type ) r->next_by_type->prev_by_type = r->prev_by_type ; if ( hashed_by_type[hash] == r ) hashed_by_type[hash] = r->next_by_type ; } /* ---------------------------------------------------------------------- */ /* By priority */ /* ---------------------------------------------------------------------- */ void instance_add_to_list_by_priority( INSTANCE * r, int32_t priority ) { unsigned int hash ; if ( priority < INSTANCE_MIN_PRIORITY ) priority = LOCINT32( r, PRIORITY ) = INSTANCE_MIN_PRIORITY; if ( priority > INSTANCE_MAX_PRIORITY ) priority = LOCINT32( r, PRIORITY ) = INSTANCE_MAX_PRIORITY; hash = HASH_PRIORITY( priority ); if ( !hashed_by_priority ) hashed_by_priority = calloc( HASH_SIZE, sizeof( INSTANCE * ) ); r->prev_by_priority = NULL ; r->next_by_priority = hashed_by_priority[hash]; if ( r->next_by_priority ) r->next_by_priority->prev_by_priority = r ; hashed_by_priority[hash] = r; r->last_priority = priority ; if ( priority < instance_min_actual_prio ) instance_min_actual_prio = priority ; if ( priority > instance_max_actual_prio ) instance_max_actual_prio = priority ; } /* ---------------------------------------------------------------------- */ void instance_remove_from_list_by_priority( INSTANCE * r ) { unsigned int hash = HASH_PRIORITY( r->last_priority ); /* Update iterator_by_priority if necessary */ if ( iterator_by_priority == r ) instance_next_by_priority() ; if ( r->prev_by_priority ) r->prev_by_priority->next_by_priority = r->next_by_priority ; if ( r->next_by_priority ) r->next_by_priority->prev_by_priority = r->prev_by_priority ; if ( hashed_by_priority[hash] == r ) hashed_by_priority[hash] = r->next_by_priority ; if ( !hashed_by_priority[hash] ) { if ( r->last_priority == instance_min_actual_prio ) { while ( instance_min_actual_prio < instance_max_actual_prio && !hashed_by_priority[ instance_min_actual_prio + INSTANCE_NORMALIZE_PRIORITY ] ) instance_min_actual_prio++; } if ( r->last_priority == instance_max_actual_prio ) { while ( instance_max_actual_prio > instance_min_actual_prio && !hashed_by_priority[ instance_max_actual_prio + INSTANCE_NORMALIZE_PRIORITY ] ) instance_max_actual_prio--; } } } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_dirty * * Adds an instance to the dirty instances list. This is a list of all * instances which priority changed since the last execution. * * PARAMS : * i Pointer to the instance * * RETURN VALUE : * None */ void instance_dirty( INSTANCE * i ) { instance_remove_from_list_by_priority( i ); instance_add_to_list_by_priority( i, LOCINT32( i, PRIORITY ) ); } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_get * * Returns a instance, given its ID. * * PARAMS : * id Integer ID of the instance * * RETURN VALUE : * Pointer to the found instance or NULL if not found */ INSTANCE * instance_get( int id ) { if ( !hashed_by_id || !id ) return NULL; return ( hashed_by_id[HASH( id )] ); } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_getid * * Allocate and return a free instance identifier code. * * PARAMS : * None * * RETURN VALUE : * A new, unused instance identifier * -1 if error */ int instance_getid() { int id = instance_maxid++ ; if ( !hashed_by_id ) hashed_by_id = calloc( HASH_SIZE, sizeof( INSTANCE * ) ); if ( id <= LAST_INSTANCE_ID && !hashed_by_id[ HASH( id ) ] ) return id; if ( instance_maxid > LAST_INSTANCE_ID ) { for ( instance_maxid = FIRST_INSTANCE_ID; instance_maxid <= LAST_INSTANCE_ID; instance_maxid++ ) { if ( !hashed_by_id[HASH( instance_maxid )] ) return instance_maxid++; } } else { for ( ; instance_maxid <= LAST_INSTANCE_ID; instance_maxid++ ) { if ( !hashed_by_id[HASH( instance_maxid )] ) return instance_maxid++; } for ( instance_maxid = FIRST_INSTANCE_ID; instance_maxid < id; instance_maxid++ ) { if ( !hashed_by_id[HASH( instance_maxid )] ) return instance_maxid++; } } return -1; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_duplicate * * Create a copy of an instance, using the same local/private data * - Updates the instance list, adding the new instance * - Marks all local and private strings * - Updates all parents local family variables * * The new instance uses the same code pointer as the father * (it begins execution at the same line, instead of the first one) * * PARAMS : * father Pointer to the original instance * * RETURN VALUE : * Pointer to the new instance */ INSTANCE * instance_duplicate( INSTANCE * father ) { INSTANCE * r, * brother ; int n, pid ; uint32_t type ; if ( ( pid = instance_getid() ) == -1 ) return NULL; r = ( INSTANCE * ) calloc( 1, sizeof( INSTANCE ) ) ; assert( r ) ; r->pridata = ( int * ) malloc( father->private_size + 4 ) ; r->pubdata = ( int * ) malloc( father->public_size + 4 ) ; r->locdata = ( int * ) malloc( local_size + 4 ) ; r->code = father->code ; r->codeptr = father->codeptr ; r->exitcode = father->exitcode ; r->errorcode = father->errorcode ; r->proc = father->proc ; r->call_level = father->call_level ; r->switchval = 0; r->switchval_string = 0; r->cased = 0; r->breakpoint = 0 ; r->private_size = father->private_size ; r->public_size = father->public_size ; r->first_run = 1 ; if ( father->private_size > 0 ) memcpy( r->pridata, father->pridata, r->private_size ) ; if ( father->public_size > 0 ) memcpy( r->pubdata, father->pubdata, r->public_size ) ; if ( local_size > 0 ) memcpy( r->locdata, father->locdata, local_size ) ; /* Inicializa datos de jerarquia */ /* Crea el proceso clónico como si lo hubiera llamado el padre */ type = LOCDWORD( father, PROCESS_TYPE ) ; LOCDWORD( r, PROCESS_ID ) = pid ; LOCDWORD( r, SON ) = 0 ; LOCDWORD( r, SMALLBRO ) = 0 ; LOCDWORD( r, FATHER ) = LOCDWORD( father, PROCESS_ID ) ; brother = instance_get( LOCDWORD( father, SON ) ) ; if ( brother ) { LOCDWORD( r, BIGBRO ) = LOCDWORD( brother, PROCESS_ID ) ; LOCDWORD( brother, SMALLBRO ) = pid ; } else { LOCDWORD( r, BIGBRO ) = 0 ; } LOCDWORD( father, SON ) = pid ; /* Actualiza las cuentas de uso de las cadenas */ for ( n = 0; n < r->proc->string_count; n++ ) string_use( PRIDWORD( r, r->proc->strings[n] ) ) ; /* Strings privadas */ for ( n = 0; n < r->proc->pubstring_count; n++ ) string_use( PUBDWORD( r, r->proc->pubstrings[n] ) ) ; /* Strings publicas */ for ( n = 0; n < local_strings; n++ ) string_use( LOCDWORD( r, localstr[n] ) ) ; /* Strings locales */ r->prev = NULL ; r->next = first_instance ; if ( first_instance ) first_instance->prev = r; first_instance = r ; instance_add_to_list_by_id( r, pid ); instance_add_to_list_by_instance( r ); instance_add_to_list_by_type( r, type ); instance_add_to_list_by_priority( r, LOCINT32( r, PRIORITY ) ); /* The called_by pointer should be set only when the caller * is waiting for this process to return */ r->called_by = NULL; r->stack = malloc( father->stack[0] ); memmove(r->stack, father->stack, (int)father->stack_ptr - (int)father->stack); r->stack_ptr = &r->stack[1]; /* Initialize list pointers */ LOCDWORD( r, STATUS ) = STATUS_RUNNING; if ( instance_create_hook_count ) for ( n = 0; n < instance_create_hook_count; n++ ) instance_create_hook_list[n]( r ); return r ; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_new * * Create a new instance, using the default local/private data * - Updates the instance list, adding the new instance * - Marks all local and private strings * - Updates all parents local family variables * * PARAMS : * proc Pointer to the procedure definition * father Pointer to the father instance (may be NULL) * * RETURN VALUE : * Pointer to the new instance */ INSTANCE * instance_new( PROCDEF * proc, INSTANCE * father ) { INSTANCE * r, * brother; int n, pid; if ( ( pid = instance_getid() ) == -1 ) return NULL; r = ( INSTANCE * ) calloc( 1, sizeof( INSTANCE ) ) ; assert( r ) ; r->pridata = ( int * ) malloc( proc->private_size + 4 ) ; r->pubdata = ( int * ) malloc( proc->public_size + 4 ) ; r->locdata = ( int * ) malloc( local_size + 4 ) ; r->code = proc->code ; r->codeptr = proc->code ; r->exitcode = proc->exitcode ; r->errorcode = proc->errorcode ; r->proc = proc ; r->call_level = 0 ; r->switchval = 0; r->switchval_string = 0; r->cased = 0; r->breakpoint = 0 ; r->private_size = proc->private_size ; r->public_size = proc->public_size ; r->first_run = 1 ; if ( proc->private_size > 0 ) memcpy( r->pridata, proc->pridata, proc->private_size ) ; if ( proc->public_size > 0 ) memcpy( r->pubdata, proc->pubdata, proc->public_size ) ; if ( local_size > 0 ) memcpy( r->locdata, localdata, local_size ) ; /* Inicializa datos de jerarquia */ LOCDWORD( r, PROCESS_TYPE ) = proc->type ; LOCDWORD( r, PROCESS_ID ) = pid ; LOCDWORD( r, SON ) = 0 ; LOCDWORD( r, SMALLBRO ) = 0 ; if ( father ) { LOCDWORD( r, FATHER ) = LOCDWORD( father, PROCESS_ID ) ; brother = instance_get( LOCDWORD( father, SON ) ) ; if ( brother ) { LOCDWORD( r, BIGBRO ) = LOCDWORD( brother, PROCESS_ID ) ; LOCDWORD( brother, SMALLBRO ) = pid ; } else { LOCDWORD( r, BIGBRO ) = 0 ; } LOCDWORD( father, SON ) = pid ; } else { LOCDWORD( r, FATHER ) = 0 ; LOCDWORD( r, BIGBRO ) = 0 ; } /* Cuenta los usos de las variables tipo cadena */ for ( n = 0; n < proc->string_count; n++ ) string_use( PRIDWORD( r, proc->strings[n] ) ) ; /* Strings privadas */ for ( n = 0; n < proc->pubstring_count; n++ ) string_use( PUBDWORD( r, proc->pubstrings[n] ) ) ; /* Strings publicas */ for ( n = 0; n < local_strings; n++ ) string_use( LOCDWORD( r, localstr[n] ) ) ; /* Strings locales */ r->prev = NULL ; r->next = first_instance ; if ( first_instance ) first_instance->prev = r; first_instance = r ; instance_add_to_list_by_id( r, pid ); instance_add_to_list_by_instance( r ); instance_add_to_list_by_type( r, proc->type ); instance_add_to_list_by_priority( r, 0 ); /* The called_by pointer should be set only when the caller * is waiting for this process to return */ r->called_by = NULL; r->stack = malloc( STACK_SIZE ); r->stack_ptr = &r->stack[1]; r->stack[0] = STACK_SIZE; /* Initialize list pointers */ LOCDWORD( r, STATUS ) = STATUS_RUNNING; if ( instance_create_hook_count ) for ( n = 0; n < instance_create_hook_count; n++ ) instance_create_hook_list[n]( r ); return r ; } /* ---------------------------------------------------------------------- */ INSTANCE * instance_getfather( INSTANCE * i ) { return instance_get( LOCDWORD( i, FATHER ) ) ; } /* ---------------------------------------------------------------------- */ INSTANCE * instance_getson( INSTANCE * i ) { return instance_get( LOCDWORD( i, SON ) ) ; } /* ---------------------------------------------------------------------- */ INSTANCE * instance_getbigbro( INSTANCE * i ) { return instance_get( LOCDWORD( i, BIGBRO ) ) ; } /* ---------------------------------------------------------------------- */ INSTANCE * instance_getsmallbro( INSTANCE * i ) { return instance_get( LOCDWORD( i, SMALLBRO ) ) ; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_destroy_all * * Destroy all instances. Simply calls instance_destroy * for any and every instance in existence. * * PARAMS : * except Don't destroy this instance (used for LET_ME_ALONE) * * RETURN VALUE : * None */ void instance_destroy_all( INSTANCE * except ) { INSTANCE * i, * next ; i = first_instance ; while ( i ) { next = i->next ; if ( i != except ) instance_destroy( i ) ; i = next ; } } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_destroy * * Destroy an instance, effectively * - Updates any instance list, removing the given instance * - Discards all local and private strings * - Updates all parents local family variables * - Frees any memory involved * * PARAMS : * r Pointer to the instance * * RETURN VALUE : * None */ void instance_destroy( INSTANCE * r ) { INSTANCE * father, * bigbro, * smallbro; int n ; LOCDWORD( r, STATUS ) = STATUS_RUNNING; if ( instance_destroy_hook_count ) for ( n = 0; n < instance_destroy_hook_count; n++ ) instance_destroy_hook_list[n]( r ); /* Actualiza la cuenta de referencia de las variables tipo string */ for ( n = 0 ; n < r->proc->string_count ; n++ ) string_discard( PRIDWORD( r, r->proc->strings[n] ) ) ; /* Strings privadas */ for ( n = 0 ; n < r->proc->pubstring_count ; n++ ) string_discard( PUBDWORD( r, r->proc->pubstrings[n] ) ) ; /* Strings publicas */ for ( n = 0 ; n < local_strings ; n++ ) string_discard( LOCDWORD( r, localstr[n] ) ) ; /* Strings locales */ /* Actualiza árbol de jerarquias */ bigbro = instance_get( LOCDWORD( r, BIGBRO ) ) ; /* Tengo hermano mayor? */ if ( bigbro ) LOCDWORD( bigbro, SMALLBRO ) = LOCDWORD( r, SMALLBRO ) ; /* El hermano menor de mi hermano mayor es mi hermano menor */ smallbro = instance_get( LOCDWORD( r, SMALLBRO ) ) ; /* Tengo hermano menor? */ if ( smallbro ) LOCDWORD( smallbro, BIGBRO ) = LOCDWORD( r, BIGBRO ) ; /* El hermano mayor de mi hermano menor es mi hermano mayor */ father = instance_get( LOCDWORD( r, FATHER ) ) ; /* Tengo padre? */ if ( father && instance_get( LOCDWORD( father, SON ) ) == r ) LOCDWORD( father, SON ) = LOCDWORD( r, BIGBRO ); /* Si tengo padre y soy el hijo menor, mi hermano mayor pasa a ser el menor hijo de mi padre */ /* Quita la instancia de la lista */ if ( r->prev ) r->prev->next = r->next ; if ( r->next ) r->next->prev = r->prev ; if ( first_instance == r ) first_instance = r->next ; /* Remove the instance from all hash lists */ instance_remove_from_list_by_id( r, LOCDWORD( r, PROCESS_ID ) ); instance_remove_from_list_by_instance( r ); instance_remove_from_list_by_type( r, LOCDWORD( r, PROCESS_TYPE ) ); instance_remove_from_list_by_priority( r ); if ( r->stack ) free( r->stack ) ; if ( r->locdata ) free( r->locdata ) ; if ( r->pubdata ) free( r->pubdata ) ; if ( r->pridata ) free( r->pridata ) ; free( r ) ; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_exists * * Given an instance pointer, returns TRUE if it is still valid. * * PARAMS : * i Pointer to the instance * * RETURN VALUE : * 1 if the instance pointer is in the global instance list, 0 otherwise */ int instance_exists( INSTANCE * r ) { INSTANCE * i ; if ( !hashed_by_instance || !r ) return 0; i = hashed_by_instance[HASH_INSTANCE( r )]; while ( i ) { if ( r == i ) return 1 ; i = i->next_by_instance ; } return 0 ; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_next_by_priority * * Gets the next instance pointer until no more instances are * returned. Instances are returned sorted by priority. * * PARAMS : * None * * RETURN VALUE : * Pointer to the next priority on the list or NULL * if there is no more instances. The next call in this * case will return a pointer to the first instance * (the one with the lower priority) */ INSTANCE * instance_next_by_priority() { INSTANCE * r = iterator_by_priority ; if ( iterator_by_priority ) iterator_by_priority = iterator_by_priority->next_by_priority; if ( !iterator_by_priority ) { if ( !hashed_by_priority ) return NULL; if ( iterator_pos < ( instance_min_actual_prio + INSTANCE_NORMALIZE_PRIORITY ) || iterator_pos > ( instance_max_actual_prio + INSTANCE_NORMALIZE_PRIORITY ) ) iterator_pos = instance_max_actual_prio + INSTANCE_NORMALIZE_PRIORITY + 1; while ( --iterator_pos >= ( instance_min_actual_prio + INSTANCE_NORMALIZE_PRIORITY ) && !( iterator_by_priority = hashed_by_priority[iterator_pos] ) ); } return ( r ) ; } /* ---------------------------------------------------------------------- */ /* * FUNCTION : instance_get_by_type * * Returns a instance, given its type. * * PARAMS : * type Integer type of the first instance * context Pointer to an INSTANCE * use as context (internal use) * * RETURN VALUE : * Pointer to the found instance or NULL if not found */ /* context = NULL = start scan context = pointer = continue scan context = -1 = end scan */ INSTANCE * instance_get_by_type( uint32_t type, INSTANCE ** context ) { INSTANCE * i; if ( !context || !hashed_by_type || !type /* || type >= FIRST_INSTANCE_ID */ ) return NULL; if ( !*context ) /* start scan */ i = hashed_by_type[HASH( type )]; else if ( ( i = *context ) == ( INSTANCE * ) -1 ) /* End scan */ return ( *context = NULL ); if ( i ) /* Valid instance, continue scan */ { if ( i->next_by_type ) *context = i->next_by_type ; else *context = ( INSTANCE * ) -1 ; /* Next call will be "end scan" */ return i; } /* Here only if hashed_by_type[HASH( type )] is NULL */ return ( *context = NULL ) ; /* return is null, then end scan */ } /* ---------------------------------------------------------------------- */