/*!
  @file           RTEThread_Console.cpp
  @author         Stefan Paeck
  @special area   Kernel Console Thread
  @brief          Main
  @see            

\if EMIT_LICENCE
    ========== licence begin  GPL
    Copyright (c) 2002-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

\endif
*/




/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/
#if !defined (_WIN32)
#include <pthread.h>
#endif

#include    "heo07.h"  // sqlresumethread ()
#include    "geo007_1.h"

#include    "RunTime/RTE_CompilerFeatures.h"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Topic.hpp"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include    "RunTime/RTE_MessageList.hpp"
#include    "RunTime/RTE_Message.hpp"
#include    "RunTime/RTE_Console_Thread_Messages.hpp"
#include    "RunTime/RTE_ConsoleCommon.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleCommandQueue.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleCommandWorker.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleRequestor.hpp"
#include    "RunTime/RTE_ConsoleRequestCommunication.hpp"
#include    "RunTime/Threading/RTEThread_Console.h"
#include    "RunTime/RTE_KGS.hpp"
#include    "RunTime/RTE_KSS.h"
#include    "RunTime/RTE_ConsoleStandardDataRequests.h"
#include    "RunTime/RTE_ConsoleDataCommunication.hpp"

#if defined (LINUX)                                         /*&if $OSSPEC = LINUX*/
#include    "RunTime/RTE_ConsoleSemaphoreTimeoutList.hpp"   /* nocheck */
#endif                                                      /*&endif*/        

#include    "RunTime/RTE_ConsoleTraceTopic.hpp"

#if defined (_WIN32) && defined (_FASTCAP)
# include "fastcap.h"   /* nocheck */
#endif


extern SAPDBTrace_Topic Console_Trace;
     
/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/

#if defined (_WIN32) 
# define SET_THREAD_STATE(_state)    RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.ThrdState = _state;
#else
# define SET_THREAD_STATE(_state)    RTE_KGS::Instance().GetKGS()->console.state = _state;
#endif


/*===========================================================================*
 *  DECLARATIONS                                                             *
 *===========================================================================*/


static  ConsoleWorkerInfo               consoleWorkerInfo[NUM_CONSOLE_WORKER_THREADS];
#if defined (LINUX)
static  ConsoleSemaphoreTimeoutInfo     consoleSemaphoreTimeoutWorkerInfo;
#endif

/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/



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


/*===========================================================================*
 *  STATIC/INLINE FUNCTION PROTOTYPES                                        *
 *===========================================================================*/


static  SAPDB_Bool  InitConsoleThread           (SAPDBErr_MessageList &             messageList);
static  SAPDB_Bool  PutCommandIntoCommandQueue  (RTEThread_ConsoleRequest const &   request,
                                                 SAPDBErr_MessageList &             messageList);

static  void        ConsoleMain                 (void);

/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/

/*===========================================================================*
 *  STATIC FUNCTIONS                                                         *
 *===========================================================================*/

static  SAPDB_Bool InitConsoleThread 
(
    SAPDBErr_MessageList                  &   messageList
)
{
   /*===========================================================================*
    *  Locals                                                                   *
    *===========================================================================*/
    SAPDB_UInt4             threadIndex;
    SAPDB_UInt4             numActThread = 0;

   /*===========================================================================*
    *  Instructions                                                             *
    *===========================================================================*/
    SAPDBTRACE_ROUTINE_DEBUG("InitConsoleThread", Console_Trace, 1);

    for ( threadIndex = 0; threadIndex < NUM_CONSOLE_WORKER_THREADS; threadIndex++ )
    {
        if (!StartConsoleWorkerThread (consoleWorkerInfo[threadIndex], threadIndex))
        {
            consoleWorkerInfo[threadIndex].state = CONSOLE_WORKER_STATE_START_FAILED;       
            consoleWorkerInfo[threadIndex].index = CONSOLE_WORKER_INDEX_UNKNOWN;
        }
        else
        {
            ++numActThread;
            consoleWorkerInfo[threadIndex].state = CONSOLE_WORKER_STATE_STARTED 
                                                   | CONSOLE_WORKER_STATE_WAITING;
            consoleWorkerInfo[threadIndex].index = threadIndex;
        }

        SAPDBTRACE_WRITELN(Console_Trace, 1, "Worker thread " << threadIndex 
                                                              << ", state: " 
                                                              << consoleWorkerInfo[threadIndex].state);

    }

    if (0 == numActThread)
    {
        messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_WORKER_THREADS);
        return false;
    }

