Integrating live coding feature (aka Live++) into UE4.
Allows fast iteration of C++ changes without restarting the application. To use, select the "Live Coding (Experimental)" mode from the drop down menu next to the editor's compile button, or type "LiveCoding" into the console for a monolithic build. Press Ctrl+Alt+F11 to find changes and compile.
Changes vs standalone Live++ version:
* UBT is used to execute builds. This allows standard UE4 adaptive unity mode, allows us to reuse object files when we do regular builds, supports using any build executor allowed by UBT (XGE, SNDBS, etc..).
* Adding new source files is supported.
* Custom visualizer for FNames is supported via a weakly linked symbol in a static library (Engine/Extras/NatvisHelpers).
* Settings are exposed in the editor's project settings dialog.
* Standalone application has been rewritten as a Slate app ("LiveCodingConsole"). There is an additional option to start the program as hidden, where it will not be visible until Ctrl+Alt+F11 is hit. Similarly, closing the window will hide it instead of closing the application.
* Does not require a standalone licensed version of Live++.
Known issues:
* Does not currently support class layout changes / object reinstancing
#rb none
[FYI] Marc.Audy, Stefan.Boberg, Nick.Penwarden
#jira
#ROBOMERGE-SOURCE: CL 5304722 in //UE4/Release-4.22/...
#ROBOMERGE-BOT: RELEASE (Release-4.22 -> Main)
[CL 5309051 by ben marsh in Main branch]
2019-03-05 18:49:25 -05:00
|
|
|
// Copyright 2011-2019 Molecular Matters GmbH, all rights reserved.
|
|
|
|
|
|
|
|
|
|
#include "LC_Thread.h"
|
|
|
|
|
#include "LC_Platform.h"
|
|
|
|
|
#include "LC_Logging.h"
|
|
|
|
|
#include <process.h>
|
|
|
|
|
|
2019-03-06 12:44:16 -05:00
|
|
|
// BEGIN EPIC MODS
|
|
|
|
|
#pragma warning(push)
|
|
|
|
|
#pragma warning(disable:6322) // warning C6322: Empty _except block.
|
|
|
|
|
#pragma warning(disable:6258) // warning C6258: Using TerminateThread does not allow proper thread clean up.
|
|
|
|
|
// END EPIC MODS
|
Integrating live coding feature (aka Live++) into UE4.
Allows fast iteration of C++ changes without restarting the application. To use, select the "Live Coding (Experimental)" mode from the drop down menu next to the editor's compile button, or type "LiveCoding" into the console for a monolithic build. Press Ctrl+Alt+F11 to find changes and compile.
Changes vs standalone Live++ version:
* UBT is used to execute builds. This allows standard UE4 adaptive unity mode, allows us to reuse object files when we do regular builds, supports using any build executor allowed by UBT (XGE, SNDBS, etc..).
* Adding new source files is supported.
* Custom visualizer for FNames is supported via a weakly linked symbol in a static library (Engine/Extras/NatvisHelpers).
* Settings are exposed in the editor's project settings dialog.
* Standalone application has been rewritten as a Slate app ("LiveCodingConsole"). There is an additional option to start the program as hidden, where it will not be visible until Ctrl+Alt+F11 is hit. Similarly, closing the window will hide it instead of closing the application.
* Does not require a standalone licensed version of Live++.
Known issues:
* Does not currently support class layout changes / object reinstancing
#rb none
[FYI] Marc.Audy, Stefan.Boberg, Nick.Penwarden
#jira
#ROBOMERGE-SOURCE: CL 5304722 in //UE4/Release-4.22/...
#ROBOMERGE-BOT: RELEASE (Release-4.22 -> Main)
[CL 5309051 by ben marsh in Main branch]
2019-03-05 18:49:25 -05:00
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
|
|
|
|
|
|
|
|
|
struct THREADNAME_INFO
|
|
|
|
|
{
|
|
|
|
|
ULONG_PTR dwType; // Must be 0x1000.
|
|
|
|
|
ULONG_PTR szName; // Pointer to name (in user addr space).
|
|
|
|
|
ULONG_PTR dwThreadID; // Thread ID (-1 = caller thread).
|
|
|
|
|
ULONG_PTR dwFlags; // Reserved for future use, must be zero.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SetThreadName(const char* threadName)
|
|
|
|
|
{
|
|
|
|
|
// code for setting a thread's name taken from http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
|
|
|
|
// note that the pragma directives in the code sample are wrong, and will not work for 32-bit builds.
|
|
|
|
|
THREADNAME_INFO info;
|
|
|
|
|
info.dwType = 0x1000;
|
|
|
|
|
info.szName = reinterpret_cast<ULONG_PTR>(threadName);
|
|
|
|
|
info.dwThreadID = static_cast<ULONG_PTR>(-1);
|
|
|
|
|
info.dwFlags = 0;
|
|
|
|
|
|
|
|
|
|
__try
|
|
|
|
|
{
|
|
|
|
|
RaiseException(MS_VC_EXCEPTION, 0, 4u, reinterpret_cast<ULONG_PTR*>(&info));
|
|
|
|
|
}
|
|
|
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace thread
|
|
|
|
|
{
|
|
|
|
|
unsigned int GetId(void)
|
|
|
|
|
{
|
|
|
|
|
return ::GetCurrentThreadId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int GetId(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
return ::GetThreadId(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Handle Create(unsigned int stackSize, Function function, void* context)
|
|
|
|
|
{
|
|
|
|
|
const uintptr_t result = _beginthreadex(nullptr, stackSize, function, context, 0u, nullptr);
|
|
|
|
|
if (result == 0u)
|
|
|
|
|
{
|
|
|
|
|
const DWORD error = ::GetLastError();
|
|
|
|
|
LC_ERROR_USER("Error 0x%X while trying to create thread", error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return reinterpret_cast<Handle>(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Join(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
::WaitForSingleObject(handle, INFINITE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Terminate(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
::TerminateThread(handle, 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Yield(void)
|
|
|
|
|
{
|
|
|
|
|
::_mm_pause();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Sleep(unsigned int milliSeconds)
|
|
|
|
|
{
|
|
|
|
|
::Sleep(milliSeconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CancelIO(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
::CancelSynchronousIo(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Handle Open(unsigned int threadId)
|
|
|
|
|
{
|
|
|
|
|
return ::OpenThread(THREAD_ALL_ACCESS, Windows::FALSE, threadId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Close(Handle& handle)
|
|
|
|
|
{
|
|
|
|
|
::CloseHandle(handle);
|
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Suspend(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
::SuspendThread(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Resume(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
::ResumeThread(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int GetPriority(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
return ::GetThreadPriority(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SetPriority(Handle handle, int priority)
|
|
|
|
|
{
|
|
|
|
|
::SetThreadPriority(handle, priority);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Context GetContext(Handle handle)
|
|
|
|
|
{
|
|
|
|
|
CONTEXT threadContext = {};
|
|
|
|
|
threadContext.ContextFlags = CONTEXT_ALL;
|
|
|
|
|
::GetThreadContext(handle, &threadContext);
|
|
|
|
|
|
|
|
|
|
return threadContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SetContext(Handle handle, const Context& context)
|
|
|
|
|
{
|
|
|
|
|
::SetThreadContext(handle, &context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const void* ReadInstructionPointer(const Context& context)
|
|
|
|
|
{
|
|
|
|
|
#if LC_64_BIT
|
|
|
|
|
return reinterpret_cast<const void*>(context.Rip);
|
|
|
|
|
#else
|
|
|
|
|
return reinterpret_cast<const void*>(context.Eip);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void WriteInstructionPointer(Context& context, const void* ip)
|
|
|
|
|
{
|
|
|
|
|
#if LC_64_BIT
|
|
|
|
|
context.Rip = reinterpret_cast<DWORD64>(ip);
|
|
|
|
|
#else
|
|
|
|
|
context.Eip = reinterpret_cast<DWORD>(ip);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SetName(const char* name)
|
|
|
|
|
{
|
|
|
|
|
SetThreadName(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-06 12:44:16 -05:00
|
|
|
|
|
|
|
|
// BEGIN EPIC MODS
|
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
// END EPIC MODS
|