/*!***************************************************************************

  module      : RTEIO_DevSpaceInfo.cpp

  -------------------------------------------------------------------------

  responsible : JoergM

  special area: InputOutput
  description : Wrapper of parallel devspace formatting

  last changed: 2001-04-05  16:11
  see also    : 

  -------------------------------------------------------------------------



    ========== licence begin  GPL
    Copyright (c) 2001-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




*****************************************************************************/


/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include "RunTime/InputOutput/RTEIO_DevSpaceInfo.hpp"
#include "RunTime/RTE_MessageList.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Topic.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include "RunTime/MemoryManagement/RTEMem_Allocator.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"
#include "heo07.h"
#include "heo58.h"
#include "heo47.h"
#include "RunTime/RTE_MessageList.hpp"
#include "RunTime/RTE_Message.hpp"

#if !(defined(_WIN32))  /*&if $OSSPEC not in [ WIN32 ]*/ 
#include "hen40.h"      /* nocheck */
#endif                  /*&endif*/

/* external defined trace topic */
extern SAPDBTrace_Topic Devspace_Trace;

externC void vdevsizeWithImage ( tsp00_Int4                  type_spec_devno,
                                 tsp2_dev_type_Param         devtype,
                                 tsp00_Int4    VAR_VALUE_REF devcapacity,
                                 tsp00_TaskId                pidResponsibleForCall,
                                 char                       *formattingPageImage,
                                 tsp00_ErrText VAR_ARRAY_REF errtext,
                                 tsp00_Bool    VAR_VALUE_REF pOk  );

/*===========================================================================*
 *  typedefs                                                                 *
 *===========================================================================*/

class FormattingThreadInfo 
{
public:
    FormattingThreadInfo():devSpaceVector(RTEMem_Allocator::Instance()) {}

    RTEIO_DevSpaceInfoVector  devSpaceVector;
    tsp00_TaskId              pidResponsibleForCall;
    teo07_Thread              formatterThread;
    SAPDBErr_MessageList      errList;
    bool                      threadStarted;
    bool                      forceSelfIO;
    bool                      ok;
};

typedef Container_Vector<SAPDB_Int4> DeviceIndexVector;

/*===========================================================================*
 *  static prototypes                                                        *
 *===========================================================================*/

/*!
  Function: AnalyzeDevspaceVector
  Description: Find each devspace physical disk

  Build up a vector of physicalDeviceIndices comparing and calculate the number of threads
  neededs (which is the number of unique devices).
 */
static void AnalyzeDevspaceVector( RTEIO_DevSpaceInfoVector &   infoVector,
                                   DeviceIndexVector &          physicalDeviceIndexVector,
                                   SAPDB_Int4 &                 numberOfThreadsNeeded, 
                                   SAPDB_UInt                   formattingMode);

/*!
  Function: FindNumberOfDevspaceOnDevice
  Description: Count the number of identical entries beginning with first.

  Return value: Return the number of devspaces on device
 */
static SAPDB_Int4 FindNumberOfDevspaceOnDevice( DeviceIndexVector & physicalDeviceIndexVector, 
                                                SAPDB_Int4          first);

/*!
  Function: FindDevspacesOnSameDevice
  Description: Copy references to devspace info for one device

  Updates the first index to the next device in the physicalDeviceIndexVector
 */
static void FindDevspacesOnSameDevice( RTEIO_DevSpaceInfoVector & infoVector, 
                                       DeviceIndexVector &        physicalDeviceIndexVector, 
                                       RTEIO_DevSpaceInfoVector & threadInfoVector, 
                                       SAPDB_Int4               & first);

/*!
  Function: StartFormatterThread
  Description: Start the formatter for a specific device
  
  The formatter gets the list of devspaces on that device.
  Any error during formatting is stored in FormattingThreadInfo

  Return value: true start succeeded, false start failed
 */
static bool StartFormatterThread(FormattingThreadInfo &threadInfo);

/*!
  Function: RTEIO_IsOnSamePhysicalDevice
  Description: Test if two devspaces reside on same physical device

  Arguments:    firstDevSpace  [in]     devspace info for first devspace
                secondDevSpace [in]     devspace to compare with
                formattingMode [in]     mode of formatting: RTE_FORMATTING_MODE_SERIAL/PARALLEL/AUTO

  Return value: true if on some physical device, false if not
 */