#if defined (LINUX)
    //Start Semaphore Timeout Thread
    tsp00_ErrTextc      threadErrtext;
    teo07_ThreadErr     threadOk;


    consoleSemaphoreTimeoutWorkerInfo.maxSemaphores = NUM_CONSOLE_WORKER_THREADS; 
    consoleSemaphoreTimeoutWorkerInfo.maxSleeptime  = RTE_KGS::Instance().GetKGS()->consoleSemTimeout/1000;    

    if (!RTE_ConsoleSemaphoreTimeoutList::CreateInstance(consoleSemaphoreTimeoutWorkerInfo.maxSemaphores, 
                                                         messageList))
    {
        return false;
    }

    sqlbeginthread(CONSOLE_SEMAPHORE_TIMEOUT_WORKER_STACK_SIZE,
                   SemaphoreTimeoutThread,
                   &consoleSemaphoreTimeoutWorkerInfo,
                   THR_CREATE_NORMAL_EO07,
                   &consoleSemaphoreTimeoutWorkerInfo.thread,
                   threadErrtext,
                   &threadOk );
    if ( threadOk != THR_OK_EO07 )
    {
        RTE_Message (SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_TIMEOUT_THRD_START, 
                                          SAPDB_ToString (threadOk), 
                                          threadErrtext));
        return false;
    }
#endif

    return true;
}

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

static  SAPDB_Bool PutCommandIntoCommandQueue 
(
    RTEThread_ConsoleRequest    const   &   request,
    SAPDBErr_MessageList                &   messageList
)
{
   /*===========================================================================*
    *  Locals                                                                   *
    *===========================================================================*/
    static  SAPDB_UInt4     lastThreadIndex = NUM_CONSOLE_WORKER_THREADS;
            SAPDB_UInt4     threadIndex;
            teo07_ThreadErr err;
	        tsp00_ErrTextc  errtext;

   /*===========================================================================*
    *  Instructions                                                             *
    *===========================================================================*/
    SAPDBTRACE_ROUTINE_DEBUG("PutCommandIntoCommandQueue", Console_Trace, 1);
    
    RTEThread_ConsoleCommandQueue::Instance().PutCommand (request);

    // The last activated thread should be prefered as worker for the new request
    if (lastThreadIndex < NUM_CONSOLE_WORKER_THREADS
        && (0 != (consoleWorkerInfo[lastThreadIndex].state & CONSOLE_WORKER_STATE_WAITING)))
        
    {
        threadIndex = lastThreadIndex;
        SAPDBTRACE_WRITELN(Console_Trace, 7, "Trigger console worker " << threadIndex
                                             << " again"); 
    }
    else
    {
        for (threadIndex = 0; 
             (threadIndex < NUM_CONSOLE_WORKER_THREADS)
             && (0 == (consoleWorkerInfo[threadIndex].state & CONSOLE_WORKER_STATE_WAITING));
             threadIndex++ )
        {
            SAPDBTRACE_WRITELN(Console_Trace, 7, "Console worker thread " << threadIndex
                                             << " is busy");
        }

        lastThreadIndex = threadIndex;
    }

    if (threadIndex < NUM_CONSOLE_WORKER_THREADS)
    {
       // Trigger the worker thread
        SAPDBTRACE_WRITELN (Console_Trace, 7, "Resuming console worker " << threadIndex);
        consoleWorkerInfo[threadIndex].state &= CONSOLE_WORKER_STATE_BASE_MASK;
        consoleWorkerInfo[threadIndex].state |= CONSOLE_WORKER_STATE_READY;
        sqlresumethread(consoleWorkerInfo[threadIndex].thread, errtext, &err);
        if ( err != THR_OK_EO07 )
        {
            messageList = messageList + SAPDBErr_MessageList (RTE_CONTEXT, 
                                                              RTEERR_THREAD_CONS_WORKER_THRD_RES, 
                                                              SAPDB_ToString (threadIndex), 
                                                              SAPDB_ToString (err), 
                                                              errtext);
            return false;
        }
    }
    else
    {
        RTE_Message (SAPDBErr_MessageList (RTE_CONTEXT, RTEINFO_THREAD_CONS_WORKER_THRD_BUSY)); 
    }

    return true;
}

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

externC void * RTEThread_Console ( void *)
{
   /*===========================================================================*
    *  Locals                                                                   *
    *===========================================================================*/

   /*===========================================================================*
    *  Instructions                                                             *
    *===========================================================================*/
#if defined (_WIN32) 
    __try 
#endif
    {
        ConsoleMain ();  
    }
#if defined (_WIN32) 
    __except( sql50k_excepthandler(GetExceptionInformation()) ) {;}
#endif
    RTE_Message (SAPDBErr_MessageList (RTE_CONTEXT, RTEINFO_KERNEL_THREAD_STOPPED, "CONSOLE" ));
#if defined (_WIN32)
    EXITPROCESS(0);
#endif
    return NULL;
}

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


