//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (C) 1993, 1996 by Borland International, All Rights Reserved
// Copyright (C) 1998  by Yura Bidus
//----------------------------------------------------------------------------
 
#if defined(BI_COMP_BORLANDC)
# pragma hdrstop
#endif
 
#define CHECKS_CPP
 
//
// Make sure __TRACE is defined so that we can provide
// run-time support in non-debug versions of the library.
//
#if defined(__TRACE)
#  define __TRACE_PREVIOUSLY_DEFINED
#endif
 
#undef  __TRACE
#define __TRACE
 
#if !defined(__TRACE_PREVIOUSLY_DEFINED)
#define _DONT_DECLARE_DEF_DIAG_GROUP // We need this define so checks.h won't try to declare Def diag group.
#endif
 
#include <owl/defs.h>
 
#if defined(BI_COMP_BORLANDC)
# pragma option -w-inl // Disable warning "Functions containing 'statement' is not expanded inline".
#endif
 
#include <owl/private/checks.h>
 
#if defined(UNICODE)
# if defined(BI_COMP_GNUC)
#    include <stdlib.h>
# else
#    include <malloc.h>
# endif
#endif
 
#if defined(BI_MULTI_THREAD_RTL)
#include <owl/thread.h>
#endif
 
#include <owl/module.h>
 
using namespace std;
namespace owl {
 
//
// Definition of the default diagnostic group "Def" (expands to TDiagGroupDef)
//
#if defined(_BUILDOWLDLL)
# if defined(__DEBUG) && defined(__TRACE_PREVIOUSLY_DEFINED)
    OWL_DIAG_DEFINE_EXPORTGROUP_INIT(OWL_INI, Def, 1, 0); // JJH
# else
    DIAG_DEFINE_EXPORTGROUP_INIT(OWL_INI, Def, 1, 0);
# endif
#else
# if defined(__DEBUG) && defined(__TRACE_PREVIOUSLY_DEFINED)
    OWL_DIAG_DEFINE_GROUP_INIT(OWL_INI, Def, 1, 0); // DLN
# else
    DIAG_DEFINE_GROUP_INIT(OWL_INI, Def, 1, 0);
# endif
#endif
 
struct TDiagProcessData
{
  TDiagBaseHook* GlobalHook;
  TDiagBase* DiagGroupStaticHead;
 
private:
 
  TDiagProcessData() {}
  TDiagProcessData(const TDiagProcessData&); // forbidden
  TDiagProcessData& operator =(const TDiagProcessData&); // forbidden
 
  static TDiagProcessData& GetInstance()
  {
    //
    // Note that while this lazy initialization avoids problems with global initialization order,
    // the initial call of this function is not thread-safe (pre C++11). This should not be a
    // problem here because diagnostics groups are initialized during program start-up, at which
    // time only the main thread should be running. But, as a safe-guard, we ensure this function
    // is called during program start-up using InitDiagProcessDataInstance below.
    //
    // This safe-guard can be removed when C++11 compliant compilers are mandated.
    //
    // Also note that the singleton construction may throw an exception. Since there is no way to
    // continue without this singleton, we make no attempt to handle it here. We assume that
    // the exception will terminate the program, or if it is handled, that subsequent calls will
    // be retried still within a single thread.
    //
    static TDiagProcessData d; // initial call (construction) not thread-safe pre-C++11
    return d;
  }
 
  static TDiagProcessData& InitDiagProcessDataInstance;
 
public:
 
#if defined(BI_MULTI_THREAD_RTL)
 
  template <class TData, bool Shared>
  class TLockT
    : public TMRSWSection::TLock
  {
  public:
 
    TData& Data;
 
    explicit TLockT(TDiagProcessData& data = TDiagProcessData::GetInstance())
    : TMRSWSection::TLock(data.Section, Shared, true), // wait
      Data(data)
    {}
  };
 
  typedef TLockT<TDiagProcessData, false> TLock;
  typedef TLockT<const TDiagProcessData, true> TConstLock;
 
  template <class TData, bool Shared>
  friend class TLockT;
 
private:
 
  TMRSWSection Section;
 
#else
 
  template <class TData>
  class TLockT
  {
  public:
 
    TData& Data;
 
    explicit TLockT(TDiagProcessData& data = TDiagProcessData::GetInstance())
    : Data(data)
    {}
  };
 
