/****************************************************************************
Desc: This routine functions as a thread.  It keeps the gigaload screen
		up to date.
****************************************************************************/
RCODE FLMAPI gigaScreenThread(
	IF_Thread *		pThread)
{
	FLMUINT		uiCurrTime;

	for (;;)
	{
		// See if we should shut down.

		if( pThread->getShutdownFlag())
		{
			break;
		}

		uiCurrTime = FLM_GET_TIMER();

		// Update the display

		gigaUpdateMemInfo();

		pThread->sleep( 1000);
	}

	return( FERR_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_IOBuffer::notifyComplete(
	RCODE					completionRc)
{
	f_assert( m_bPending);
	
	m_bPending = FALSE;
	m_bCompleted = TRUE;
	m_completionRc = completionRc;
	m_uiEndTime = FLM_GET_TIMER();
	m_uiElapsedTime = FLM_TIMER_UNITS_TO_MILLI( 
		FLM_ELAPSED_TIME( m_uiEndTime, m_uiStartTime));

	if( m_fnCompletion)
	{
		m_fnCompletion( this, m_pvData);
		m_fnCompletion = NULL;
		m_pvData = NULL;
	}

	if( m_pBufferMgr)
	{
		f_assert( m_eList == MGR_LIST_PENDING);
		f_mutexLock( m_pBufferMgr->m_hMutex);
		
		m_pBufferMgr->unlinkFromList( this);
		m_pBufferMgr->linkToList( &m_pBufferMgr->m_pFirstUsed, this);
		
		if( RC_OK( m_pBufferMgr->m_completionRc) && RC_BAD( completionRc))
		{
			m_pBufferMgr->m_completionRc = completionRc;
		}
		
		f_mutexUnlock( m_pBufferMgr->m_hMutex);		
	}
}
Exemple #3
0
/****************************************************************************
Desc:
****************************************************************************/
void TestBase::beginTest( 
	const char * 	pszTestName) 
{
	m_pszTestName = pszTestName;
	display( m_pszTestName);
	display( " ... ");
	m_uiStartTime = FLM_GET_TIMER();
}
Exemple #4
0
/****************************************************************************
Desc:
****************************************************************************/
void TestBase::endTest(
	FLMBOOL	bPassed)
{
	FLMUINT	uiEndTime = FLM_GET_TIMER();
	FLMUINT	uiElapsedMilli = FLM_TIMER_UNITS_TO_MILLI( 
										FLM_ELAPSED_TIME( uiEndTime, m_uiStartTime));

	displayTestResults( bPassed, uiElapsedMilli);
	if (m_bLog)
	{
		logTestResults( bPassed);
	}

	(void)m_pReporter->recordUnitTestResults(
		m_pszTestName, bPassed, m_szFailInfo);
}
/********************************************************************
Desc:
*********************************************************************/
void gigaUpdateLoadTimes( void)
{
	FLMUINT		uiElapsedTime;
	FLMUINT		uiCurrTime;
	FLMUINT		uiSecs;
	FLMUINT		uiAddsPerSec;
	char			szElapsedTime [20];

	uiCurrTime = FLM_GET_TIMER();
	uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, gv_ui10SecStartTime);

	// Calculate and display the average for the last 10 seconds.

	uiSecs = FLM_TIMER_UNITS_TO_SECS( uiElapsedTime);
	uiAddsPerSec = (gv_uiTotalLoaded - gv_ui10SecTotal) / uiSecs;

	f_mutexLock( gv_hWindowMutex);
	gigaOutputUINT( ADDS_PER_SEC_CURRENT, uiAddsPerSec, TRUE);

	gv_ui10SecTotal = gv_uiTotalLoaded;
	gv_ui10SecStartTime = uiCurrTime;

	// Calculate and display the overall average

	uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, gv_uiStartTime);
	uiSecs = FLM_TIMER_UNITS_TO_SECS( uiElapsedTime);
	uiAddsPerSec = gv_uiTotalLoaded / uiSecs;

	gigaOutputUINT( ADDS_PER_SEC_OVERALL, uiAddsPerSec, TRUE);

	f_sprintf( szElapsedTime, "%u:%02u:%02u",
		(unsigned)uiSecs / 3600,
		(unsigned)(uiSecs % 3600) / 60,
		(unsigned)uiSecs % 60);

	gigaOutputStr( ELAPSED_TIME_ROW, szElapsedTime, TRUE);

	f_mutexUnlock( gv_hWindowMutex);
}
/****************************************************************************
Desc:
****************************************************************************/
void FTKAPI F_IOBuffer::setPending( void)
{
	f_assert( !m_bPending);
	
	if( m_pBufferMgr)
	{
		f_assert( m_eList == MGR_LIST_USED);
		
		f_mutexLock( m_pBufferMgr->m_hMutex);
		m_pBufferMgr->unlinkFromList( this);
		m_pBufferMgr->linkToList( &m_pBufferMgr->m_pFirstPending, this);
		f_mutexUnlock( m_pBufferMgr->m_hMutex);
	}

#ifndef FLM_UNIX
	f_assert( !m_pAsyncClient || 
				 f_semGetSignalCount( ((F_FileAsyncClient *)m_pAsyncClient)->m_hSem) == 0);
#endif

	m_bPending = TRUE;
	m_uiStartTime = FLM_GET_TIMER();
	m_uiEndTime = 0;
}
/****************************************************************************
Desc:	Index a set of documents or until time runs out.
****************************************************************************/
RCODE F_Db::indexSetOfRows(
	FLMUINT					uiIndexNum,
	FLMUINT64				ui64StartRowId,
	FLMUINT64				ui64EndRowId,
	IF_IxStatus *			pIxStatus,
	IF_IxClient *			pIxClient,
	SFLM_INDEX_STATUS *	pIndexStatus,
	FLMBOOL *				pbHitEnd,
	IF_Thread *				pThread)
{
	RCODE				rc = NE_SFLM_OK;
	FLMUINT64		ui64RowId;
	FLMUINT64		ui64LastRowId = 0;
	F_INDEX *		pIndex = NULL;
	F_TABLE *		pTable;
	IF_LockObject *
						pDatabaseLockObj = m_pDatabase->m_pDatabaseLockObj;
	FLMBOOL			bHitEnd = FALSE;
	FLMUINT			uiCurrTime;
	FLMUINT			uiLastStatusTime = 0;
	FLMUINT			uiStartTime;
	FLMUINT			uiMinTU;
	FLMUINT			uiStatusIntervalTU;
	FLMUINT64		ui64RowsProcessed = 0;
	FLMBOOL			bRelinquish = FALSE;
	FLMBYTE			ucKey[ FLM_MAX_NUM_BUF_SIZE];
	FLMUINT			uiKeyLen;
	void *			pvTmpPoolMark = m_tempPool.poolMark();
	F_Btree *		pbtree = NULL;
	FLMBOOL			bNeg;
	FLMUINT			uiBytesProcessed;
	F_Row *			pRow = NULL;

	uiMinTU = FLM_MILLI_TO_TIMER_UNITS( 500);
	uiStatusIntervalTU = FLM_SECS_TO_TIMER_UNITS( 10);
	uiStartTime = FLM_GET_TIMER();

	if (RC_BAD( rc = krefCntrlCheck()))
	{
		goto Exit;
	}

	pIndex = m_pDict->getIndex( uiIndexNum);
	flmAssert( pIndex);
	
	flmAssert( !(pIndex->uiFlags & IXD_SUSPENDED));

	// Get a btree

	if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &pbtree)))
	{
		goto Exit;
	}

	pTable = m_pDict->getTable( pIndex->uiTableNum);
	flmAssert( pTable);

	if (RC_BAD( rc = pbtree->btOpen( this, &pTable->lfInfo,
								FALSE, TRUE)))
	{
		goto Exit;
	}

	uiKeyLen = sizeof( ucKey);
	if (RC_BAD( rc = flmNumber64ToStorage( ui64StartRowId, &uiKeyLen,
									ucKey, FALSE, TRUE)))
	{
		goto Exit;
	}
	if( RC_BAD( rc = pbtree->btLocateEntry(
								ucKey, sizeof( ucKey), &uiKeyLen, FLM_INCL)))
	{
		if (rc == NE_SFLM_EOF_HIT || rc == NE_SFLM_NOT_FOUND)
		{
			rc = NE_SFLM_OK;
			bHitEnd = TRUE;
			goto Commit_Keys;
		}

		goto Exit;
	}
	
	for (;;)
	{
		
		// See what row we're on
	
		if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey,
									&ui64RowId, &bNeg, &uiBytesProcessed)))
		{
			goto Exit;
		}

		if (ui64RowId > ui64EndRowId)
		{
			break;
		}
		
		if( RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->retrieveRow( this,
									pIndex->uiTableNum, ui64RowId, &pRow)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = buildKeys( pIndex, pTable, pRow, TRUE, NULL)))
		{
			goto Exit;
		}

		// See if there is an indexing callback

		if (pIxClient)
		{
			if (RC_BAD( rc = pIxClient->doIndexing( this, uiIndexNum,
								pIndex->uiTableNum, pRow)))
			{
				goto Exit;
			}
		}

		ui64LastRowId = ui64RowId;
		ui64RowsProcessed++;

		if (pIndexStatus)
		{
			pIndexStatus->ui64RowsProcessed++;
			pIndexStatus->ui64LastRowIndexed = ui64LastRowId;
		}

		// Get the current time

		uiCurrTime = FLM_GET_TIMER();

		// Break out if someone is waiting for an update transaction.

		if (pThread)
		{
			if (pThread->getShutdownFlag())
			{
				bRelinquish = TRUE;
				break;
			}

			if (pDatabaseLockObj->getWaiterCount())
			{
				// See if our minimum run time has elapsed

				if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU)
				{
					if (ui64RowsProcessed < 50)
					{
						// If there are higher priority waiters in the lock queue,
						// we want to relinquish.

						if (pDatabaseLockObj->haveHigherPriorityWaiter(
							FLM_BACKGROUND_LOCK_PRIORITY))
						{
							bRelinquish = TRUE;
							break;
						}
					}
					else
					{
						bRelinquish = TRUE;
						break;
					}
				}
			}
			else
			{

				// Even if no one has requested a lock for a long time, we
				// still want to periodically commit our transaction so
				// we won't lose more than uiMaxCPInterval timer units worth
				// of work if we crash.  We will run until we exceed the checkpoint
				// interval and we see that someone (the checkpoint thread) is
				// waiting for the write lock.

				if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >
					gv_SFlmSysData.uiMaxCPInterval &&
					m_pDatabase->m_pWriteLockObj->getWaiterCount())
				{
					bRelinquish = TRUE;
					break;
				}
			}
		}

		if (FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >=
					uiStatusIntervalTU)
		{
			uiLastStatusTime = uiCurrTime;
			if( pIxStatus)
			{
				if( RC_BAD( rc = pIxStatus->reportIndex( ui64LastRowId)))
				{
					goto Exit;
				}
			}

			// Send indexing completed event notification

			if( gv_SFlmSysData.EventHdrs[ SFLM_EVENT_UPDATES].pEventCBList)
			{
				flmDoEventCallback( SFLM_EVENT_UPDATES,
						SFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(),
						0, uiIndexNum, ui64LastRowId,
						NE_SFLM_OK);
			}

			// Log a progress message

			flmLogIndexingProgress( uiIndexNum, ui64LastRowId);
		}

		// Need to go to the next row.

		if( RC_BAD( rc = pbtree->btNextEntry(
									ucKey, sizeof( ucKey), &uiKeyLen)))
		{
			if (rc == NE_SFLM_EOF_HIT)
			{
				rc = NE_SFLM_OK;
				bHitEnd = TRUE;
				break;
			}
			goto Exit;
		}
	}

