<commit_before>#include "thread_manager.h"
#include "core_manager.h"
#include "performance_model.h"
#include "hooks_manager.h"
#include "config.h"
#include "log.h"
#include "transport.h"
#include "simulator.h"
#include "clock_skew_minimization_object.h"
#include "core.h"
#include "thread.h"
#include "scheduler.h"
#include "syscall_server.h"

#include <sys/syscall.h>
#include "os_compat.h"

const char* ThreadManager::stall_type_names[] = {
   "unscheduled", "broken", "join", "mutex", "cond", "barrier", "futex", "pause", "sleep"
};
static_assert(ThreadManager::STALL_TYPES_MAX == sizeof(ThreadManager::stall_type_names) / sizeof(char*),
              "Not enough values in ThreadManager::stall_type_names");

ThreadManager::ThreadManager()
   : m_thread_tls(TLS::create())
   , m_scheduler(Scheduler::create(this))
{
}

ThreadManager::~ThreadManager()
{
   for (UInt32 i = 0; i < m_thread_state.size(); i++)
   {
      if (m_thread_state[i].status != Core::IDLE)
         fprintf(stderr, "Thread %d still active when ThreadManager destructs\n", i);
      delete m_threads[i];
   }

   delete m_thread_tls;
   delete m_scheduler;
}

Thread* ThreadManager::getThreadFromID(thread_id_t thread_id)
{
   LOG_ASSERT_ERROR((size_t)thread_id < m_threads.size(), "Invalid thread_id %d", thread_id);
   return m_threads.at(thread_id);
}
Thread* ThreadManager::getCurrentThread(int threadIndex)
{
   return m_thread_tls->getPtr<Thread>(threadIndex);
}

Thread* ThreadManager::findThreadByTid(pid_t tid)
{
   for (UInt32 thread_id = 0; thread_id < m_threads.size(); ++thread_id)
   {
      if (m_threads.at(thread_id)->m_os_info.tid == tid)
         return m_threads.at(thread_id);
   }
   return NULL;
}

Thread* ThreadManager::createThread(app_id_t app_id)
{
   ScopedLock sl(m_thread_lock);
   return createThread_unlocked(app_id);
}

Thread* ThreadManager::createThread_unlocked(app_id_t app_id)
{
   thread_id_t thread_id = m_threads.size();
   Thread *thread = new Thread(thread_id, app_id);
   m_threads.push_back(thread);
   m_thread_state.push_back(ThreadState());
   m_thread_state[thread->getId()].status = Core::INITIALIZING;

   core_id_t core_id = m_scheduler->threadCreate(thread_id);
   if (core_id != INVALID_CORE_ID)
   {
      Core *core = Sim()->getCoreManager()->getCoreFromID(core_id);
      thread->setCore(core);
      core->setState(Core::INITIALIZING);
   }

   return thread;
}

void ThreadManager::onThreadStart(thread_id_t thread_id, SubsecondTime time)
{
   ScopedLock sl(m_thread_lock);
   LOG_PRINT("onThreadStart(%i)", thread_id);

   Thread *thread = getThreadFromID(thread_id);

   m_thread_tls->set(thread);
   thread->updateCoreTLS();

   HooksManager::ThreadTime args = { thread_id: thread_id, time: time };
   Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_START, (UInt64)&args);

   Core *core = thread->getCore();
   if (core)
   {
      // Set the CoreState to 'RUNNING'
      core->setState(Core::RUNNING);

      PerformanceModel *pm = core->getPerformanceModel();
      // If the core already has a later time, we have to wait
      time = std::max(time, pm->getElapsedTime());
      pm->queueDynamicInstruction(new SpawnInstruction(time));

      LOG_PRINT("Setting status[%i] -> RUNNING", thread_id);
      m_thread_state[thread_id].status = Core::RUNNING;

      HooksManager::ThreadMigrate args = { thread_id: thread_id, core_id: core->getId(), time: time };
      Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_MIGRATE, (UInt64)&args);
   }
   else
   {
      m_thread_state[thread_id].status = Core::STALLED;
      m_thread_state[thread_id].stalled_reason = STALL_UNSCHEDULED;
   }

   if (m_thread_state[thread_id].waiter != INVALID_THREAD_ID)
   {
      getThreadFromID(m_thread_state[thread_id].waiter)->signal(time);
      m_thread_state[thread_id].waiter = INVALID_THREAD_ID;
   }
}