static bool RTEIO_IsOnSamePhysicalDevice (RTEIO_DevSpaceInfo   *firstDevSpace, 
                                          RTEIO_DevSpaceInfo   *secondDevSpace, 
                                          SAPDB_UInt            formattingMode);

/*!
  Function: RTEIO_UpdatePhysicalDeviceInformation
  Description: Update information about physical devices of known devspaces
 */
static void RTEIO_UpdatePhysicalDeviceInformation(RTEIO_DevSpaceInfoVector &infoVector);
/*!
  Function: DoFormatDevspace
  Description: Does the real formatting action

  Arguments: logicalDevNo [in] logicat device number
             devType [in] device type
             devCapacityInPages [inout] device capacity (set if 0)
             pidResponsibleForCall [in] the original task initiating parallel formatting
             formattingPageImage [in] NULL if no page image, else image replicated on each page
             errTextc [out] errort text if false is returned
  Return value: true if call succeeded, false if call failed
 */
static bool DoFormatDevspace( SAPDB_Int4          logicalDevNo,
                              tsp2_dev_type_Param devType,
                              SAPDB_UInt4        &devCapacityInPages,
                              tsp00_TaskId        pidResponsibleForCall, 
                              const SAPDB_Byte   *formattingPageImage,
                              tsp00_ErrTextc     &errTextc);

/*===========================================================================*
 *  CLASSES, STRUCTURES, TYPES, UNIONS ...                                   *
 *===========================================================================*/

 /*!
  Function: RTEIO_FormatDevspaceVector
  Description: Format all devspaces given in the vector in parallel

  The vector is inspected and a physical index vector is setup. This index vector
  contains a unique index for each physical device. The devspaces on each device 
  build a group, that is formatted in serial by a worker thread. For each group
  a worker thread is started, that works in parallel. The last group is handled
  by the routine itself in parallel with the worker threads. After all worker threads
  finished their work, the call returns. During formatting the devspace capacity 
  information may be updated.
  
  In case of error the combined errList of all errors reported by the worker threads is returned.

  A non starting thread is not considered an error, but the job is done by the routine
  itself.

  Arguments:    taskId [in]             the task id of the caller
                formattingMode [in]     mode of formatting: RTE_FORMATTING_MODE_SERIAL/PARALLEL/AUTO
                infoVector [inout]      vector of devspaces to format
                errList [out]           message list to fill

  Return value: true on success for all, false on any error (errList is filled)
 */
