// Copyright © 1998 by Jon Shemitz, all rights reserved.
// Permission is hereby granted to freely use, modify, and
// distribute this source code PROVIDED that all six lines of
// this copyright and contact notice are included without any
// changes. Questions? Comments? Offers of work?
// mailto:jon@midnightbeach.com - http://www.midnightbeach.com

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "SynchedThreads.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)

// Simple threads

TSimpleThread::TSimpleThread( TThreadFunction _Action,
                              void* _Data, bool RunNow)
             : TThread(true), // initialize suspended
               ThreadFunction(_Action), Data(_Data)
{
  FreeOnTerminate = true;
  if (RunNow) Resume();
} // TSimpleThread::TSimpleThread

void TSimpleThread::AbortThread()
{
  Suspend(); // Can't kill a running thread
  Free();    // Kills thread
} // TSimpleThread::AbortThread

// Wait threads (basic synchronization)

void MsgWaitForSingleObject(HANDLE Handle)
{
  while (true)
    if (MsgWaitForMultipleObjects( 1, & Handle, False,
                                   INFINITE, QS_ALLINPUT )
        == WAIT_OBJECT_0 + 1) Application->ProcessMessages();
    else break;
} // MsgWaitForSingleObject

TProcessInformation SpawnProcess(char* Command)
{
  TStartupInfo StartupInfo;
  TProcessInformation Result;

  memset(& StartupInfo, 0, sizeof(TStartupInfo));
  StartupInfo.cb = sizeof(TStartupInfo);
  CreateProcess( NULL, Command, NULL, NULL, false, 0, NULL, NULL,
                 & StartupInfo, & Result );
  return Result;
} // SpawnProcess

TWaitThread::TWaitThread( TThreadFunction _Action, void* _Data )
           : TSimpleThread(_Action, _Data, false),
             AbortFlag(NULL) { };

void TWaitThread::AbortThread()
{
  assert(AbortFlag != NULL);
  *AbortFlag = true;
  inherited::AbortThread();
} // TWaitThread::AbortThread

void TWaitThread::Run(bool MsgWait) throw (EAbort*)
{
  bool Aborted = false;
  AbortFlag = & Aborted;
  Resume();
  if (MsgWait) MsgWaitForSingleObject((HANDLE) Handle);
  else inherited::WaitFor();
  if (Aborted) Abort;
} // TWaitThread::Run

void MsgWaitForThread( TWaitThread*& Thread,
                       TThreadFunction Handler, void* Data)
     throw (EAbort*)
{
  Thread = new TWaitThread(Handler, Data);
  Thread->MsgWaitFor();
  Thread = NULL;
} // TWaitThread::TWaitThread

// Stop/start threads

EAbortedThread::EAbortedThread()
              : std::runtime_error("Aborted thread") {};

EThreadInUse::EThreadInUse()
            : std::logic_error("Thread in use") {};

TStopStartThread::TStopStartThread()
                : TSimpleThread((TThreadFunction) NULL, NULL, false)
{
  Event = CreateEvent(NULL, true, false, NULL);
          // API call is smaller and simpler than VCL wrapper
  assert(Event != NULL);

  Waiting = Aborted = false;
} // TStopStartThread::TStopStartThread

void TStopStartThread::Run( TThreadFunction _Action, void* _Data,
                            bool MsgWait )
     throw (EAbort*, EAbortedThread, EThreadInUse)
{
  if (Waiting) throw EThreadInUse();
  if (Aborted) throw EAbortedThread();

  Waiting        = true;
  ThreadFunction = _Action;
  Data           = _Data;
  ResetEvent(Event);
  Resume();

  if (MsgWait) MsgWaitForSingleObject(Event);
  else WaitForSingleObject(Event, INFINITE);

  Waiting = false;
  if (Aborted) Abort(); // throw EAbort();
} // TStopStartThread::Run

void __fastcall TStopStartThread::Execute()
{
  while (!Terminated) {
    assert(ThreadFunction != NULL);
    ThreadFunction(Data);

    SetEvent(Event);
    Suspend();
  }
} // TStopStartThread::Execute

void TStopStartThread::AbortThread()
{
  Suspend(); // deleting a running thread leaves process alive
  Aborted = true;
  SetEvent(Event);
} // TStopStartThread::AbortThread