void ThreadManager::onThreadExit(thread_id_t thread_id)
{
   ScopedLock sl(m_thread_lock);

   Thread *thread = getThreadFromID(thread_id);
   CoreManager *core_manager = Sim()->getCoreManager();
   SubsecondTime time = core_manager->getCurrentCore()->getPerformanceModel()->getElapsedTime();

   LOG_ASSERT_ERROR((UInt32)thread_id < m_thread_state.size(), "Thread id out of range: %d", thread_id);

   assert(m_thread_state[thread_id].status == Core::RUNNING);
   m_thread_state[thread_id].status = Core::IDLE;

   // Implement pthread_join
   wakeUpWaiter(thread_id, time);

   // Implement CLONE_CHILD_CLEARTID
   if (thread->m_os_info.clear_tid)
   {
      uint32_t zero = 0;
      thread->getCore()->accessMemory(Core::NONE, Core::WRITE, thread->m_os_info.tid_ptr, (char*)&zero, sizeof(zero));

      SubsecondTime end_time; // ignored
      Sim()->getSyscallServer()->futexWake(thread_id, (int*)thread->m_os_info.tid_ptr, 1, FUTEX_BITSET_MATCH_ANY, time, end_time);
   }

   HooksManager::ThreadTime args = { thread_id: thread_id, time: time };
   Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_EXIT, (UInt64)&args);
   if (Sim()->getClockSkewMinimizationServer())
      Sim()->getClockSkewMinimizationServer()->signal();

   // Set the CoreState to 'IDLE'
   core_manager->getCurrentCore()->setState(Core::IDLE);

   m_thread_tls->set(NULL);
   thread->setCore(NULL);
   thread->updateCoreTLS();
}

thread_id_t ThreadManager::spawnThread(thread_id_t thread_id, app_id_t app_id, thread_func_t func, void *arg)
{
   ScopedLock sl(getLock());

   SubsecondTime time_start = SubsecondTime::Zero();
   if (thread_id != INVALID_THREAD_ID)
   {
      Thread *thread = getThreadFromID(thread_id);
      Core *core = thread->getCore();
      time_start = core->getPerformanceModel()->getElapsedTime();
   }

   LOG_PRINT("(1) spawnThread with func: %p and arg: %p", func, arg);<commit_msg>[thread_manager] In onThreadExit(), use thread->getCore(), rather thangetCurrentCore() through the TLS which is both slower and potentially out-of-date<commit_after>#include "thread_manager.h"
#include "core_manager.h"
#include "performance_model.h"
#include "hooks_manager.h"
#include "config.h"
#include "log.h"
#include "transport.h"
#include "simulator.h"
#include "clock_skew_minimization_object.h"
#include "core.h"
#include "thread.h"
#include "scheduler.h"
#include "syscall_server.h"

#include <sys/syscall.h>
#include "os_compat.h"

const char* ThreadManager::stall_type_names[] = {
   "unscheduled", "broken", "join", "mutex", "cond", "barrier", "futex", "pause", "sleep"
};
static_assert(ThreadManager::STALL_TYPES_MAX == sizeof(ThreadManager::stall_type_names) / sizeof(char*),
              "Not enough values in ThreadManager::stall_type_names");

ThreadManager::ThreadManager()
   : m_thread_tls(TLS::create())
   , m_scheduler(Scheduler::create(this))
{
}

ThreadManager::~ThreadManager()
{
   for (UInt32 i = 0; i < m_thread_state.size(); i++)
   {
      if (m_thread_state[i].status != Core::IDLE)
         fprintf(stderr, "Thread %d still active when ThreadManager destructs\n", i);
      delete m_threads[i];
   }

   delete m_thread_tls;
   delete m_scheduler;
}

Thread* ThreadManager::getThreadFromID(thread_id_t thread_id)
{
   LOG_ASSERT_ERROR((size_t)thread_id < m_threads.size(), "Invalid thread_id %d", thread_id);
   return m_threads.at(thread_id);
}
Thread* ThreadManager::getCurrentThread(int threadIndex)
{
   return m_thread_tls->getPtr<Thread>(threadIndex);
}

Thread* ThreadManager::findThreadByTid(pid_t tid)
{
   for (UInt32 thread_id = 0; thread_id < m_threads.size(); ++thread_id)
   {
      if (m_threads.at(thread_id)->m_os_info.tid == tid)
         return m_threads.at(thread_id);
   }
   return NULL;
}

Thread* ThreadManager::createThread(app_id_t app_id)
{
   ScopedLock sl(m_thread_lock);
   return createThread_unlocked(app_id);
}

Thread* ThreadManager::createThread_unlocked(app_id_t app_id)
{
   thread_id_t thread_id = m_threads.size();
   Thread *thread = new Thread(thread_id, app_id);
   m_threads.push_back(thread);
   m_thread_state.push_back(ThreadState());
   m_thread_state[thread->getId()].status = Core::INITIALIZING;

   core_id_t core_id = m_scheduler->threadCreate(thread_id);
   if (core_id != INVALID_CORE_ID)
   {
      Core *core = Sim()->getCoreManager()->getCoreFromID(core_id);
      thread->setCore(core);
      core->setState(Core::INITIALIZING);
   }

   return thread;
}

void ThreadManager::onThreadStart(thread_id_t thread_id, SubsecondTime time)
{
   ScopedLock sl(m_thread_lock);
   LOG_PRINT("onThreadStart(%i)", thread_id);

   Thread *thread = getThreadFromID(thread_id);

   m_thread_tls->set(thread);
   thread->updateCoreTLS();

   HooksManager::ThreadTime args = { thread_id: thread_id, time: time };
   Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_START, (UInt64)&args);

   Core *core = thread->getCore();
   if (core)
   {
      // Set the CoreState to 'RUNNING'
      core->setState(Core::RUNNING);

      PerformanceModel *pm = core->getPerformanceModel();
      // If the core already has a later time, we have to wait
      time = std::max(time, pm->getElapsedTime());
      pm->queueDynamicInstruction(new SpawnInstruction(time));

      LOG_PRINT("Setting status[%i] -> RUNNING", thread_id);
      m_thread_state[thread_id].status = Core::RUNNING;

      HooksManager::ThreadMigrate args = { thread_id: thread_id, core_id: core->getId(), time: time };
      Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_MIGRATE, (UInt64)&args);
   }
   else
   {
      m_thread_state[thread_id].status = Core::STALLED;
      m_thread_state[thread_id].stalled_reason = STALL_UNSCHEDULED;
   }

   if (m_thread_state[thread_id].waiter != INVALID_THREAD_ID)
   {
      getThreadFromID(m_thread_state[thread_id].waiter)->signal(time);
      m_thread_state[thread_id].waiter = INVALID_THREAD_ID;
   }
}