bool RTEIO_FormatDevspaceVector(RTE_TaskId taskId,
                                SAPDB_UInt formattingMode,
                                RTEIO_DevSpaceInfoVector &infoVector,
                                SAPDBErr_MessageList &errList)
{
    SAPDBTRACE_ROUTINE_DEBUG("RTEIO_FormatDevspaceVector", Devspace_Trace, 1);

    if ( infoVector.IsEmpty() )
    {
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "nothing to do");
        return true;
    }

    //---------------------------------------------------------------------------------------------
    // Analyze the given infoVector to get physicalDeviceIndex vector and number of threads needed
    DeviceIndexVector physicalDeviceIndexVector(RTEMem_Allocator::Instance());
    if ( !physicalDeviceIndexVector.Initialize(infoVector.GetSize()) )
    {
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "allocation failed");
        errList = SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_ALLOCATION_FAILED, "DeviceIndexVector" );
        return false;
    }

    SAPDB_Int4 numberOfThreadsNeeded;
    AnalyzeDevspaceVector(infoVector, physicalDeviceIndexVector, numberOfThreadsNeeded, formattingMode);

    //---------------------------------------------------------------------------------------------
    // Build formatting thread info vector and try to start each thread
    Container_Vector<FormattingThreadInfo> formatterVector(RTEMem_Allocator::Instance());
    if ( !formatterVector.Initialize(numberOfThreadsNeeded) )
    {
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "allocation of " << numberOfThreadsNeeded << " failed");
        errList = SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_ALLOCATION_FAILED, "ThreadVector" );
        return false;
    }

    SAPDB_Int4 threadIndex;
    SAPDB_Int4 whichFirst = 0;
    for ( threadIndex = 0; threadIndex < numberOfThreadsNeeded; threadIndex++ )
    {
        formatterVector[threadIndex].threadStarted = false;
        formatterVector[threadIndex].forceSelfIO = false;

        SAPDB_Int4 numberOfDevspaceOnDevice = FindNumberOfDevspaceOnDevice(physicalDeviceIndexVector, whichFirst);

        if ( !formatterVector[threadIndex].devSpaceVector.Initialize(numberOfDevspaceOnDevice) )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 1, "allocation failed");
            formatterVector[threadIndex].errList = SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_ALLOCATION_FAILED, "DevSpaceVector" );
            formatterVector[threadIndex].ok = false;
        }
        else
        {
            FindDevspacesOnSameDevice(infoVector, physicalDeviceIndexVector, formatterVector[threadIndex].devSpaceVector, whichFirst);

            formatterVector[threadIndex].pidResponsibleForCall = (tsp00_TaskId)taskId;
            formatterVector[threadIndex].ok = true;

            // last thread is not started (handled by caller...)
            if ( ( threadIndex == (numberOfThreadsNeeded - 1) )
              || !StartFormatterThread(formatterVector[threadIndex]) )
            {
                formatterVector[threadIndex].forceSelfIO = true;
            }
            else
            {
                formatterVector[threadIndex].threadStarted = true;
            }
        }
    }

    //---------------------------------------------------------------------------------------------
    // handle IO for those threads that did not start
    for ( threadIndex = 0; threadIndex < numberOfThreadsNeeded; threadIndex++ )
    {
        if ( formatterVector[threadIndex].forceSelfIO )
        {
            FormattingThreadInfo & threadInfo = formatterVector[threadIndex];

            RTEIO_DevSpaceInfoVector &    infoVector = threadInfo.devSpaceVector;
            RTEIO_DevSpaceInfoVector::SizeType  size = infoVector.GetSize();
            RTEIO_DevSpaceInfoVector::IndexType which;

            tsp00_Int4      threadPrio;
            teo07_ThreadErr threadOk;
            threadOk = sqlgetmythreadpriority(threadPrio);
            if ( THR_OK_EO07 != threadOk )
            {
                SAPDBTRACE_WRITELN(Devspace_Trace, 7, "getting formatting thread priority failed" );
            }
            else
            {
                threadOk = sqlsetmythreadpriority( THR_PRIO_IDLE_EO07 );
                if ( THR_OK_EO07 != threadOk )
                {
                    SAPDBTRACE_WRITELN(Devspace_Trace, 7, "setting formatting thread idle priority failed" );
                }
            }

            for ( which = 0; which < size; which++ )
            {
                RTEIO_DevSpaceInfo & devInfo = *(infoVector[which]);
                SAPDB_UInt4 devCapacityInPages = devInfo.GetCapacityInPages();
                tsp00_ErrTextc errTextc;

                threadInfo.ok = DoFormatDevspace( devInfo.GetLogicalDevNo(),
                                                  devInfo.GetType(),
                                                  devCapacityInPages,
                                                  threadInfo.pidResponsibleForCall,
                                                  devInfo.GetFormattingPageImage(),
                                                  errTextc);
                if ( !threadInfo.ok )
                {
                    SAPDBTRACE_WRITELN(Devspace_Trace, 7, which << " formatting failed: " << errTextc);
                    threadInfo.errList = SAPDBErr_MessageList(RTE_CONTEXT, 0, RTEERR_FORMATTING_FAILED, errTextc.asCharp() );
                    break;
                }
                else
                {
                    devInfo.SetCapacityInPages(devCapacityInPages);
                }
            }

            if ( THR_OK_EO07 == threadOk )
            {
                threadOk = sqlsetmythreadpriority(threadPrio);
                if ( THR_OK_EO07 != threadOk )
                {
                    SAPDBTRACE_WRITELN(Devspace_Trace, 7, "resetting formatting thread priority failed" );
                }
            }
        }
    }

    //---------------------------------------------------------------------------------------------
    // Wait only for those threads that have been started
    for ( threadIndex = 0; threadIndex < numberOfThreadsNeeded; threadIndex++ )
    {
        if ( formatterVector[threadIndex].threadStarted )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 1, "waiting for formatter thread " << threadIndex);

            SAPDB_Int4 joinStatus;
            tsp00_ErrTextc threadErrtext;
            teo07_ThreadErr threadOk;
            sqljointhread(formatterVector[threadIndex].formatterThread, &joinStatus, threadErrtext, &threadOk );
            if ( threadOk != THR_OK_EO07 )
            {
                SAPDBTRACE_WRITELN(Devspace_Trace, 1, "formatter thread " << threadIndex << " join error " << &threadErrtext[0] );
            }
        }
    }

    bool totalResult = true;

    //---------------------------------------------------------------------------------------------
    // collect status information and build a complete list for caller
    for ( threadIndex = 0; threadIndex < numberOfThreadsNeeded; threadIndex++ )
    {
        if ( !formatterVector[threadIndex].ok )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 1, "formatter thread " << threadIndex << " reported error");
            totalResult = false;
            errList.AppendNewMessage(formatterVector[threadIndex].errList);
        }
    }

    return totalResult;
}


