text: // LICENSE NOTICE ==================================================================================
  
  /**
   * netzhaut - Web Browser Engine
   * Copyright (C) 2020 The netzhaut Authors
   * Published under MIT
   */
  
  // INCLUDE =========================================================================================
  
  #include "Thread.h"
  #include "Process.h"
  
  #include "../Util/Time.h"
  #include "../Common/Log.h"
  #include "../Common/Macro.h"
  #include NH_FLOW
  #include NH_DEFAULT_CHECK
  
  #include <string.h>
  #include <stdio.h>
  
  #ifdef __unix__
      #include <sys/sysinfo.h>
      #include <sys/types.h>
      #include <sys/wait.h>
      #include <sys/time.h>
  #elif defined(_WIN32) || defined (WIN32)
      #include <windows.h>
  #endif
  
  // INIT ============================================================================================
  
  static inline void nh_initThread(
      nh_Thread *Thread_p)
  {
  NH_BEGIN()
  
      Thread_p->CurrentWorkload_p = NULL;
      Thread_p->depth = 0;
      Thread_p->id = 0;
  
  NH_SILENT_END()
  }
  
  static inline void nh_initWorkload(
      nh_Workload *Workload_p)
  {
  NH_BEGIN()
  
      Workload_p->signal = NH_SIGNAL_INACTIVE;
      Workload_p->crucial = NH_FALSE;
  
      Workload_p->SignalCounter.idle = 0;
      Workload_p->SignalCounter.ok = 0;
  
      Workload_p->name_p   = NULL;
      Workload_p->path_p   = NULL;
      Workload_p->args_p   = NULL;
      Workload_p->Thread_p = NULL;
  
      Workload_p->init_f = NULL;
      Workload_p->run_f = NULL;
  
      Workload_p->Timing.idleDelay = 0;
      Workload_p->Timing.runDelay  = 0;
      Workload_p->Timing.haltDelay = 0;
      Workload_p->Timing.waitDelay = 0;
      Workload_p->Timing.turnAround = 0.0;
  
  NH_SILENT_END()
  }
  
  nh_ThreadPool nh_initThreadPool()
  {
  NH_BEGIN()
  
      nh_ThreadPool ThreadPool;
      ThreadPool.threadCount = 0;
  
      nh_initThread(&ThreadPool.Main);
  
      for (int i = 0; i < NH_MAX_THREADS; ++i) {
          nh_initThread(&ThreadPool.Threads_p[i]);
      }
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) {
          nh_initWorkload(&ThreadPool.Workloads_p[i]);
      }
  
  NH_END(ThreadPool)
  }
  
  // RUN =============================================================================================
  
  NH_RESULT nh_runWorkload(
      nh_Workload *Workload_p, NH_BOOL *idle_p)
  {
  NH_BEGIN()
  
      NH_CHECK_NULL(Workload_p)
  
      nh_SystemTime TAT = nh_getSystemTime();
  
      if (Workload_p->signal == NH_SIGNAL_INIT) 
      {
          if (Workload_p->init_f != NULL) {
              Workload_p->args_p = Workload_p->init_f(Workload_p);
          }
          Workload_p->signal = NH_SIGNAL_DONE;
      }
      else {
          Workload_p->signal = Workload_p->run_f(Workload_p->args_p);
          if (Workload_p->signal != NH_SIGNAL_IDLE && idle_p) {*idle_p = NH_FALSE;}
          switch (Workload_p->signal)
          {
              case NH_SIGNAL_OK    : 
                  Workload_p->SignalCounter.ok++;
                  break;
              case NH_SIGNAL_IDLE  : 
                  Workload_p->SignalCounter.idle++;
                  break;
              case NH_SIGNAL_DONE  : 
              {
                  nh_initWorkload(Workload_p); 
                  NH_DIAGNOSTIC_END(NH_SUCCESS)
              }
              case NH_SIGNAL_ERROR : 
              {
                  nh_initWorkload(Workload_p); 
                  NH_DIAGNOSTIC_END(NH_ERROR_BAD_STATE) 
              }
          }
      }
  
      Workload_p->Timing.turnAround = nh_getSystemTimeDiffInSeconds(TAT, nh_getSystemTime());
  
  NH_DIAGNOSTIC_END(NH_SUCCESS)
  }
  
  unsigned int nh_runWorkloads()
  {
  NH_BEGIN()
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(0)}
  
      NH_BOOL idle = NH_TRUE;
      unsigned int count = 0;
      nh_Thread *Thread_p = nh_getThread();
  
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) 
      {
          nh_Workload *Workload_p = &ThreadPool_p->Workloads_p[i];
  
          if (Workload_p->Thread_p == Thread_p) 
          {
              Thread_p->CurrentWorkload_p = Workload_p;
              NH_CHECK(nh_runWorkload(Workload_p, &idle))
              count++;
          }
      }
  
      nh_logThread(ThreadPool_p, Thread_p);
  
      if (idle) {nh_sleepMs(10);}
  
  NH_END(count)
  }
  
  //static void nh_runThread(
  //    void *index_p)
  //{
  //    nh_Thread *Thread_p = &ThreadPool.Threads_p[*((int*)index_p)];
  //
  //#ifdef __unix__
  //    Thread_p->id = pthread_self();
  //#elif defined(_WIN32) || defined (WIN32)
  //    Thread_p->id = GetCurrentThreadId();
  //#endif
  //
  //NH_BEGIN()
  //
  //    nh_initializeThreadMemory();
  //
  //    NH_BOOL run = NH_TRUE;
  //    while (run) {run = nh_runWorkloads();}
  //
  //#ifdef __unix__
  //
  //    pthread_exit(NULL);
  //
  //#endif
  //
  //NH_SILENT_END()
  //}
  
  static void nh_startThread(
      int index)
  {
  NH_BEGIN()
  
  #ifdef __unix__
      // TODO
  #elif defined(_WIN32) || defined (WIN32)
  //        _beginthread(nh_runPhysicalThread, 0, (void*)Data_p);
  #endif
  
  NH_SILENT_END()
  }
  
  // KEEP RUNNING? ===================================================================================
  
  NH_BOOL nh_keepRunning()
  {
  NH_BEGIN()
  
      nh_checkForks();
  
  NH_END(nh_activeWorkloads(NH_TRUE) > 0 || nh_activeForks() > 0 ? NH_TRUE : NH_FALSE)
  }
  
  // ACTIVATE ========================================================================================
  
  static void nh_assignToThread(
      nh_ThreadPool *ThreadPool_p, nh_Workload *Workload_p)
  {
  NH_BEGIN()
  
      if (ThreadPool_p->threadCount == 0) {Workload_p->Thread_p = &ThreadPool_p->Main;}
      else {
          // TODO
      }
  
  NH_SILENT_END()
  }
  
  static void nh_waitForInitCompletion(
      nh_Workload *Workload_p)
  {
  NH_BEGIN()
  
      if (Workload_p->Thread_p == nh_getThread()) {
          nh_runWorkload(Workload_p, NULL);
      }
      else {while (Workload_p->signal != NH_SIGNAL_DONE) {}}
  
  NH_SILENT_END()
  }
  
  void *nh_activateWorkload(
      void *(*init_f)(nh_Workload*), NH_SIGNAL (*run_f)(void*), void *args_p, NH_BOOL crucial)
  {
  NH_BEGIN()
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {return NULL;}
  
      nh_Workload *Workload_p = NULL;
  
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) {
          if (ThreadPool_p->Workloads_p[i].signal == NH_SIGNAL_INACTIVE) {
              Workload_p = &ThreadPool_p->Workloads_p[i]; 
              break;
          }
      }
  
      if (Workload_p == NULL) {NH_END(NULL)}
  
      Workload_p->signal  = NH_SIGNAL_INIT;
      Workload_p->args_p  = args_p;
      Workload_p->init_f  = init_f;
      Workload_p->run_f   = run_f;
      Workload_p->crucial = crucial;
  
      nh_assignToThread(ThreadPool_p, Workload_p);
      nh_waitForInitCompletion(Workload_p);
  
  NH_END(Workload_p->args_p)
  }
  
  // COUNT ==========================================================================================
  
  int nh_activeThreads()
  {
  NH_BEGIN()
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(0)}
  
      int count = 0;
      for (int i = 0; i < NH_MAX_THREADS; ++i) {
          if (ThreadPool_p->Threads_p[i].id != 0) {count++;}
      }
  
  NH_END(count)
  }
  
  int nh_activeWorkloads(
      NH_BOOL onlyCrucial)
  {
  NH_BEGIN()
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(0)}
  
      int count = 0;
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) {
          if (ThreadPool_p->Workloads_p[i].signal != NH_SIGNAL_INACTIVE) {
              if (onlyCrucial && !ThreadPool_p->Workloads_p[i].crucial) {continue;}
              count++;
          }
      }
  
  NH_END(count)
  }
  
  // GET =============================================================================================
  
  nh_ThreadPool *_nh_getThreadPool()
  {
  return &NH_PROCESS_POOL.Main.ThreadPool;
  }
  
  nh_ThreadPool *nh_getThreadPool()
  {
  NH_BEGIN()
  NH_END(_nh_getThreadPool())
  }
  
  nh_Thread *_nh_getThread()
  {
      nh_ThreadPool *ThreadPool_p = _nh_getThreadPool();
      if (ThreadPool_p == NULL) {return NULL;}
  
      for (int i = 0; i < NH_MAX_THREADS; ++i) 
      {
  #ifdef __unix__
          if (ThreadPool_p->Threads_p[i].id == pthread_self())
  #elif defined(_WIN32) || defined (WIN32)
          if (ThreadPool_p->Threads_p[i].id == GetCurrentThreadId())
  #endif
          {
              return &ThreadPool_p->Threads_p[i];
          }
      }
  
      return &ThreadPool_p->Main;
  }
  
  nh_Thread *nh_getThread()
  {
  NH_BEGIN()
  NH_END(_nh_getThread())
  }
  
  nh_Workload *nh_getWorkloadFromArgs(
      void *args_p)
  {
  NH_BEGIN()
  
      if (args_p == NULL) {NH_END(NULL)}
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(NULL)}
  
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) {
          if (args_p == ThreadPool_p->Workloads_p[i].args_p) {
              NH_END(&ThreadPool_p->Workloads_p[i])
          }
      }
  
  NH_END(NULL)
  }
  
  nh_Thread *nh_getThreadFromArgs(
      void *args_p)
  {
  NH_BEGIN()
  
      if (args_p == NULL) {NH_END(NULL)}
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(NULL)}
  
      for (int i = 0; i < NH_MAX_WORKLOADS; ++i) {
          if (args_p == ThreadPool_p->Workloads_p[i].args_p) {
              NH_END(ThreadPool_p->Workloads_p[i].Thread_p)
          }
      }
  
  NH_END(NULL)
  }
  
  int nh_getThreadIndex()
  {
  NH_BEGIN()
  
      nh_ThreadPool *ThreadPool_p = nh_getThreadPool();
      if (ThreadPool_p == NULL) {NH_END(0)}
  
      nh_Thread *Thread_p = nh_getThread();
      for (int i = 0; i < NH_MAX_THREADS; ++i) {
          if (Thread_p == &ThreadPool_p->Threads_p[i]) {NH_END(i)}
      }
  
  NH_END(0)
  }
  
  // SLEEP ===========================================================================================
  
  NH_RESULT nh_sleepMs(
      int milliseconds)
  {
  NH_BEGIN()
  
  #ifdef WIN32
      Sleep(milliseconds);
  #elif _POSIX_C_SOURCE >= 199309L
      struct timespec ts;
      ts.tv_sec = milliseconds / 1000;
      ts.tv_nsec = (milliseconds % 1000) * 1000000;
      nanosleep(&ts, NULL);
  #else
      usleep(milliseconds * 1000);
  #endif
  
  NH_DIAGNOSTIC_END(NH_SUCCESS)
  }
  
  