void ThreadManager::onThreadExit(thread_id_t thread_id)
{
   ScopedLock sl(m_thread_lock);

   LOG_ASSERT_ERROR((UInt32)thread_id < m_thread_state.size(), "Thread id out of range: %d", thread_id);

   Thread *thread = getThreadFromID(thread_id);
   Core *core = thread->getCore();
   LOG_ASSERT_ERROR(core != NULL, "Thread ended while not running on a core?");

   SubsecondTime time = core->getPerformanceModel()->getElapsedTime();

   assert(m_thread_state[thread_id].status == Core::RUNNING);
   m_thread_state[thread_id].status = Core::IDLE;

   // Implement pthread_join
   wakeUpWaiter(thread_id, time);

   // Implement CLONE_CHILD_CLEARTID
   if (thread->m_os_info.clear_tid)
   {
      uint32_t zero = 0;
      core->accessMemory(Core::NONE, Core::WRITE, thread->m_os_info.tid_ptr, (char*)&zero, sizeof(zero));

      SubsecondTime end_time; // ignored
      Sim()->getSyscallServer()->futexWake(thread_id, (int*)thread->m_os_info.tid_ptr, 1, FUTEX_BITSET_MATCH_ANY, time, end_time);
   }

   HooksManager::ThreadTime args = { thread_id: thread_id, time: time };
   Sim()->getHooksManager()->callHooks(HookType::HOOK_THREAD_EXIT, (UInt64)&args);
   if (Sim()->getClockSkewMinimizationServer())
      Sim()->getClockSkewMinimizationServer()->signal();

   // Set the CoreState to 'IDLE'
   core->setState(Core::IDLE);

   m_thread_tls->set(NULL);
   thread->setCore(NULL);
   thread->updateCoreTLS();
}

thread_id_t ThreadManager::spawnThread(thread_id_t thread_id, app_id_t app_id, thread_func_t func, void *arg)
{
   ScopedLock sl(getLock());

   SubsecondTime time_start = SubsecondTime::Zero();
   if (thread_id != INVALID_THREAD_ID)
   {
      Thread *thread = getThreadFromID(thread_id);
      Core *core = thread->getCore();
      time_start = core->getPerformanceModel()->getElapsedTime();
   }

   LOG_PRINT("(1) spawnThread with func: %p and arg: %p", func, arg);
{'fraction_non_alphanumeric': 0.1411875913036006, 'fraction_numerical': 0.003265446420898857, 'mean_word_length': 4.89564336372847, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 97, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5301122', 'n_tokens_mistral': 4087, 'n_tokens_neox': 4033, 'n_words': 693}