/*!---------------------------------------------------------------------------
  Function: AnalyzeDevspaceVector
  Description: Find each devspace physical disk

  Build up a vector of physicalDeviceIndices comparing and calculate the number of threads
  neededs (which is the number of unique devices).
 */
static void AnalyzeDevspaceVector( RTEIO_DevSpaceInfoVector &   infoVector,
                                   DeviceIndexVector &          physicalDeviceIndexVector,
                                   SAPDB_Int4 &                 numberOfThreadsNeeded,
                                   SAPDB_UInt                   formattingMode)
{
    SAPDB_Int4 size = infoVector.GetSize();
    SAPDB_Int4 index0;
    SAPDB_Int4 index1;

    physicalDeviceIndexVector[0] = 0;
    numberOfThreadsNeeded = 1;

    if (formattingMode == RTE_FORMATTING_MODE_AUTO)
    {
        RTEIO_UpdatePhysicalDeviceInformation(infoVector);
    }

    for ( index0 = 1; index0 < size; index0++ )
    {
        for ( index1 = 0; index1 < index0; index1++ )
        {
            if ( physicalDeviceIndexVector[index1] == index1
              && RTEIO_IsOnSamePhysicalDevice( infoVector[index0], infoVector[index1], formattingMode ) )
            {
                SAPDBTRACE_WRITELN(Devspace_Trace, 7, index0 << " on same device as " << index1);
                physicalDeviceIndexVector[index0] = index1;
                break;
            }
            else
            {
                SAPDBTRACE_WRITELN(Devspace_Trace, 9, index0 << " on other device as " << index1);
            }
        }
        if ( index1 == index0 )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, index1 << " on its own device");
            physicalDeviceIndexVector[index0] = index0;
            ++numberOfThreadsNeeded;
        }
    }
}


/*!---------------------------------------------------------------------------
  Function: FindNumberOfDevspaceOnDevice
  Description: Count the number of identical entries beginning with first.

  Return value: Return the number of devspaces on device
 */
static SAPDB_Int4 FindNumberOfDevspaceOnDevice( DeviceIndexVector & physicalDeviceIndexVector, 
                                                SAPDB_Int4        first)
{
    SAPDBTRACE_ROUTINE_DEBUG("FindNumberOfDevspaceOnDevice", Devspace_Trace, 5);

    SAPDB_Int4 size = physicalDeviceIndexVector.GetSize();
    SAPDB_Int4 which;
    SAPDB_Int4 numberOfDevspacesOnDevice = 1; // first is always found

    for ( which = (first+1); which < size; which++ )
    {
        if ( physicalDeviceIndexVector[which] == physicalDeviceIndexVector[first] )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, which << " on same device as " << first);
            ++numberOfDevspacesOnDevice;
        }
        else
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 9, which << " on other device as " << first);
        }
    }

    SAPDBTRACE_WRITELN(Devspace_Trace, 7, numberOfDevspacesOnDevice << " on same device as " << first);

    return numberOfDevspacesOnDevice;
}


/*!---------------------------------------------------------------------------
  Function: FindDevspacesOnSameDevice
  Description: Copy references to devspace info for one device

  Updates the first index to the next device in the physicalDeviceIndexVector
 */
