Changeset 317a5b5 in rtems
- Timestamp:
- 11/02/99 19:43:52 (23 years ago)
- Branches:
- 4.10, 4.11, 4.8, 4.9, 5, master
- Children:
- b568ccb
- Parents:
- 811fae1
- Files:
-
- 30 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
c/src/exec/score/src/Makefile.in
r811fae1 r317a5b5 23 23 MP_C_PIECES = $(MP_C_PIECES_$(HAS_MP)_V) 24 24 25 OBJECT_C_PIECES = object objectallocate objectallocatebyindex \ 26 objectclearname objectcomparenameraw objectcomparenamestring \ 27 objectcopynameraw objectcopynamestring objectextendinformation \ 28 objectfree objectget objectgetbyindex objectgetnext \ 29 objectinitializeinformation objectnametoid objectshrinkinformation 30 25 31 THREAD_C_PIECES = thread threadchangepriority threadclearstate threadclose \ 26 32 threadcreateidle threaddelayended threaddispatch threadevaluatemode \ … … 33 39 # C and C++ source names, if any, go here -- minus the .c or .cc 34 40 C_PIECES = apiext chain coremsg coremutex coresem coretod heap interr isr \ 35 object$(THREAD_C_PIECES) threadq userext watchdog wkspace \41 $(OBJECT_C_PIECES) $(THREAD_C_PIECES) threadq userext watchdog wkspace \ 36 42 $(MP_C_PIECES) 37 43 C_FILES = $(C_PIECES:%=%.c) -
c/src/exec/score/src/object.c
r811fae1 r317a5b5 64 64 #endif 65 65 } 66 67 /*PAGE68 *69 * _Objects_Extend_information70 *71 * This routine extends all object information related data structures.72 *73 * Input parameters:74 * information - object information table75 *76 * Output parameters: NONE77 */78 79 void _Objects_Extend_information(80 Objects_Information *information81 )82 {83 Objects_Control *the_object;84 void *name_area;85 Chain_Control Inactive;86 unsigned32 block_count;87 unsigned32 block;88 unsigned32 index_base;89 unsigned32 minimum_index;90 unsigned32 index;91 92 /*93 * Search for a free block of indexes. The block variable ends up set94 * to block_count + 1 if the table needs to be extended.95 */96 97 minimum_index = _Objects_Get_index( information->minimum_id );98 index_base = minimum_index;99 block = 0;100 101 if ( information->maximum < minimum_index )102 block_count = 0;103 else {104 block_count = information->maximum / information->allocation_size;105 106 for ( ; block < block_count; block++ ) {107 if ( information->object_blocks[ block ] == NULL )108 break;109 else110 index_base += information->allocation_size;111 }112 }113 114 /*115 * If the index_base is the maximum we need to grow the tables.116 */117 118 if (index_base >= information->maximum ) {119 ISR_Level level;120 void **object_blocks;121 Objects_Name *name_table;122 unsigned32 *inactive_per_block;123 Objects_Control **local_table;124 unsigned32 maximum;125 void *old_tables;126 127 /*128 * Growing the tables means allocating a new area, doing a copy and129 * updating the information table.130 *131 * If the maximum is minimum we do not have a table to copy. First132 * time through.133 *134 * The allocation has :135 *136 * void *objects[block_count];137 * unsigned32 inactive_count[block_count];138 * Objects_Name *name_table[block_count];139 * Objects_Control *local_table[maximum];140 *141 * This is the order in memory. Watch changing the order. See the memcpy142 * below.143 */144 145 /*146 * Up the block count and maximum147 */148 149 block_count++;150 151 maximum = information->maximum + information->allocation_size;152 153 /*154 * Allocate the tables and break it up.155 */156 157 if ( information->auto_extend ) {158 object_blocks = (void**)159 _Workspace_Allocate(160 block_count *161 (sizeof(void *) + sizeof(unsigned32) + sizeof(Objects_Name *)) +162 ((maximum + minimum_index) * sizeof(Objects_Control *))163 );164 165 if ( !object_blocks )166 return;167 }168 else {169 object_blocks = (void**)170 _Workspace_Allocate_or_fatal_error(171 block_count *172 (sizeof(void *) + sizeof(unsigned32) + sizeof(Objects_Name *)) +173 ((maximum + minimum_index) * sizeof(Objects_Control *))174 );175 }176 177 /*178 * Break the block into the various sections.179 *180 */181 182 inactive_per_block = (unsigned32 *) _Addresses_Add_offset(183 object_blocks, block_count * sizeof(void*) );184 name_table = (Objects_Name *) _Addresses_Add_offset(185 inactive_per_block, block_count * sizeof(unsigned32) );186 local_table = (Objects_Control **) _Addresses_Add_offset(187 name_table, block_count * sizeof(Objects_Name *) );188 189 /*190 * Take the block count down. Saves all the (block_count - 1)191 * in the copies.192 */193 194 block_count--;195 196 if ( information->maximum > minimum_index ) {197 198 /*199 * Copy each section of the table over. This has to be performed as200 * separate parts as size of each block has changed.201 */202 203 memcpy( object_blocks,204 information->object_blocks,205 block_count * sizeof(void*) );206 memcpy( inactive_per_block,207 information->inactive_per_block,208 block_count * sizeof(unsigned32) );209 memcpy( name_table,210 information->name_table,211 block_count * sizeof(Objects_Name *) );212 memcpy( local_table,213 information->local_table,214 (information->maximum + minimum_index) * sizeof(Objects_Control *) );215 }216 else {217 218 /*219 * Deal with the special case of the 0 to minimum_index220 */221 for ( index = 0; index < minimum_index; index++ ) {222 local_table[ index ] = NULL;223 }224 }225 226 /*227 * Initialise the new entries in the table.228 */229 230 object_blocks[block_count] = NULL;231 inactive_per_block[block_count] = 0;232 name_table[block_count] = NULL;233 234 for ( index=index_base ;235 index < ( information->allocation_size + index_base );236 index++ ) {237 local_table[ index ] = NULL;238 }239 240 _ISR_Disable( level );241 242 old_tables = information->object_blocks;243 244 information->object_blocks = object_blocks;245 information->inactive_per_block = inactive_per_block;246 information->name_table = name_table;247 information->local_table = local_table;248 information->maximum = maximum;249 information->maximum_id =250 _Objects_Build_id(251 information->the_class, _Objects_Local_node, information->maximum252 );253 254 _ISR_Enable( level );255 256 if ( old_tables )257 _Workspace_Free( old_tables );258 259 block_count++;260 }261 262 /*263 * Allocate the name table, and the objects264 */265 266 if ( information->auto_extend ) {267 information->object_blocks[ block ] =268 _Workspace_Allocate(269 (information->allocation_size * information->name_length) +270 (information->allocation_size * information->size)271 );272 273 if ( !information->object_blocks[ block ] )274 return;275 }276 else {277 information->object_blocks[ block ] =278 _Workspace_Allocate_or_fatal_error(279 (information->allocation_size * information->name_length) +280 (information->allocation_size * information->size)281 );282 }283 284 name_area = (Objects_Name *) _Addresses_Add_offset(285 information->object_blocks[ block ],286 (information->allocation_size * information->size)287 );288 information->name_table[ block ] = name_area;289 290 /*291 * Initialize objects .. add to a local chain first.292 */293 294 _Chain_Initialize(295 &Inactive,296 information->object_blocks[ block ],297 information->allocation_size,298 information->size299 );300 301 /*302 * Move from the local chain, initialise, then append to the inactive chain303 */304 305 index = index_base;306 307 while ( (the_object = (Objects_Control *) _Chain_Get( &Inactive ) ) != NULL ) {308 309 the_object->id =310 _Objects_Build_id(311 information->the_class, _Objects_Local_node, index312 );313 314 the_object->name = (void *) name_area;315 316 name_area = _Addresses_Add_offset( name_area, information->name_length );317 318 _Chain_Append( &information->Inactive, &the_object->Node );319 320 index++;321 }322 323 information->inactive_per_block[ block ] = information->allocation_size;324 information->inactive += information->allocation_size;325 }326 327 /*PAGE328 *329 * _Objects_Shrink_information330 *331 * This routine shrinks object information related data structures.332 * The object's name and object space are released. The local_table333 * etc block does not shrink. The InActive list needs to be scanned334 * to find the objects are remove them.335 * Input parameters:336 * information - object information table337 * the_block - the block to remove338 *339 * Output parameters: NONE340 */341 342 void _Objects_Shrink_information(343 Objects_Information *information344 )345 {346 Objects_Control *the_object;347 Objects_Control *extract_me;348 unsigned32 block_count;349 unsigned32 block;350 unsigned32 index_base;351 unsigned32 index;352 353 /*354 * Search the list to find block or chunnk with all objects inactive.355 */356 357 index_base = _Objects_Get_index( information->minimum_id );358 block_count = ( information->maximum - index_base ) / information->allocation_size;359 360 for ( block = 0; block < block_count; block++ ) {361 if ( information->inactive_per_block[ block ] == information->allocation_size ) {362 363 /*364 * XXX - Not to sure how to use a chain where you need to iterate and365 * and remove elements.366 */367 368 the_object = (Objects_Control *) information->Inactive.first;369 370 /*371 * Assume the Inactive chain is never empty at this point372 */373 374 do {375 index = _Objects_Get_index( the_object->id );376 377 if ((index >= index_base) &&378 (index < (index_base + information->allocation_size))) {379 380 /*381 * Get the next node before the node is extracted382 */383 384 extract_me = the_object;385 386 if ( !_Chain_Is_last( &the_object->Node ) )387 the_object = (Objects_Control *) the_object->Node.next;388 else389 the_object = NULL;390 391 _Chain_Extract( &extract_me->Node );392 }393 else {394 the_object = (Objects_Control *) the_object->Node.next;395 }396 }397 while ( the_object && !_Chain_Is_last( &the_object->Node ) );398 399 /*400 * Free the memory and reset the structures in the object' information401 */402 403 _Workspace_Free( information->object_blocks[ block ] );404 information->name_table[ block ] = NULL;405 information->object_blocks[ block ] = NULL;406 information->inactive_per_block[ block ] = 0;407 408 information->inactive -= information->allocation_size;409 410 return;411 }412 413 index_base += information->allocation_size;414 }415 }416 417 /*PAGE418 *419 * _Objects_Initialize_information420 *421 * This routine initializes all object information related data structures.422 *423 * Input parameters:424 * information - object information table425 * the_class - object class426 * supports_global - TRUE if this is a global object class427 * maximum - maximum objects of this class428 * size - size of this object's control block429 * is_string - TRUE if names for this object are strings430 * maximum_name_length - maximum length of each object's name431 * is_thread - TRUE if this class is threads432 *433 * Output parameters: NONE434 */435 436 void _Objects_Initialize_information(437 Objects_Information *information,438 Objects_Classes the_class,439 boolean supports_global,440 unsigned32 maximum,441 unsigned32 size,442 boolean is_string,443 unsigned32 maximum_name_length,444 boolean is_thread445 )446 {447 static Objects_Control *null_local_table = NULL;448 449 unsigned32 minimum_index;450 unsigned32 index;451 unsigned32 name_length;452 453 information->the_class = the_class;454 information->is_string = is_string;455 information->is_thread = is_thread;456 457 information->local_table = 0;458 information->name_table = 0;459 information->inactive_per_block = 0;460 information->object_blocks = 0;461 462 information->inactive = 0;463 464 /*465 * Set the entry in the object information table.466 */467 468 _Objects_Information_table[ the_class ] = information;469 470 /*471 * Set the size of the object472 */473 474 information->size = size;475 476 /*477 * Are we operating in unlimited, or auto-extend mode478 */479 480 information->auto_extend = (maximum & OBJECTS_UNLIMITED_OBJECTS) ? TRUE : FALSE;481 maximum &= ~OBJECTS_UNLIMITED_OBJECTS;482 483 /*484 * The allocation unit is the maximum value485 */486 487 information->allocation_size = maximum;488 489 /*490 * Provide a null local table entry for the case of any empty table.491 */492 493 information->local_table = &null_local_table;494 495 /*496 * Calculate minimum and maximum Id's497 */498 499 if ( maximum == 0 ) minimum_index = 0;500 else minimum_index = 1;501 502 information->minimum_id =503 _Objects_Build_id( the_class, _Objects_Local_node, minimum_index );504 505 /*506 * Calculate the maximum name length507 */508 509 name_length = maximum_name_length;510 511 if ( name_length & (OBJECTS_NAME_ALIGNMENT-1) )512 name_length = (name_length + OBJECTS_NAME_ALIGNMENT) &513 ~(OBJECTS_NAME_ALIGNMENT-1);514 515 information->name_length = name_length;516 517 _Chain_Initialize_empty( &information->Inactive );518 519 /*520 * Initialize objects .. if there are any521 */522 523 if ( maximum ) {524 525 /*526 * Reset the maximum value. It will be updated when the information is527 * extended.528 */529 530 information->maximum = 0;531 532 /*533 * Always have the maximum size available so the current performance534 * figures are create are met. If the user moves past the maximum535 * number then a performance hit is taken.536 */537 538 _Objects_Extend_information( information );539 540 }541 542 /*543 * Take care of multiprocessing544 */545 546 if ( supports_global == TRUE && _System_state_Is_multiprocessing ) {547 548 information->global_table =549 (Chain_Control *) _Workspace_Allocate_or_fatal_error(550 (_Objects_Maximum_nodes + 1) * sizeof(Chain_Control)551 );552 553 for ( index=1; index <= _Objects_Maximum_nodes ; index++ )554 _Chain_Initialize_empty( &information->global_table[ index ] );555 }556 else557 information->global_table = NULL;558 }559 560 /*PAGE561 *562 * _Objects_Allocate563 *564 * DESCRIPTION:565 *566 * This function allocates a object control block from567 * the inactive chain of free object control blocks.568 */569 570 Objects_Control *_Objects_Allocate(571 Objects_Information *information572 )573 {574 Objects_Control *the_object =575 (Objects_Control *) _Chain_Get( &information->Inactive );576 577 if ( information->auto_extend ) {578 /*579 * If the list is empty then we are out of objects and need to580 * extend information base.581 */582 583 if ( !the_object ) {584 _Objects_Extend_information( information );585 the_object = (Objects_Control *) _Chain_Get( &information->Inactive );586 }587 588 if ( the_object ) {589 unsigned32 block;590 591 block = _Objects_Get_index( the_object->id ) -592 _Objects_Get_index( information->minimum_id );593 block /= information->allocation_size;594 595 information->inactive_per_block[ block ]--;596 information->inactive--;597 }598 }599 600 return the_object;601 }602 603 /*PAGE604 *605 * _Objects_Allocate_by_index606 *607 * DESCRIPTION:608 *609 * This function allocates the object control block610 * specified by the index from the inactive chain of611 * free object control blocks.612 */613 614 Objects_Control *_Objects_Allocate_by_index(615 Objects_Information *information,616 unsigned32 index,617 unsigned32 sizeof_control618 )619 {620 Objects_Control *the_object;621 void *p;622 623 if ( index && information->maximum >= index ) {624 the_object = _Objects_Get_local_object( information, index );625 if ( the_object )626 return NULL;627 628 /* XXX629 * This whole section of code needs to be addressed.630 * + The 0 should be dealt with more properly so we can autoextend.631 * + The pointer arithmetic is probably too expensive.632 * + etc.633 */634 635 p = _Addresses_Add_offset( information->object_blocks[ 0 ],636 (information->allocation_size * information->name_length) ),637 638 p = _Addresses_Add_offset( p, (sizeof_control * (index - 1)) );639 the_object = (Objects_Control *)p;640 _Chain_Extract( &the_object->Node );641 642 return the_object;643 }644 645 /*646 * Autoextend will have to be thought out as it applies647 * to user assigned indices.648 */649 650 return NULL;651 }652 653 654 655 /*PAGE656 *657 * _Objects_Free658 *659 * DESCRIPTION:660 *661 * This function frees a object control block to the662 * inactive chain of free object control blocks.663 */664 665 void _Objects_Free(666 Objects_Information *information,667 Objects_Control *the_object668 )669 {670 unsigned32 allocation_size = information->allocation_size;671 672 _Chain_Append( &information->Inactive, &the_object->Node );673 674 if ( information->auto_extend ) {675 unsigned32 block;676 677 block =678 _Objects_Get_index( the_object->id ) - _Objects_Get_index( information->minimum_id );679 block /= information->allocation_size;680 681 information->inactive_per_block[ block ]++;682 information->inactive++;683 684 /*685 * Check if the threshold level has been met of686 * 1.5 x allocation_size are free.687 */688 689 if ( information->inactive > ( allocation_size + ( allocation_size >> 1 ) ) ) {690 _Objects_Shrink_information( information );691 }692 }693 }694 695 /*PAGE696 *697 * _Objects_Clear_name698 *699 * XXX700 */701 702 void _Objects_Clear_name(703 void *name,704 unsigned32 length705 )706 {707 unsigned32 index;708 unsigned32 maximum = length / OBJECTS_NAME_ALIGNMENT;709 unsigned32 *name_ptr = (unsigned32 *) name;710 711 for ( index=0 ; index < maximum ; index++ )712 *name_ptr++ = 0;713 }714 715 /*PAGE716 *717 * _Objects_Copy_name_string718 *719 * XXX720 */721 722 void _Objects_Copy_name_string(723 void *source,724 void *destination725 )726 {727 unsigned8 *source_p = (unsigned8 *) source;728 unsigned8 *destination_p = (unsigned8 *) destination;729 730 do {731 *destination_p++ = *source_p;732 } while ( *source_p++ );733 }734 735 /*PAGE736 *737 * _Objects_Copy_name_raw738 *739 * XXX740 */741 742 void _Objects_Copy_name_raw(743 void *source,744 void *destination,745 unsigned32 length746 )747 {748 unsigned32 *source_p = (unsigned32 *) source;749 unsigned32 *destination_p = (unsigned32 *) destination;750 unsigned32 tmp_length = length / OBJECTS_NAME_ALIGNMENT;751 752 while ( tmp_length-- )753 *destination_p++ = *source_p++;754 }755 756 /*PAGE757 *758 * _Objects_Compare_name_string759 *760 * XXX761 */762 763 boolean _Objects_Compare_name_string(764 void *name_1,765 void *name_2,766 unsigned32 length767 )768 {769 unsigned8 *name_1_p = (unsigned8 *) name_1;770 unsigned8 *name_2_p = (unsigned8 *) name_2;771 unsigned32 tmp_length = length;772 773 do {774 if ( *name_1_p++ != *name_2_p++ )775 return FALSE;776 if ( !tmp_length-- )777 return FALSE;778 } while ( *name_1_p );779 780 return TRUE;781 }782 783 /*PAGE784 *785 * _Objects_Compare_name_raw786 *787 * XXX788 */789 790 boolean _Objects_Compare_name_raw(791 void *name_1,792 void *name_2,793 unsigned32 length794 )795 {796 unsigned32 *name_1_p = (unsigned32 *) name_1;797 unsigned32 *name_2_p = (unsigned32 *) name_2;798 unsigned32 tmp_length = length / OBJECTS_NAME_ALIGNMENT;799 800 while ( tmp_length-- )801 if ( *name_1_p++ != *name_2_p++ )802 return FALSE;803 804 return TRUE;805 }806 807 808 /*PAGE809 *810 * _Objects_Name_to_id811 *812 * These kernel routines search the object table(s) for the given813 * object name and returns the associated object id.814 *815 * Input parameters:816 * information - object information817 * name - user defined object name818 * node - node indentifier (0 indicates any node)819 * id - address of return ID820 *821 * Output parameters:822 * id - object id823 * OBJECTS_SUCCESSFUL - if successful824 * error code - if unsuccessful825 */826 827 Objects_Name_to_id_errors _Objects_Name_to_id(828 Objects_Information *information,829 Objects_Name name,830 unsigned32 node,831 Objects_Id *id832 )833 {834 boolean search_local_node;835 Objects_Control *the_object;836 unsigned32 index;837 unsigned32 name_length;838 Objects_Name_comparators compare_them;839 840 if ( name == 0 )841 return OBJECTS_INVALID_NAME;842 843 search_local_node = FALSE;844 845 if ( information->maximum != 0 &&846 (node == OBJECTS_SEARCH_ALL_NODES || node == OBJECTS_SEARCH_LOCAL_NODE ||847 _Objects_Is_local_node( node ) ) )848 search_local_node = TRUE;849 850 if ( search_local_node ) {851 name_length = information->name_length;852 853 if ( information->is_string ) compare_them = _Objects_Compare_name_string;854 else compare_them = _Objects_Compare_name_raw;855 856 for ( index = 1; index <= information->maximum; index++ ) {857 858 the_object = information->local_table[ index ];859 860 if ( !the_object || !the_object->name )861 continue;862 863 if ( (*compare_them)( name, the_object->name, name_length ) ) {864 *id = the_object->id;865 return OBJECTS_SUCCESSFUL;866 }867 }868 }869 870 if ( _Objects_Is_local_node( node ) || node == OBJECTS_SEARCH_LOCAL_NODE )871 return OBJECTS_INVALID_NAME;872 873 #if defined(RTEMS_MULTIPROCESSING)874 return ( _Objects_MP_Global_name_search( information, name, node, id ) );875 #else876 return OBJECTS_INVALID_NAME;877 #endif878 }879 880 /*PAGE881 *882 * _Objects_Get883 *884 * This routine sets the object pointer for the given885 * object id based on the given object information structure.886 *887 * Input parameters:888 * information - pointer to entry in table for this class889 * id - object id to search for890 * location - address of where to store the location891 *892 * Output parameters:893 * returns - address of object if local894 * location - one of the following:895 * OBJECTS_ERROR - invalid object ID896 * OBJECTS_REMOTE - remote object897 * OBJECTS_LOCAL - local object898 */899 900 Objects_Control *_Objects_Get(901 Objects_Information *information,902 Objects_Id id,903 Objects_Locations *location904 )905 {906 Objects_Control *the_object;907 unsigned32 index;908 909 index = _Objects_Get_index( id );910 911 if ( information->maximum >= index ) {912 _Thread_Disable_dispatch();913 if ( (the_object = _Objects_Get_local_object( information, index )) != NULL ) {914 *location = OBJECTS_LOCAL;915 return( the_object );916 }917 _Thread_Enable_dispatch();918 *location = OBJECTS_ERROR;919 return( NULL );920 }921 *location = OBJECTS_ERROR;922 #if defined(RTEMS_MULTIPROCESSING)923 _Objects_MP_Is_remote(924 information,925 _Objects_Build_id( information->the_class, _Objects_Local_node, index ),926 location,927 &the_object928 );929 return the_object;930 #else931 return NULL;932 #endif933 }934 935 /*PAGE936 *937 * _Objects_Get_by_index938 *939 * This routine sets the object pointer for the given940 * object id based on the given object information structure.941 *942 * Input parameters:943 * information - pointer to entry in table for this class944 * index - object index to check for945 * location - address of where to store the location946 *947 * Output parameters:948 * returns - address of object if local949 * location - one of the following:950 * OBJECTS_ERROR - invalid object ID951 * OBJECTS_REMOTE - remote object952 * OBJECTS_LOCAL - local object953 */954 955 Objects_Control *_Objects_Get_by_index(956 Objects_Information *information,957 unsigned32 index,958 Objects_Locations *location959 )960 {961 Objects_Control *the_object;962 963 if ( information->maximum >= index ) {964 _Thread_Disable_dispatch();965 if ( (the_object = _Objects_Get_local_object( information, index )) != NULL ) {966 *location = OBJECTS_LOCAL;967 return( the_object );968 }969 _Thread_Enable_dispatch();970 *location = OBJECTS_ERROR;971 return( NULL );972 }973 974 /*975 * With just an index, you can't access a remote object.976 */977 978 _Thread_Enable_dispatch();979 *location = OBJECTS_ERROR;980 return NULL;981 }982 983 /*PAGE984 *985 * _Objects_Get_next986 *987 * Like _Objects_Get, but considers the 'id' as a "hint" and988 * finds next valid one after that point.989 * Mostly used for monitor and debug traversal of an object.990 *991 * Input parameters:992 * information - pointer to entry in table for this class993 * id - object id to search for994 * location - address of where to store the location995 * next_id - address to store next id to try996 *997 * Output parameters:998 * returns - address of object if local999 * location - one of the following:1000 * OBJECTS_ERROR - invalid object ID1001 * OBJECTS_REMOTE - remote object1002 * OBJECTS_LOCAL - local object1003 * next_id - will contain a reasonable "next" id to continue traversal1004 *1005 * NOTE:1006 * assumes can add '1' to an id to get to next index.1007 */1008 1009 Objects_Control *1010 _Objects_Get_next(1011 Objects_Information *information,1012 Objects_Id id,1013 Objects_Locations *location_p,1014 Objects_Id *next_id_p1015 )1016 {1017 Objects_Control *object;1018 Objects_Id next_id;1019 1020 if (_Objects_Get_index(id) == OBJECTS_ID_INITIAL_INDEX)1021 next_id = information->minimum_id;1022 else1023 next_id = id;1024 1025 do {1026 /* walked off end of list? */1027 if (_Objects_Get_index(next_id) > information->maximum)1028 {1029 *location_p = OBJECTS_ERROR;1030 goto final;1031 }1032 1033 /* try to grab one */1034 object = _Objects_Get(information, next_id, location_p);1035 1036 next_id++;1037 1038 } while (*location_p != OBJECTS_LOCAL);1039 1040 *next_id_p = next_id;1041 return object;1042 1043 final:1044 *next_id_p = OBJECTS_ID_FINAL;1045 return 0;1046 }1047 -
cpukit/score/src/object.c
r811fae1 r317a5b5 64 64 #endif 65 65 } 66 67 /*PAGE68 *69 * _Objects_Extend_information70 *71 * This routine extends all object information related data structures.72 *73 * Input parameters:74 * information - object information table75 *76 * Output parameters: NONE77 */78 79 void _Objects_Extend_information(80 Objects_Information *information81 )82 {83 Objects_Control *the_object;84 void *name_area;85 Chain_Control Inactive;86 unsigned32 block_count;87 unsigned32 block;88 unsigned32 index_base;89 unsigned32 minimum_index;90 unsigned32 index;91 92 /*93 * Search for a free block of indexes. The block variable ends up set94 * to block_count + 1 if the table needs to be extended.95 */96 97 minimum_index = _Objects_Get_index( information->minimum_id );98 index_base = minimum_index;99 block = 0;100 101 if ( information->maximum < minimum_index )102 block_count = 0;103 else {104 block_count = information->maximum / information->allocation_size;105 106 for ( ; block < block_count; block++ ) {107 if ( information->object_blocks[ block ] == NULL )108 break;109 else110 index_base += information->allocation_size;111 }112 }113 114 /*115 * If the index_base is the maximum we need to grow the tables.116 */117 118 if (index_base >= information->maximum ) {119 ISR_Level level;120 void **object_blocks;121 Objects_Name *name_table;122 unsigned32 *inactive_per_block;123 Objects_Control **local_table;124 unsigned32 maximum;125 void *old_tables;126 127 /*128 * Growing the tables means allocating a new area, doing a copy and129 * updating the information table.130 *131 * If the maximum is minimum we do not have a table to copy. First132 * time through.133 *134 * The allocation has :135 *136 * void *objects[block_count];137 * unsigned32 inactive_count[block_count];138 * Objects_Name *name_table[block_count];139 * Objects_Control *local_table[maximum];140 *141 * This is the order in memory. Watch changing the order. See the memcpy142 * below.143 */144 145 /*146 * Up the block count and maximum147 */148 149 block_count++;150 151 maximum = information->maximum + information->allocation_size;152 153 /*154 * Allocate the tables and break it up.155 */156 157 if ( information->auto_extend ) {158 object_blocks = (void**)159 _Workspace_Allocate(160 block_count *161 (sizeof(void *) + sizeof(unsigned32) + sizeof(Objects_Name *)) +162 ((maximum + minimum_index) * sizeof(Objects_Control *))163 );164 165 if ( !object_blocks )166 return;167 }168 else {169 object_blocks = (void**)170 _Workspace_Allocate_or_fatal_error(171 block_count *172 (sizeof(void *) + sizeof(unsigned32) + sizeof(Objects_Name *)) +173 ((maximum + minimum_index) * sizeof(Objects_Control *))174 );175 }176 177 /*178 * Break the block into the various sections.179 *180 */181 182 inactive_per_block = (unsigned32 *) _Addresses_Add_offset(183 object_blocks, block_count * sizeof(void*) );184 name_table = (Objects_Name *) _Addresses_Add_offset(185 inactive_per_block, block_count * sizeof(unsigned32) );186 local_table = (Objects_Control **) _Addresses_Add_offset(187 name_table, block_count * sizeof(Objects_Name *) );188 189 /*190 * Take the block count down. Saves all the (block_count - 1)191 * in the copies.192 */193 194 block_count--;195 196 if ( information->maximum > minimum_index ) {197 198 /*199 * Copy each section of the table over. This has to be performed as200 * separate parts as size of each block has changed.201 */202 203 memcpy( object_blocks,204 information->object_blocks,205 block_count * sizeof(void*) );206 memcpy( inactive_per_block,207 information->inactive_per_block,208 block_count * sizeof(unsigned32) );209 memcpy( name_table,210 information->name_table,211 block_count * sizeof(Objects_Name *) );212 memcpy( local_table,213 information->local_table,214 (information->maximum + minimum_index) * sizeof(Objects_Control *) );215 }216 else {217 218 /*219 * Deal with the special case of the 0 to minimum_index220 */221 for ( index = 0; index < minimum_index; index++ ) {222 local_table[ index ] = NULL;223 }224 }225 226 /*227 * Initialise the new entries in the table.228 */229 230 object_blocks[block_count] = NULL;231 inactive_per_block[block_count] = 0;232 name_table[block_count] = NULL;233 234 for ( index=index_base ;235 index < ( information->allocation_size + index_base );236 index++ ) {237 local_table[ index ] = NULL;238 }239 240 _ISR_Disable( level );241 242 old_tables = information->object_blocks;243 244 information->object_blocks = object_blocks;245 information->inactive_per_block = inactive_per_block;246 information->name_table = name_table;247 information->local_table = local_table;248 information->maximum = maximum;249 information->maximum_id =250 _Objects_Build_id(251 information->the_class, _Objects_Local_node, information->maximum252 );253 254 _ISR_Enable( level );255 256 if ( old_tables )257 _Workspace_Free( old_tables );258 259 block_count++;260 }261 262 /*263 * Allocate the name table, and the objects264 */265 266 if ( information->auto_extend ) {267 information->object_blocks[ block ] =268 _Workspace_Allocate(269 (information->allocation_size * information->name_length) +270 (information->allocation_size * information->size)271 );272 273 if ( !information->object_blocks[ block ] )274 return;275 }276 else {277 information->object_blocks[ block ] =278 _Workspace_Allocate_or_fatal_error(279 (information->allocation_size * information->name_length) +280 (information->allocation_size * information->size)281 );282 }283 284 name_area = (Objects_Name *) _Addresses_Add_offset(285 information->object_blocks[ block ],286 (information->allocation_size * information->size)287 );288 information->name_table[ block ] = name_area;289 290 /*291 * Initialize objects .. add to a local chain first.292 */293 294 _Chain_Initialize(295 &Inactive,296 information->object_blocks[ block ],297 information->allocation_size,298 information->size299 );300 301 /*302 * Move from the local chain, initialise, then append to the inactive chain303 */304 305 index = index_base;306 307 while ( (the_object = (Objects_Control *) _Chain_Get( &Inactive ) ) != NULL ) {308 309 the_object->id =310 _Objects_Build_id(311 information->the_class, _Objects_Local_node, index312 );313 314 the_object->name = (void *) name_area;315 316 name_area = _Addresses_Add_offset( name_area, information->name_length );317 318 _Chain_Append( &information->Inactive, &the_object->Node );319 320 index++;321 }322 323 information->inactive_per_block[ block ] = information->allocation_size;324 information->inactive += information->allocation_size;325 }326 327 /*PAGE328 *329 * _Objects_Shrink_information330 *331 * This routine shrinks object information related data structures.332 * The object's name and object space are released. The local_table333 * etc block does not shrink. The InActive list needs to be scanned334 * to find the objects are remove them.335 * Input parameters:336 * information - object information table337 * the_block - the block to remove338 *339 * Output parameters: NONE340 */341 342 void _Objects_Shrink_information(343 Objects_Information *information344 )345 {346 Objects_Control *the_object;347 Objects_Control *extract_me;348 unsigned32 block_count;349 unsigned32 block;350 unsigned32 index_base;351 unsigned32 index;352 353 /*354 * Search the list to find block or chunnk with all objects inactive.355 */356 357 index_base = _Objects_Get_index( information->minimum_id );358 block_count = ( information->maximum - index_base ) / information->allocation_size;359 360 for ( block = 0; block < block_count; block++ ) {361 if ( information->inactive_per_block[ block ] == information->allocation_size ) {362 363 /*364 * XXX - Not to sure how to use a chain where you need to iterate and365 * and remove elements.366 */367 368 the_object = (Objects_Control *) information->Inactive.first;369 370 /*371 * Assume the Inactive chain is never empty at this point372 */373 374 do {375 index = _Objects_Get_index( the_object->id );376 377 if ((index >= index_base) &&378 (index < (index_base + information->allocation_size))) {379 380 /*381 * Get the next node before the node is extracted382 */383 384 extract_me = the_object;385 386 if ( !_Chain_Is_last( &the_object->Node ) )387 the_object = (Objects_Control *) the_object->Node.next;388 else389 the_object = NULL;390 391 _Chain_Extract( &extract_me->Node );392 }393 else {394 the_object = (Objects_Control *) the_object->Node.next;395 }396 }397 while ( the_object && !_Chain_Is_last( &the_object->Node ) );398 399 /*400 * Free the memory and reset the structures in the object' information401 */402 403 _Workspace_Free( information->object_blocks[ block ] );404 information->name_table[ block ] = NULL;405 information->object_blocks[ block ] = NULL;406 information->inactive_per_block[ block ] = 0;407 408 information->inactive -= information->allocation_size;409 410 return;411 }412 413 index_base += information->allocation_size;414 }415 }416 417 /*PAGE418 *419 * _Objects_Initialize_information420 *421 * This routine initializes all object information related data structures.422 *423 * Input parameters:424 * information - object information table425 * the_class - object class426 * supports_global - TRUE if this is a global object class427 * maximum - maximum objects of this class428 * size - size of this object's control block429 * is_string - TRUE if names for this object are strings430 * maximum_name_length - maximum length of each object's name431 * is_thread - TRUE if this class is threads432 *433 * Output parameters: NONE434 */435 436 void _Objects_Initialize_information(437 Objects_Information *information,438 Objects_Classes the_class,439 boolean supports_global,440 unsigned32 maximum,441 unsigned32 size,442 boolean is_string,443 unsigned32 maximum_name_length,444 boolean is_thread445 )446 {447 static Objects_Control *null_local_table = NULL;448 449 unsigned32 minimum_index;450 unsigned32 index;451 unsigned32 name_length;452 453 information->the_class = the_class;454 information->is_string = is_string;455 information->is_thread = is_thread;456 457 information->local_table = 0;458 information->name_table = 0;459 information->inactive_per_block = 0;460 information->object_blocks = 0;461 462 information->inactive = 0;463 464 /*465 * Set the entry in the object information table.466 */467 468 _Objects_Information_table[ the_class ] = information;469 470 /*471 * Set the size of the object472 */473 474 information->size = size;475 476 /*477 * Are we operating in unlimited, or auto-extend mode478 */479 480 information->auto_extend = (maximum & OBJECTS_UNLIMITED_OBJECTS) ? TRUE : FALSE;481 maximum &= ~OBJECTS_UNLIMITED_OBJECTS;482 483 /*484 * The allocation unit is the maximum value485 */486 487 information->allocation_size = maximum;488 489 /*490 * Provide a null local table entry for the case of any empty table.491 */492 493 information->local_table = &null_local_table;494 495 /*496 * Calculate minimum and maximum Id's497 */498 499 if ( maximum == 0 ) minimum_index = 0;500 else minimum_index = 1;501 502 information->minimum_id =503 _Objects_Build_id( the_class, _Objects_Local_node, minimum_index );504 505 /*506 * Calculate the maximum name length507 */508 509 name_length = maximum_name_length;510 511 if ( name_length & (OBJECTS_NAME_ALIGNMENT-1) )512 name_length = (name_length + OBJECTS_NAME_ALIGNMENT) &513 ~(OBJECTS_NAME_ALIGNMENT-1);514 515 information->name_length = name_length;516 517 _Chain_Initialize_empty( &information->Inactive );518 519 /*520 * Initialize objects .. if there are any521 */522 523 if ( maximum ) {524 525 /*526 * Reset the maximum value. It will be updated when the information is527 * extended.528 */529 530 information->maximum = 0;531 532 /*533 * Always have the maximum size available so the current performance534 * figures are create are met. If the user moves past the maximum535 * number then a performance hit is taken.536 */537 538 _Objects_Extend_information( information );539 540 }541 542 /*543 * Take care of multiprocessing544 */545 546 if ( supports_global == TRUE && _System_state_Is_multiprocessing ) {547 548 information->global_table =549 (Chain_Control *) _Workspace_Allocate_or_fatal_error(550 (_Objects_Maximum_nodes + 1) * sizeof(Chain_Control)551 );552 553 for ( index=1; index <= _Objects_Maximum_nodes ; index++ )554 _Chain_Initialize_empty( &information->global_table[ index ] );555 }556 else557 information->global_table = NULL;558 }559 560 /*PAGE561 *562 * _Objects_Allocate563 *564 * DESCRIPTION:565 *566 * This function allocates a object control block from567 * the inactive chain of free object control blocks.568 */569 570 Objects_Control *_Objects_Allocate(571 Objects_Information *information572 )573 {574 Objects_Control *the_object =575 (Objects_Control *) _Chain_Get( &information->Inactive );576 577 if ( information->auto_extend ) {578 /*579 * If the list is empty then we are out of objects and need to580 * extend information base.581 */582 583 if ( !the_object ) {584 _Objects_Extend_information( information );585 the_object = (Objects_Control *) _Chain_Get( &information->Inactive );586 }587 588 if ( the_object ) {589 unsigned32 block;590 591 block = _Objects_Get_index( the_object->id ) -592 _Objects_Get_index( information->minimum_id );593 block /= information->allocation_size;594 595 information->inactive_per_block[ block ]--;596 information->inactive--;597 }598 }599 600 return the_object;601 }602 603 /*PAGE604 *605 * _Objects_Allocate_by_index606 *607 * DESCRIPTION:608 *609 * This function allocates the object control block610 * specified by the index from the inactive chain of611 * free object control blocks.612 */613 614 Objects_Control *_Objects_Allocate_by_index(615 Objects_Information *information,616 unsigned32 index,617 unsigned32 sizeof_control618 )619 {620 Objects_Control *the_object;621 void *p;622 623 if ( index && information->maximum >= index ) {624 the_object = _Objects_Get_local_object( information, index );625 if ( the_object )626 return NULL;627 628 /* XXX629 * This whole section of code needs to be addressed.630 * + The 0 should be dealt with more properly so we can autoextend.631 * + The pointer arithmetic is probably too expensive.632 * + etc.633 */634 635 p = _Addresses_Add_offset( information->object_blocks[ 0 ],636 (information->allocation_size * information->name_length) ),637 638 p = _Addresses_Add_offset( p, (sizeof_control * (index - 1)) );639 the_object = (Objects_Control *)p;640 _Chain_Extract( &the_object->Node );641 642 return the_object;643 }644 645 /*646 * Autoextend will have to be thought out as it applies647 * to user assigned indices.648 */649 650 return NULL;651 }652 653 654 655 /*PAGE656 *657 * _Objects_Free658 *659 * DESCRIPTION:660 *661 * This function frees a object control block to the662 * inactive chain of free object control blocks.663 */664 665 void _Objects_Free(666 Objects_Information *information,667 Objects_Control *the_object668 )669 {670 unsigned32 allocation_size = information->allocation_size;671 672 _Chain_Append( &information->Inactive, &the_object->Node );673 674 if ( information->auto_extend ) {675 unsigned32 block;676 677 block =678 _Objects_Get_index( the_object->id ) - _Objects_Get_index( information->minimum_id );679 block /= information->allocation_size;680 681 information->inactive_per_block[ block ]++;682 information->inactive++;683 684 /*685 * Check if the threshold level has been met of686 * 1.5 x allocation_size are free.687 */688 689 if ( information->inactive > ( allocation_size + ( allocation_size >> 1 ) ) ) {690 _Objects_Shrink_information( information );691 }692 }693 }694 695 /*PAGE696 *697 * _Objects_Clear_name698 *699 * XXX700 */701 702 void _Objects_Clear_name(703 void *name,704 unsigned32 length705 )706 {707 unsigned32 index;708 unsigned32 maximum = length / OBJECTS_NAME_ALIGNMENT;709 unsigned32 *name_ptr = (unsigned32 *) name;710 711 for ( index=0 ; index < maximum ; index++ )712 *name_ptr++ = 0;713 }714 715 /*PAGE716 *717 * _Objects_Copy_name_string718 *719 * XXX720 */721 722 void _Objects_Copy_name_string(723 void *source,724 void *destination725 )726 {727 unsigned8 *source_p = (unsigned8 *) source;728 unsigned8 *destination_p = (unsigned8 *) destination;729 730 do {731 *destination_p++ = *source_p;732 } while ( *source_p++ );733 }734 735 /*PAGE736 *737 * _Objects_Copy_name_raw738 *739 * XXX740 */741 742 void _Objects_Copy_name_raw(743 void *source,744 void *destination,745 unsigned32 length746 )747 {748 unsigned32 *source_p = (unsigned32 *) source;749 unsigned32 *destination_p = (unsigned32 *) destination;750 unsigned32 tmp_length = length / OBJECTS_NAME_ALIGNMENT;751 752 while ( tmp_length-- )753 *destination_p++ = *source_p++;754 }755 756 /*PAGE757 *758 * _Objects_Compare_name_string759 *760 * XXX761 */762 763 boolean _Objects_Compare_name_string(764 void *name_1,765 void *name_2,766 unsigned32 length767 )768 {769 unsigned8 *name_1_p = (unsigned8 *) name_1;770 unsigned8 *name_2_p = (unsigned8 *) name_2;771 unsigned32 tmp_length = length;772 773 do {774 if ( *name_1_p++ != *name_2_p++ )775 return FALSE;776 if ( !tmp_length-- )777 return FALSE;778 } while ( *name_1_p );779 780 return TRUE;781 }782 783 /*PAGE784 *785 * _Objects_Compare_name_raw786 *787 * XXX788 */789 790 boolean _Objects_Compare_name_raw(791 void *name_1,792 void *name_2,793 unsigned32 length794 )795 {796 unsigned32 *name_1_p = (unsigned32 *) name_1;797 unsigned32 *name_2_p = (unsigned32 *) name_2;798 unsigned32 tmp_length = length / OBJECTS_NAME_ALIGNMENT;799 800 while ( tmp_length-- )801 if ( *name_1_p++ != *name_2_p++ )802 return FALSE;803 804 return TRUE;805 }806 807 808 /*PAGE809 *810 * _Objects_Name_to_id811 *812 * These kernel routines search the object table(s) for the given813 * object name and returns the associated object id.814 *815 * Input parameters:816 * information - object information817 * name - user defined object name818 * node - node indentifier (0 indicates any node)819 * id - address of return ID820 *821 * Output parameters:822 * id - object id823 * OBJECTS_SUCCESSFUL - if successful824 * error code - if unsuccessful825 */826 827 Objects_Name_to_id_errors _Objects_Name_to_id(828 Objects_Information *information,829 Objects_Name name,830 unsigned32 node,831 Objects_Id *id832 )833 {834 boolean search_local_node;835 Objects_Control *the_object;836 unsigned32 index;837 unsigned32 name_length;838 Objects_Name_comparators compare_them;839 840 if ( name == 0 )841 return OBJECTS_INVALID_NAME;842 843 search_local_node = FALSE;844 845 if ( information->maximum != 0 &&846 (node == OBJECTS_SEARCH_ALL_NODES || node == OBJECTS_SEARCH_LOCAL_NODE ||847 _Objects_Is_local_node( node ) ) )848 search_local_node = TRUE;849 850 if ( search_local_node ) {851 name_length = information->name_length;852 853 if ( information->is_string ) compare_them = _Objects_Compare_name_string;854 else compare_them = _Objects_Compare_name_raw;855 856 for ( index = 1; index <= information->maximum; index++ ) {857 858 the_object = information->local_table[ index ];859 860 if ( !the_object || !the_object->name )861 continue;862 863 if ( (*compare_them)( name, the_object->name, name_length ) ) {864 *id = the_object->id;865 return OBJECTS_SUCCESSFUL;866 }867 }868 }869 870 if ( _Objects_Is_local_node( node ) || node == OBJECTS_SEARCH_LOCAL_NODE )871 return OBJECTS_INVALID_NAME;872 873 #if defined(RTEMS_MULTIPROCESSING)874 return ( _Objects_MP_Global_name_search( information, name, node, id ) );875 #else876 return OBJECTS_INVALID_NAME;877 #endif878 }879 880 /*PAGE881 *882 * _Objects_Get883 *884 * This routine sets the object pointer for the given885 * object id based on the given object information structure.886 *887 * Input parameters:888 * information - pointer to entry in table for this class889 * id - object id to search for890 * location - address of where to store the location891 *892 * Output parameters:893 * returns - address of object if local894 * location - one of the following:895 * OBJECTS_ERROR - invalid object ID896 * OBJECTS_REMOTE - remote object897 * OBJECTS_LOCAL - local object898 */899 900 Objects_Control *_Objects_Get(901 Objects_Information *information,902 Objects_Id id,903 Objects_Locations *location904 )905 {906 Objects_Control *the_object;907 unsigned32 index;908 909 index = _Objects_Get_index( id );910 911 if ( information->maximum >= index ) {912 _Thread_Disable_dispatch();913 if ( (the_object = _Objects_Get_local_object( information, index )) != NULL ) {914 *location = OBJECTS_LOCAL;915 return( the_object );916 }917 _Thread_Enable_dispatch();918 *location = OBJECTS_ERROR;919 return( NULL );920 }921 *location = OBJECTS_ERROR;922 #if defined(RTEMS_MULTIPROCESSING)923 _Objects_MP_Is_remote(924 information,925 _Objects_Build_id( information->the_class, _Objects_Local_node, index ),926 location,927 &the_object928 );929 return the_object;930 #else931 return NULL;932 #endif933 }934 935 /*PAGE936 *937 * _Objects_Get_by_index938 *939 * This routine sets the object pointer for the given940 * object id based on the given object information structure.941 *942 * Input parameters:943 * information - pointer to entry in table for this class944 * index - object index to check for945 * location - address of where to store the location946 *947 * Output parameters:948 * returns - address of object if local949 * location - one of the following:950 * OBJECTS_ERROR - invalid object ID951 * OBJECTS_REMOTE - remote object952 * OBJECTS_LOCAL - local object953 */954 955 Objects_Control *_Objects_Get_by_index(956 Objects_Information *information,957 unsigned32 index,958 Objects_Locations *location959 )960 {961 Objects_Control *the_object;962 963 if ( information->maximum >= index ) {964 _Thread_Disable_dispatch();965 if ( (the_object = _Objects_Get_local_object( information, index )) != NULL ) {966 *location = OBJECTS_LOCAL;967 return( the_object );968 }969 _Thread_Enable_dispatch();970 *location = OBJECTS_ERROR;971 return( NULL );972 }973 974 /*975 * With just an index, you can't access a remote object.976 */977 978 _Thread_Enable_dispatch();979 *location = OBJECTS_ERROR;980 return NULL;981 }982 983 /*PAGE984 *985 * _Objects_Get_next986 *987 * Like _Objects_Get, but considers the 'id' as a "hint" and988 * finds next valid one after that point.989 * Mostly used for monitor and debug traversal of an object.990 *991 * Input parameters:992 * information - pointer to entry in table for this class993 * id - object id to search for994 * location - address of where to store the location995 * next_id - address to store next id to try996 *997 * Output parameters:998 * returns - address of object if local999 * location - one of the following:1000 * OBJECTS_ERROR - invalid object ID1001 * OBJECTS_REMOTE - remote object1002 * OBJECTS_LOCAL - local object1003 * next_id - will contain a reasonable "next" id to continue traversal1004 *1005 * NOTE:1006 * assumes can add '1' to an id to get to next index.1007 */1008 1009 Objects_Control *1010 _Objects_Get_next(1011 Objects_Information *information,1012 Objects_Id id,1013 Objects_Locations *location_p,1014 Objects_Id *next_id_p1015 )1016 {1017 Objects_Control *object;1018 Objects_Id next_id;1019 1020 if (_Objects_Get_index(id) == OBJECTS_ID_INITIAL_INDEX)1021 next_id = information->minimum_id;1022 else1023 next_id = id;1024 1025 do {1026 /* walked off end of list? */1027 if (_Objects_Get_index(next_id) > information->maximum)1028 {1029 *location_p = OBJECTS_ERROR;1030 goto final;1031 }1032 1033 /* try to grab one */1034 object = _Objects_Get(information, next_id, location_p);1035 1036 next_id++;1037 1038 } while (*location_p != OBJECTS_LOCAL);1039 1040 *next_id_p = next_id;1041 return object;1042 1043 final:1044 *next_id_p = OBJECTS_ID_FINAL;1045 return 0;1046 }1047
Note: See TracChangeset
for help on using the changeset viewer.