// ---------------------------------------------------------------
// forget_heur_branches
// Purpose : Heuristic forget
// --------------------------------------------------------------
int32 CTmTxBranches::forget_heur_branches (CTmTxBase *pp_txn,
                                           int64 pv_flags)
{
    int32 lv_error = FEOK;

    TMTrace (2, ("CTmTxBranches::forget_heur_branches ENTRY : ID (%d,%d), flags " PFLL "\n",
             pp_txn->node(), pp_txn->seqnum(), pv_flags));
    tm_log_event(DTM_TMTX_FORGET_HEURISTIC, SQ_LOG_WARNING, "DTM_TMTX_FORGET_HEURISTIC",
                 -1,-1,pp_txn->node(), pp_txn->seqnum());
    gv_tm_info.write_trans_state (pp_txn->transid(), TM_TX_STATE_FORGOTTEN_HEUR, pp_txn->abort_flags(), false);
    pp_txn->wrote_trans_state(true);
    if (pp_txn->tx_state() != TM_TX_STATE_FORGOTTEN_HEUR)
    {
       lv_error = forget_branches (pp_txn, pv_flags);
       switch (lv_error)
       {
         case XA_OK:
            pp_txn->tx_state(TM_TX_STATE_FORGOTTEN_HEUR);
            break;
         case XAER_RMFAIL:
            //TODO recovery case, what to do here?
         abort();
         default:
            tm_log_event(DTM_TMTX_INVALID_BRANCH, SQ_LOG_CRIT, "DTM_TMTX_INVALID_BRANCH");
            TMTrace (1, ("CTmTxBranches::forget_heur_branches - Invalid branch state\n"));
            abort (); 
         break;
       }
    }
    return lv_error;
} // forget_heur_branches
// --------------------------------------------------------------
// CxaTM_RMMessage::send_rm
// Purpose - send the message to the RM
// Caller may lock.
// --------------------------------------------------------------
int CxaTM_RMMessage::send_rm() 
{
   short lv_ret = -1;
   int   lv_xaError = XA_OK;

   XATrace(XATM_TraceExit,
           ("XATM: CxaTM_RMMessage::send_rm ENTRY. msgid=%d, retries=%d/%d.\n", 
            iv_msgid, sendAttempts(), RM()->maxSendRetries()));
   lock();
   // Make sure that this CxaTM_RMMessage object doesn't already have a link outstanding
   if (msgid())
   {
      tm_log_event(DTM_XATM_SENDTORM_FAILED, SQ_LOG_CRIT, "DTM_XATM_SENDTORM_FAILED",
                   -1, RM()->getRmid(), -1, -1, msgid());
      XATrace(XATM_TraceError, ("XATM: CxaTM_RMMessage::send_rm : XATM Message object "
                                "already has a link outstanding, msgid %d\n", msgid()));
      abort();
   }

   if (iv_reqLen > RM_MAX_DATA || iv_rspLen > RM_MAX_DATA)
   {
      // EMS DTM_XATM_SENDTORM_FAILED
      tm_log_event(DTM_XATM_SENDTORM_FAILED2, SQ_LOG_CRIT, "DTM_XATM_SENDTORM_FAILED2",
                   -1,RM()->getRmid(),-1,-1,-1,XAER_RMFAIL,-1,-1,-1,-1,-1,-1,-1,
                   RM_MAX_DATA,iv_reqLen,iv_rspLen);
      XATrace(XATM_TraceError, ("XATM: CxaTM_RMMessage::send_rm : "
                                " Request or reply buffer too large, returning XAER_RMFAIL.\n"));
      lv_xaError = XAER_RMFAIL;
   }
   else
   {
      inc_sendAttempts();
      lv_ret = link();
   }

   if (lv_ret)
   {
      // EMS DTM_XATM_SENDTORM_FAILED
      tm_log_event(DTM_XATM_SENDTORM_FAILED, SQ_LOG_CRIT, "DTM_XATM_SENDTORM_FAILED", lv_ret, ip_RM->getRmid());
      lv_xaError = XAER_RMFAIL;
   }
   unlock();

   XATrace((lv_xaError?XATM_TraceExitError:XATM_TraceExit),
           ("XATM: CxaTM_RMMessage::send_rm EXIT returning %s, BMSG_LINK_ returned %d, msgid=%d, retries=%d/%d.\n", 
            XAtoa(lv_xaError), lv_ret, iv_msgid, sendAttempts(), RM()->maxSendRetries()));
   return lv_xaError;
} //CxaTM_RMMessage::send_rm
//----------------------------------------------------------------------------
// CxaTM_RMMessage::constructPoolElement
// Purpose : Callback for CTmPool elements.
// This method is called to construct a CxaTM_RMMessage object by CTmPool::newElement.
//----------------------------------------------------------------------------
CxaTM_RMMessage * CxaTM_RMMessage::constructPoolElement(int64 pv_msgNum)
{

   XATrace (XATM_TraceRMMsg, ("CxaTM_RMMessage::constructPoolElement : ENTRY Instantiating new RMMessage object, msgNum " PFLL ".\n", 
                pv_msgNum));

   CxaTM_RMMessage *lp_msg = new CxaTM_RMMessage(pv_msgNum);
   if (!lp_msg)
   {
      tm_log_event(DTM_LOGIC_ERROR_RM_OBJ, SQ_LOG_CRIT, "DTM_LOGIC_ERROR_RM_OBJ");
      XATrace (XATM_TraceError, ("CxaTM_RMMessage::constructPoolElement : Failed to instantiate "
                                 " RMMessage object.\n"));
      abort();
   }

   XATrace (XATM_TraceRMMsg, ("CxaTM_RMMessage::constructPoolElement : EXIT RMMessage object %p, msgNum " PFLL " instantiated.\n", 
                (void *) lp_msg, pv_msgNum));
   return lp_msg;
} //CxaTM_RMMessage::constructPoolElement
// --------------------------------------------------------------
// CTmTimer::eventQ_push
// Purpose - push a new event to the timer event queue.
// These are pushed in FIFO order.
// --------------------------------------------------------------
void CTmTimer::eventQ_push(CTmEvent * pp_event)
{
    char        la_buf[DTM_STRING_BUF_SIZE];
    CTmTimerEvent *lp_tevent = (CTmTimerEvent *) pp_event; 

    if (lp_tevent == NULL)
    {
        sprintf(la_buf, "Timer request to be queued is NULL.\n");
        tm_log_event(DTM_TMTIMER_BAD_EVENT, SQ_LOG_CRIT, "DTM_TMTIMER_BAD_EVENT");
        TMTIME_TRACE (1, XATM_TraceTimerExitError,("CTmTimer::eventQ_push - %s", la_buf));
        abort ();
    }

   eventQ()->push(lp_tevent);

   if (lp_tevent->transaction())
   {
      TMTIME_TRACE (3, XATM_TraceTimerExit, ("CTmTimer::eventQ_push : signaling timer thread, event %p, "
               "cmd %d, trans obj %p, request %d, wakeup interval %d, repeat %d.\n",
               (void *) lp_tevent, lp_tevent->command(), 
               (void *) lp_tevent->transaction(), lp_tevent->requestType(),
               lp_tevent->wakeupInterval(),
               lp_tevent->wakeupRepeat()));
   }
   else
   {
      TMTIME_TRACE (3, XATM_TraceTimerExit, ("CTmTimer::eventQ_push : signaling timer thread, event %p, "
               "cmd %d, request %d, wakeup interval %d, repeat %d.\n",
               (void *) lp_tevent, lp_tevent->command(), 
               lp_tevent->requestType(),
               lp_tevent->wakeupInterval(),
               lp_tevent->wakeupRepeat()));
   }
   
   eventQ_CV()->signal(true /*lock*/); 
} //CTmTimer::eventQ_push
// ---------------------------------------------------------------------------
// CxaTM_RMMessage::link
// Purpose : Call to BMSG_LINK_.
// This function will retry any retriable errors such as FENOLCB (30).
// Parameters:
//  pv_maxretries - maximum retries for retriable errors.
//  Returns error from BMSG_LINK_ call.
// ---------------------------------------------------------------------------
short CxaTM_RMMessage::link(int32 pv_maxretries)