static void FindDevspacesOnSameDevice( RTEIO_DevSpaceInfoVector & infoVector, 
                                       DeviceIndexVector &          physicalDeviceIndexVector, 
                                       RTEIO_DevSpaceInfoVector & threadInfoVector, 
                                       SAPDB_Int4               & first)
{
    SAPDBTRACE_ROUTINE_DEBUG("FindDevspacesOnSameDevice", Devspace_Trace, 5);

    SAPDB_Int4 size = physicalDeviceIndexVector.GetSize();
    SAPDB_Int4 which;
    SAPDB_Int4 tmpWhich;
    SAPDB_Int4 nextFirst = size;

    threadInfoVector[0] = infoVector[first];

    for ( tmpWhich = 1, which = (first+1); which < size; which++ )
    {
        if ( physicalDeviceIndexVector[which] == physicalDeviceIndexVector[first] )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, which << " on same device as " << first);
            threadInfoVector[tmpWhich] = infoVector[which];
            tmpWhich++;
        }
        else
        {
            if ( nextFirst > which )
            {
                nextFirst = which;
                SAPDBTRACE_WRITELN(Devspace_Trace, 7, which << " choosen as next first");
            }
            SAPDBTRACE_WRITELN(Devspace_Trace, 9, which << " on other device as " << first);
        }
    }

    first = nextFirst;
}

/*!---------------------------------------------------------------------------
  Function: FormatterWorker
  Description: Main routine of formatter worker
  
  The formatter gets the list of devspaces on that device.
  Any error during formatting is stored in FormattingThreadInfo

  Return value: returns its argument
 */
extern "C" void *FormatterWorker(void *arg)
{
    FormattingThreadInfo & threadInfo = *(FormattingThreadInfo *)arg;

    RTEIO_DevSpaceInfoVector &    infoVector = threadInfo.devSpaceVector;
    RTEIO_DevSpaceInfoVector::SizeType  size = infoVector.GetSize();
    RTEIO_DevSpaceInfoVector::IndexType which;

    /* Let the thread run on idle priority if possible */
    (void)sqlsetmythreadpriority(THR_PRIO_IDLE_EO07); 

    for ( which = 0; which < size; which++ )
    {
        RTEIO_DevSpaceInfo & devInfo = *(infoVector[which]);
        SAPDB_UInt4 devCapacityInPages = devInfo.GetCapacityInPages();
        tsp00_ErrTextc errTextc;

        threadInfo.ok = DoFormatDevspace( devInfo.GetLogicalDevNo(),
                                          devInfo.GetType(),
                                          devCapacityInPages,
                                          threadInfo.pidResponsibleForCall,
                                          devInfo.GetFormattingPageImage(),
                                          errTextc);
        if ( !threadInfo.ok )
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, which << " formatting failed: " << errTextc);
            threadInfo.errList = SAPDBErr_MessageList(RTE_CONTEXT, 0, RTEERR_FORMATTING_FAILED, errTextc.asCharp() );
            sqlendthread(which+1);
            break;
        }
        else
        {
            devInfo.SetCapacityInPages(devCapacityInPages);
        }
    }

    sqlendthread(0);

    return arg;
}

/*!---------------------------------------------------------------------------
  Function: StartFormatterThread
  Description: Start the formatter for a specific device
  
  The formatter gets the list of devspaces on that device.
  Any error during formatting is stored in FormattingThreadInfo

  Return value: true start succeeded, false start failed
 */
static bool StartFormatterThread(FormattingThreadInfo &threadInfo)
{
    tsp00_ErrTextc threadErrtext;
    teo07_ThreadErr threadOk;

#define FORMATTER_THREAD_STACK_SIZE (512*1024)
    sqlbeginthread(FORMATTER_THREAD_STACK_SIZE,
                   FormatterWorker,
                   &threadInfo,
                   THR_CREATE_NORMAL_EO07,
                   &threadInfo.formatterThread,
                   threadErrtext,
                   &threadOk );
    if ( threadOk != THR_OK_EO07 )
    {
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "start thread failed: " << &threadErrtext[0] );
        return false;
    }
    return true;
}

