/*!
    @ingroup        Restart
    @file           Rst_RedoTrafficControl.cpp      
    @author         JuergenA
    @author         UweH
    @brief          defines the classes to synchronize the parallel redo
*/
/*

    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



 */
#include "Restart/Rst_IRedoTrafficControl.hpp"
#include "Restart/Rst_RedoTrafficControl.hpp"
#include "SAPDBCommon/SAPDB_Types.hpp"
#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_IRawAllocator.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"
#include "KernelCommon/Kernel_TaskWaitList.hpp"
#include "KernelCommon/Kernel_IAdminBackupRecovery.hpp"
#include "Logging/Log_Types.hpp"
#include "Logging/Log_Transaction.hpp"
#include "Restart/Rst_Exceptions.hpp"
#include "gsp00.h"
#include "ggg00.h"
#include "ggg92.h"   // used by hkb51.h
#include "heo55k.h"  // vbegexcl
#include "heo56.h"  // vsuspend
#include "hgg08.h"
#include "hkb50.h"   // k50RegisterTransaction
#include "hkb51.h"
#include "hkb53.h"   // k53server_drop_aux_files
/// suspend reason used when nothing to do
#define LOG_WAIT_FOR_VACANT_ACTION   231
/// suspend reason used waiting for another trans, which has to be commited before
#define LOG_WAIT_FOR_PRECEDING_TRANS 232
/// WAIT_REASON_EMPTY_LIST
#define WAIT_REASON_EMPTY_LIST       228
/// global instance access (singleton)
Rst_IRedoTrafficControl* Rst_IRedoTrafficControl::m_pInstance = NULL;
/// forward declaration
class Rst_IoSeqEntry;
/*!
   @class          Rst_DoubleChainEntry
   @brief          a double chained entry
 */
class Rst_DoubleChainEntry
{
private:
    /// pointer to the successor-node in the chain
    Rst_DoubleChainEntry *m_pNext;
    /// pointer to the predecessor in the chain
    Rst_DoubleChainEntry *m_pPrev;
public:
    /// constructor
    Rst_DoubleChainEntry ()
    : m_pNext (NULL),
      m_pPrev (NULL)
    {}
	/// returns the previous entry
    Rst_DoubleChainEntry *GetPrev () const
    {
        return m_pPrev;
    }
	/// returns the following entry
    Rst_DoubleChainEntry *GetNext () const
    {
        return m_pNext;
    }
	/// inserts an entry after the given entry
    /// if the given prev entry is 0 then the entry is added as new first
    void Insert (Rst_DoubleChainEntry* &pFirstListEntry,
                 Rst_DoubleChainEntry* &pLastListEntry,
                 Rst_DoubleChainEntry*  pPrevListEntry)
    {
		// PTS 1124684 UH 2003-10-16 written from earlier Append()
        if (0 == pFirstListEntry)
        {
            m_pPrev         = NULL;
            m_pNext         = NULL;
            pFirstListEntry = this;
            pLastListEntry  = this;
            return;
        }

        m_pPrev = pPrevListEntry;

        if (pPrevListEntry == 0)
        {
            m_pNext         = pFirstListEntry;
            pFirstListEntry = this;
        }
        else
        {
            m_pNext                 = pPrevListEntry->m_pNext;
            pPrevListEntry->m_pNext = this;
        }

        if ( m_pNext != 0 )
            m_pNext->m_pPrev = this;

        if (pPrevListEntry == pLastListEntry)
            pLastListEntry = this;
    }
    /// removes this entry from the entry list
    void Remove (Rst_DoubleChainEntry *&pFirstListEntry,
                 Rst_DoubleChainEntry *&pLastListEntry)
    {
        if (NULL == m_pPrev)
            pFirstListEntry = m_pNext;
        else
            m_pPrev->m_pNext = m_pNext;
        
        if (NULL == m_pNext)
            pLastListEntry = m_pPrev;
        else
            m_pNext->m_pPrev = m_pPrev;
        
        m_pPrev = NULL;
        m_pNext = NULL;
    }
};
/*!
   @class Rst_DoubleChainList
   @brief a double chained list without any allocation
 */
class Rst_DoubleChainList
{
private:
    /// this is the first element in the chain
    Rst_DoubleChainEntry *m_pFirst;
    /// this is the last element in the chain
    Rst_DoubleChainEntry *m_pLast;
public:
    /// constructor
    inline Rst_DoubleChainList ()
    : m_pFirst (NULL),
      m_pLast  (NULL)
    {}
	/// inserts an entry after the given entry if it is 0 then it is inserted at first
    void Insert (Rst_DoubleChainEntry*  pEntry,
                 Rst_DoubleChainEntry*  pPrevEntry)
    {
        pEntry->Insert (m_pFirst, m_pLast, pPrevEntry);
    }
    /*!
        @brief Inserts an entry at the end of the list
        @param pEntry [in] the pointer to the entry to be appended
     */
    void Append (Rst_DoubleChainEntry *pEntry)
    {
        pEntry->Insert (m_pFirst, m_pLast, m_pLast);
    }
	/// removes an entry from the list and returns the pointer to the removed entry
    Rst_DoubleChainEntry *GetAndRemoveAny ()
    {
        Rst_DoubleChainEntry *pEntry = m_pFirst;
        Remove (pEntry);
        return (pEntry);
    }
    /// returns the pointer to the first entry of the list
    Rst_DoubleChainEntry *GetFirst () const
    {
        return m_pFirst;
    }
	/// returns the pointer to the last entry of the list
    inline Rst_DoubleChainEntry *GetLast () const
    {
        return m_pLast;
    }
	/// returns true, if the list is empty
    bool IsEmpty () const
    {
        return NULL == m_pFirst;
    }
    /*!
        @brief  removes the entry from the list
        @param  pEntry [in] the pointer to the entry to be removed
     */
    void Remove (Rst_DoubleChainEntry *pEntry)
    {
        if ( pEntry != NULL )
            pEntry->Remove (m_pFirst, m_pLast);
    }
};
/*!
    @class          Rst_JobEntry
    @brief          an entry of the job container list
 */