  typedef TLockT<TDiagProcessData> TLock;
  typedef TLockT<const TDiagProcessData> TConstLock;
 
  template <class TData>
  friend class TLockT;
 
#endif
 
};
 
//
// Ensure singleton initialization at start-up (single-threaded, safe).
//
TDiagProcessData& TDiagProcessData::InitDiagProcessDataInstance = TDiagProcessData::GetInstance();
 
void
TDiagBase::Trace(const tstring& msg, int level, LPCSTR fname, int line)
{
  Message("Trace", msg, level, fname, line);
}
 
void
TDiagBase::Warn(const tstring& msg, int level, LPCSTR fname, int line)
{
  Message("Warning", msg, level, fname, line);
}
 
TDiagBaseHook*
TDiagBase::SetGlobalHook(TDiagBaseHook* hook)
{
  TDiagProcessData::TLock lock;
  TDiagBaseHook* oldHook = lock.Data.GlobalHook;
  lock.Data.GlobalHook = hook;
  return oldHook;
}
 
TDiagBase::TDiagBase(LPCSTR name, bool enable, int level)
:
  Name(name),
  Enabled(enable),
  Level(level),
  LocalHook(0),
  NextGroup{nullptr}
{
  AddDiagGroup(this);
}
 
TDiagBase::~TDiagBase()
{
  RemoveDiagGroup(this);
}
 
void
TDiagBase::AddDiagGroup(TDiagBase* group)
{
  TDiagProcessData::TLock lock;
  if (lock.Data.DiagGroupStaticHead == 0)
    lock.Data.DiagGroupStaticHead = group;
  else
  {
    TDiagBase* last = lock.Data.DiagGroupStaticHead;
    while (last->NextGroup)
      last = last->NextGroup;
    last->NextGroup = group;
  }
}
 
void
TDiagBase::RemoveDiagGroup(TDiagBase* group)
{
  TDiagProcessData::TLock lock;
  if (lock.Data.DiagGroupStaticHead == group)
    lock.Data.DiagGroupStaticHead = group->NextGroup;
  else
  {
    TDiagBase* last = lock.Data.DiagGroupStaticHead;
    while (last->NextGroup != group)
      last = last->NextGroup;
    last->NextGroup = group->NextGroup;
  }
}
 
//
// NOTE: To access the linked list of diagnostics groups in a thread-safe manner, the caller of
// this function should ideally lock the structure for the duration of the access, since in theory
// other threads may otherwise mutate the linked list. In the current design, this is not possible
// since access to TDiagBase::NextGroup is not synchronized. In practice, the mutation of the
// linked list only happens at startup and shutdown, so this problem should not be an issue.
//
TDiagBase*
TDiagBase::GetDiagGroup(TDiagBase* curr)
{
  if(curr)
    return curr->NextGroup;
 
  TDiagProcessData::TConstLock lock;
  return lock.Data.DiagGroupStaticHead;
}
 
//
// Sends the specified message to the debug output device.
// If a global or local hook is registered, then it is used to output the string.
// Otherwise, the system debug output function is used.
//
void
TDiagBase::Output(const tstring& msg)
{
  {
    TDiagProcessData::TConstLock lock;
    if (lock.Data.GlobalHook)
    {
      lock.Data.GlobalHook->Output(this, msg.c_str());
      return;
    }
  }
  if (LocalHook)
    LocalHook->Output(this, msg.c_str());
  else
  {
    OWL_OUTPUT_DEBUG_STRING(msg.c_str());
  }
}
 
void
TDiagBase::Message(LPCSTR type, const tstring& msg, int level, LPCSTR file, int line)
{
  _USES_CONVERSION;
  tostringstream out;
#if defined(BI_COMP_MSC)
  out << _A2W(file) << _T("(");
  out << line << _T(") : ") << _A2W(type)
#else
  out << _A2W(type) << _T(" ");
  out << _A2W(file) << _T(" ") << line
#endif
    << _T(": [") << Name << _T(':') << level << _T("] ")
    << msg
    << _T("\r\n");
 
  Output(out.str());
}
 
static LONG TraceIndent = 0;
 
TDiagFunction::TDiagFunction(TDiagBase& group, int level, LPCTSTR function, LPCSTR file, int line)
:
  Group(group), Level(level), Function(function), File(file), Line(line),
  Enabled(group.IsEnabled() && level <= group.GetLevel())
{
  if (Enabled)
  {
    Trace(_T(">> "), 0);
    InterlockedIncrement(&TraceIndent);
  }
}
 
TDiagFunction::TDiagFunction(TDiagBase& group, int level, LPCTSTR function, LPCTSTR params, LPCSTR file, int line)
:
  Group(group), Level(level), Function(function), File(file), Line(line),
  Enabled(group.IsEnabled() && level <= group.GetLevel())
{
  if (Enabled)
  {
    Trace(_T(">> "), params);
    InterlockedIncrement(&TraceIndent);
  }
}
 
TDiagFunction::~TDiagFunction()
{
  if (Enabled)
  {
    InterlockedDecrement(&TraceIndent);
    Trace(_T("<< "), 0);
  }
}
 
void
TDiagFunction::Trace(LPCTSTR prefix, LPCTSTR params)
{
  tostringstream out;
  for (int indent = TraceIndent; indent--;)
    out << _T(" ");
  out << prefix << Function;
  if (params)
    out << _T(" (") << params << _T(")");
  Group.Trace(out.str().c_str(), Level, File, Line);
}
 
namespace
{
  tstring
  MakeString(LPCSTR type, const tstring& msg, LPCSTR file, int line)
  {
    _USES_CONVERSION;
    tostringstream s;
    s << _A2W(type) << _T(" failed in \"");
    s << _A2W(file) << _T("\" at line ") << line << _T(": ") << msg;
    return s.str();
  }
}
 
TDiagException::TDiagException(LPCSTR type, const tstring& msg, LPCSTR file, int line)
:
  std::exception(),
  delegate(MakeString(type, msg, file, line))
{}
 
const char*
TDiagException::what() const noexcept
{
  return delegate.what();
}
 
tstring
TDiagException::why() const
{
  return delegate.why();
}
 
int
TDiagException::BreakMessage(LPCSTR type, const tstring& msg, LPCSTR file, int line)
{
  _USES_CONVERSION;
 
  static LONG entranceCount = 0;
  struct TReentranceGuard
  {
    TReentranceGuard() {InterlockedIncrement(&entranceCount);}
    ~TReentranceGuard() {InterlockedDecrement(&entranceCount);}
  }
  guard;
 
  tstring error = MakeString(type, msg, file, line);
  if (entranceCount > 1)
  {
    TRACE(_T("Re-entrant call to TDiagException::BreakMessage, error: ") << error);
    OWL_BREAK
    TRACE(_T("Aborting TDiagException::BreakMessage..."));
    return IDABORT;
  }
 
  // Get the active popup window for the current thread.
  //
  HWND hParent = ::GetActiveWindow();
  if (hParent)
  {
    HWND hPopup = ::GetLastActivePopup(hParent);
    if (hPopup)
      hParent = hPopup;
  }
 
  // Temporarily remove any WM_QUIT message in the queue,
  // otherwise the message box will not display.
  //
  struct TQuitMsgHider
  {
    MSG QuitMsg;
    bool DidRemoveQuitMsg;
    TQuitMsgHider() : DidRemoveQuitMsg(PeekMessage(&QuitMsg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE)) {}
    ~TQuitMsgHider() {if (DidRemoveQuitMsg) PostQuitMessage(static_cast<int>(QuitMsg.wParam));}
  }
  qmh;
 
  error += _T("\n\nSelect Abort to terminate, Retry to debug, or Ignore to continue.");
  int ret = ::MessageBox(hParent, error.c_str(), _A2W(type),
    MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_TASKMODAL | MB_ICONHAND | MB_SETFOREGROUND);
  return ret;
}
 
TPreconditionFailure::TPreconditionFailure(const tstring& msg, LPCSTR file, int line)
: TDiagException("Precondition", msg, file, line)
{}
 
TCheckFailure::TCheckFailure(const tstring& msg, LPCSTR file, int line)
: TDiagException("Check", msg, file, line)
{}
 
} // OWL namespace

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: QuitMsg.

V811 Decreased performance. Excessive type casting: string -> char * -> string. Consider inspecting first argument of the function Trace.

V832 It's better to use '= default;' syntax instead of empty constructor body.