/*!---------------------------------------------------------------------------
  Function: DoFormatDevspace
  Description: Does the real formatting action

  Implemented as a wrapper for pascal interface coded vdevsizeWithImage

  Arguments: logicalDevNo [in] logicat device number
             devType [in] device type
             devCapacityInPages [inout] device capacity (set if 0)
             pidResponsibleForCall [in] the original task initiating parallel formatting
             formattingPageImage [in] NULL if no page image, else image replicated on each page
             errTextc [out] errort text if false is returned
  Return value: true if call succeeded, false if call failed
 */
static bool DoFormatDevspace( SAPDB_Int4          logicalDevNo,
                              tsp2_dev_type_Param devType,
                              SAPDB_UInt4        &devCapacityInPages,
                              tsp00_TaskId        pidResponsibleForCall, 
                              const SAPDB_Byte   *formattingPageImage,
                              tsp00_ErrTextc     &errTextc)
{
    tsp00_ErrText errtext;
    tsp00_Bool    ok;
    tsp00_Int4    devcapacity = (tsp00_Int4)devCapacityInPages;

    vdevsizeWithImage( logicalDevNo, 
                       devType, 
                       devcapacity, 
                       pidResponsibleForCall, 
                       (char *)formattingPageImage,
                       errtext, 
                       ok);
    if ( 0 == ok )
    {
        errTextc.p2c(errtext);
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "DoFormatDevspace failed:" << errTextc.asCharp() );
    }
    else
    {
        devCapacityInPages = (SAPDB_UInt4)devcapacity;
    }
    return ok != 0;
}

/*!---------------------------------------------------------------------------
  Function: RTEIO_IsOnSamePhysicalDevice
  Description: Check given devspaces are placed on same physical device

  Depending on the formatting mode it is checked if the devspaces are on the same device. 
    
  Arguments: firstDevSpace [in]     first devspac
             secondDevSpace [in]    second devspace
             formattingMode [in]    mode of formatting: RTE_FORMATTING_MODE_SERIAL/PARALLEL/AUTO

  Return value: true if on same physical device, false otherwise.
                TRUE:   Formatting mode is 'SERIAL' or formatting mode is 'AUTO' and devspaces are on 
                        same device (UNIX only).
                FALSE:  Formatting mode is 'PARALLEL' or formatting mode is 'AUTO' and devspaces are on
                        different devices (UNIX only).
 */
static bool RTEIO_IsOnSamePhysicalDevice(RTEIO_DevSpaceInfo *firstDevSpace, RTEIO_DevSpaceInfo *secondDevSpace,
                                         SAPDB_UInt formattingMode)
{
    if (formattingMode == RTE_FORMATTING_MODE_PARALLEL)
    {
        return (false);
    }
    else if (formattingMode == RTE_FORMATTING_MODE_SERIAL)
    {
        return (true);
    }
    else /* RTE_FORMATTING_MODE_AUTO: Only possible for UNIX at the moment */
    {
        DEV_DESCR      *firstDevsp, *secondDevsp;

        firstDevsp = sql58k_get_devsp_descr ( firstDevSpace->GetType(), firstDevSpace->GetLogicalDevNo() - 1);
        secondDevsp = sql58k_get_devsp_descr ( secondDevSpace->GetType(), secondDevSpace->GetLogicalDevNo() - 1);
    
        if (firstDevsp->physicalDevID == RTE_UNDEF_ID || secondDevsp->physicalDevID == RTE_UNDEF_ID)
        {
            return (true); /* forces serial formatting for UNIX */
        }
        else
        {
            return (firstDevsp->physicalDevID == secondDevsp->physicalDevID);
        }
    }
}

/*!
  Function: RTEIO_UpdatePhysicalDevInfoForDevspace
  Description: Update information about physical devices of a specific devspace
  
  Currently this update takes only place on UNIX. 

  Arguments: pDevspace [in/out]     Pointer to devspace descriptor
 */
externC void RTEIO_UpdatePhysicalDevInfoForDevspace (DEV_DESCR      *pDevspace)
{
#if defined(_WIN32)
#else
    struct stat     info;
    char   errCodeStr [10];

    if (stat(pDevspace->szDevspaceName, &info) != 0)
    {
        sql47_itoa(errno, errCodeStr, sizeof(errCodeStr) ) ;
        RTE_Message (SAPDBErr_MessageList(RTE_CONTEXT, RTEWARN_IO_GET_DEV_ID, pDevspace->szDevspaceName, sqlerrs(), errCodeStr));
    }
    else
    {
        pDevspace->physicalDevID = info.st_dev;
    }
#endif

    return;
}