class Rst_JobEntry : public Rst_DoubleChainEntry
{
private:
    /// transaction index of this job
    tgg00_TransIndex  m_OwnIndex;
    /// flag, which denotes, whether this job is executing an rollbackward or rollforward
    Log_ActionType    m_UndoOrRedo; 
    /// pointer to the corresponding IOSequence related task-information
    Rst_IoSeqEntry   *m_pIoSeqEntry;
    /// transaction context
    Log_Transaction   m_Transaction;
public:
    /// default constructor sets all nil
    Rst_JobEntry()
    : m_OwnIndex   (cgg_nil_transindex),
      m_UndoOrRedo (Log_NoOp),
      m_pIoSeqEntry(NULL)
    {}
    /// returns pointer to the corresponding IoSeqEntry
    Rst_IoSeqEntry *GetIoSeqEntry () const
    {
        return (m_pIoSeqEntry);
    }
	/// returns the following entry
    Rst_JobEntry *GetNext () const
    {
        return ( REINTERPRET_CAST (Rst_JobEntry*, Rst_DoubleChainEntry::GetNext()) );
    }
	/// returns pointer of the transaction handle of this job
    Log_Transaction &GetTransaction ()
    {
        return m_Transaction;
    }
	/// returns TransIndex of this job
    tgg00_TransIndex GetTransIndex () const
    {
        return (m_OwnIndex);
    }
	/// returns the action type commit or rollback
    Log_ActionType GetUndoOrRedo () const
    {
        return (m_UndoOrRedo);
    }
	/// initializes the job entry
    void Init (Log_ActionType   UndoOrRedo,
               Rst_IoSeqEntry  *pIoSeqEntry,
               Log_Transaction &transaction)
    {
        m_UndoOrRedo   = UndoOrRedo;
        m_pIoSeqEntry  = pIoSeqEntry;
        m_Transaction.CopyFromRedoReaderToRedoJobList(transaction);
    }
	/// assigns the OwnIndex
    void SetOwnIndex (tgg00_TransIndex OwnIndex)
    {
        m_OwnIndex = OwnIndex;
    }
};
/*!
    @class  Rst_IoSeqEntry
    @brief  entry of a task wait stack
 */
class Rst_IoSeqEntry: public Rst_DoubleChainEntry
{
private:
    /// The IOSequence of the waiting task
    Log_IOSequenceNo    m_IoSeq;
    /// the list of jobs, waiting for this IOSequence
    Rst_DoubleChainList m_JobList;
    /// the corresponding task-id's, which are waiting for this IOSequence
    Kernel_TaskWaitList m_WaitStack;
public:
    /// returns the first job entry
    Rst_JobEntry* GetFirstJobEntry () const
    {
        return static_cast<Rst_JobEntry*>(m_JobList.GetFirst());
    }
	/// returns the previous entry
    Rst_IoSeqEntry* GetPrev () const
    {
        return static_cast<Rst_IoSeqEntry*>(Rst_DoubleChainEntry::GetPrev());
    }
    /// returns the following entry
    Rst_IoSeqEntry* GetNext () const
    {
        return static_cast<Rst_IoSeqEntry*>(Rst_DoubleChainEntry::GetNext());
    }
	/// returns the I/O sequence contained in the entry
    Log_IOSequenceNo GetIoSeq () const
    {
        return m_IoSeq;
    }
	/// initializes the I/O sequence entry
    void Init (Log_IOSequenceNo IoSeq)
    {
        m_IoSeq = IoSeq;
    }
	/// appends a job into the job list
    void AppendJobEntry (Rst_JobEntry *pJobEntry)
    {
        m_JobList.Append (pJobEntry);
    }
	/// inserts a task id into the WaitStack for this IoSeq
    void InsertWaitTask (tsp00_TaskId       TaskId,
                         tgg00_WaitContext &WaitContext)
    {
        m_WaitStack.InsertAsFirst(TaskId, WaitContext);
    }
	/// returns true, if the job list is empty
    bool IsEmptyJobList () const
    {
        return ( m_JobList.IsEmpty() );
    }
	/// removes a job entry from the job list for this IoSeq
    void RemoveJobEntry (Rst_JobEntry *pJobEntry)
    {
        m_JobList.Remove (pJobEntry);
    }
	/// resumes all tasks waiting for this IoSeq
    void TryResumeTasks()
    {
        if ( m_JobList.IsEmpty() )
            m_WaitStack.ResumeAll();
    }
};
/*!
    @class Rst_IoSeqList
    @brief a list containing I/O sequence entries
 */