Commit_Keys:

	if (RC_BAD( rc = keysCommit( TRUE)))
	{
		goto Exit;
	}

	// If at the end, change index state.

	if (bHitEnd)
	{
		if (RC_BAD( rc = setIxStateInfo( uiIndexNum, 0, 0)))
		{
			goto Exit;
		}

		// setIxStateInfo may have changed to a new dictionary, so pIxd is no
		// good after this point

		pIndex = NULL;
	}
	else if (ui64RowsProcessed)
	{
		if (RC_BAD( rc = setIxStateInfo( uiIndexNum, ui64LastRowId,
										IXD_OFFLINE)))
		{
			goto Exit;
		}

		// setIxStateInfo may have changed to a new dictionary, so pIndex is no
		// good after this point

		pIndex = NULL;
	}
	
	// Log the rows that were indexed, if any
	
	if (ui64LastRowId)
	{
		if (RC_BAD( rc = m_pDatabase->m_pRfl->logIndexSet( this, uiIndexNum,
									ui64StartRowId, ui64LastRowId)))
		{
			goto Exit;
		}
	}

Exit:

	// We want to make one last call if we are in the foreground or if
	// we actually did some indexing.

	if (gv_SFlmSysData.EventHdrs[ SFLM_EVENT_UPDATES].pEventCBList)
	{
		flmDoEventCallback( SFLM_EVENT_UPDATES,
				SFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(),
				0, uiIndexNum,
				(FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastRowId),
				NE_SFLM_OK);
	}

	flmLogIndexingProgress( uiIndexNum,
		(FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastRowId));

	if (pIxStatus)
	{
		(void) pIxStatus->reportIndex( ui64LastRowId);
	}

	if (pbHitEnd)
	{
		*pbHitEnd = bHitEnd;
	}

	krefCntrlFree();
	m_tempPool.poolReset( pvTmpPoolMark);

	if (pbtree)
	{
		gv_SFlmSysData.pBtPool->btpReturnBtree( &pbtree);
	}

	if (pRow)
	{
		pRow->ReleaseRow();
	}

	return( rc);
}
/****************************************************************************
Desc: Retrieves the Checkpoint info for the database passed in.  This assumes
		global mutex has already been locked.
*****************************************************************************/
void F_Database::getCPInfo(
	XFLM_CHECKPOINT_INFO *		pCheckpointInfo)
{
	FLMUINT	uiElapTime;
	FLMUINT	uiCurrTime;

	flmAssert( pCheckpointInfo);

	f_memset( pCheckpointInfo, 0, sizeof( XFLM_CHECKPOINT_INFO));
	if (m_pCPInfo)
	{
		pCheckpointInfo->bRunning = m_pCPInfo->bDoingCheckpoint;
		if (pCheckpointInfo->bRunning)
		{
			if (m_pCPInfo->uiStartTime)
			{
				uiCurrTime = FLM_GET_TIMER();

				uiElapTime = FLM_ELAPSED_TIME( uiCurrTime,
							m_pCPInfo->uiStartTime);
				pCheckpointInfo->ui32RunningTime = (FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime);
			}
			else
			{
				pCheckpointInfo->ui32RunningTime = 0;
			}
			pCheckpointInfo->bForcingCheckpoint =
				m_pCPInfo->bForcingCheckpoint;
			if (m_pCPInfo->uiForceCheckpointStartTime)
			{
				uiCurrTime = FLM_GET_TIMER();
				uiElapTime = FLM_ELAPSED_TIME( uiCurrTime,
							m_pCPInfo->uiForceCheckpointStartTime);
				pCheckpointInfo->ui32ForceCheckpointRunningTime = 
					(FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime);
			}
			else
			{
				pCheckpointInfo->ui32ForceCheckpointRunningTime = 0;
			}
			pCheckpointInfo->ui32ForceCheckpointReason =
				(FLMUINT32)m_pCPInfo->iForceCheckpointReason;
			pCheckpointInfo->bWritingDataBlocks =
				m_pCPInfo->bWritingDataBlocks;
			pCheckpointInfo->ui32LogBlocksWritten =
				(FLMUINT32)m_pCPInfo->uiLogBlocksWritten;
			pCheckpointInfo->ui32DataBlocksWritten =
				(FLMUINT32)m_pCPInfo->uiDataBlocksWritten;
		}
		pCheckpointInfo->ui32BlockSize = (FLMUINT32)m_uiBlockSize;
		pCheckpointInfo->ui32DirtyCacheBytes =
			(FLMUINT32)(m_uiDirtyCacheCount * m_uiBlockSize);
		if (m_pCPInfo->uiStartWaitTruncateTime)
		{
			uiCurrTime = FLM_GET_TIMER();

			uiElapTime = FLM_ELAPSED_TIME( uiCurrTime,
						m_pCPInfo->uiStartWaitTruncateTime);
			pCheckpointInfo->ui32WaitTruncateTime = 
				(FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime);
		}
		else
		{
			pCheckpointInfo->ui32WaitTruncateTime = 0;
		}
	}
}
/****************************************************************************
Desc: Try to perform a checkpoint on the database.  Returns TRUE if we need
		to terminate.
****************************************************************************/
FLMBOOL F_Database::tryCheckpoint(
	IF_Thread *			pThread,
	CP_INFO *			pCPInfo)
{
	RCODE					rc = NE_XFLM_OK;
	FLMBOOL				bTerminate = FALSE;
	FLMBOOL				bForceCheckpoint;
	FLMINT				iForceReason;
	FLMUINT				uiCurrTime;
	XFLM_DB_STATS *	pDbStats;

	// See if we should terminate the thread.

	if (pThread->getShutdownFlag())
	{
		// Set terminate flag to TRUE and then see if
		// we have been set up to do one final checkpoint
		// to flush dirty buffers to disk.

		bTerminate = TRUE;
	}

	// Determine if we need to force a checkpoint.

	bForceCheckpoint = FALSE;
	iForceReason = 0;
	uiCurrTime = (FLMUINT)FLM_GET_TIMER();
	
	if (bTerminate)
	{
		bForceCheckpoint = TRUE;
		iForceReason = XFLM_CP_SHUTTING_DOWN_REASON;
	}
	else if (!m_pRfl->seeIfRflVolumeOk() || RC_BAD( m_CheckpointRc))
	{
		bForceCheckpoint = TRUE;
		iForceReason = XFLM_CP_RFL_VOLUME_PROBLEM;
	}
	else if ((FLM_ELAPSED_TIME( uiCurrTime, m_uiLastCheckpointTime) >=
				 gv_XFlmSysData.uiMaxCPInterval) ||
				(!gv_XFlmSysData.uiMaxCPInterval))
	{
		bForceCheckpoint = TRUE;
		iForceReason = XFLM_CP_TIME_INTERVAL_REASON;
	}

	if (gv_XFlmSysData.Stats.bCollectingStats)
	{

		// Statistics are being collected for the system.  Therefore,
		// if we are not currently collecting statistics in the
		// start.  If we were collecting statistics, but the
		// start time was earlier than the start time in the system
		// statistics structure, reset the statistics.

		if (!pCPInfo->Stats.bCollectingStats)
		{
			flmStatStart( &pCPInfo->Stats);
		}
		else if (pCPInfo->Stats.uiStartTime <
					gv_XFlmSysData.Stats.uiStartTime)
		{
			flmStatReset( &pCPInfo->Stats, FALSE);
		}
		(void)flmStatGetDb( &pCPInfo->Stats, this,
						0, &pDbStats, NULL, NULL);
	}
	else
	{
		pDbStats = NULL;
	}

	// Lock write object - If we are forcing a checkpoint
	// wait until we get the lock.  Otherwise, if we can't get
	// the lock without waiting, don't do anything.

	if (bForceCheckpoint ||
		 (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache &&
		  (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize >
			gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache))
	{
		if (RC_BAD( rc = dbWriteLock( pCPInfo->hWaitSem, pDbStats)))
		{

			// THIS SHOULD NEVER HAPPEN BECAUSE dbWriteLock will
			// wait forever for the lock!

			RC_UNEXPECTED_ASSERT( rc);
			goto Exit;
		}
		pThread->setThreadStatusStr( "Forcing checkpoint");

		// Must wait for any RFL writes to complete.

		(void)m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, TRUE);
	}
	else
	{
		if (RC_BAD( dbWriteLock( pCPInfo->hWaitSem, pDbStats, 0)))
		{
			goto Exit;
		}

		pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);

		// See if we actually need to do the checkpoint.  If the
		// current transaction ID and the last checkpoint transaction
		// ID are the same, no updates have occurred that would require
		// a checkpoint to take place.

		if (m_lastCommittedDbHdr.ui64RflLastCPTransID ==
			 m_lastCommittedDbHdr.ui64CurrTransID ||
			 !m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, FALSE))
		{
			dbWriteUnlock();
			goto Exit;
		}
	}

	// Do the checkpoint.

	(void)doCheckpoint( pCPInfo->hWaitSem, pDbStats, pCPInfo->pSFileHdl, FALSE,
						bForceCheckpoint, iForceReason, 0, 0);
	if (pDbStats)
	{
		(void)flmStatUpdate( &pCPInfo->Stats);
	}

	dbWriteUnlock();

	// Set the thread's status

	pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);

