/* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ #ifdef REV_INFO #ifndef lint static char rcsid[] = "$XConsortium: MrmIbuffer.c /main/16 1996/11/13 13:55:44 drk $" #endif #endif /* (c) Copyright 1989, 1990, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */ /* *++ * FACILITY: * * UIL Resource Manager (URM): IDB Facility * Buffer management routines * * ABSTRACT: * * These routines manage the buffer pool for all IDB files, and manage * reading and writing actual file records into these buffers on request. * *-- */ /* * * INCLUDE FILES * */ #include #include #include #include "MrmMsgI.h" /* * * TABLE OF CONTENTS * * Idb__BM_InitBufferVector - Allocates and initializes * the buffer vector * * Idb__BM_GetBuffer - Acquire a free buffer * * Idb__BM_MarkActivity - Mark a buffer as having seen activity * * Idb__BM_MarkModified - Mark a buffer as modified for write * * Idb__BM_GetRecord - Get a record into some buffer * * Idb__BM_InitRecord - Initialize a new record into * some buffer * * Idb__BM_InitDataRecord - Initialize a new data record into * some buffer * * Idb__BM_Decommit - Through with buffer contents * * Idb__BM_DecommitAll - Decommit all buffers for some file */ /* * * OWN VARIABLE DECLARATIONS * */ /* * This module manages a pool of buffers for all open IDB files. The pool * is located and managed via the following globals. */ /* * The following defines the number of buffers in the buffer pool. Once * the pool is initialized, this number is invariant (currently). This * variable will be accessible via currently undefined means for applications * to tailor the buffer pool size at startup. */ externaldef(idb__buffer_pool_size) int idb__buffer_pool_size = 8 ; /* * The following pointer locates a vector of pointers to buffers. This pointer * is initially NULL. The first request for a buffer detects that the buffer * has not been allocated, and does the following: * o Allocate a vector of buffer pointers (IDBRecordBufferPtr) containing * the number of entries specified in idb__buffer_pool_size. * * Thereafter, this vector locates the buffer pool. */ static IDBRecordBufferPtr idb__buffer_pool_vec = NULL ; /* * The following counter is used to maintain the activity count in all * buffers. */ static long int idb__buffer_activity_count = 1 ; /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_InitBufferVector is responsible for initializaing * the buffer pool. This routine allocates the vector of record * buffers, but does not allocate the actual buffer for each * record, which is done on demand. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_InitBufferVector (void) { /* * Local variables */ Cardinal ndx ; /* loop variable */ IDBRecordBufferPtr bufptr ; /* entry into buffer pool vector */ idb__buffer_pool_vec = (IDBRecordBufferPtr) XtMalloc (idb__buffer_pool_size*sizeof(IDBRecordBuffer)) ; if ( idb__buffer_pool_vec == NULL ) return Urm__UT_Error ("Idb__BM_InitBufferVector", _MrmMMsg_0000, NULL, NULL, MrmFAILURE) ; for ( ndx=0,bufptr=idb__buffer_pool_vec ; ndxvalidation = IDBRecordBufferValid; bufptr->activity = 0 ; bufptr->access = 0 ; bufptr->cur_file = NULL ; bufptr->modified = FALSE ; bufptr->IDB_record = NULL ; } return MrmSUCCESS; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_GetBuffer acquires a buffer from the buffer pool. The * buffer chosen is some buffer with the lowest activity count. If * necessary, its contents are written to disk. Note that a buffer * which has been decommitted (activity count = 0) may be used * immediately - a search for a buffer with a lower activity count * is not required. * * This routine is responsible for initializaing the buffer pool if * it is not initialized. It is also responsible for acquiring * a record buffer if a buffer pool vector entry is needed but has none. * * FORMAL PARAMETERS: * * file_id Open IDB file * buffer_return Returns a free buffer, contents undefined * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_GetBuffer (IDBFile file_id, IDBRecordBufferPtr *buffer_return) { /* * Local macro to complete allocation of a buffer to a file, and * mark its activity. */ #define _IDB_Getbuffer_return() \ { \ (*buffer_return)->cur_file = file_id; \ (*buffer_return)->access = file_id->access; \ Idb__BM_MarkActivity (*buffer_return); \ return MrmSUCCESS; \ } /* * Local variables */ Cardinal result ; /* function results */ int ndx ; /* loop index */ long int lowest ; /* lowest activity count found */ IDBRecordBufferPtr curbuf ; /* current buffer being examined */ /* * If the buffer pool is uninitialized, allocate it and * return the first buffer in the pool. Else search the buffer pool for the * buffer with the lowest activity. Decommited/0 activity buffers can * be returned immediately. */ if (idb__buffer_pool_vec == NULL) { result = Idb__BM_InitBufferVector () ; if ( result != MrmSUCCESS ) return result ; *buffer_return = idb__buffer_pool_vec ; } else { lowest = idb__buffer_activity_count; for ( ndx=0,curbuf=idb__buffer_pool_vec ; ndxactivity == 0 ) { *buffer_return = curbuf ; break ; } if ( curbuf->activity < lowest ) { *buffer_return = curbuf ; lowest = curbuf->activity ; } } } /* * Allocate a record buffer if required, and return immediately if * the buffer is decommitted or not yet used. */ if ( (*buffer_return)->IDB_record == NULL ) { (*buffer_return)->IDB_record = (IDBDummyRecord *) XtMalloc(sizeof(IDBDummyRecord)) ; if ( (*buffer_return)->IDB_record == NULL ) return Urm__UT_Error ("Idb__BM_GetBuffer", _MrmMMsg_0001, NULL, NULL, MrmFAILURE) ; _IDB_Getbuffer_return (); } if ( (*buffer_return)->activity == 0 ) _IDB_Getbuffer_return (); /* * We have set the buffer pointer. See if it needs to be updated on * disk, and do so if required. */ if ( ((*buffer_return)->access == URMWriteAccess) && ((*buffer_return)->modified) ) { result = Idb__BM_Decommit (*buffer_return) ; if ( result != MrmSUCCESS ) return result ; } /* * Allocate the buffer to the file, and return. */ _IDB_Getbuffer_return (); } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_MarkActivity asserts that the buffer has seen some activity, * and updates its activity count. This is done by storing the current * idb__buffer_activity_count++ in the buffer. * * FORMAL PARAMETERS: * * buffer Record buffer to mark as modified * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_MarkActivity (IDBRecordBufferPtr buffer) { if ( ! Idb__BM_Valid(buffer) ) return Urm__UT_Error ("Idb__BM_MarkActivity", _MrmMMsg_0002, NULL, NULL, MrmNOT_VALID) ; buffer->activity = idb__buffer_activity_count++ ; return MrmSUCCESS ; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_MarkModified asserts that the buffer has been modified, * and sets its modify flag. Its activity count is updated. * * FORMAL PARAMETERS: * * buffer Record buffer to mark as modified * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_MarkModified (IDBRecordBufferPtr buffer) { if ( ! Idb__BM_Valid(buffer) ) return Urm__UT_Error ("Idb__BM_MarkModified", _MrmMMsg_0002, NULL, NULL, MrmNOT_VALID) ; buffer->activity = idb__buffer_activity_count++ ; buffer->modified = TRUE ; return MrmSUCCESS ; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_GetRecord reads a record from an open IDB file into a buffer. * If the record is already available in some buffer, then that buffer is * returned without re-reading the record from disk. Otherwise, a buffer is * acquired, the record read into it, and the buffer is returned. The * buffer's activity count is updated. * * FORMAL PARAMETERS: * * file_id Open IDB file * record The desired record number * buffer_return Returns buffer containing record * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmNOT_FOUND record doesn't exist * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_GetRecord (IDBFile file_id, IDBRecordNumber record, IDBRecordBufferPtr *buffer_return) { /* * Local variables */ Cardinal result ; /* function results */ int ndx ; /* loop index */ IDBRecordBufferPtr curbuf ; /* current buffer being examined */ unsigned char *buf_src; /* tmp pointer to location in uid buffer */ /* * If buffer pool is unallocated, get a buffer (which WILL allocate it), * and read the record into that. Else see if the record is already in * memory, and return it if so. If the record is not found, get a buffer * to read it into. We exit this if statement with a buffer ready for * the read operation. */ if ( idb__buffer_pool_vec == NULL ) { result = Idb__BM_GetBuffer (file_id, buffer_return) ; if ( result != MrmSUCCESS ) return result ; } else { for ( ndx=0,curbuf=idb__buffer_pool_vec ; ndxcur_file==file_id) && (curbuf->IDB_record->header.record_num==record) ) { *buffer_return = curbuf ; Idb__BM_MarkActivity (*buffer_return) ; return MrmSUCCESS ; } } result = Idb__BM_GetBuffer (file_id, buffer_return) ; if ( result != MrmSUCCESS ) return result ; } /* * Read the record into the buffer. */ if ( file_id->in_memory ) { buf_src = file_id->uid_buffer + (record-1)*IDBRecordSize; UrmBCopy(buf_src, (*buffer_return)->IDB_record, IDBRecordSize); result = MrmSUCCESS; } else result = Idb__FU_GetBlock(file_id->lowlevel_id, record, (char*)(*buffer_return)->IDB_record) ; if ( result != MrmSUCCESS ) return Urm__UT_Error ("Idb__BM_GetRecord", _MrmMMsg_0003, file_id, NULL, result) ; file_id->get_count++ ; /* * Validate the record, this is the first routine that is called to read * from a newly opened file. If the byte order is different, we find it * here. */ if ( (*buffer_return)->IDB_record->header.validation != IDBRecordHeaderValid ) { swapbytes( (*buffer_return)->IDB_record->header.validation ); if ((*buffer_return)->IDB_record->header.validation == IDBRecordHeaderValid) { /* must be a file needing byte swapping */ file_id->byte_swapped = TRUE; Idb__BM_SwapRecordBytes (*buffer_return); Idb__BM_MarkActivity (*buffer_return); return MrmSUCCESS ; } /* byte swapping has done no good, return error */ return Urm__UT_Error("Idb__BM_GetRecord", _MrmMMsg_0005, file_id, NULL, MrmNOT_VALID) ; } /* * Record successfully read */ Idb__BM_MarkActivity (*buffer_return) ; return MrmSUCCESS ; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_InitRecord initializes a new record for the file. A buffer * is acquired, and the record number and record type set. The record * number may be specified; if the record number given is <= 0, then the * next available record is taken from the file header. The next * available record number is updated to the new record number if it is * greater than the current value. The record is marked for write access, * and as modified. It is not written to disk. The buffer's activity * count is updated. * * FORMAL PARAMETERS: * * file_id Open IDB file * record The desired record number. This will be a new record in * the file * type The record type, from IDBrt... * buffer_return Returns buffer initialized for record, with type set. * Access is URMWriteAccess, modified. * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_InitRecord (IDBFile file_id, IDBRecordNumber record, MrmType type, IDBRecordBufferPtr *buffer_return) { if ((Idb__BM_GetBuffer (file_id, buffer_return)) != MrmSUCCESS) return MrmFAILURE; /* * Set the record number if required. Since the last_record parameter * records the last record currently in use, it must be incremented * before use to give the new last record. */ if (record <= 0) record = ++file_id->last_record; if (record > file_id->last_record) file_id->last_record = record; (*buffer_return)->IDB_record->header.validation = IDBRecordHeaderValid ; (*buffer_return)->IDB_record->header.record_num = record ; (*buffer_return)->IDB_record->header.record_type = type ; (*buffer_return)->access = file_id->access ; (*buffer_return)->cur_file = file_id ; (*buffer_return)->modified = TRUE ; Idb__BM_MarkActivity (*buffer_return) ; /* * Update the record type counter in the file header */ file_id->rt_counts[type]++ ; /* * Successfully initialized */ return MrmSUCCESS ; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_InitDataRecord initializes a new data record for the file. * A buffer is acquired, and the record number and record type set. * The record number may be specified; if the record number given * is <= 0, then the next available record is taken from the file * header. The next available record number is updated to the * new record number if it is greater than the current value. The * record is marked for write access, and as modified. It is not * written to disk. The buffer's activity count is updated. This * routine is nearly identical to InitRecord. * * FORMAL PARAMETERS: * * file_id Open IDB file * buffer_return Returns buffer initialized for record, with type set. * Access is URMWriteAccess, modified. * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_InitDataRecord (IDBFile file_id, IDBRecordBufferPtr *buffer_return) { /* * Local variables */ Cardinal result ; /* function results */ IDBDataRecordPtr data_rec; /* data record specific */ /* * Get a new record */ result = Idb__BM_InitRecord (file_id, 0, IDBrtData, buffer_return) ; if ( result != MrmSUCCESS ) return result ; /* * Set the last data record pointer in the file */ file_id->last_data_record = _IdbBufferRecordNumber (*buffer_return) ; /* * Initialize the record contents */ data_rec = (IDBDataRecord *) (*buffer_return)->IDB_record; data_rec->data_header.free_ptr = 0 ; data_rec->data_header.free_count = IDBDataFreeMax ; data_rec->data_header.num_entry = 0 ; data_rec->data_header.last_entry = 0 ; Idb__BM_MarkActivity (*buffer_return) ; return MrmSUCCESS ; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_Decommit asserts that the buffer contents are no longer * required to be in memory. If the buffer is marked for write access * and as modified, it is written to disk. Then the modified flag is * reset and the activity count set to 0 (least value). * * Note that GetRecord may legally re-acquire a decommitted buffer. * * FORMAL PARAMETERS: * * buffer Record buffer to decommit * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_Decommit (IDBRecordBufferPtr buffer) { /* * Local variables */ Cardinal result ; /* function results */ if ( ! Idb__BM_Valid(buffer) ) return Urm__UT_Error ("Idb__BM_Decommit", _MrmMMsg_0002, NULL, NULL, MrmNOT_VALID) ; if ((buffer->access == URMWriteAccess) && (buffer->modified == TRUE)) { result = Idb__FU_PutBlock (buffer->cur_file->lowlevel_id, buffer->IDB_record->header.record_num, (char*)buffer->IDB_record) ; if ( result != MrmSUCCESS ) return Urm__UT_Error ("Idb__BM_Decommit", _MrmMMsg_0004, NULL, NULL, MrmNOT_VALID) ; buffer->cur_file->put_count++ ; buffer->activity = 0; buffer->modified = FALSE; } return MrmSUCCESS; } /* *++ * * PROCEDURE DESCRIPTION: * * Idb__BM_DecommitAll decommits all the buffers currently allocated * for a file. It returns an error immediately if there is any * problem decommitting any buffer. * * Since this routine removes all buffers for a file in prepration * for closing it, it also wipes out the file pointer in each buffer. * * FORMAL PARAMETERS: * * file_id IDB file for which to write all modified buffers * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * MrmSUCCESS operation succeeded * MrmFAILURE some other failure * * SIDE EFFECTS: * *-- */ Cardinal Idb__BM_DecommitAll (IDBFile file_id) { /* * Local variables */ Cardinal result ; /* function result */ Cardinal ndx ; /* loop variable */ IDBRecordBufferPtr curbuf ; /* ptr to element in buff pool */ if ( idb__buffer_pool_vec == NULL ) return MrmFAILURE; for ( ndx=0,curbuf=idb__buffer_pool_vec ; ndxcur_file == file_id) { result = Idb__BM_Decommit (curbuf) ; if ( result != MrmSUCCESS ) return result ; curbuf->cur_file = NULL ; } } return MrmSUCCESS ; }