class Rst_IoSeqList: public Rst_DoubleChainList
{
public:
    /*!
        @brief   removes an entry from the list
        @return  the pointer to the removed entry
     */
    Rst_IoSeqEntry* GetAndRemoveAny ()
    {
        return static_cast<Rst_IoSeqEntry*>(Rst_DoubleChainList::GetAndRemoveAny());
    }
	/// the pointer to the first entry of the list
    Rst_IoSeqEntry* GetFirst () const
    {
        return static_cast<Rst_IoSeqEntry*>(Rst_DoubleChainList::GetFirst());
    }
	/// returns the pointer to the last entry of the list
    Rst_IoSeqEntry* GetLast () const
    {
        return static_cast<Rst_IoSeqEntry*>(Rst_DoubleChainList::GetLast());
    }
	/// returns the I/O sequence of the last entry, nil if list is empty
    Log_IOSequenceNo GetLastIoSeq () const
    {
        if ( IsEmpty() )
            return Log_IOSequenceNo();
        return GetLast()->GetIoSeq();
    }
    /// inserts a new entry at the correct position
    void Insert(Rst_IoSeqEntry   *pEntry,
                Log_IOSequenceNo  oldestKnownEOTSequence)
    {
		// PTS 1124684 UH 2003-10-16 new
        Rst_DoubleChainList::Insert( pEntry, 
                                     SearchPrevEntry(pEntry->GetIoSeq(),
                                                     oldestKnownEOTSequence) );
    }
    /// appends a new entry at the end
    void Append(Rst_IoSeqEntry *pEntry)
    {
        Rst_DoubleChainList::Append(pEntry);
    }
    /*!
        @brief finds the largest IoSeqEntry which is less IoSeq parameter and waits there
        @param RedoTaskId [in] the callers taskid
        @param WaitContext [in/out] used for waiting
        @param pOwnEotSeqEntry [in] pointer to IoSeqEntry containing the end of trans entry
        @param IoSeq [in] sequence of the log entry handled by the redo task
        @param FirstIoSeq [in] This is used to compare overflown io sequences.
        @param WaitInserted [out] true, if the task id is inserted into a wait list
     */
    void WaitForIoSeq (tsp00_TaskId       RedoTaskId,
                       tgg00_WaitContext &WaitContext,
                       Log_IOSequenceNo   IoSeq,
                       Log_IOSequenceNo   FirstIoSeq,
                       bool              &WaitInserted)
    {
        SAPDBTRACE_METHOD_DEBUG ("Rst_IoSeqList::WaitForIoSeq", LogTrans_Trace, 5);
        
        WaitInserted = false;
        
        if ( GetFirst() == 0 )
            return; // list is empty
            
        if ( LogTrans_Trace.TracesLevel(6) )
            Kernel_VTrace() << "REDOTRAFFIC: check ioseq: " << IoSeq
                            << ", first eotseq: " << GetFirst()->GetIoSeq() << NewLine;

        if ( IoSeq == GetFirst()->GetIoSeq()
             ||
             // PTS 1120201 UH 2003-02-17
             Log_IOSequenceNo::LeftIsOlder(IoSeq, GetFirst()->GetIoSeq(), FirstIoSeq) )
            return;
            
        Rst_IoSeqEntry *pCurr = GetFirst();
     
        if ( NULL == pCurr )
            RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                       "Rst_IoSeqList::WaitForIoSeq() pCurr is nil.") );
     
        Rst_IoSeqEntry *pNext = pCurr->GetNext();
    
        if ( NULL == pNext )
            RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                       "Rst_IoSeqList::WaitForIoSeq() pNext is nil.") );
                       
        while ( Log_IOSequenceNo::LeftIsOlder(pNext->GetIoSeq(), IoSeq, FirstIoSeq) ) // PTS 1120201 UH 2003-02-17
        {
            pCurr = pNext;
            pNext = pNext->GetNext();
        }
    
        if ( Log_IOSequenceNo::LeftIsOlder(pCurr->GetIoSeq(), IoSeq, FirstIoSeq) )// PTS 1120201 UH 2003-02-17
        {
            pCurr->InsertWaitTask (RedoTaskId, WaitContext);
            WaitInserted = true;
        }
        if ( WaitInserted && LogTrans_Trace.TracesLevel(6) )
            Kernel_VTrace() << "REDOTRAFFIC: wait for eotseq: " << pCurr->GetIoSeq();
    }
    /// search an entry which has the greatest ioseq which is smaller then the given
    /// If the list is empty or the given ioseq is smaller then all 0 is returned.
    Rst_IoSeqEntry* SearchPrevEntry(const Log_IOSequenceNo IoSeq,
                                    const Log_IOSequenceNo oldestKnownEOTSequence)
    {
		// PTS 1124684 UH 2003-10-16 new
        if ( GetFirst() == 0 || GetLast() == 0)
            return 0;

        const Log_IOSequenceNo firstIoSeq = GetFirst()->GetIoSeq();
        const Log_IOSequenceNo lastIoSeq  = GetLast()->GetIoSeq();

        for ( Rst_IoSeqEntry* pCurrentEntry = GetLast();
              pCurrentEntry != 0;
              pCurrentEntry = pCurrentEntry->GetPrev() )
        {
            if ( Log_IOSequenceNo::LeftIsYounger(IoSeq, pCurrentEntry->GetIoSeq(), oldestKnownEOTSequence) )
                return pCurrentEntry;
        }
        return 0;
    }
    /// search an entry which has the same ioseq if none is found 0 is returned.
    Rst_IoSeqEntry* SearchEntry(const Log_IOSequenceNo IoSeq)
    {
		// PTS 1124684 UH 2003-10-16 new
        for ( Rst_IoSeqEntry* pCurrentEntry = GetLast();
              pCurrentEntry != 0;
              pCurrentEntry = pCurrentEntry->GetPrev() )
        {
            if ( IoSeq == pCurrentEntry->GetIoSeq() )
                return pCurrentEntry;
        }
        return 0;
    }
};
/*!
   @class  Rst_RedoTrafficControl
   @brief  lists to synchronize the parallel redo (singleton)
 */
class Rst_RedoTrafficControl: public Rst_IRedoTrafficControl
{
private:
    /// maximum number of concurrent running transactions
    SAPDB_UInt              m_NumberOfTransEntries;
    /// flag which indicates the end of the Redo
    bool                    m_EndOfLog;
    /// List of idle ReDo-Tasks
    Kernel_TaskWaitList     m_VacantJobWait;
    /// redo reader task waiting in CloseOpenTrans
    tsp00_TaskId            m_TaskWaitingForEmptyList;
    /// list of RedoTransactions sorted by the EndOfTrans-IOSequence
    Rst_IoSeqList           m_IoSeqList;
    /// list of free RedoTransactions
    Rst_IoSeqList           m_IoSeqFreeList;
    /// pointer to next jobentry, which has to be redone
    Rst_JobEntry           *m_pFirstVacancy;
    /// List of jobs per IOSequence (sorted by IOSequence)
    Rst_IoSeqEntry         *m_pIoSeqContainer;
    /// Array of transactions. All Tranactions of one IOSequence are linked.
    Rst_JobEntry           *m_pJobContainer;
    /// used for dynamic allocating memory
    SAPDBMem_IRawAllocator &m_RawAllocator;
    /// global flag, which inicates, whether the Redo had been aborted
    volatile bool          &m_redoIsAborted;
    /// number of jobs inserted
	SAPDB_UInt             &m_inserted;
    /// number of jobs removed
	SAPDB_UInt             &m_removed;
    /// this flag prevents the redo tasks to start before all transactions are restored from savepoint
    bool                    m_RedoProcessingDisabled; // PTS 1124684 UH 2003-10-16
    Log_IOSequenceNo        m_OldestKnownEOTSequence; // PTS 1124684 UH 2003-10-16
    /// private ctor only used once during initialization
    Rst_RedoTrafficControl (SAPDBMem_IRawAllocator &RawAllocator,
                            volatile bool          &redoIsAborted,
                            SAPDB_UInt             &inserted,
                            SAPDB_UInt             &removed)
    : m_NumberOfTransEntries    (0),
      m_EndOfLog                (false),
      m_pFirstVacancy           (NULL),
      m_TaskWaitingForEmptyList (cgg_nil_pid),
      m_pIoSeqContainer         (NULL),
      m_pJobContainer           (NULL),
      m_RawAllocator            (RawAllocator),
      m_redoIsAborted           (redoIsAborted),
      m_inserted                (inserted),
      m_removed                 (removed),
      m_RedoProcessingDisabled  (true) // PTS 1124684 UH 2003-10-16
    {}
	/// returns an IoSeqEntry which is removed from the free list
    Rst_IoSeqEntry *GetAndRemoveFromFreeIoSeqList ()
    {
        return ( m_IoSeqFreeList.GetAndRemoveAny() );
    }
	/// returns the pointer to the job entry specified by its TransIndex
    Rst_JobEntry *GetJobEntry (tgg00_TransIndex TransIndex)
    {
        if (cgg_nil_transindex == TransIndex)
            return (NULL);
        
        Rst_JobEntry* entry = (m_pJobContainer+TransIndex-1);
        
        if ( TransIndex != entry->GetTransIndex() )
        {
            RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                       "Rst_RedoTrafficControl::GetJobEntry: transindex mismatch") );
        }
        return entry;
    }
    /// puts an IoSeqEntry into the free list
    void PutIntoFreeIoSeqList (Rst_IoSeqEntry *pFreeEntry)
    {
        m_IoSeqFreeList.Append (pFreeEntry);
    }
	/// a redo task removes a job and resumes waiting tasks if possible
    void RemoveJob (
        tsp00_TaskId  RedoTaskId,
        Rst_JobEntry *pJobEntry);
	/// checks the joblist
    void CheckJobEntry (tsp00_TaskId         taskid,
                        tgg00_TransIndex     index,
                        Log_IOSequenceNo     iosequence,
                        const tgg91_TransNo &transno);
    /*!
        @class Rst_RedoSync
        @brief class to get exclusive access to the redo lists
     */
    class Rst_RedoSync
    {
    private:
        /// taskid needed for the leaving of the associated region
        tsp00_TaskId m_CallingTaskId;
    public:
        /// enters g08redocontrol region
        Rst_RedoSync (tsp00_TaskId CallingTaskId)
        {
            m_CallingTaskId = CallingTaskId;
            vbegexcl (m_CallingTaskId, g08redocontrol);
        }
        /// leaves g08redocontrol region
        ~Rst_RedoSync ()
        {
            vendexcl (m_CallingTaskId, g08redocontrol);
        }
    };