Exit:

	return( bTerminate);
}
/********************************************************************
Desc: Loads the database with objects.
*********************************************************************/
RCODE gigaLoadDatabase( void)
{
	RCODE				rc = NE_FLM_OK;
	FLMBOOL			bTransActive = FALSE;
	FLMBOOL			bCommitTrans = FALSE;
	FLMUINT			uiObjsInTrans = 0;
	FLMUINT			uiChar = 0;
	FLMUINT			bSuspend = FALSE;
	FlmRecord *		pNewRec = NULL;

	// Set cache size, if specified on command line.

	if( gv_uiCacheSize)
	{
		if( RC_BAD( rc = FlmSetHardMemoryLimit( 0, FALSE, 0,
			gv_uiCacheSize, 0)))
		{
			gigaOutputRcErr( "setting cache size", rc);
			goto Exit;
		}
	}

	// Set block cache percentage, if it is not default.
	
	if( gv_uiBlockCachePercentage != 50)
	{
		if( RC_BAD( rc = FlmConfig( FLM_BLOCK_CACHE_PERCENTAGE,
			(void *)gv_uiBlockCachePercentage, (void *)0)))
		{
			gigaOutputRcErr( "setting block cache percentage", rc);
			goto Exit;
		}
	}

	// Set the maximum and low dirty cache, if one was specified

	if( gv_uiMaxDirtyCache)
	{
		if( RC_BAD( rc = FlmConfig( FLM_MAX_DIRTY_CACHE,
			(void *)gv_uiMaxDirtyCache, (void *)gv_uiLowDirtyCache)))
		{
			gigaOutputRcErr( "setting maximum dirty cache", rc);
			goto Exit;
		}
	}

	// Set checkpoint interval, if one is specified.

	if( gv_uiCPInterval != 0xFFFFFFFF)
	{
		if( RC_BAD( rc = FlmConfig( FLM_MAX_CP_INTERVAL, 
			(void *)gv_uiCPInterval, (void *)0)))
		{
			gigaOutputRcErr( "setting checkpoint interval", rc);
			goto Exit;
		}
	}
	
	// Enable/Disable direct I/O
	
	if( RC_BAD( rc = FlmConfig( FLM_DIRECT_IO_STATE, 
		(void *)!gv_bDisableDirectIO, NULL)))
	{
		goto Exit;
	}

	// Create the database.
	
	(void)FlmDbRemove( gv_szDibName, gv_szDataDir, gv_szRflDir, TRUE);
	
	if( RC_BAD( rc = FlmDbCreate( gv_szDibName, gv_szDataDir, gv_szRflDir, 
		NULL, gv_pszGigaDictionary, NULL, &gv_hDb)))
	{
		gigaOutputRcErr( "creating database", rc);
		goto Exit;
	}
	
	if( RC_BAD( rc = FlmDbConfig( gv_hDb, 
		FDB_RFL_FOOTPRINT_SIZE, (void *)(512 * 1024 * 1024), NULL)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = FlmDbConfig( gv_hDb, 
		FDB_RBL_FOOTPRINT_SIZE, (void *)(512 * 1024 * 1024), NULL)))
	{
		goto Exit;
	}

	// Create the display

	gv_uiTotalLoaded = 0;
	gv_ui10SecTotal = 0;
	
	f_mutexLock( gv_hWindowMutex);
	FTXWinClear( gv_pWindow);
	f_mutexUnlock( gv_hWindowMutex);

	gigaOutputLabel( MAX_CACHE_ROW, "Maximum Cache Size (bytes)");
	gigaOutputLabel( USED_CACHE_ROW, "Cache Used (bytes)");
	gigaOutputLabel( ITEMS_CACHED_ROW, "Cache Used (items)");
	gigaOutputLabel( DIRTY_CACHE_ROW, "Dirty Cache (bytes)");
	gigaOutputLabel( LOG_CACHE_ROW, "Log Cache (bytes)");
	gigaOutputLabel( FREE_CACHE_ROW, "Free Cache (bytes)");
	gigaOutputLabel( CP_STATE_ROW, "Checkpoint State");
	
	gigaUpdateMemInfo();

	gigaOutputLabel( DB_NAME_ROW, "Database Name");
	gigaOutputStr( DB_NAME_ROW, gv_szDibName);

	gigaOutputLabel( TOTAL_TO_LOAD_ROW, "Total To Load");
	gigaOutputUINT( TOTAL_TO_LOAD_ROW, gv_uiTotalToLoad);

	gigaOutputLabel( TRANS_SIZE_ROW, "Transaction Size");
	gigaOutputUINT( TRANS_SIZE_ROW, gv_uiTransSize);

	gigaOutputLabel( TOTAL_LOADED_ROW, "Total Loaded");
	gigaOutputUINT( TOTAL_LOADED_ROW, gv_uiTotalLoaded);

	gigaOutputLabel( ADDS_PER_SEC_CURRENT, "Adds/Sec. (10 secs)");
	gigaOutputUINT( ADDS_PER_SEC_CURRENT, 0);

	gigaOutputLabel( ADDS_PER_SEC_OVERALL, "Adds/Sec. (overall)");
	gigaOutputUINT( ADDS_PER_SEC_OVERALL, 0);

	gigaOutputLabel( ELAPSED_TIME_ROW, "Elapsed Time");
	gigaOutputStr( ELAPSED_TIME_ROW, "<none>");

	if( RC_BAD( rc = gigaStartScreenThread()))
	{
		goto Exit;
	}
	
	gv_ui10SecStartTime = gv_uiStartTime = FLM_GET_TIMER();
	gv_ui10Secs = FLM_SECS_TO_TIMER_UNITS( 10);
	gv_ui1Sec = FLM_SECS_TO_TIMER_UNITS( 1);
	
	for( ;;)
	{
		// See if we have been told to shut down, or if the user 
		// has pressed escape.

		if( gv_bShutdown)
		{
			break;
		}

		// Every 127 objects, see if character was pressed and update 
		// count on screen.

		if( (gv_uiTotalLoaded & 0x7F) == 0)
		{
			f_yieldCPU();
			
			if( (uiChar = gigaSeeIfQuit()) != 0)
			{
				if( uiChar == FKB_ESCAPE)
				{
					break;
				}
				else if( uiChar == 's' || uiChar == 'S')
				{
					bSuspend = TRUE;
				}
			}

			// Check for other keyboard options
		}
		else if( (gv_uiTotalLoaded & 0x7) == 0)
		{
			FLMUINT		uiElapsedTime;
			FLMUINT		uiCurrTime;

			uiCurrTime = FLM_GET_TIMER();

			// If at least 10 seconds have elapsed, redisplay the average
			// rate values.

			if( (uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime,
				gv_ui10SecStartTime)) >= gv_ui10Secs)
			{
				gigaUpdateLoadTimes();
			}
		}

		// Start a transaction, if one is not going.

		if( !bTransActive)
		{
			if( bSuspend)
			{
				uiChar = gigaGetInput(
					"Load suspended, press any character to continue loading: ",
					NULL);
				bSuspend = FALSE;
			}
			
			if( RC_BAD( rc = gigaStartTrans()))
			{
				goto Exit;
			}
			
			bTransActive = TRUE;
			bCommitTrans = FALSE;
			uiObjsInTrans = 0;
		}

		// Increment the load counters and determine if this will be the
		// last object of the transaction.

		gv_uiTotalLoaded++;
		uiObjsInTrans++;
		
		if( uiObjsInTrans == gv_uiTransSize ||
			 gv_uiTotalLoaded == gv_uiTotalToLoad)
		{
			bCommitTrans = TRUE;
		}

		// Create a new object.

		if( RC_BAD( rc = gigaMakeNewRecord( &pNewRec)))
		{
			goto Exit;
		}
		
		if( RC_BAD( rc = FlmRecordAdd( gv_hDb, FLM_DATA_CONTAINER, 
			NULL, pNewRec, FLM_DONT_INSERT_IN_CACHE)))
		{
			goto Exit;
		}
		
		// Commit when we reach the transaction size or the total to load.
		// NOTE: The bCommitTrans flag is set above.

		if( bCommitTrans)
		{
			if( RC_BAD( rc = gigaCommitTrans()))
			{
				goto Exit;
			}
			
			bTransActive = FALSE;
		}

		// See if we are done.

		if( gv_uiTotalLoaded == gv_uiTotalToLoad)
		{
			flmAssert( !bTransActive);
			break;
		}
	}