static void ConsoleMain ( void )

{
   /*===========================================================================*
    *  Locals                                                                   *
    *===========================================================================*/

    RTEThread_ConsoleRequest            request;
    SAPDBErr_MessageList                messageList;
    SAPDB_UInt4                         rc;

   /*===========================================================================*
    *  Instructions                                                             *
    *===========================================================================*/

//UNIX:
//	en81_Set_KT_TLS ( & KGS->console );


#if defined (_WIN32) 

#   if defined(_FASTCAP)
    CAPNameThread ("Console");
#   endif

    rc = SET_THREAD_PRIO (RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.lThrdPrio,
                          RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.Tid,
                          RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.hThrd);


    if ( rc != NO_ERROR )
    {
        RTE_Crash (SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SETTING_THRD_PRIO, 
                                         SAPDB_ToString(RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.Tid)));
    }


    if (RTE_KGS::Instance().GetKGS()->XParam->fUseMemEnhancement)
    {
        pkss->consoleShmSize = max (SAPDB_ALIGN (RTE_CONSOLE_SHM_SIZE_AWE, RTE_KGS::Instance().GetKGS()->ulSystemPageSize), 
                                    SAPDB_ALIGN (SAPDB_ALIGN (2 * sizeof (RTE_ConsoleSemaphore), SAPDB_ALIGNMENT)
                                                    + SAPDB_ALIGN (RTEComm_PacketHeader::PacketHeaderBufferSize(), SAPDB_ALIGNMENT)
                                                    + sizeof (CONSOLE_DATA_REC),
                                                 RTE_KGS::Instance().GetKGS()->ulSystemPageSize));
    }
    else
    {
        pkss->consoleShmSize = max (SAPDB_ALIGN (RTE_CONSOLE_SHM_SIZE, RTE_KGS::Instance().GetKGS()->ulSystemPageSize), 
                                    SAPDB_ALIGN (SAPDB_ALIGN (2 * sizeof (RTE_ConsoleSemaphore), SAPDB_ALIGNMENT)
                                                    + SAPDB_ALIGN (RTEComm_PacketHeader::PacketHeaderBufferSize(), SAPDB_ALIGNMENT)
                                                    + sizeof (CONSOLE_DATA_REC),
                                                 RTE_KGS::Instance().GetKGS()->ulSystemPageSize));
    }

    pkss->consoleSemTimeout = RTE_CONSOLE_TIMEOUT;
#else //due to missing system page size for UNIX we may lose some space in the allocated memory
	RTE_KGS::Instance().GetKGS()->consoleShmSize    = max (SAPDB_ALIGN (RTE_CONSOLE_SHM_SIZE, SAPDB_ALIGNMENT),
                                                           SAPDB_ALIGN (2 * sizeof (RTE_ConsoleSemaphore), SAPDB_ALIGNMENT)
                                                           + SAPDB_ALIGN (RTEComm_PacketHeader::PacketHeaderBufferSize(), SAPDB_ALIGNMENT) 
                                                           + SAPDB_ALIGN (sizeof (CONSOLE_DATA_REC), SAPDB_ALIGNMENT));
	RTE_KGS::Instance().GetKGS()->consoleSemTimeout = RTE_CONSOLE_TIMEOUT;
#endif

    if (!InitTrace ("console.trc", messageList))
    {
        RTE_Message (messageList);
		messageList.ClearMessageList ();
    }


    SAPDBTRACE_ROUTINE_DEBUG("RTEThread_Console", Console_Trace, 1);

    if (!InitConsoleThread (messageList))
    {
        RTE_Crash (messageList);
    }


    RTE_Message (SAPDBErr_MessageList (RTE_CONTEXT, RTEINFO_KERNEL_THREAD_STARTED, "CONSOLE" ));

#if defined (_WIN32) 
    RTE_KGS::Instance().GetKGS()->Console.ThrdCtrlHeader.ulStartTime = kgs.ulCurrTime;
#else
	RTE_KGS::Instance().GetKGS()->console.start_time = KGS->current_time;
#endif

    for (;;)
	{
        SET_THREAD_STATE(KT_SLEEPING);

        if (!RTEThread_ConsoleRequestor::Instance ().ReadRequest (request, messageList))
        {
            RTE_Message (messageList);
		    messageList.ClearMessageList ();
            continue;
        }
        SET_THREAD_STATE(KT_RUNNING);

        if (!PutCommandIntoCommandQueue (request, messageList))
        {
            RTE_Message (messageList);
		    messageList.ClearMessageList ();
            continue;
        }
    }

 }