public:
	/// deallocation of containers
    ~Rst_RedoTrafficControl()
    {
        if ( m_NumberOfTransEntries == 0 )
            return;
        if ( NULL != m_pIoSeqContainer )
            destroyarray (m_pIoSeqContainer,m_NumberOfTransEntries,m_RawAllocator);
        if ( NULL != m_pJobContainer )
            destroyarray (m_pJobContainer,m_NumberOfTransEntries,m_RawAllocator);
        m_NumberOfTransEntries = 0;
    }
	/// allocates the Rst_RedoTrafficControl singleton with its containers und supply
    static void AllocateInstance (SAPDBMem_IRawAllocator &RawAllocator,
                                  volatile bool          &redoIsAborted,
                                  SAPDB_UInt             &inserted,
                                  SAPDB_UInt             &removed,
                                  bool                   &IsOk)
    {  
        SAPDBTRACE_ROUTINE_DEBUG ("Rst_RedoTraffic::AllocateInstance", LogTrans_Trace, 5);
        
        if (NULL == m_pInstance)
        {
            m_pInstance = new (RawAllocator)
                            Rst_RedoTrafficControl ( RawAllocator,
                                                     redoIsAborted,
                                                     inserted,
                                                     removed );
        }
        IsOk = NULL != m_pInstance;
    }
    /// invalidates the static pointer to the global instance pointer. This method does NOT
    /// release any memory
    static void InvalidateInstancePtr ()
    {
        SAPDBTRACE_ROUTINE_DEBUG ("Rst_RedoTraffic::InvalidateInstancePtr", LogTrans_Trace, 5);
        m_pInstance = NULL;
    }
    
	/// returns the pointer to the global instance of the singleton
    static Rst_RedoTrafficControl *InstancePtr ()
    { 
        return ( REINTERPRET_CAST (Rst_RedoTrafficControl*, m_pInstance) );
    }
    /*!
        @brief allocates the I/O sequence container and the job container

        The transactions of the database are limited by NumberOfTransEntries.
        if every transaction is closed at a different LogPage, so everey transaction
        has a different IoSeq. Therefore the IoSeqContainer has NumberOfTransEntries
        elements. The TransIndex has a range 1..NumberOfTransEntries. To avoid
        arithmetic expressions when accessing the JobContainer vector, the vector
        consists of NumberOfTransEntries+1 elements (0 .. n+1).
     */
    void AllocateContainer (bool &IsOk)
    {  
        SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::AllocateContainer", LogTrans_Trace, 5);
        
        IsOk = true;
        
        if ( m_NumberOfTransEntries > 0 )
        {
            SAPDBTRACE_WRITELN (LogTrans_Trace, 6, "NOT OK (m_NumberOfTransEntries > 0)");
            IsOk = false;
            return;
        }
        
        m_NumberOfTransEntries = k51number_of_trans_entries();
        
        if ( NULL == m_pIoSeqContainer )
        {
            newarray (m_pIoSeqContainer, m_NumberOfTransEntries, m_RawAllocator);
            if (NULL == m_pIoSeqContainer)
            {
                SAPDBTRACE_WRITELN (LogTrans_Trace, 6, "NOT OK (NULL == m_pIoSeqContainer)");
                IsOk = false;
                return;
            }
        }
        
        if ( NULL == m_pJobContainer )
        {
            newarray (m_pJobContainer, m_NumberOfTransEntries, m_RawAllocator);
            if ( NULL == m_pJobContainer )
            {
                SAPDBTRACE_WRITELN (LogTrans_Trace, 6, "NOT OK (NULL == m_pJobContainer)");
                IsOk = false;
                return;
            }
        }
        
        SAPDB_UInt i;
        
        for (i = 0; i < m_NumberOfTransEntries; ++i)
            m_IoSeqFreeList.Append (m_pIoSeqContainer+i);

        for (i = 0; i < m_NumberOfTransEntries; ++i)
            (m_pJobContainer+i)->SetOwnIndex (i+1);
    }
	/// returns the allocator used to allocate this singleton
    SAPDBMem_IRawAllocator& GetRawAllocatorUsed ()
    {
        return (m_RawAllocator);
    }
    /*!
        @brief          inserts a job. This method is provided for the log reader task.
        @param          LogReaderTaskId        [in] 
        @param          TransIndex             [in] 
        @param          EndOfTransIoSeq        [in] 
        @param          UndoOrRedo             [in] 
        @param          checkAscendingSequence [in]
        @param          Transaction           [in] 
        @param          IsOk                      [out] 
     */
    virtual void InsertJob (
        tsp00_TaskId      LogReaderTaskId,
        tgg00_TransIndex  TransIndex,
        Log_IOSequenceNo  EndOfTransIoSeq,
        Log_ActionType    UndoOrRedo,
        Log_IOSequenceNo  oldestKnownEOTSequence, // PTS 1124684 UH 2003-10-16
        bool              checkAscendingSequence, // PTS 1113641 UH 2002-01-25
        Log_Transaction  &Transaction,
        bool             &IsOk);
	/// executes vacant jobs. This method is provided for redo tasks.
    virtual void ExecuteJobs (tgg00_TransContext &TransContext);
	/// the log reader task sets the state: end of log read
    virtual void SetEndOfLogRead (tsp00_TaskId LogReaderTaskId)
    {
        SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::SetEndOfLogRead", LogTrans_Trace, 5);
        Rst_RedoSync RedoSync (LogReaderTaskId);
        m_EndOfLog = true;
        m_VacantJobWait.ResumeAll();
    }
    /*!
        @brief  a redo task checks if preceding transactions are closed
        @param  RedoTaskId [in] 
        @param  WaitContext [in/out] 
        @param  IoSeq [in] sequence of the log entry handled by the redo task
        @return true if caller has to wait
     */
    virtual bool CheckIoSeq (tsp00_TaskId       RedoTaskId,
                             tgg00_WaitContext &WaitContext,
                             Log_IOSequenceNo   IoSeq)
    {
        SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::CheckIoSeq", LogTrans_Trace, 5);
        
        bool WaitInserted = false;
        {
            Rst_RedoSync RedoSync (RedoTaskId);
            if ( LogTrans_Trace.TracesLevel(6) )
                WriteToTrace ("REDOTRAFFIC: WaitForIoSeq");
            m_IoSeqList.WaitForIoSeq (RedoTaskId, WaitContext, IoSeq,
                                      m_OldestKnownEOTSequence, WaitInserted);
        }
        return WaitInserted;
    }
    /// a redo task waits until preceding transactions are closed
    virtual void WaitForIoSeq (tsp00_TaskId RedoTaskId)
    {
        SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::WaitForIoSeq", LogTrans_Trace, 5);
        vsuspend (RedoTaskId, LOG_WAIT_FOR_PRECEDING_TRANS);
    }
    /// This is used to wait until the whole list is empty
    virtual void WaitForAllRedoTasks ( tsp00_TaskId RedoTaskId )
	{
        {
            Rst_RedoSync RedoSync (RedoTaskId);
            if ( m_TaskWaitingForEmptyList != cgg_nil_pid )
                RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoTrafficControl::WaitForAllRedoTasks: m_TaskWaitingForEmptyList != nil_pid") );
            if ( m_IoSeqList.IsEmpty() )
                return;
            m_TaskWaitingForEmptyList = RedoTaskId;
            m_VacantJobWait.ResumeAll();
        }
        vsuspend (RedoTaskId, WAIT_REASON_EMPTY_LIST);
        m_TaskWaitingForEmptyList = cgg_nil_pid;
	}
    /// The log reader calls this after recreation of all open transactions from last savepoint
    virtual void EnableRedoProcessing (tsp00_TaskId LogReaderTaskId)
    // PTS 1124684 UH 2003-10-16 new
    {
        // PTS 1126105 mb 2003-12-10
        SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::EnableRedoProcessing", LogTrans_Trace, 5);
        Rst_RedoSync RedoSync (LogReaderTaskId);

        Rst_IoSeqEntry *firstIoSeqEntry = m_IoSeqList.GetFirst();
        m_RedoProcessingDisabled        = false;
        m_pFirstVacancy = firstIoSeqEntry == 0
                          ? 0
                          : firstIoSeqEntry->GetFirstJobEntry();
        m_VacantJobWait.ResumeAll();
    }
	/// writes some basic information into the trace 
	virtual void WriteToTrace (const char *title = 0) const;
	/// This returns the youngest iosequence inserted to the redo list
	virtual Log_IOSequenceNo GetLastIOSequence (tsp00_TaskId taskid) const;
    /// maximum number of transactions which are configure
    virtual SAPDB_UInt GetMaximumNumberOfTransactions() const;
    /// a list of transno's is returned, which is currently known by redo traffic control
	virtual bool GetTransactions (tsp00_TaskId            taskid,
                                  Rst_Transactions       &listOfTransToSkip) const; // PTS 1134103 UH 2005-03-07 new
};