Exit:

	if( pNewRec)
	{
		pNewRec->Release();
	}

	if( bTransActive)
	{
		(void)FlmDbTransAbort( gv_hDb);
	}
	
	if( gv_hDb != HFDB_NULL)
	{
		FlmDbCheckpoint( gv_hDb, FLM_NO_TIMEOUT);
		gigaStopScreenThread();
		FlmDbClose( &gv_hDb);

		// This will cause us to wait for the last checkpoint
		// to finish.

		(void)FlmConfig( FLM_CLOSE_FILE, (void *)gv_szDibName,
								(void *)gv_szDataDir);
	}
	
	gigaUpdateLoadTimes();
	gigaStopScreenThread();
	f_threadDestroy( &gv_pIxManagerThrd);
	
	return( rc);
}
/****************************************************************************
Name:	flstIndexManagerThread
Desc:	Thread that displays the current status of all indexes in a database
Note:	The caller must open the database and pass a handle to the thread.
		The handle will be closed when the thread exits.
*****************************************************************************/
RCODE FTKAPI flstIndexManagerThread(
	IF_Thread *		pThread)
{
	RCODE						rc = NE_XFLM_OK;
	F_DynamicList *		pList = f_new F_DynamicList;
	FTX_WINDOW *			pTitleWin;
	FTX_WINDOW *			pListWin;
	FTX_WINDOW *			pHeaderWin;
	FTX_WINDOW *			pMsgWin;
	FLMUINT					uiIterations = 0;
	FLMUINT					uiScreenCols;
	FLMUINT					uiScreenRows;
	FLMUINT					uiIndex;
	FLMUINT					uiUpdateInterval;
	FLMUINT					uiLastUpdateTime;
	IX_DISPLAY_INFO		IxDispInfo;
	IX_DISPLAY_INFO *		pDispInfo;
	DLIST_NODE *			pTmpNd;
	FLMUINT					uiKey;
	FLMBOOL					bShowOnline = TRUE;
	F_Db *					pDb = (F_Db *)pThread->getParm1();
	FLMUINT					uiOneSec;
	FLMBOOL					bScreenLocked = FALSE;
	IX_Event					event;
	FLMBOOL					bRegisteredForEvent = FALSE;
	IF_DbSystem *			pDbSystem = NULL;

	event.setDispInfo( &IxDispInfo);

#define FIMT_TITLE_HEIGHT		1
#define FIMT_HEADER_HEIGHT		4
#define FIMT_LOG_HEIGHT			10

	f_memset( &IxDispInfo, 0, sizeof( IX_DISPLAY_INFO));
	IxDispInfo.hScreenMutex = F_MUTEX_NULL;
	IxDispInfo.pDb = (F_Db *)pDb;
	IxDispInfo.bShowTime = TRUE;

	if( RC_BAD( f_mutexCreate( &IxDispInfo.hScreenMutex)))
	{
		goto Exit;
	}

	if( RC_BAD( FTXScreenInit( "Index Manager", &IxDispInfo.pScreen)))
	{
		goto Exit;
	}

	FTXScreenGetSize( IxDispInfo.pScreen, &uiScreenCols, &uiScreenRows);
	FTXScreenDisplay( IxDispInfo.pScreen);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, 0,
		FIMT_TITLE_HEIGHT, &pTitleWin)))
	{
		goto Exit;
	}

	FTXWinSetBackFore( pTitleWin, FLM_RED, FLM_WHITE);
	FTXWinClear( pTitleWin);
	FTXWinPrintStr( pTitleWin, "FLAIM Index Manager");
	FTXWinSetCursorType( pTitleWin, FLM_CURSOR_INVISIBLE);
	FTXWinOpen( pTitleWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen,
		uiScreenCols, FIMT_HEADER_HEIGHT,
		&pHeaderWin)))
	{
		goto Exit;
	}

	FTXWinMove( pHeaderWin, 0, FIMT_TITLE_HEIGHT);
	FTXWinSetBackFore( pHeaderWin, FLM_BLUE, FLM_WHITE);
	FTXWinClear( pHeaderWin);
	FTXWinSetCursorType( pHeaderWin, FLM_CURSOR_INVISIBLE);
	FTXWinSetScroll( pHeaderWin, FALSE);
	FTXWinSetLineWrap( pHeaderWin, FALSE);
	FTXWinOpen( pHeaderWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
		uiScreenRows - FIMT_TITLE_HEIGHT - FIMT_HEADER_HEIGHT - FIMT_LOG_HEIGHT,
		&pListWin)))
	{
		goto Exit;
	}
	FTXWinMove( pListWin, 0, FIMT_TITLE_HEIGHT + FIMT_HEADER_HEIGHT);
	FTXWinOpen( pListWin);
	pList->setup( pListWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols, FIMT_LOG_HEIGHT,
		&IxDispInfo.pLogWin)))
	{
		goto Exit;
	}

	FTXWinDrawBorder( IxDispInfo.pLogWin);
	FTXWinMove( IxDispInfo.pLogWin, 0, uiScreenRows - FIMT_LOG_HEIGHT);
	FTXWinSetBackFore( IxDispInfo.pLogWin, FLM_BLUE, FLM_WHITE);
	FTXWinClear( IxDispInfo.pLogWin);
	FTXWinSetCursorType( IxDispInfo.pLogWin, FLM_CURSOR_INVISIBLE);
	FTXWinSetScroll( IxDispInfo.pLogWin, TRUE);
	FTXWinSetLineWrap( IxDispInfo.pLogWin, FALSE);
	FTXWinOpen( IxDispInfo.pLogWin);
	
	if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem)))
	{
		goto Exit;
	}

	if (RC_BAD( rc = pDbSystem->registerForEvent(
		XFLM_EVENT_UPDATES, &event)))
	{
		goto Exit;
	}
	bRegisteredForEvent = TRUE;

	FTXWinSetFocus( pListWin);
	uiIterations = 0;
	uiUpdateInterval = FLM_SECS_TO_TIMER_UNITS( 1);
	uiOneSec = FLM_SECS_TO_TIMER_UNITS( 1);
	uiLastUpdateTime = 0;
	while( !gv_bShutdown)
	{
		FLMUINT	uiCurrTime = FLM_GET_TIMER();

		if( bScreenLocked)
		{
			f_mutexUnlock( IxDispInfo.hScreenMutex);
			bScreenLocked = FALSE;
		}

		if( FLM_ELAPSED_TIME( uiCurrTime, uiLastUpdateTime) >= uiUpdateInterval)
		{
Update_Screen:

			if( !bScreenLocked)
			{
				f_mutexLock( IxDispInfo.hScreenMutex);
				bScreenLocked = TRUE;
			}

			FTXWinSetCursorPos( pHeaderWin, 0, 1);
			if( IxDispInfo.bShowTime)
			{
				FTXWinPrintf( pHeaderWin, "Index Index           State Last       Rate       Keys       Documents  Time");
			}
			else
			{
				FTXWinPrintf( pHeaderWin, "Index Index           State Last       Rate       Keys       Documents  Trans");
			}
			FTXWinClearToEOL( pHeaderWin);
			FTXWinPrintf( pHeaderWin, "\n");
			FTXWinPrintf( pHeaderWin, "Num.  Name                  DOC");

			if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS)))
			{
				goto Exit;
			}

			pTmpNd = pList->getFirst();
			uiIndex = 0;
			for( ;;)
			{
				if( RC_BAD( pDb->indexGetNext( &uiIndex)))
				{
					break;
				}

				// Remove all invalid entries

				while( pTmpNd && pTmpNd->uiKey < uiIndex)
				{
					uiKey = pTmpNd->uiKey;
					pTmpNd = pTmpNd->pNext;
					pList->remove( uiKey);
				}

				if (RC_BAD( rc = pDb->indexStatus( uiIndex, &IxDispInfo.IndexStatus)))
				{
					goto Exit;
				}

				if( !bShowOnline &&
					IxDispInfo.IndexStatus.eState == XFLM_INDEX_ONLINE)
				{
					if( pTmpNd && pTmpNd->uiKey == uiIndex)
					{
						uiKey = pTmpNd->uiKey;
						pTmpNd = pTmpNd->pNext;
						pList->remove( uiKey);
					}
					continue;
				}

				if( pTmpNd && pTmpNd->uiKey == uiIndex)
				{
					FLMUINT	uiOldest;
					FLMUINT	uiElapsed;

					pDispInfo = (IX_DISPLAY_INFO *)pTmpNd->pvData;
					f_strcpy( IxDispInfo.szName, pDispInfo->szName);

					// Copy the saved information.

					f_memcpy( &IxDispInfo.ui64SaveDocsProcessed [0],
								&pDispInfo->ui64SaveDocsProcessed [0],
								sizeof( FLMUINT) * MAX_VALS_TO_SAVE);
					f_memcpy( &IxDispInfo.uiDocSaveTime [0],
								&pDispInfo->uiDocSaveTime [0],
								sizeof( FLMUINT) * MAX_VALS_TO_SAVE);
					uiOldest = IxDispInfo.uiOldestSaved = pDispInfo->uiOldestSaved;

					// Recalculate the indexing rate.

					uiCurrTime = FLM_GET_TIMER();
					uiElapsed = (uiCurrTime - IxDispInfo.uiDocSaveTime [uiOldest]) /
															uiOneSec;
					if (uiElapsed && IxDispInfo.IndexStatus.ui64DocumentsProcessed)
					{
						if( IxDispInfo.ui64SaveDocsProcessed[ uiOldest] <
							IxDispInfo.IndexStatus.ui64DocumentsProcessed)
						{
							IxDispInfo.uiIndexingRate =
										// Records processed in time period
										(FLMUINT)((IxDispInfo.IndexStatus.ui64DocumentsProcessed -
										 IxDispInfo.ui64SaveDocsProcessed [uiOldest]) / uiElapsed);
						}
						else
						{
							IxDispInfo.uiIndexingRate = 0;
						}
					}
					else
					{
						IxDispInfo.uiIndexingRate = 0;
					}

					// Overwrite the oldest with the current data.

					IxDispInfo.uiDocSaveTime [uiOldest] = uiCurrTime;
					IxDispInfo.ui64SaveDocsProcessed [uiOldest] =
							IxDispInfo.IndexStatus.ui64DocumentsProcessed;

					// Move oldest pointer for next update.

					if (++IxDispInfo.uiOldestSaved == MAX_VALS_TO_SAVE)
					{
						IxDispInfo.uiOldestSaved = 0;
					}
				}
				else
				{
					FLMUINT			uiLoop;
					FLMUINT			uiBufLen;
					F_DataVector	srchKey;

					uiCurrTime = FLM_GET_TIMER();
					IxDispInfo.uiIndexingRate = 0;
					for (uiLoop = 0; uiLoop < MAX_VALS_TO_SAVE; uiLoop++)
					{
						IxDispInfo.ui64SaveDocsProcessed [uiLoop] =
								IxDispInfo.IndexStatus.ui64DocumentsProcessed;
						IxDispInfo.uiDocSaveTime [uiLoop] = uiCurrTime;
					}
					IxDispInfo.uiOldestSaved = 0;

					// Retrieve index name

					if (RC_BAD( srchKey.setUINT( 0, ELM_INDEX_TAG)))
					{
						break;
					}
					if (RC_BAD( srchKey.setUINT( 1, uiIndex)))
					{
						break;
					}

					if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX,
											&srchKey, XFLM_EXACT, &srchKey)))
					{
						if (rc != NE_XFLM_NOT_FOUND)
						{
							break;
						}
					}
					else
					{
						F_DOMNode *	pNode = NULL;

						if (RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION,
													srchKey.getDocumentID(), &pNode)))
						{
							if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
							{
								break;
							}
						}
						else
						{
							uiBufLen = sizeof( IxDispInfo.szName);
							rc = pNode->getAttributeValueUTF8( pDb,
									ATTR_NAME_TAG, (FLMBYTE *)IxDispInfo.szName, uiBufLen);
							pNode->Release();
							if (rc != NE_XFLM_OK &&
								 rc != NE_XFLM_DOM_NODE_NOT_FOUND &&
								 rc != NE_XFLM_CONV_DEST_OVERFLOW)
							{
								break;
							}
						}
					}
				}

				pList->update( uiIndex, ixDisplayHook, &IxDispInfo, sizeof( IxDispInfo));
				pList->refresh();

				if( pTmpNd && pTmpNd->uiKey == uiIndex)
				{
					pTmpNd = pTmpNd->pNext;
				}
			}
			pDb->transAbort();
			uiLastUpdateTime = FLM_GET_TIMER();
			pList->refresh();
		}

		if( !bScreenLocked)
		{
			f_mutexLock( IxDispInfo.hScreenMutex);
			bScreenLocked = TRUE;
		}

		if( RC_OK( FTXWinTestKB( pListWin)))
		{
			FLMUINT		uiChar;

			FTXWinInputChar( pListWin, &uiChar);
			f_mutexUnlock( IxDispInfo.hScreenMutex);
			bScreenLocked = FALSE;

			switch( uiChar)
			{
				case 'O':
				case 'o':
				{
					bShowOnline = !bShowOnline;
					goto Update_Screen;
				}

				case '+':
				case 'r':
				{
					if( (pTmpNd = pList->getCurrent()) != NULL)
					{
						if (RC_BAD( rc = pDb->indexResume( pTmpNd->uiKey)))
						{
							goto Exit;
						}
						goto Update_Screen;
					}
					break;
				}

				case 's':
				{
					if( (pTmpNd = pList->getCurrent()) != NULL)
					{
						if (RC_BAD( rc = pDb->indexSuspend( pTmpNd->uiKey)))
						{
							goto Exit;
						}
						goto Update_Screen;
					}
					break;
				}

				case FKB_ALT_S:
				case 'S':
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					FTXMessageWindow( IxDispInfo.pScreen, FLM_RED, FLM_WHITE,
								"Suspending all indexes ....",
								NULL, &pMsgWin);

					f_mutexUnlock( IxDispInfo.hScreenMutex);

					if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS)))
					{
						uiIndex = 0;
						for( ;;)
						{
							if( RC_BAD( pDb->indexGetNext( &uiIndex)))
							{
								break;
							}
							if (RC_BAD( pDb->indexSuspend( uiIndex)))
							{
								break;
							}
						}
						if (RC_BAD( pDb->transCommit()))
						{
							(void)pDb->transAbort();
						}
					}

					if( pMsgWin)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						FTXWinFree( &pMsgWin);
						f_mutexUnlock( IxDispInfo.hScreenMutex);
					}
					goto Update_Screen;
				}

				case 'R':
				case FKB_ALT_R:
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					FTXMessageWindow( IxDispInfo.pScreen, FLM_RED, FLM_WHITE,
						"Resuming all indexes                                ",
						NULL,
						&pMsgWin);
					f_mutexUnlock( IxDispInfo.hScreenMutex);

					if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS)))
					{
						uiIndex = 0;
						for( ;;)
						{
							if( RC_BAD( pDb->indexGetNext( &uiIndex)))
							{
								break;
							}

							if (RC_BAD( pDb->indexResume( uiIndex)))
							{
								break;
							}
						}
						if (RC_BAD( pDb->transCommit()))
						{
							(void)pDb->transAbort();
							break;
						}
					}
					if( pMsgWin)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						FTXWinFree( &pMsgWin);
						f_mutexUnlock( IxDispInfo.hScreenMutex);
					}
					goto Update_Screen;
				}

				case 'T':
				case 't':
				{
					IxDispInfo.bShowTime = !IxDispInfo.bShowTime;
					goto Update_Screen;
				}

				case '?':
				{
					FTX_WINDOW *		pHelpWin = NULL;
					FTX_WINDOW *		pHelpTitle = NULL;
					F_DynamicList *	pHelpList = NULL;
					FLMUINT				uiItem = 0;
					char					szTmpBuf [100];

					f_mutexLock( IxDispInfo.hScreenMutex);
					bScreenLocked = TRUE;

					if( (pHelpList = f_new F_DynamicList) == NULL)
					{
						goto Help_Exit;
					}

					if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
						1, &pHelpTitle)))
					{
						goto Help_Exit;
					}

					FTXWinSetBackFore( pHelpTitle, FLM_RED, FLM_WHITE);
					FTXWinClear( pHelpTitle);
					FTXWinSetCursorType( pHelpTitle, FLM_CURSOR_INVISIBLE);
					FTXWinSetScroll( pHelpTitle, FALSE);
					FTXWinSetLineWrap( pHelpTitle, FALSE);
					FTXWinPrintf( pHelpTitle, "FLAIM Index Manager - Help");
					FTXWinOpen( pHelpTitle);

					if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
						uiScreenRows - 1, &pHelpWin)))
					{
						goto Help_Exit;
					}
					FTXWinDrawBorder( pHelpWin);
					FTXWinOpen( pHelpWin);
					pHelpList->setup( pHelpWin);

					f_sprintf( szTmpBuf, "R, ALT_R  Resume all indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "S, ALT_S  Suspend all indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "o, O      Toggle display of on-line indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "+, r      Resume selected index with auto on-line option");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "s         Suspend selected index");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					pHelpList->refresh();
					pHelpWin = pHelpList->getListWin();

					f_mutexUnlock( IxDispInfo.hScreenMutex);
					bScreenLocked = FALSE;

					while( !gv_bShutdown)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						bScreenLocked = TRUE;

						if( RC_OK( FTXWinTestKB( pHelpWin)))
						{
							FLMUINT		uiTmpChar;
							
							FTXWinInputChar( pHelpWin, &uiTmpChar);
							if( uiTmpChar == FKB_ESCAPE)
							{
								break;
							}
							pHelpList->defaultKeyAction( uiTmpChar);
						}

						f_mutexUnlock( IxDispInfo.hScreenMutex);
						bScreenLocked = FALSE;
						f_sleep( 10);
					}