/*!
  Function: RTEIO_UpdatePhysicalDeviceInformation
  Description: Update information about physical devices of all devspaces in the info vector

  Currently this update takes only place on UNIX. 

  Arguments: infoVector     [in]    vector of devspaces to format
 */
static void RTEIO_UpdatePhysicalDeviceInformation (RTEIO_DevSpaceInfoVector &infoVector)
{
    SAPDBTRACE_ROUTINE_DEBUG("RTEIO_UpdatePhysicalDeviceInformation", Devspace_Trace, 7);
    SAPDB_Int4      size = infoVector.GetSize();
    SAPDB_Int4      idx;
    DEV_DESCR      *pDevsp;

    for (idx = 0; idx < size; ++idx)
    {
        pDevsp = sql58k_get_devsp_descr (infoVector[idx]->GetType(), infoVector[idx]->GetLogicalDevNo() - 1);
        RTEIO_UpdatePhysicalDevInfoForDevspace (pDevsp);
        if (pDevsp->physicalDevID == RTE_UNDEF_ID)
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, pDevsp->szDevspaceName << " on device " << "UNDEF_ID");
        }
        else
        {
            SAPDBTRACE_WRITELN(Devspace_Trace, 7, pDevsp->szDevspaceName << " on device " << pDevsp->physicalDevID);
        }
        
    }

    return;
}


/*!---------------------------------------------------------------------------
  Function: RTEIO_FormatSingleDevspace
  Description: Format a single devspaces given

  During formatting the devspace capacity information may be updated.

  Arguments: singleDevspace [inout] devspace to format
             errList [out] message list to fill
  Return value: true on success for all, false on any error (errList is filled)
 */
bool RTEIO_FormatSingleDevspace(RTE_TaskId taskId,
                                RTEIO_DevSpaceInfo &singleInfo,
                                SAPDBErr_MessageList &errList)
{
    SAPDBTRACE_ROUTINE_DEBUG("RTEIO_FormatSingleDevspace", Devspace_Trace, 1);

    tsp00_Int4     devCapacity = (tsp00_Int4)singleInfo.GetCapacityInPages();
    tsp00_ErrText  errtext;
    tsp00_ErrTextc errtextC;
    tsp00_Bool     ok;
    tsp00_TaskId   pidResponsibleForCall = (tsp00_TaskId)taskId;

    vdevsizeWithImage ( singleInfo.GetLogicalDevNo(),
                        singleInfo.GetType(),
                        devCapacity,
                        pidResponsibleForCall,
                        (char *)singleInfo.GetFormattingPageImage(),
                        errtext,
                        ok );
    if ( 0 == ok )
    {
        errtextC.p2c(errtext);
        SAPDBTRACE_WRITELN(Devspace_Trace, 1, "Formatting devspace failed:" << errtextC.asCharp() );
        errList = SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_FORMATTING_FAILED, errtextC.asCharp() );
        return false;
    }

    singleInfo.SetCapacityInPages(devCapacity);

    return true;
}

/*------------------------------------------------------------*/

RTE_VolumeAccessMode RTEIO_DevSpaceInfo::GetAccessMode()
{
    if ( RTE_VolumeAccessModeUnknown == m_AccessMode )
    {
        DEV_DESCR      *pVolume;

        pVolume = sql58k_get_devsp_descr ( this->m_Type, this->m_LogicalDevNo - 1);
        m_AccessMode = pVolume->accessMode;
    }
    return m_AccessMode;
}

#ifdef TEST_CODE

#define KERNEL_LZU
#include "geo50_0.h" /* nocheck */
#include "heo58.h" /* nocheck */


externC void vgetpid ( tsp00_TaskId VAR_VALUE_REF callerPid );

/*!---------------------------------------------------------------------------
  Function: RTEIO_BuildFormatDevspaceVector
  Description: Build a vector with all data and log devspaces found 

  Arguments: infoVector [inout] the vector to fill
             errlist [out] filled on error
  Return value: True
 */