//----------------------------------------------------------------------------

void Rst_RedoTrafficControl::ExecuteJobs (tgg00_TransContext &TransContext)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::ExecuteJobs", LogTrans_Trace, 5);
    
    if (e_ok != TransContext.trError_gg00)
    {
        SAPDBTRACE_WRITELN (LogTrans_Trace, 6, "trError: " << TransContext.trError_gg00);
        return;
    }
    
    Rst_JobEntry *pJob;
    bool          WaitInserted;
    bool          EndOfLog = false;
    
    while ( e_ok == TransContext.trError_gg00
            &&
            ! EndOfLog
            &&
            ! m_redoIsAborted )
    {    
        WaitInserted = false;
        pJob         = NULL;
        
        {
            Rst_RedoSync RedoSync (TransContext.trTaskId_gg00);
            
            if ( NULL == m_pFirstVacancy
                 ||
                 m_RedoProcessingDisabled ) // PTS 1124684 UH 2003-10-16
            {
                EndOfLog = m_EndOfLog;
                if ( ! EndOfLog )
                {
                    m_VacantJobWait.InsertAsFirst(TransContext.trTaskId_gg00,
                                                  TransContext.trWaitContext_gg00);
                    WaitInserted = true;
                }
            }
            else
            {
                pJob = m_pFirstVacancy;
                
                m_pFirstVacancy = pJob->GetNext();
                
                if (NULL == m_pFirstVacancy)
                {
                    Rst_IoSeqEntry *pCurrIoSeq = pJob->GetIoSeqEntry()->GetNext();
                    
                    if (NULL != pCurrIoSeq)
                    {
                        // every IoSeqEntry has at least one job entry (scan of IoSeqList not necessary) 
                        m_pFirstVacancy = pCurrIoSeq->GetFirstJobEntry ();
                    }            
                }
            }
        }
        
        // outside RedoSync region
        
        if ( NULL == pJob )
        {
            if (WaitInserted)
                vsuspend (TransContext.trTaskId_gg00, LOG_WAIT_FOR_VACANT_ACTION);
        }
        else
        {
            if ( m_redoIsAborted )
                break;
                
            TransContext.trIndex_gg00        = pJob->GetTransIndex();
            TransContext.trWriteTransId_gg00 = pJob->GetTransaction().GetRedoTransNo();
            
            int           checkindex  = TransContext.trIndex_gg00;
            tgg91_TransNo redoTransNo = TransContext.trWriteTransId_gg00;

			pJob->GetTransaction().InitializeForRedo (TransContext);

			if ( LogTrans_Trace.TracesLevel(6) )
				Kernel_VTrace() << "REDOTRAFFIC: start T" << redoTransNo.gg90GetInt4()
								<< ", EOTSEQ: " << pJob->GetIoSeqEntry()->GetIoSeq()
								<< ", KIND: " << (pJob->GetUndoOrRedo()==Log_Commit?"redo":"undo")
                                << NewLine;
                                
            switch ( pJob->GetUndoOrRedo() )
            {
            case Log_Commit:

                if ( pJob->GetTransaction().EntriesToRedoExist() )
                {
                    Log_Transaction::RedoResult RedoResult;
                    
                    RedoResult = pJob->GetTransaction().Redo();
                    
                    if ( TransContext.trError_gg00 == e_ok
                         &&
                         Log_Transaction::redoOk == RedoResult )
                    {
                        // synchronize with other commits
                        if ( CheckIoSeq (TransContext.trTaskId_gg00,
                                         TransContext.trWaitContext_gg00,
                                         pJob->GetIoSeqEntry()->GetIoSeq()) )
                            WaitForIoSeq (TransContext.trTaskId_gg00);
                    }
                    else
                    {
                        RTE_Message( Restart_Exception(__CONTEXT__,RST_REDO_ERROR,
                                                   SAPDB_ToString(TransContext.trWriteTransId_gg00.gg90GetInt4(),_T_d),
                                                   "committed",
                                                   SAPDB_ToString(RedoResult,_T_d),
                                                   SAPDB_ToString(TransContext.trError_gg00,_T_d) ) );
                        if ( TransContext.trError_gg00 == e_ok
                             &&
                             Log_Transaction::redoOk != RedoResult )
                        {
                            TransContext.trError_gg00 = e_log_error;
                        }
                    }
                }
                break;

            case Log_Rollback:

                if ( pJob->GetTransaction().UndoEntriesExist() )
                {
                    Log_Transaction::RollbackResult RollbackResult;

                    if ( CheckIoSeq (TransContext.trTaskId_gg00,
                                     TransContext.trWaitContext_gg00,
                                     pJob->GetIoSeqEntry()->GetIoSeq()) )
                        WaitForIoSeq (TransContext.trTaskId_gg00);
                    
                    RollbackResult = pJob->GetTransaction().Rollback ( Log_Transaction::endOfTrans,
                                                                       LOG_NIL_SUBTRANS_NO );
                    
                    if ( TransContext.trError_gg00 != e_ok
                         ||
                         Log_Transaction::undoOk != RollbackResult )
                    {
                        RTE_Message( Restart_Exception(__CONTEXT__,RST_REDO_ERROR,
                                                   SAPDB_ToString(TransContext.trWriteTransId_gg00.gg90GetInt4(),_T_d),
                                                   "rollbacked",
                                                   SAPDB_ToString(RollbackResult,_T_d),
                                                   SAPDB_ToString(TransContext.trError_gg00,_T_d) ) );
                        if ( TransContext.trError_gg00 == e_ok
                             &&
                             Log_Transaction::undoOk != RollbackResult )
                        {
                            TransContext.trError_gg00 = e_log_error;
                        }
                    }
                }
                break;
                
            default:
                RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoTrafficControl::ExecuteJobs() invalid redo type") );
            }
            
            if ( e_ok == TransContext.trError_gg00
                 &&
                 checkindex != TransContext.trIndex_gg00 )
                RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoTrafficControl::ExecuteJobs() transindex mismatch") );


            if ( e_ok == TransContext.trError_gg00 ) // PTS 1132836 UH 2004-12-03 do not remove trans on error
            {
                // The following sequence of CleanUp is important  !!!
                
                RemoveJob (TransContext.trTaskId_gg00, pJob);
                
                pJob->GetTransaction().Delete();
    
                if (e_ok != TransContext.trError_gg00)
                    RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                               "Rst_RedoTrafficControl::ExecuteJobs() LogTrans.Delete()") );
    
                k51remove_locks                 ( TransContext.trTaskId_gg00,
                                                  TransContext.trIndex_gg00,
                                                  TransContext.trState_gg00 );
                k51del_transentry_from_locklist ( TransContext.trTaskId_gg00,
                                                  TransContext.trIndex_gg00 );
    
                // really drop all dropped files
                k53server_drop_aux_files (TransContext);
    
                TransContext.trWriteTransId_gg00.gg90SetNil();
                TransContext.trLogContext_gg00 = NULL;
                TransContext.trIndex_gg00      = cgg_nil_transindex;
            }
        }
    }

    if ( e_ok != TransContext.trError_gg00 )
        Kernel_IAdminBackupRecovery::Instance().AbortRedoLog(TransContext.trError_gg00);
}