{
    short lv_ret = 0;
    int32 lv_retries = 0;
    bool lv_exit = false;
    TM_Txid_Internal *lp_transid = (TM_Txid_Internal *) &xid()->data[0];

    XATrace(XATM_TraceExit, ("CxaTM_RMMessage::link ENTRY : ID (%d,%d), linker "
            "tag(rmid) %d.\n", lp_transid->iv_node, lp_transid->iv_seq_num, 
            ip_RM->getRmid()));

    do {
         lv_ret = BMSG_LINK_(ip_RM->getRmPhandle(),     // phandle, 
                           &iv_msgid,                   // msgid
                           NULL,                        // reqctrl
                           0,                           // reqctrlsize
                           NULL,                        // replyctrl
                           0,                           // replyctrlmax
                           (char *) Req(),              // reqdata
                           ReqLen(),                    // reqdatasize
                           (char *) Rsp(),              // replydata
                           RspLen(),                    // replydatamax
                           ip_RM->getRmid(),            // linkertag
                           TSE_LINK_PRIORITY,           // pri
                           0,                           // xmitclass
                           BMSG_LINK_LDONEQ);           // linkopts

       lv_retries++;
       if (lv_ret == FENOLCB && 
          //((pv_maxretries == -1 && (lv_retries % TM_LINKRETRY_RETRIES == 0)) ||
          (pv_maxretries == -1 ||
           (pv_maxretries > 0 && (lv_retries <= pv_maxretries))))
       {  // Message Descriptor depletion.  This means we ran out of MDs.
          // This is retriable, and we want to slow down the TM to allow
          // some of the outstanding requests to complete.
          XATrace(XATM_TraceError, ("CxaTM_RMMessage::link BMSG_LINK_ error %d, "
                       "linker tag(rmid) %d, retires %d/%d - Pausing thread for "
                       "%dms before retrying.\n", lv_ret, ip_RM->getRmid(), 
                       lv_retries, pv_maxretries, TM_LINKRETRY_PAUSE));
          tm_log_event(DTM_TM_LINK_PAUSED, SQ_LOG_WARNING, "DTM_TM_LINK_PAUSED", 
                   lv_ret, -1, lp_transid->iv_node, lp_transid->iv_seq_num, -1, -1, -1, -1, lv_retries, 
                   -1, -1, -1, -1, TM_LINKRETRY_PAUSE /*pause in ms*/, ip_RM->getRmid());
          SB_Thread::Sthr::sleep(TM_LINKRETRY_PAUSE); // in msec
       }
       if (lv_ret != FENOLCB)
          lv_exit = true;
       else
          if (pv_maxretries > 0 && lv_retries >= pv_maxretries)
             lv_exit = true;

    } while (!lv_exit);

    if (lv_ret)
    {
       XATrace(XATM_TraceExit, ("CxaTM_RMMessage::link EXIT : returning error %d.\n", lv_ret));
    }
    else
    {
       XATrace(XATM_TraceExit, ("CxaTM_RMMessage::link EXIT : returning msgid %d.\n", iv_msgid));
    }
    return lv_ret;
} //CxaTM_RMMessage::link
// --------------------------------------------------------------
// CxaTM_RM::checkError
// Check the error returned by BMSG_BREAK_ and determine whether
// to retry the LINK.
// If break returns an error we retry the link - resend the 
// message to the RM.
// We don't need to cancel/abandon the message. If Seabed 
// returned an error it already abandoned it.
// pv_breakError - return code from BMSG_BREAK_.
// pp_xaError will contain the XA error code on return.
// pv_softRetry - true (default) Will retry true (retry link) if an
//                     XA_RETRY occurs.  This happens if a 
//                     FEPATHDOWN, FENOLCB, or an XA_RETRY occur.
//                false Will return retry = false if one of these
//                     errors occurs. This allows the XA_RETRY to
//                     be passed up to the next level for xa_recover.
// pv_transid only used for event messages (error reporting).
// return code
//    true = retry link.
//    false = Don't retry. Retries exceeded or error is not 
//            retryable.
// --------------------------------------------------------------
bool CxaTM_RMMessage::checkError(short pv_breakError, int *pp_xaError, 
                                 bool pv_softRetry, int64 pv_transid)
{
   bool lv_retryLink = false;
   *pp_xaError = XA_OK;
   char la_buf[DTM_STRING_BUF_SIZE];
   CTmTxKey lv_txn = (CTmTxKey) pv_transid;

   XATrace(XATM_TraceExit,
           ("XATM: CxaTM_RMMessage::checkError ENTRY txn ID (%d,%d) msgid=%d, breakError=%d, "
            "rmid=%d, softRetry=%d.\n", lv_txn.node(), lv_txn.seqnum(), iv_msgid, pv_breakError, 
            RM()->getRmid(), pv_softRetry));

   lock();
   // For XARM clients connecting to a DTM TM, we always retry the error.
   // DTM TMs don't run as process pairs, but we are attempting to send to
   // a specific node which might go down and switch to a spare. Also the
   // TSE might switch to it's backup.
   // If it's a retryable error (FEPATHDOWN (201) or FEOWNERSHIP (200)) or
   // FENOLCB (30) from a TSE then we'll retry. 
   if ((tm_XARM_generic_library() && pv_breakError) || 
       pv_breakError == FEPATHDOWN ||
       pv_breakError == FEOWNERSHIP ||
      pv_breakError == FENOLCB )
   {
      // Retry sending the request to the RM.  This is used to deal with failovers.
      if (ip_RM->inc_totalRetries())
      {
         sprintf(la_buf, "Retrying send to RM. BMSG_BREAK_ failed for Txn ID (%d,%d) with "
                 "error %d, rmid=%d, msgid=%d, retries=%d/%d\n", 
                 lv_txn.node(), lv_txn.seqnum(), pv_breakError, RM()->getRmid(), msgid(), 
                 sendAttempts(), RM()->maxSendRetries());

         tm_log_event(DTM_TM_INFO_MSGBRK_FAIL3, SQ_LOG_WARNING,
                      "DTM_TM_INFO_MSGBRK_FAIL3", pv_breakError, RM()->getRmid(),lv_txn.node(),lv_txn.seqnum(),
                      msgid(),-1,-1,-1, sendAttempts(),-1,-1,-1,-1,-1,-1,-1,RM()->getRMnameDetail());
         XATrace(XATM_TraceError,("XATM: CxaTM_RMMessage::checkError : %s", la_buf));
      }
      if ((pv_breakError == FEPATHDOWN) || (pv_breakError == FENOLCB))          
         *pp_xaError = XA_RETRY;

      lv_retryLink = can_we_retrySend(pp_xaError);
   }
   else
   {
      if (pv_breakError == FEOK)
      {
         *pp_xaError = getRmError();
         if (*pp_xaError != XA_OK)
         {
            XATrace(XATM_TraceDetail,("XATM: CxaTM_RMMessage::checkError : "
                    "XA error %s encountered.\n", XAtoa(*pp_xaError)));
            // If the RM responds XA_RETRY, we retry as for a break error, but allow
            // this to retry forever.
            if (*pp_xaError == XA_RETRY)
            {
               lv_retryLink = can_we_retrySend(pp_xaError);
               clear_sendAttempts();
            }
         }
      }
      else
      {
         sprintf(la_buf, "RM connection error. txn ID (%d,%d) BMSG_BREAK_ failed with "
                 "error %d, rmid=%d, msgid=%d, retries=%d/%d\n", 
                 lv_txn.node(), lv_txn.seqnum(), pv_breakError, RM()->getRmid(), msgid(), 
                 sendAttempts(), RM()->maxSendRetries());

         tm_log_event(DTM_TM_INFO_MSGBRK_FAIL4, SQ_LOG_WARNING,
                      "DTM_TM_INFO_MSGBRK_FAIL4",pv_breakError, RM()->getRmid(), 
                      lv_txn.node(),lv_txn.seqnum(),msgid(),-1,-1,-1, sendAttempts(),
                      -1,-1,-1,-1,-1,-1,-1,RM()->getRMnameDetail());
         XATrace(XATM_TraceError,("XATM: CxaTM_RMMessage::checkError : %s", la_buf));
         *pp_xaError = XAER_RMFAIL;
      }
   }
   unlock();

   if (!pv_softRetry && *pp_xaError == XA_RETRY)
      lv_retryLink = false;

   XATrace(XATM_TraceExit,
           ("XATM: CxaTM_RMMessage::checkError EXIT txn ID (%d,%d) msgid=%d, returning "
            "retryLink=%d, xaError=%s.\n",
            lv_txn.node(), lv_txn.seqnum(), msgid(), lv_retryLink, XAtoa(*pp_xaError)));
   return lv_retryLink;
} //CxaTM_RMMessage::checkError
// -------------------------------------------------------------
// tmTimer_RecoveryWait
// Purpose : Wait for transaction count to be 0 before continuing
// -------------------------------------------------------------
void tmTimer_RecoveryWait(CTmTimerEvent * pp_event)
{

   int32 lv_nid = pp_event->request()->u.iv_tmrecovery_internal.iv_nid;
   
   if (lv_nid == -1) //Cluster recovery
   {
       int32 lv_activeTxns = gv_tm_info.transactionPool()->get_inUseList()->size();
       int32 lv_queuedTxns = gv_tm_info.ClusterRecov()->txnStateList()->size();

       TMTrace(2, ("tmTimer_RecoveryWait : Cluster ENTRY. Indoubt queued: %d, in progress %d.\n",
               lv_queuedTxns, lv_activeTxns));

       //Check the number of in doubt transactions
       if (lv_activeTxns + lv_queuedTxns > 0 ) 
       {
          int32 lv_availableTxnObjs = 
                MIN(gv_tm_info.transactionPool()->get_maxPoolSize(), gv_tm_info.maxRecoveringTxns())  - lv_activeTxns;
          if (lv_availableTxnObjs > 0)
          {
              gv_tm_info.ClusterRecov()->queueTxnObjects();
              gv_tm_info.ClusterRecov()->resolve_in_doubt_txs(-1/*all tms*/, false/*no delay*/);
          }
          else
              gv_tm_info.addTMRecoveryWait(lv_nid, 1000 /*1 sec*/);
       }
       else 
       {
          gv_tm_info.ClusterRecov()->completeRecovery();
          delete gv_tm_info.ClusterRecov();
          gv_tm_info.ClusterRecov(NULL);
          tm_log_event(DTM_RECOVERY_COMPLETED, SQ_LOG_NOTICE, "DTM_RECOVERY_COMPLETED", 
                       -1, -1, gv_tm_info.nid());
          TMTrace(1, ("System recover : DTM%d System startup recovery completed.\n", gv_tm_info.nid()));
       }
    }
    else 
    {
        int32 lv_queuedTxns = gv_tm_info.NodeRecov(lv_nid)->txnStateList()->size();

        TMTrace(2, ("tmTimer_RecoveryWait : Node ENTRY. Node %d has indoubt queued: %d.\n",
                lv_nid, lv_queuedTxns));

        //Check the number of in doubt transactions
        if (lv_queuedTxns > 0 )
        {
           int32 lv_availableTxnObjs =
                  gv_tm_info.transactionPool()->get_maxPoolSize() - gv_tm_info.num_active_txs();
           if (lv_availableTxnObjs)
           {
               gv_tm_info.NodeRecov(lv_nid)->queueTxnObjects();
               gv_tm_info.NodeRecov(lv_nid)->resolve_in_doubt_txs(lv_nid, 1000 /*1 sec*/);
           }
           else
               gv_tm_info.addTMRecoveryWait(lv_nid, 1000 /*1 sec*/);
        }
        else 
        {
           gv_tm_info.NodeRecov(lv_nid)->update_registry_txs_to_recover(0);
        }

     }

     TMTrace(2, ("tmTimer_RecoveryWait : EXIT.\n"));
}
//----------------------------------------------------------------------------
// timerThread_main
// Purpose : Main for timer thread
//----------------------------------------------------------------------------
void * timerThread_main(void *arg)
{
   CTmTimerEvent   *lp_event;
   CTmTimer        *lp_timerTh;
   bool             lv_exit = false;
   bool                lv_deleteEvent = false;

   arg = arg;
   CTmTxMessage *lp_msg;

   TMTrace(2, ("timerThread_main : ENTRY.\n"));

   while (gv_tm_info.tmTimer() == NULL || gv_tm_info.tmTimer()->state() != TmTimer_Up || gv_tm_info.tmAuditObj() == NULL)
   {
      SB_Thread::Sthr::usleep(10);
   }
   // Now we can set a pointer to the CTmTimer object because it exits
   lp_timerTh = gv_tm_info.tmTimer();

   TMTrace(2, ("timerThread_main : Thread %s(%p) State Up.\n",
      lp_timerTh->get_name(), (void *) lp_timerTh));

   while (!lv_exit)
   {
      lp_event = (CTmTimerEvent *) lp_timerTh->eventQ_pop();

      if (lp_event)
      {
         switch (lp_event->command())
         {
         case TmTimerCmd_Queue:
         {
            // Queue request to timer list
            TMTrace(3, ("timerThread_main : TmTimerCmd_Queue: Queue timer event. "
               "Txn ID %d, Wakeup interval %d, repeat %d, Request %d\n",
               ((lp_event->transaction())?lp_event->transaction()->seqnum():0),
               lp_event->wakeupInterval(), lp_event->wakeupRepeat(),
               lp_event->requestType()));
            lp_event->command(TmTimerCmd_Timer); //Change to timer event
            lp_timerTh->timerList()->add(lp_event);
            break;
         } //case TmTimerCmd_Queue Incoming request

         case TmTimerCmd_Timer:
         {
            // A timer popped, queue request to the transactions event queue
            TMTrace(3, ("timerThread_main : TmTimerCmd_Timer: Timer pop. "
               "Txn ID %d, Wakeup interval %d, repeat %d, Request %d, msgid %d\n",
               ((lp_event->transaction())?lp_event->transaction()->seqnum():0),
               lp_event->wakeupInterval(), lp_event->wakeupRepeat(),
               lp_event->requestType(), lp_event->msg()->msgid()));
            lp_timerTh->lock();

            switch (lp_event->requestType())
            {
            case TM_MSG_TXINTERNAL_CONTROLPOINT:
               // Control Point (must be Lead TM).
               lp_timerTh->last_cp_written(SB_Thread::Sthr::time());
               tmTimer_initiate_cp();
               break;
            case TM_MSG_TXINTERNAL_STATS:
               tmTimer_stats();
               break;
            case TM_MSG_TXINTERNAL_RMRETRY:
               break;
            case TM_MSG_TXINTERNAL_TMRESTART_RETRY:
               // If this is the first and only event or the last of several - then service it.  If 
               // it is not the last one (of duplicate events), then ignore it. 
               if (gv_tm_info.get_restartTimerEvent(lp_event->request()->u.iv_tmrestart_internal.iv_nid)
                        == lp_event)
               {
                   gv_tm_info.reset_restartTimerEvent (lp_event->request()->u.iv_tmrestart_internal.iv_nid);
                   tmTimer_TMRestartRetry(lp_event);
               }
               lv_deleteEvent = true;
               break;
            case TM_MSG_TXINTERNAL_SHUTDOWNP1_WAIT:
               lp_msg = (CTmTxMessage *) lp_event;
               gv_tm_info.ShutdownPhase1Wait(lp_msg);
               break;
            case TM_MSG_TYPE_ATTACHRM:
               lp_msg = new CTmTxMessage(lp_event->request());
               gv_tm_info.attachRm(lp_msg);
               lv_deleteEvent = true;
               delete lp_msg;
               break;
            case TM_MSG_TYPE_ENABLETRANS:
               lp_msg = new CTmTxMessage(lp_event->request());
               gv_tm_info.enableTrans(lp_msg);
               lv_deleteEvent = true;
               delete lp_msg;
               break;
            case TM_MSG_TYPE_DISABLETRANS:
               lp_msg = new CTmTxMessage(lp_event->request());
               gv_tm_info.disableTrans(lp_msg);
               lv_deleteEvent = true;
               delete lp_msg;
               break;
            case TM_MSG_TXINTERNAL_RECOVERY_WAIT:
               tmTimer_RecoveryWait(lp_event);
               lv_deleteEvent = true;
               break;
            case TM_MSG_TXINTERNAL_INITIALIZE_RMS:
               tmTimer_initializeRMs();
               lv_deleteEvent = true;
               break;
            case TM_MSG_TXINTERNAL_SYSTEM_RECOVERY:
               tmTimer_recoverSystem();
               lv_deleteEvent = true;
               break;
            default:
            {
                if (lp_event->transaction())
                {
                   // Notify transaction object
                   CTmTxMessage *lp_msg = new CTmTxMessage(lp_event->request());
                   lp_event->transaction()->queueToTransaction(lp_event->transaction()->transid(), 
                                                               lp_msg);
                   lv_deleteEvent = true;
                }
                else if (lp_event->thread())
                   // Notify non-transactional thread
                   lp_event->thread()->eventQ_push((CTmEvent *) lp_event);
                else
                {
                   TMTrace(1, ("timerThread_main : Timer Thread %p. No transaction or "
                            "thread pointer for event %p, Txn ptr %p, cmd %d, reqType %d, Wakeup interval %d, repeat %d.\n",
                            (void *) lp_timerTh, (void *) lp_event, (void *) lp_event->transaction(), lp_event->command(), 
                            lp_event->requestType(), lp_event->wakeupInterval(), lp_event->wakeupRepeat()));
                   tm_log_event (DTM_TMTIMER_BAD_EVENT2, SQ_LOG_WARNING, "DTM_TMTIMER_BAD_EVENT2",
                       lp_event->request()->iv_msg_hdr.miv_err.error, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                       lp_event->requestType(), lp_event->request()->iv_msg_hdr.dialect_type,
                       -1, NULL, -1, lp_event->wakeupInterval(), lp_event->wakeupRepeat());
                   // abort(); Don't want to abort, just discard event.
                   lv_deleteEvent = true;
                }
            } //default
            } //switch 

            if (lp_event->wakeupRepeat() != 0 && lv_deleteEvent == false)
            {
               lp_event->dec_wakeupRepeat();
               lp_timerTh->timerList()->add(lp_event);
            }
            lp_timerTh->unlock();
            break;
         } //case TmTimerCmd_Timer timer pop

         case TmTimerCmd_Stop:
         {
            lp_timerTh->lock();
            lv_exit = true;
            TMTrace(1, ("timerThread_main : Stop thread received.\n"));
            lv_deleteEvent = true;
            lp_timerTh->state(TmTimer_Down);
            // Unlock on exit
            break;
         } //case TmTimerCmd_Stop the timer thread

         case TmTimerCmd_Cancelled:
         {
            TMTrace(1, ("timerThread_main : Cancelled timer event %p popped.\n",
               (void *) lp_event));
            lv_deleteEvent = true;
            break;
         } //case TmTimerCmd_Cancelled

         default:
         {
            // EMS DTM_TXTHREAD_BAD_EVENT
            TMTrace(1, ("timerThread_main: Timer Thread (0x%p) main received an "
                "unexpected event %p, reqType %d, cmd %d, terminating.\n", 
                (void *) lp_timerTh, (void *) lp_event, lp_event->requestType(), lp_event->command()));
            tm_log_event(DTM_TMTIMER_UNEXP_EVENT, SQ_LOG_CRIT, "DTM_TMTIMER_UNEXP_EVENT",
                         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,lp_event->requestType(),lp_event->command());
            abort();
         }
         } //switch
         if (lv_deleteEvent)
         {
            TMTrace(2, ("timerThread_main : thread_deleted p_event (0x%p), cmd %d, type %d.\n", 
                    (void *) lp_event, lp_event->command(), lp_event->requestType()));
            delete lp_event;
            lv_deleteEvent = false;
         }
      } // if lp_event
   } //while

   TMTrace(2, ("timerThread_main : EXIT.\n"));

   lp_timerTh->unlock();
   return NULL;
} //timerThread_main
// -----------------------------------------------------------
// adp_send_audit
// Purpose - send audit buffer to adp
// return values : 0 - nothing to do
//                 1 - rollover
//                 2 - threshold hit
// ------------------------------------------------------------
int32 TM_Audit::adp_send_audit(char *pp_buffer, int32 pv_length, int64 pv_vsn, bool pv_force)
{
    int32 lv_notify = 0;
    int64 lv_old_seq = 0;
    int32 lv_error = FEEOF; // Set to non-zero for first pass to make sure we enter the while loop.
    int32 lv_error2 = 0;
    int32 lv_send_count = 0;
    const int lc_adp_send_retry_delay = 100; // .1 sec
    const int lc_adp_send_retry_maxretries = 30;

    if (!iv_initialized)
        return -1;

    TMTrace(2, ("TM_Audit::adp_send_audit: ENTER\n"));

    //If we all in state Quiesce, we can no longer talk to the ADP
    if (gv_tm_info.state() == TM_STATE_QUIESCE)
     {
        TMTrace(2, ("TM_Audit::adp_send_audit: Quiesced so not sending audit EXIT\n"));
        return 0;
     }

    iv_mutex.lock();
    TMTrace(4, ("TM_Audit::adp_send_audit: lock obtained\n"));
    AuditTrailPosition_Struct lv_buffer_pos;
   lv_buffer_pos.Sequence = lv_buffer_pos.rba = 0;

    while ((gv_tm_info.state() != TM_STATE_QUIESCE) &&
           (lv_error) && (++lv_send_count < lc_adp_send_retry_maxretries))
    {
      // We only force control points and commit/abort records.  Forgotten
      // ones we allow TSEs to buffer if they choose
        if (lv_error && (lv_send_count < lc_adp_send_retry_maxretries) &&
         (lv_send_count > 0))
           SB_Thread::Sthr::sleep(lc_adp_send_retry_delay);
    }
    if (gv_tm_info.state() == TM_STATE_QUIESCE)
     {
        TMTrace(2, ("TM_Audit::adp_send_audit: Quiesced so not sending audit EXIT(2)\n"));
        return 0;
     }

    if (lv_error)
    {
       // if after our retries and the ASE lib retries, we still can't write a record,
       // we have no choice but to die.
       tm_log_event(DTM_AUDIT_FAILED_WRITE, SQ_LOG_CRIT, "DTM_AUDIT_FAILED_WRITE", lv_error);
       TMTrace(1, ("TM_Audit::adp_send_audit: Failed to write audit to adp. Error "
                   "%d Shutting down Seaquest.\n", lv_error));
       msg_mon_shutdown(MS_Mon_ShutdownLevel_Abrupt);
       // Abort without core - don't need the core in this case.
       struct rlimit limit;
       limit.rlim_cur = 0;
       limit.rlim_max = 0;
       setrlimit(RLIMIT_CORE, &limit);
       abort();

    }
    // we rolled over, this will initiate a cp
    if (pv_force)  // we'll have a buffer position
   {
       if (iv_position != lv_buffer_pos.Sequence)
       {
             TMTrace(3, ("adp_send_audit: ROLLING OVER from " PFLL " to %d\n", iv_position, lv_buffer_pos.Sequence));
             lv_old_seq = iv_position;
             iv_position = lv_buffer_pos.Sequence;
             if (lv_old_seq != -1)
             {
                 iv_vsn = 1;
                 lv_notify = 1;
                 iv_notified_threshold = false;  // rolled over, under threshold
                 lv_error2 = gv_tm_info.write_rollover_control_point();
                 if(lv_error2) {
                     // Unable to inform lead TM to write control point
                     tm_log_event(DTM_ROLLOVER_CP_ERROR, SQ_LOG_CRIT, "DTM_ROLLOVER_CP_ERROR",
                                  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,lv_error2);
                     TMTrace(1, ("TM_Audit::adp_send_audit: Error occurred attempting to write rollover control point"
                                 " %d.\n", lv_error2));
                     abort();
                 }
             }
       }
       else if ((!iv_notified_threshold) &&
             (lv_buffer_pos.rba > CP_THRESHOLD))
       {
             TMTrace(3, ("adp_send_audit: THRESHOLD from " PFLL " to %d\n", iv_position, lv_buffer_pos.Sequence));
             lv_notify = 2;
             iv_notified_threshold = true;
       }
   }

    iv_mutex.unlock();
    TMTrace(2, ("TM_Audit::adp_send_audit: EXIT. notify=%d, vsn=" PFLL ", notified threshold=%d\n",
        lv_notify, iv_vsn, iv_notified_threshold));
    return lv_notify;
}
// -------------------------------------------------------------
// adp_module_init
// Purpose - initialize module for use
// -------------------------------------------------------------
void TM_Audit::adp_module_init()
{
    TMTrace(2, ("TM_Audit::adp_module_init: ENTER\n"));

    MS_Mon_Reg_Get_Type lv_info;
    char  lv_my_pname[8+1];
    char  lv_tlog_pname[8+1];
    int   lv_len = 8;
    int   lv_err = 0;
    int32 lv_oid = 0;
    TPT_DECL (lv_phandle);
    int   lv_TLOG_index = TLOG_AuditTrailIndex;

    if (iv_initialized)
        return;

    msg_mon_get_my_process_name(lv_my_pname,lv_len);

    lv_err = msg_mon_reg_get(MS_Mon_ConfigType_Process, false,
                             lv_my_pname, (char *)"TMASE", &lv_info);
    if ((lv_err == 0) && (lv_info.num_returned == 1))
    {
        strncpy (lv_tlog_pname, lv_info.list[0].value, 8);
        TMTrace(3, ("TM_Audit::adp_module_init: %s opening TLOG %s\n", lv_my_pname, lv_tlog_pname));

        lv_err = msg_mon_open_process(lv_tlog_pname, &lv_phandle, &lv_oid);

        if (!lv_err)
        {
            memcpy (&iv_adp_phandle, &lv_phandle, sizeof (SB_Phandle_Type));
            // If we using multiple TLOGs, we need to set the correct index (= nid)
            if (gv_tm_info.TLOGperTM())
               lv_TLOG_index = gv_tm_info.nid();
            iv_initialized = true;
        }
        else
        {
           TMTrace(1, ("TM_Audit::adp_module_init: error %d opening TLOG %s\n",
                    lv_err, lv_tlog_pname));
           tm_log_event(DTM_AUDIT_FAILED_ADPOPEN, SQ_LOG_CRIT, "DTM_AUDIT_FAILED_ADPOPEN",
                        lv_err, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                        -1, -1, -1, -1, lv_my_pname);

           // Fast fail here if $TLOG is gone as we can't continue in M8.
           if (lv_err == FENOSUCHDEV)
           {
              gv_tm_info.error_shutdown_abrupt(lv_err);
              // Abort without core - don't need the core in this case.
              struct rlimit limit;
              limit.rlim_cur = 0;
              limit.rlim_max = 0;
              setrlimit(RLIMIT_CORE, &limit);
              abort();
           }
           iv_initialized = false;
        }
    } // lv_err = 0
    else
       iv_initialized = false;

    if (gv_tm_info.TLOGperTM())
    {
       TMTrace(2, ("TM_Audit::adp_module_init: EXIT, initialized = %d, name %s, TLOG%d.\n",
               iv_initialized, ia_vol_name2, lv_TLOG_index));
    }
    else
    {
       TMTrace(2, ("TM_Audit::adp_module_init: EXIT, initialized = %d, name %s, single TLOG.\n",
               iv_initialized, ia_vol_name2));
    }
}
bool TM_Audit::prepare_trans_state(Audit_Transaction_State *pp_state_rec,
                                   char *pp_write_buffer, TM_Transid_Type *pv_transid,
                                   int32 pv_nid, int32 pv_state, int32 pv_abort_flags, int64 *pp_vsn=0)
{
    int32   lv_rec_type = -1;
    bool    lv_force = true;

    if (gv_tm_info.iv_trace_level >= 2)
    {
       TM_Txid_Internal *lp_transid = (TM_Txid_Internal *) pv_transid;
       trace_printf("TM_Audit::prepare_trans_state : ENTRY, txn ID (%d,%d), state=%d.\n",
                    lp_transid->iv_node, lp_transid->iv_seq_num, pv_state);
    }

    pv_nid = pv_nid; //810

    switch (pv_state)
    {
    case TM_TX_STATE_BEGINNING:
    case TM_TX_STATE_ACTIVE:
    case TM_TX_STATE_IDLE: //XARM only
    case TM_TX_STATE_NOTX:
    case TM_TX_STATE_PREPARING:
        {
            lv_rec_type = Active_Trans_State;
            break;
        }
    case TM_TX_STATE_FORGOTTEN:
    case TM_TX_STATE_FORGOTTEN_HEUR:
    case TM_TX_STATE_FORGETTING:
    case TM_TX_STATE_TERMINATING: //XARM only
        {
            lv_rec_type = Forgotten_Trans_State;
            lv_force = false;
            break;
        }
    case TM_TX_STATE_COMMITTED:
    case TM_TX_STATE_COMMITTING:
        {
            lv_rec_type = Committed_Trans_State;
            break;
        }
    case TM_TX_STATE_ABORTING:
    case TM_TX_STATE_ABORTING_PART2:
        {
            lv_rec_type = Aborting_Trans_State;
            break;
        }
    case TM_TX_STATE_ABORTED:
        {
            lv_rec_type = Aborted_Trans_State;
            break;
        }
    case TM_TX_STATE_HUNGCOMMITTED:
        {
            lv_rec_type = HungCommitted_Trans_State;
            break;
        }
    case TM_TX_STATE_HUNGABORTED:
        {
            lv_rec_type = HungAborted_Trans_State;
            break;
        }
    default:
        {
            // bad transaction state record. very, very bad!
            tm_log_event(DTM_TM_AUD_INVALID_TRANS_STATE, SQ_LOG_CRIT, "DTM_TM_AUD_INVALID_TRANS_STATE",
               -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,pv_state);
            TMTrace(2, ("TM_Audit::prepare_trans_state : Invalid transaction state %d reached.\n",
                    pv_state));
            abort();
        }
    }

    initialize_hdr(&pp_state_rec->iv_hdr, REC_SIZE,
                  TM_Transaction_State, pv_transid);

    pp_state_rec->iv_time_stamp = SB_Thread::Sthr::time();
    pp_state_rec->iv_state = (short) lv_rec_type;
    pp_state_rec->iv_abort_flags = pv_abort_flags;
    pp_state_rec->iv_length = pp_state_rec->iv_hdr.iv_length;
    iv_mutex.lock();
    memcpy (&pp_state_rec->iv_hdr.iv_vsn, &iv_vsn, sizeof (iv_vsn));
    if (pp_vsn != NULL)
        *pp_vsn = iv_vsn;
    iv_vsn++;
    iv_mutex.unlock();
    memcpy (pp_write_buffer, pp_state_rec, REC_SIZE);
    TMTrace(2, ("TM_Audit::prepare_trans_state : EXIT\n"));
    return lv_force;
}