static bool RTEIO_BuildFormatDevspaceVector( RTEIO_DevSpaceInfoVector &infoVector,
                                             SAPDBErr_MessageList &errList)
{
    SAPDBTRACE_ROUTINE_DEBUG("RTEIO_FormatDevspaceVector", Devspace_Trace, 1);

    SAPDB_Int4 devspaceIndex;

    SAPDB_Int4 totalDataDevspaces = KGS->XParam->lMaxDataDevspaces;
    for ( devspaceIndex = 0; devspaceIndex < KGS->XParam->lMaxDataDevspaces; ++devspaceIndex )
    {
        if ( KGS->XParam->DevspDescr[sp2dt_data].Devspaces[devspaceIndex].lDevspaceType == DEVSPACE_UNUSED )
        {
            --totalDataDevspaces;
        }
    }

    SAPDB_Int4 totalLogDevspaces = KGS->XParam->lMaxArchiveLogs;
    for ( devspaceIndex = 0; devspaceIndex < KGS->XParam->lMaxArchiveLogs; ++devspaceIndex )
    {
        if ( KGS->XParam->DevspDescr[sp2dt_log].Devspaces[devspaceIndex].lDevspaceType == DEVSPACE_UNUSED )
        {
            --totalLogDevspaces;
        }
    }

    infoVector.Initialize(totalDataDevspaces + totalLogDevspaces);

    SAPDB_Int4 currentIndex = 0;
    for ( devspaceIndex = 0; devspaceIndex < KGS->XParam->lMaxDataDevspaces; ++devspaceIndex )
    {
        if ( KGS->XParam->DevspDescr[sp2dt_data].Devspaces[devspaceIndex].lDevspaceType != DEVSPACE_UNUSED )
        {
            infoVector[currentIndex] = new (RTEMem_Allocator::Instance()) RTEIO_DevSpaceInfo(devspaceIndex + 1,
                                                                                             sp2dt_data,
                                    KGS->XParam->DevspDescr[sp2dt_data].Devspaces[devspaceIndex].lDevspaceSize,
                                                                                             (const SAPDB_Byte *)0 );
            ++currentIndex;
        }
    }
    SAPDB_Byte logPage[sizeof(tsp00_Page)];
    memset(logPage, 0, sizeof(logPage));

    for ( devspaceIndex = 0; devspaceIndex < KGS->XParam->lMaxArchiveLogs; ++devspaceIndex )
    {
        if ( KGS->XParam->DevspDescr[sp2dt_log].Devspaces[devspaceIndex].lDevspaceType != DEVSPACE_UNUSED )
        {
            infoVector[currentIndex] = new (RTEMem_Allocator::Instance()) RTEIO_DevSpaceInfo(devspaceIndex + 1,
                                                                                             sp2dt_log,
                                    KGS->XParam->DevspDescr[sp2dt_log].Devspaces[devspaceIndex].lDevspaceSize,
                                                                                             &logPage[0] );
            ++currentIndex;
        }
    }

    return true;
}

/*!--------------------------------------------------------------------------
  Function: RTEIO_FormatAllDataAndLogdevspaces
  Description: Do formatting of all data and logdevspaces known

  Test code only!!!!
 */

extern "C" void RTEIO_FormatAllDataAndLogdevspaces()
{
    tsp00_TaskId    pidResponsibleForCall;


    SAPDBTRACE_ROUTINE_DEBUG("RTEIO_FormatDevspaceVector", Devspace_Trace, 1);

    SAPDBErr_MessageList errList;

    RTE_Message( SAPDBErr_MessageList(RTE_CONTEXT, RTEINFO_START_FORMATTING) );

    RTEIO_DevSpaceInfoVector infoVector(RTEMem_Allocator::Instance());

    if ( !RTEIO_BuildFormatDevspaceVector(infoVector, errList) )
    {
        RTE_Message(errList);
        return;
    }

    vgetpid(pidResponsibleForCall);

    if ( !RTEIO_FormatDevspaceVector((RTE_TaskId)pidResponsibleForCall, infoVector, errList) )
    {
        RTE_Message(errList);
        return;
    }

    RTE_Message( SAPDBErr_MessageList(RTE_CONTEXT, RTEINFO_FORMATTING_DONE) );
}
#endif /* NO_TEST_CODE */
