<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 <NAME>. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/kernel/objects/xthread.h"

#include <gflags/gflags.h>

#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/mutex.h"
#include "xenia/base/threading.h"
#include "xenia/cpu/cpu.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/native_list.h"
#include "xenia/kernel/objects/xevent.h"
#include "xenia/kernel/objects/xuser_module.h"
#include "xenia/profiling.h"

DEFINE_bool(ignore_thread_priorities, true,
            "Ignores game-specified thread priorities.");
DEFINE_bool(ignore_thread_affinities, true,
            "Ignores game-specified thread affinities.");

namespace xe {
namespace kernel {

using namespace xe::cpu;

uint32_t next_xthread_id = 0;
thread_local XThread* current_thread_tls = nullptr;
xe::mutex critical_region_;

XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
                 uint32_t xapi_thread_startup, uint32_t start_address,
                 uint32_t start_context, uint32_t creation_flags)
    : XObject(kernel_state, kTypeThread),
      thread_id_(++next_xthread_id),
      thread_handle_(0),
      pcr_address_(0),
      thread_state_address_(0),
      thread_state_(0),
      irql_(0) {
  creation_params_.stack_size = stack_size;
  creation_params_.xapi_thread_startup = xapi_thread_startup;
  creation_params_.start_address = start_address;
  creation_params_.start_context = start_context;

  // top 8 bits = processor ID (or 0 for default)
  // bit 0 = 1 to create suspended
  creation_params_.creation_flags = creation_flags;

  // Adjust stack size - min of 16k.
  if (creation_params_.stack_size < 16 * 1024) {
    creation_params_.stack_size = 16 * 1024;
  }

  apc_list_ = new NativeList(kernel_state->memory());

  event_ = object_ref<XEvent>(new XEvent(kernel_state));
  event_->Initialize(true, false);

  char thread_name[32];
  snprintf(thread_name, xe::countof(thread_name), "XThread%04X", handle());
  set_name(thread_name);

  // The kernel does not take a reference. We must unregister in the dtor.
  kernel_state_->RegisterThread(this);
}

XThread::~XThread() {
  // Unregister first to prevent lookups while deleting.
  kernel_state_->UnregisterThread(this);

  delete apc_list_;

  event_.reset();

  PlatformDestroy();

  if (thread_state_) {
    delete thread_state_;
  }
  kernel_state()->memory()->SystemHeapFree(scratch_address_);
  kernel_state()->memory()->SystemHeapFree(tls_address_);
  kernel_state()->memory()->SystemHeapFree(pcr_address_);

  if (thread_handle_) {
    // TODO(benvanik): platform kill
    XELOGE("Thread disposed without exiting");
  }
}

bool XThread::IsInThread(XThread* other) { return current_thread_tls == other; }

XThread* XThread::GetCurrentThread() {
  XThread* thread = current_thread_tls;
  if (!thread) {
    assert_always("Attempting to use kernel stuff from a non-kernel thread");
  }
  return thread;
}

uint32_t XThread::GetCurrentThreadHandle() {
  XThread* thread = XThread::GetCurrentThread();
  return thread->handle();
}

uint32_t XThread::GetCurrentThreadId(const uint8_t* pcr) {
  return xe::load_and_swap<uint32_t>(pcr + 0x2D8 + 0x14C);
}

uint32_t XThread::last_error() {
  uint8_t* p = memory()->TranslateVirtual(thread_state_address_);
  return xe::load_and_swap<uint32_t>(p + 0x160);
}

void XThread::set_last_error(uint32_t error_code) {
  uint8_t* p = memory()->TranslateVirtual(thread_state_address_);
  xe::store_and_swap<uint32_t>(p + 0x160, error_code);
}

void XThread::set_name(const std::string& name) {
  name_ = name;
  xe::threading::set_name(thread_handle_, name);
}

uint8_t GetFakeCpuNumber(uint8_t proc_mask) {
  if (!proc_mask) {
    return 0;  // is this reasonable?
  }
  assert_false(proc_mask & 0xC0);

  uint8_t cpu_number = 7 - xe::lzcnt(proc_mask);
  assert_true(1 << cpu_number == proc_mask);
  return cpu_number;
}

X_STATUS XThread::Create() {
  // Allocate thread state block from heap.
  // This is set as r13 for user code and some special inlined Win32 calls
  // (like GetLastError/etc) will poke it directly.
  // We try to use it as our primary store of data just to keep things all
  // consistent.
  // 0x000: pointer to tls data
  // 0x100: pointer to self?
  // 0x14C: thread id
  // 0x150: if >0 then error states don't get set
  // 0x160: last error
  // So, at offset 0x100 we have a 4b pointer to offset 200, then have the
  // structure.
  pcr_address_ = memory()->SystemHeapAlloc(0x2D8 + 0xAB0);
  thread_state_address_ = pcr_address_ + 0x2D8;
  if (!thread_state_address_) {
    XELOGW("Unable to allocate thread state block");
    return X_STATUS_NO_MEMORY;
  }

  // Set native info.
  SetNativePointer(thread_state_address_, true);

  auto module = kernel_state()->GetExecutableModule();

  // Allocate thread scratch.
  // This is used by interrupts/APCs/etc so we can round-trip pointers through.
  scratch_size_ = 4 * 16;
  scratch_address_ = memory()->SystemHeapAlloc(scratch_size_);

  // Allocate TLS block.
  uint32_t tls_size = 32;  // Default 32 (is this OK?)
  if (module && module->xex_header()) {
    const xe_xex2_header_t* header = module->xex_header();

    // FIXME: Is this correct?
    tls_size = header->tls_info.data_size;
  }

  tls_address_ = memory()->SystemHeapAlloc(tls_size);
  if (!tls_address_) {
    XELOGW("Unable to allocate thread local storage block");
    return X_STATUS_NO_MEMORY;
  }

  // Copy in default TLS info (or zero it out)
  if (module && module->xex_header()) {
    const xe_xex2_header_t* header = module->xex_header();

    // Copy in default TLS info.
    memory()->Copy(tls_address_, header->tls_info.raw_data_address, header->tls_info.data_size);
  } else {
    memory()->Fill(tls_address_, tls_size, 0);
  }

  // Allocate processor thread state.
  // This is thread safe.
  thread_state_ = new ThreadState(kernel_state()->processor(), thread_id_,
                                  ThreadStackType::kUserStack, 0,
                                  creation_params_.stack_size, pcr_address_);
  XELOGI("XThread%04X (%X) Stack: %.8X-%.8X", handle(),
         thread_state_->thread_id(), thread_state_->stack_limit(),
         thread_state_->stack_base());

  // Exports use this to get the kernel.
  thread_state_->context()->kernel_state = kernel_state_;

  uint8_t proc_mask =
      static_cast<uint8_t>(creation_params_.creation_flags >> 24);

  uint8_t* pcr = memory()->TranslateVirtual(pcr_address_);
  std::memset(pcr, 0x0, 0x2D8 + 0xAB0);  // Zero the PCR
  xe::store_and_swap<uint32_t>(pcr + 0x000, tls_address_);
  xe::store_and_swap<uint32_t>(pcr + 0x030, pcr_address_);
  xe::store_and_swap<uint32_t>(pcr + 0x070, thread_state_->stack_address() +
                                                thread_state_->stack_size());
  xe::store_and_swap<uint32_t>(pcr + 0x074, thread_state_->stack_address());<commit_msg>Fixing (or at least improving) TLS.<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 <NAME>. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/kernel/objects/xthread.h"

#include <gflags/gflags.h>

#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/mutex.h"
#include "xenia/base/threading.h"
#include "xenia/cpu/cpu.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/native_list.h"
#include "xenia/kernel/objects/xevent.h"
#include "xenia/kernel/objects/xuser_module.h"
#include "xenia/profiling.h"

DEFINE_bool(ignore_thread_priorities, true,
            "Ignores game-specified thread priorities.");
DEFINE_bool(ignore_thread_affinities, true,
            "Ignores game-specified thread affinities.");

namespace xe {
namespace kernel {

using namespace xe::cpu;

uint32_t next_xthread_id = 0;
thread_local XThread* current_thread_tls = nullptr;
xe::mutex critical_region_;

XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
                 uint32_t xapi_thread_startup, uint32_t start_address,
                 uint32_t start_context, uint32_t creation_flags)
    : XObject(kernel_state, kTypeThread),
      thread_id_(++next_xthread_id),
      thread_handle_(0),
      pcr_address_(0),
      thread_state_address_(0),
      thread_state_(0),
      irql_(0) {
  creation_params_.stack_size = stack_size;
  creation_params_.xapi_thread_startup = xapi_thread_startup;
  creation_params_.start_address = start_address;
  creation_params_.start_context = start_context;

  // top 8 bits = processor ID (or 0 for default)
  // bit 0 = 1 to create suspended
  creation_params_.creation_flags = creation_flags;

  // Adjust stack size - min of 16k.
  if (creation_params_.stack_size < 16 * 1024) {
    creation_params_.stack_size = 16 * 1024;
  }

  apc_list_ = new NativeList(kernel_state->memory());

  event_ = object_ref<XEvent>(new XEvent(kernel_state));
  event_->Initialize(true, false);

  char thread_name[32];
  snprintf(thread_name, xe::countof(thread_name), "XThread%04X", handle());
  set_name(thread_name);

  // The kernel does not take a reference. We must unregister in the dtor.
  kernel_state_->RegisterThread(this);
}

XThread::~XThread() {
  // Unregister first to prevent lookups while deleting.
  kernel_state_->UnregisterThread(this);

  delete apc_list_;

  event_.reset();

  PlatformDestroy();

  if (thread_state_) {
    delete thread_state_;
  }
  kernel_state()->memory()->SystemHeapFree(scratch_address_);
  kernel_state()->memory()->SystemHeapFree(tls_address_);
  kernel_state()->memory()->SystemHeapFree(pcr_address_);

  if (thread_handle_) {
    // TODO(benvanik): platform kill
    XELOGE("Thread disposed without exiting");
  }
}

bool XThread::IsInThread(XThread* other) { return current_thread_tls == other; }

XThread* XThread::GetCurrentThread() {
  XThread* thread = current_thread_tls;
  if (!thread) {
    assert_always("Attempting to use kernel stuff from a non-kernel thread");
  }
  return thread;
}

uint32_t XThread::GetCurrentThreadHandle() {
  XThread* thread = XThread::GetCurrentThread();
  return thread->handle();
}

uint32_t XThread::GetCurrentThreadId(const uint8_t* pcr) {
  return xe::load_and_swap<uint32_t>(pcr + 0x2D8 + 0x14C);
}

uint32_t XThread::last_error() {
  uint8_t* p = memory()->TranslateVirtual(thread_state_address_);
  return xe::load_and_swap<uint32_t>(p + 0x160);
}

void XThread::set_last_error(uint32_t error_code) {
  uint8_t* p = memory()->TranslateVirtual(thread_state_address_);
  xe::store_and_swap<uint32_t>(p + 0x160, error_code);
}

void XThread::set_name(const std::string& name) {
  name_ = name;
  xe::threading::set_name(thread_handle_, name);
}

uint8_t GetFakeCpuNumber(uint8_t proc_mask) {
  if (!proc_mask) {
    return 0;  // is this reasonable?
  }
  assert_false(proc_mask & 0xC0);

  uint8_t cpu_number = 7 - xe::lzcnt(proc_mask);
  assert_true(1 << cpu_number == proc_mask);
  return cpu_number;
}

X_STATUS XThread::Create() {
  // Allocate thread state block from heap.
  // This is set as r13 for user code and some special inlined Win32 calls
  // (like GetLastError/etc) will poke it directly.
  // We try to use it as our primary store of data just to keep things all
  // consistent.
  // 0x000: pointer to tls data
  // 0x100: pointer to self?
  // 0x14C: thread id
  // 0x150: if >0 then error states don't get set
  // 0x160: last error
  // So, at offset 0x100 we have a 4b pointer to offset 200, then have the
  // structure.
  pcr_address_ = memory()->SystemHeapAlloc(0x2D8 + 0xAB0);
  thread_state_address_ = pcr_address_ + 0x2D8;
  if (!thread_state_address_) {
    XELOGW("Unable to allocate thread state block");
    return X_STATUS_NO_MEMORY;
  }

  // Set native info.
  SetNativePointer(thread_state_address_, true);

  auto module = kernel_state()->GetExecutableModule();

  // Allocate thread scratch.
  // This is used by interrupts/APCs/etc so we can round-trip pointers through.
  scratch_size_ = 4 * 16;
  scratch_address_ = memory()->SystemHeapAlloc(scratch_size_);

  // Allocate TLS block.
  // Games will specify a certain number of 4b slots that each thread will get.
  const uint32_t kDefaultTlsSlotCount = 32;
  uint32_t tls_slots = 0;
  uint32_t tls_extended_size = 0;
  if (module && module->xex_header()) {
    const xe_xex2_header_t* header = module->xex_header();
    tls_slots = header->tls_info.slot_count ? header->tls_info.slot_count : kDefaultTlsSlotCount;
    tls_extended_size = header->tls_info.data_size;
  } else {
    tls_slots = kDefaultTlsSlotCount;
  }

  // Allocate both the slots and the extended data.
  uint32_t tls_size = tls_slots * 4 + tls_extended_size;
  tls_address_ = memory()->SystemHeapAlloc(tls_size);
  if (!tls_address_) {
    XELOGW("Unable to allocate thread local storage block");
    return X_STATUS_NO_MEMORY;
  }

  // Zero all of TLS.
  memory()->Fill(tls_address_, tls_size, 0);
  if (tls_extended_size) {
    // If game has extended data, copy in the default values.
    const xe_xex2_header_t* header = module->xex_header();
    assert_not_zero(header->tls_info.raw_data_address);
    // TODO(benvanik): verify location relative to slots.
    memory()->Copy(tls_address_ + tls_size, header->tls_info.raw_data_address,
                   header->tls_info.raw_data_size);
  }

  // Allocate processor thread state.
  // This is thread safe.
  thread_state_ = new ThreadState(kernel_state()->processor(), thread_id_,
                                  ThreadStackType::kUserStack, 0,
                                  creation_params_.stack_size, pcr_address_);
  XELOGI("XThread%04X (%X) Stack: %.8X-%.8X", handle(),
         thread_state_->thread_id(), thread_state_->stack_limit(),
         thread_state_->stack_base());

  // Exports use this to get the kernel.
  thread_state_->context()->kernel_state = kernel_state_;

  uint8_t proc_mask =
      static_cast<uint8_t>(creation_params_.creation_flags >> 24);

  uint8_t* pcr = memory()->TranslateVirtual(pcr_address_);
  std::memset(pcr, 0x0, 0x2D8 + 0xAB0);  // Zero the PCR
  xe::store_and_swap<uint32_t>(pcr + 0x000, tls_address_);
  xe::store_and_swap<uint32_t>(pcr + 0x030, pcr_address_);
  xe::store_and_swap<uint32_t>(pcr + 0x070, thread_state_->stack_address() +
                                                thread_state_->stack_size());
  xe::store_and_swap<uint32_t>(pcr + 0x074, thread_state_->stack_address());
{'fraction_non_alphanumeric': 0.1444744290133614, 'fraction_numerical': 0.024090041466464818, 'mean_word_length': 3.981639344262295, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 105, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3728999', 'n_tokens_mistral': 5315, 'n_tokens_neox': 4921, 'n_words': 1110}