Help_Exit:
					if( !bScreenLocked)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						bScreenLocked = TRUE;
					}

					if( pHelpList)
					{
						pHelpList->Release();
					}

					if( pHelpTitle)
					{
						FTXWinFree( &pHelpTitle);
					}

					f_mutexUnlock( IxDispInfo.hScreenMutex);
					bScreenLocked = FALSE;
					break;
				}

				case FKB_ESCAPE:
				{
					goto Exit;
				}

				default:
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					pList->defaultKeyAction( uiChar);
					f_mutexUnlock( IxDispInfo.hScreenMutex);
					break;
				}
			}
			f_mutexLock( IxDispInfo.hScreenMutex);
			pList->refresh();
			f_mutexUnlock( IxDispInfo.hScreenMutex);
		}

		uiIterations++;

		if( pThread->getShutdownFlag())
		{
			break;
		}

		f_sleep( 1);
	}

Exit:

	if( pList)
	{
		pList->Release();
	}

	if (bRegisteredForEvent)
	{
		pDbSystem->deregisterForEvent( XFLM_EVENT_UPDATES, &event);
	}

	if( IxDispInfo.pScreen)
	{
		FTXScreenFree( &IxDispInfo.pScreen);
	}

	if( IxDispInfo.hScreenMutex != F_MUTEX_NULL)
	{
		f_mutexDestroy( &IxDispInfo.hScreenMutex);
	}
	
	if( pDb != NULL)
	{
		pDb->Release();
	}
	
	if( pDbSystem)
	{
		pDbSystem->Release();
	}

	return( rc);
}
/*API~***********************************************************************
Desc:	Creates a new FLAIM database.
*END************************************************************************/
RCODE XFLAPI F_DbSystem::dbCreate(
	const char *			pszFilePath,
		// [IN] Full path file name of the database which is to be created.
	const char *			pszDataDir,
		// [IN] Directory for data files.
	const char *			pszRflDir,
		// [IN] RFL directory.  NULL indicates that the RFL files should
		// be put in the same directory as the database.
	const char *			pszDictFileName,
		// [IN] Full path of a file containing dictionary definitions to be
		// imported into the dictionary collection during database
		// creation.  This is only used if pszDictBuf is NULL.  If
		// both pszDictFileName and pszDictBuf are NULL, the database
		// will be created with an empty dictionary.
	const char *			pszDictBuf,
		// [IN] Buffer containing dictionary definitions in external XML
		// format.  If the value of this parameter is NULL,
		// pszDictFileName will be used.
	XFLM_CREATE_OPTS *	pCreateOpts,
		// [IN] Create options for the database.  All members of the
		// structure should be initialized through a call to f_memset:
		//
		//		f_memset( pCreateOpts, 0, sizeof( XFLM_CREATE_OPTS));
		//
		// Once initialized, the values of specific members can be set to
		// reflect the options desired when the database is created (such
		// as block size and various roll-forward logging options).  If
		// NULL is passed as the value of this parameter, default options
		// will be used.
	FLMBOOL					bTempDb,
		// [IN] Flag indicating whether this is a temporary database.
		// Should try to minimize writing to disk if this is the case.
	IF_Db **					ppDb
		// [OUT] Pointer to a database object.  If the creation is
		// successful, the database object will be initialized.
	)
{
	RCODE				rc = NE_XFLM_OK;
	F_Db *			pDb = NULL;
	F_Database *	pDatabase = NULL;
	FLMBOOL			bDatabaseCreated = FALSE;
	FLMBOOL			bNewDatabase = FALSE;
	FLMBOOL			bMutexLocked = FALSE;
	FLMUINT			uiRflToken = 0;

	// Make sure the path looks valid
	
	if (!pszFilePath || !pszFilePath [0])
	{
		rc = RC_SET( NE_FLM_IO_INVALID_FILENAME);
		goto Exit;
	}

	// Allocate and initialize an F_Db structure.

	if (RC_BAD( rc = allocDb( &pDb, FALSE)))
	{
		goto Exit;
	}

	f_mutexLock( gv_XFlmSysData.hShareMutex);
	bMutexLocked = TRUE;

	for( ;;)
	{

		// See if we already have the file open.
		// May unlock and re-lock the global mutex.

		if (RC_BAD( rc = findDatabase( pszFilePath, pszDataDir, &pDatabase)))
		{
			goto Exit;
		}

		// Didn't find the database

		if (!pDatabase)
		{
			break;
		}

		// See if file is open or being opened.

		if (pDatabase->m_uiOpenIFDbCount || (pDatabase->m_uiFlags & DBF_BEING_OPENED))
		{
			rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
			goto Exit;
		}

		// Free the F_Database object.  May temporarily unlock the global mutex.
		// For this reason, we must call findDatabase again (see above) after
		// freeing the object.

		pDatabase->freeDatabase();
		pDatabase = NULL;
	}

	// Allocate a new F_Database object

	if (RC_BAD( rc = allocDatabase( pszFilePath, pszDataDir, bTempDb, &pDatabase)))
	{
		goto Exit;
	}

	bNewDatabase = TRUE;
	pDatabase->m_uiMaxFileSize = gv_XFlmSysData.uiMaxFileSize;

	// Link the F_Db object to the F_Database object.

	rc = pDb->linkToDatabase( pDatabase);
	
	f_mutexUnlock( gv_XFlmSysData.hShareMutex);
	bMutexLocked = FALSE;

	if (RC_BAD( rc))
	{
		goto Exit;
	}

	// If the database has not already been created, do so now.

	if (RC_OK( gv_XFlmSysData.pFileSystem->doesFileExist( pszFilePath)))
	{
		rc = RC_SET( NE_XFLM_FILE_EXISTS);
		goto Exit;
	}

	// Create the .db file.

	pDb->m_pSFileHdl->setMaxAutoExtendSize( gv_XFlmSysData.uiMaxFileSize);
	pDb->m_pSFileHdl->setExtendSize( pDb->m_pDatabase->m_uiFileExtendSize);
	
	if (RC_BAD( rc = pDb->m_pSFileHdl->createFile( 0)))
	{
		goto Exit;
	}
	bDatabaseCreated = TRUE;

	(void)flmStatGetDb( &pDb->m_Stats, pDatabase,
						0, &pDb->m_pDbStats, NULL, NULL);

	// We must have exclusive access.  Create a lock file for that
	// purpose, if there is not already a lock file.
	// NOTE: No need for a lock file if this is a temporary database.

	if (!bTempDb)
	{
		if (RC_BAD( rc = pDatabase->getExclAccess( pszFilePath)))
		{
			goto Exit;
		}
	}

	if (RC_BAD( rc = pDb->initDbFiles( pszRflDir, pszDictFileName,
											 pszDictBuf, pCreateOpts)))
	{
		goto Exit;
	}

	// Disable RFL logging (m_pRfl was initialized in initDbFiles)

	if( pDatabase->m_pRfl)
	{
		pDatabase->m_pRfl->disableLogging( &uiRflToken);
	}

	// Set FFILE stuff to same state as a completed checkpoint.

	pDatabase->m_uiFirstLogCPBlkAddress = 0;
	pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER();

	// Create a checkpoint thread - no need if this is a temporary
	// database.

	if (!bTempDb)
	{
		if (RC_BAD( rc = pDatabase->startCPThread()))
		{
			goto Exit;
		}

		if( RC_BAD( rc = pDatabase->startMaintThread()))
		{
			goto Exit;
		}
	}
		
Exit:

	if (bMutexLocked)
	{
		f_mutexUnlock( gv_XFlmSysData.hShareMutex);
	}

	if (pDb)
	{
		pDb->completeOpenOrCreate( rc, bNewDatabase);

		// completeOpenOrCreate will delete pDb if RC_BAD( rc)

		if (RC_BAD( rc))
		{
			pDb = NULL;
		}
		else
		{
			*ppDb = (IF_Db *)pDb;
			pDb = NULL;	// This isn't strictly necessary, but it makes it
							// obvious that we are no longer using the object.
		}
	}

	if (RC_BAD( rc))
	{
		if (bDatabaseCreated)
		{
			gv_pXFlmDbSystem->dbRemove( pszFilePath, pszDataDir, pszRflDir, TRUE);
		}
	}
	else if( uiRflToken)
	{
		pDatabase->m_pRfl->enableLogging( &uiRflToken);
	}

	return( rc);
}