//----------------------------------------------------------------------------
Log_IOSequenceNo Rst_RedoTrafficControl::GetLastIOSequence (tsp00_TaskId taskid) const
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::GetLastIOSequence", LogTrans_Trace, 5);
    Rst_RedoSync RedoSync (taskid); // ENTER REGION
    return m_IoSeqList.GetLastIoSeq();
}

//----------------------------------------------------------------------------

void Rst_RedoTrafficControl::InsertJob (tsp00_TaskId      LogReaderTaskId,
                                        tgg00_TransIndex  TransIndex,
                                        Log_IOSequenceNo  EndOfTransIoSeq,
                                        Log_ActionType    UndoOrRedo,
                                        Log_IOSequenceNo  oldestKnownEOTSequence, // PTS 1124684 UH 2003-10-16
                                        bool              checkAscendingSequence, // PTS 1113641 UH 2002-01-25
                                        Log_Transaction  &Transaction,
                                        bool             &IsOk)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::InsertJob", LogTrans_Trace, 5);

    if ( EndOfTransIoSeq.IsInvalid() )
    {
    	Kernel_VTrace() << "REDOTRAFFIC: insert T" << Transaction.GetRedoTransNo().gg90GetInt4()
        	            << ", EOTSEQ: " << EndOfTransIoSeq
            	        << ", KIND: " << (UndoOrRedo==Log_Commit?"redo":"undo")
                        << NewLine;
        RTE_Crash( Restart_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED, "EndOfTransIoSeq must not be NIL") );
    }

    CheckJobEntry (LogReaderTaskId, TransIndex, EndOfTransIoSeq, Transaction.GetRedoTransNo());
    
    IsOk = false;
    
    Rst_RedoSync RedoSync (LogReaderTaskId); // ENTER REGION
    
	if ( LogTrans_Trace.TracesLevel(6) )
    	Kernel_VTrace() << "REDOTRAFFIC: insert T" << Transaction.GetRedoTransNo().gg90GetInt4()
        	            << ", EOTSEQ: " << EndOfTransIoSeq
            	        << ", KIND: " << (UndoOrRedo==Log_Commit?"redo":"undo")
                        << NewLine;

    Rst_JobEntry *pJobEntry = GetJobEntry (TransIndex);
    
    if (NULL == pJobEntry)
    {
        Kernel_VTrace() << "REDOTRAFFIC: no free job entry";
        return;
    }

    if ( m_OldestKnownEOTSequence.IsInvalid() )
        m_OldestKnownEOTSequence = oldestKnownEOTSequence; // PTS 1124684 UH 2003-10-16
        
    Rst_IoSeqEntry *pEotIoSeqEntry = m_IoSeqList.SearchEntry(EndOfTransIoSeq);  // PTS 1124684 UH 2003-10-16
    
    if ( 0 == pEotIoSeqEntry )
    {
        // EndOfTransIoSeq is a new io sequence which must be added to the list
        // at the correct position
        if ( checkAscendingSequence // PTS 1113641 UH 2002-01-25
             &&
             m_IoSeqList.GetLastIoSeq().IsValid() // if list is empty this is nil
             // PTS 1120201 UH 2003-02-17
             &&
             Log_IOSequenceNo::LeftIsYounger(m_IoSeqList.GetLastIoSeq(), EndOfTransIoSeq,
                                             m_OldestKnownEOTSequence) // PTS 1124684 UH 2003-10-16
           )
        {
            Kernel_VTrace() << "EndOfTransIoSeq: " << EndOfTransIoSeq // PTS 1113641 UH 2002-01-25
                            << " > lastIOSequence: " << m_IoSeqList.GetLastIoSeq()
                            << " m_OldestKnownEOTSequence = " << m_OldestKnownEOTSequence;
            Kernel_VTrace() << "REDOTRAFFIC: Insert IO SEQUENCE FAILED";
            return;
        }
        
        pEotIoSeqEntry = GetAndRemoveFromFreeIoSeqList ();
        
        if ( 0 == pEotIoSeqEntry )
        {
            Kernel_VTrace() << "REDOTRAFFIC: no free ioseqlist entry";
            return;
        }
        
        pEotIoSeqEntry->Init(EndOfTransIoSeq);
        
        // PTS 1124684 UH 2003-10-16 use Insert() instead of Append()
        m_IoSeqList.Insert (pEotIoSeqEntry, m_OldestKnownEOTSequence);
    }

    pJobEntry->Init(UndoOrRedo, pEotIoSeqEntry, Transaction);
    
    pEotIoSeqEntry->AppendJobEntry (pJobEntry); // PTS 1124684 UH 2003-10-16
    
    if ( 0 == m_pFirstVacancy)
        m_pFirstVacancy = pJobEntry;

    if ( checkAscendingSequence ) // PTS 1124684 UH 2003-10-16
        m_VacantJobWait.ResumeAll();

    IsOk = true;
    
    // The global locklist must use now the Log_Transaction::m_Context of pJobEntry
    // This must be synchronized with the savepoint coordinator.

    kb50EnterTransRegion   (LogReaderTaskId, TransIndex);
    k50RegisterTransaction (TransIndex, &pJobEntry->GetTransaction());
    kb50LeaveTransRegion   (LogReaderTaskId, TransIndex);
    
    ++m_inserted;

    if ( LogTrans_Trace.TracesLevel(7) )
        WriteToTrace("End of InsertJob()");
}
//----------------------------------------------------------------------------
void Rst_RedoTrafficControl::CheckJobEntry (tsp00_TaskId         taskid,
                                            tgg00_TransIndex     index,
                                            Log_IOSequenceNo     iosequence,
                                            const tgg91_TransNo &transno)
{
    Rst_RedoSync    RedoSync (taskid); // ENTER REGION
    Rst_IoSeqEntry *pEotIoSeqEntry = NULL;
    Rst_JobEntry   *pJobEntry      = NULL;
    
    pEotIoSeqEntry = m_IoSeqList.GetFirst();

	while ( pEotIoSeqEntry != NULL )
	{
		pJobEntry = pEotIoSeqEntry->GetFirstJobEntry();
		while ( pJobEntry != NULL )
		{
            if ( index == pJobEntry->GetTransIndex() )
            {
                Kernel_VTrace() << "T"              << transno.gg90GetInt4()
                                << ", index: "      << index
                                << ", iosequence: " << iosequence << NewLine;
                WriteToTrace("Rst_RedoTrafficControl::CheckJobEntry() duplicate index");
                RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoTrafficControl::CheckJobEntry() duplicate index.") );
            }
            if ( transno == pJobEntry->GetTransaction().GetRedoTransNo() )
            {
                Kernel_VTrace() << "T"              << transno.gg90GetInt4()
                                << ", index: "      << index
                                << ", iosequence: " << iosequence << NewLine;
                WriteToTrace("Rst_RedoTrafficControl::CheckJobEntry() duplicate transno");
                RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoTrafficControl::CheckJobEntry() duplicate transno") );
            }
			pJobEntry = pJobEntry->GetNext();
		}
		pEotIoSeqEntry = pEotIoSeqEntry->GetNext();
	}
}
//----------------------------------------------------------------------------
void Rst_RedoTrafficControl::WriteToTrace (const char *title) const
{
    Rst_IoSeqEntry *pEotIoSeqEntry = NULL;
    Rst_JobEntry   *pJobEntry      = NULL;
    
	if ( title != NULL )
		Kernel_VTrace() << title;

    pEotIoSeqEntry = m_IoSeqList.GetFirst();


	Kernel_VTrace() << "REDOLIST: (oldestknown=" << m_OldestKnownEOTSequence << ")";

	while ( pEotIoSeqEntry != NULL )
	{
        Kernel_VTrace() << "=== IOSEQ ===: " << pEotIoSeqEntry->GetIoSeq();

		pJobEntry = pEotIoSeqEntry->GetFirstJobEntry();
		while ( pJobEntry != NULL )
		{
			Kernel_VTrace() << "T" << pJobEntry->GetTransaction().GetRedoTransNo().gg90GetInt4()
				            << ", index: " << pJobEntry->GetTransIndex()
							<< (pJobEntry->GetUndoOrRedo()==Log_Commit?", redo":", undo")
                            << (m_pFirstVacancy==pJobEntry?" => FirstVacant":"");
			pJobEntry = pJobEntry->GetNext();
		}
		pEotIoSeqEntry = pEotIoSeqEntry->GetNext();
	}
}
//----------------------------------------------------------------------------
void Rst_RedoTrafficControl::RemoveJob (tsp00_TaskId  RedoTaskId,
                                        Rst_JobEntry *pJobEntry)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::RemoveJob", LogTrans_Trace, 5);

    Rst_RedoSync RedoSync (RedoTaskId);
    
	if ( LogTrans_Trace.TracesLevel(6) )
    	Kernel_VTrace() << "REDOTRAFFIC: remove T" << pJobEntry->GetTransaction().GetRedoTransNo().gg90GetInt4()
        	            << ", EOTSEQ: " << pJobEntry->GetIoSeqEntry()->GetIoSeq()
            	        << ", KIND: " << (pJobEntry->GetUndoOrRedo()==Log_Commit?"redo":"undo");
    
    Rst_IoSeqEntry *pEotSeqEntry = pJobEntry->GetIoSeqEntry();
    
    tgg00_TransIndex transindex = pJobEntry->GetTransIndex();
    kb50EnterTransRegion   (RedoTaskId, transindex);
    k50RegisterTransaction (transindex, NULL);
    kb50LeaveTransRegion   (RedoTaskId, transindex);

    pEotSeqEntry->RemoveJobEntry (pJobEntry);
 
    if ( pEotSeqEntry->IsEmptyJobList() )
    {
        pEotSeqEntry->TryResumeTasks();
        m_IoSeqList.Remove (pEotSeqEntry);
        PutIntoFreeIoSeqList (pEotSeqEntry); // added by UH 2001-07-03
    }
    if ( m_IoSeqList.IsEmpty()
         &&
         m_TaskWaitingForEmptyList != cgg_nil_pid )
    {
        vresume(m_TaskWaitingForEmptyList);
    }

    ++m_removed;
    // WriteToTrace("REDOTRAFFIC: after remove");
}
//----------------------------------------------------------------------------
SAPDB_UInt Rst_RedoTrafficControl::GetMaximumNumberOfTransactions() const
{
    // PTS 1134103 UH 2005-03-07 new
    return m_NumberOfTransEntries;
}
//----------------------------------------------------------------------------
bool
Rst_RedoTrafficControl::GetTransactions (tsp00_TaskId            taskid,
                                         Rst_Transactions       &listOfTransToSkip) const
{
    // PTS 1134103 UH 2005-03-07 new
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoTraffic::GetTransactions", LogTrans_Trace, 5);

    Rst_RedoSync RedoSync (taskid); // ENTER REGION

    if ( ! m_RedoProcessingDisabled )
	{
        WriteToTrace ("Rst_RedoTrafficControl::GetTransactions: m_RedoProcessingDisabled must be true");
        RTE_Crash ( Restart_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED, "m_RedoProcessingDisabled must be true"));
	}

	for ( Rst_IoSeqEntry *pEotIoSeqEntry = m_IoSeqList.GetFirst();
          pEotIoSeqEntry != 0;
          pEotIoSeqEntry = pEotIoSeqEntry->GetNext() )
	{
		for ( Rst_JobEntry *pJobEntry = pEotIoSeqEntry->GetFirstJobEntry();
              pJobEntry != 0;
              pJobEntry = pJobEntry->GetNext() )
		{
            if ( ! listOfTransToSkip.Add (pJobEntry->GetTransaction().GetRedoTransNo()) )
            {
                WriteToTrace ("Rst_RedoTrafficControl::GetTransactions: Adding transno to list failed");
                RTE_Crash ( Restart_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED, "Adding transno to list") );
            }
		}
	}
    return true;
}
/*===========================================================================*
*  METHODS of Rst_RedoAllocateTrafficControl                                 *
*============================================================================*/
Rst_RedoAllocateTrafficControl::~Rst_RedoAllocateTrafficControl ()
{
    Rst_RedoTrafficControl *pRedoTrafficControl = Rst_RedoTrafficControl::InstancePtr();
    
    if (NULL == pRedoTrafficControl) return;
    
    SAPDBMem_IRawAllocator &RawAllocator = pRedoTrafficControl->GetRawAllocatorUsed();
    
    destroy (pRedoTrafficControl, RawAllocator);

    Rst_RedoTrafficControl::InvalidateInstancePtr ();
}
//----------------------------------------------------------------------------
void Rst_RedoAllocateTrafficControl::Allocate (SAPDBMem_IRawAllocator &RawAllocator,
                                               volatile bool          &redoIsAborted,
                                               SAPDB_UInt             &inserted,
                                               SAPDB_UInt             &removed,
                                               bool                   &IsOk)
{    
    Rst_RedoTrafficControl::AllocateInstance ( RawAllocator,
                                               redoIsAborted,
                                               inserted,
                                               removed,
                                               IsOk);
    
    if ( !IsOk ) return;
    
    Rst_RedoTrafficControl::InstancePtr()->AllocateContainer (IsOk);
}
