/* * Copyright (c) 2004-2005 The Trustees of Indiana University. * All rights reserved. * Copyright (c) 2004-2005 The Trustees of the University of Tennessee. * All rights reserved. * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, * University of Stuttgart. All rights reserved. * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "ompi_config.h" #include #include #include #include "include/constants.h" #include "class/ompi_pointer_array.h" #include "util/output.h" enum { TABLE_INIT = 1, TABLE_GROW = 2 }; static void ompi_pointer_array_construct(ompi_pointer_array_t *); static void ompi_pointer_array_destruct(ompi_pointer_array_t *); static bool grow_table(ompi_pointer_array_t *table, size_t soft, size_t hard); OBJ_CLASS_INSTANCE(ompi_pointer_array_t, opal_object_t, ompi_pointer_array_construct, ompi_pointer_array_destruct); /* * ompi_pointer_array constructor */ void ompi_pointer_array_construct(ompi_pointer_array_t *array) { OBJ_CONSTRUCT(&array->lock, ompi_mutex_t); array->lowest_free = 0; array->number_free = 0; array->size = 0; array->addr = 0; } /* * ompi_pointer_array destructor */ void ompi_pointer_array_destruct(ompi_pointer_array_t *array) { /* free table */ if( NULL != array->addr) { free(array->addr); } OBJ_DESTRUCT(&array->lock); } /** * add a pointer to dynamic pointer table * * @param table Pointer to ompi_pointer_array_t object (IN) * @param ptr Pointer to be added to table (IN) * * @return Array index where ptr is inserted or OMPI_ERROR if it fails */ int ompi_pointer_array_add(ompi_pointer_array_t *table, void *ptr) { void **p; int i; int index; if (0) { ompi_output(0,"ompi_pointer_array_add: IN: " " table %p (size %ld, lowest free %ld, number free %ld)" " ptr = %p\n", table, table->size, table->lowest_free, table->number_free, ptr); } assert(table != NULL); OMPI_THREAD_LOCK(&(table->lock)); if (table->addr == NULL) { /* * first time */ if (0) { ompi_output(0,"ompi_pointer_array_add: INIT: table %p\n", table); } p = (void **) malloc(TABLE_INIT * sizeof(void *)); if (p == NULL) { OMPI_THREAD_UNLOCK(&(table->lock)); return OMPI_ERROR; } table->lowest_free = 0; table->number_free = TABLE_INIT; table->size = TABLE_INIT; table->addr = p; for (i = 0; i < table->size; i++) { table->addr[i] = NULL; } } else if (table->number_free == 0) { /* need to grow table */ if (!grow_table(table, table->size * TABLE_GROW, OMPI_FORTRAN_HANDLE_MAX)) { OMPI_THREAD_UNLOCK(&(table->lock)); return OMPI_ERR_OUT_OF_RESOURCE; } } assert(table->addr != NULL); assert(table->size > 0); assert(table->lowest_free >= 0); assert(table->lowest_free < table->size); assert(table->number_free > 0); assert(table->number_free <= table->size); /* * add pointer to table, and return the index */ index = table->lowest_free; assert(table->addr[index] == NULL); table->addr[index] = ptr; table->number_free--; if (table->number_free > 0) { for (i = table->lowest_free + 1; i < table->size; i++) { if (table->addr[i] == NULL) { table->lowest_free = i; break; } } } else { table->lowest_free = table->size; } if (0) { ompi_output(0,"ompi_pointer_array_add: OUT: " " table %p (size %ld, lowest free %ld, number free %ld)" " addr[%d] = %p\n", table, table->size, table->lowest_free, table->number_free, index, ptr); } OMPI_THREAD_UNLOCK(&(table->lock)); return index; } /** * free a slot in dynamic pointer table for reuse * * * @param table Pointer to ompi_pointer_array_t object (IN) * @param ptr Pointer to be added to table (IN) * * @return Error code * * Assumption: NULL element is free element. */ int ompi_pointer_array_set_item(ompi_pointer_array_t *table, int index, void * value) { assert(table != NULL); #if 0 ompi_output(0,"ompi_pointer_array_set_item: IN: " " table %p (size %ld, lowest free %ld, number free %ld)" " addr[%d] = %p\n", table, table->size, table->lowest_free, table->number_free, index, table->addr[index]); #endif /* expand table if required to set a specific index */ OMPI_THREAD_LOCK(&(table->lock)); if (table->size <= index) { if (!grow_table(table, ((index / TABLE_GROW) + 1) * TABLE_GROW, index)) { OMPI_THREAD_UNLOCK(&(table->lock)); return OMPI_ERROR; } } /* * allow a specific index to be changed. */ if ( NULL == table->addr[index] ) { table->addr[index] = value; /* mark element as free, if NULL element */ if( NULL == value ) { if (index < table->lowest_free) { table->lowest_free = index; } } else { table->number_free--; /* Reset lowest_free if required */ if ( index == table->lowest_free ) { int i; table->lowest_free=table->size; for ( i=index; isize; i++) { if ( NULL == table->addr[i] ){ table->lowest_free = i; break; } } } } } else { table->addr[index] = value; /* mark element as free, if NULL element */ if( NULL == value ) { if (index < table->lowest_free) { table->lowest_free = index; } table->number_free++; } else { /* Reset lowest_free if required */ if ( index == table->lowest_free ) { int i; table->lowest_free=table->size; for ( i=index; isize; i++) { if ( NULL == table->addr[i] ){ table->lowest_free = i; break; } } } } } #if 0 ompi_output(0,"ompi_pointer_array_set_item: OUT: " " table %p (size %ld, lowest free %ld, number free %ld)" " addr[%d] = %p\n", table, table->size, table->lowest_free, table->number_free, index, table->addr[index]); #endif OMPI_THREAD_UNLOCK(&(table->lock)); return OMPI_SUCCESS; } /** * Test whether a certain element is already in use. If not yet * in use, reserve it. * * @param array Pointer to array (IN) * @param index Index of element to be tested (IN) * @param value New value to be set at element index (IN) * * @return true/false True if element could be reserved * False if element could not be reserved (e.g.in use). * * In contrary to array_set, this function does not allow to overwrite * a value, unless the previous value is NULL ( equiv. to free ). */ bool ompi_pointer_array_test_and_set_item (ompi_pointer_array_t *table, int index, void *value) { assert(table != NULL); assert(index >= 0); #if 0 ompi_output(0,"ompi_pointer_array_test_and_set_item: IN: " " table %p (size %ld, lowest free %ld, number free %ld)" " addr[%d] = %p\n", table, table->size, table->lowest_free, table->number_free, index, table->addr[index]); #endif /* expand table if required to set a specific index */ OMPI_THREAD_LOCK(&(table->lock)); if ( index < table->size && table->addr[index] != NULL ) { /* This element is already in use */ OMPI_THREAD_UNLOCK(&(table->lock)); return false; } /* Do we need to grow the table? */ if (table->size <= index) { if (!grow_table(table, (((index / TABLE_GROW) + 1) * TABLE_GROW), index)) { OMPI_THREAD_UNLOCK(&(table->lock)); return false; } } /* * allow a specific index to be changed. */ table->addr[index] = value; table->number_free--; /* Reset lowest_free if required */ if ( index == table->lowest_free ) { int i; table->lowest_free = table->size; for ( i=index; isize; i++) { if ( NULL == table->addr[i] ){ table->lowest_free = i; break; } } } #if 0 ompi_output(0,"ompi_pointer_array_test_and_set_item: OUT: " " table %p (size %ld, lowest free %ld, number free %ld)" " addr[%d] = %p\n", table, table->size, table->lowest_free, table->number_free, index, table->addr[index]); #endif OMPI_THREAD_UNLOCK(&(table->lock)); return true; } static bool grow_table(ompi_pointer_array_t *table, size_t soft, size_t hard) { size_t new_size; int i, new_size_int; void *p; /* Ensure that we have room to grow -- stay less than OMPI_FORTRAN_HANDLE_MAX. Note that OMPI_FORTRAN_HANDLE_MAX is min(INT_MAX, fortran INTEGER max), so it's guaranteed to fit within a [signed] int. */ if (table->size >= OMPI_FORTRAN_HANDLE_MAX) { return false; } if (soft > OMPI_FORTRAN_HANDLE_MAX) { if (hard > OMPI_FORTRAN_HANDLE_MAX) { return false; } else { new_size = hard; } } else { new_size = soft; } p = (void **) realloc(table->addr, new_size * sizeof(void *)); if (p == NULL) { return false; } /* We've already established (above) that the arithimetic below will be less than OMPI_FORTRAN_HANDLE_MAX */ new_size_int = (int) new_size; table->number_free += new_size_int - table->size; table->addr = p; for (i = table->size; i < new_size_int; ++i) { table->addr[i] = NULL; } table->size = new_size_int; return true; }