/**
 * @brief Fork the calling thread. Return a pointer to the thread table entry
 * for the forked thread through rt.
 *
 * @param thread A pointer to the thread table entry for the thread to be
 * forked.
 * @outparam rt A pointer to the pointer to be set to the entry in the thread
 * table for the newly created thread.
 */
bool thread_fork2(const thread_t* thread, thread_t** rt)
{
    // If we got an invalid thread to fork, we can't do anything here. Fail out.
    if(!thread_in_table(thread))
        return false;

    // Find a free slot
    uint32_t destpos = thread_first_empty();

    // Couldn't find a free slot. Fail out.
    if(destpos == MAX_THREADS)
        return false;

    // Copy the thread, giving it a new tid; pass back the return status
    // and the thread table entry (if the outparam is a valid pointer).
    if(thread_copy(&thread_table[destpos], thread))
    {
        if(rt)
            (*rt) = &thread_table[destpos];
        return true;
    }

    return false;
}
Exemple #2
0
void TestThisThread()
{
  hadesmem::Thread thread(::GetCurrentThreadId());
  BOOST_TEST_EQ(thread, thread);
  hadesmem::Thread thread_new(::GetCurrentThreadId());
  BOOST_TEST_EQ(thread, thread_new);
  hadesmem::Thread const thread_moved(std::move(thread_new));
  hadesmem::Thread thread_copy(thread);
  BOOST_TEST_EQ(thread_copy, thread);
  thread = thread_copy;
  BOOST_TEST_EQ(thread, thread_copy);
  thread_new = std::move(thread_copy);
  BOOST_TEST_EQ(thread, thread_new);
  BOOST_TEST(thread >= thread);
  BOOST_TEST(thread <= thread);
  BOOST_TEST_EQ(thread.GetId(), ::GetCurrentThreadId());
  std::stringstream thread_str_1;
  thread_str_1.imbue(std::locale::classic());
  thread_str_1 << thread;
  DWORD thread_id_1;
  thread_str_1 >> thread_id_1;
  BOOST_TEST_EQ(thread.GetId(), thread_id_1);
  std::wstringstream thread_str_2;
  thread_str_2.imbue(std::locale::classic());
  thread_str_2 << thread;
  DWORD thread_id_2;
  thread_str_2 >> thread_id_2;
  BOOST_TEST_EQ(thread.GetId(), thread_id_2);

  hadesmem::detail::SmartHandle quit_event(
    ::CreateEvent(nullptr, TRUE, FALSE, nullptr));
  DWORD other_id = 0;
  hadesmem::detail::SmartHandle wait_thread(
    ::CreateThread(nullptr, 0, &WaitFunc, &quit_event, 0, &other_id));
  // Disgusting hack to work around a potential race condition where we suspend
  // the thread before it's actually waiting on our event, and instead it's
  // still in its initialization routine and ends up holding a lock that the
  // main thread will need (e.g. from malloc). See thread_140224_144550.dmp.
  ::Sleep(5 * 1000);
  auto const cleanup_thread_func = [&]()
  {

    BOOST_TEST_NE(::SetEvent(quit_event.GetHandle()), 0);
    BOOST_TEST_EQ(::WaitForSingleObject(wait_thread.GetHandle(), INFINITE),
                  WAIT_OBJECT_0);
  };
  {
    ScopeExit<decltype(cleanup_thread_func)> cleanup_thread(
      cleanup_thread_func);

    hadesmem::Thread const other_thread(other_id);
    BOOST_TEST_EQ(hadesmem::SuspendThread(other_thread), 0UL);
    BOOST_TEST_EQ(hadesmem::SuspendThread(other_thread), 1UL);
    BOOST_TEST_EQ(hadesmem::ResumeThread(other_thread), 2UL);
    BOOST_TEST_EQ(hadesmem::ResumeThread(other_thread), 1UL);

    {
      hadesmem::SuspendedThread const suspend_thread(other_thread.GetId());

      CONTEXT const full_context =
        hadesmem::GetThreadContext(other_thread, CONTEXT_FULL);
      CONTEXT const all_context =
        hadesmem::GetThreadContext(other_thread, CONTEXT_ALL);
      hadesmem::SetThreadContext(other_thread, full_context);
      hadesmem::SetThreadContext(other_thread, all_context);
      BOOST_TEST_THROWS(hadesmem::GetThreadContext(thread, CONTEXT_FULL),
                        hadesmem::Error);
    }

    {
      hadesmem::SuspendedProcess const suspend_process(::GetCurrentProcessId());
    }
  }
}