/*
 *  IXSetThreadName.cpp
 *  Author: Benjamin Sergeant
 *  Copyright (c) 2018 2020 Machine Zone, Inc. All rights reserved.
 */
#include "IXSetThreadName.h"

// unix systems
#if defined(__APPLE__) || defined(__linux__) || defined(BSD)
#include <pthread.h>
#endif

// freebsd needs this header as well
#if defined(BSD)
#include <pthread_np.h>
#endif

#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif

// Windows
#ifdef _WIN32
#include <windows.h>
#endif

namespace ix
{
#ifdef _WIN32
	const DWORD MS_VC_EXCEPTION = 0x406D1388;

#pragma pack(push, 8)
	typedef struct tagTHREADNAME_INFO
	{
		DWORD dwType;     // Must be 0x1000.
		LPCSTR szName;    // Pointer to name (in user addr space).
		DWORD dwThreadID; // Thread ID (-1=caller thread).
		DWORD dwFlags;    // Reserved for future use, must be zero.
	} THREADNAME_INFO;
#pragma pack(pop)

	void SetThreadName(DWORD dwThreadID, const char* threadName)
	{
#ifndef __GNUC__
		THREADNAME_INFO info;
		info.dwType = 0x1000;
		info.szName = threadName;
		info.dwThreadID = dwThreadID;
		info.dwFlags = 0;

		__try
		{
			RaiseException(
				MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
		}
#endif
	}
#endif

	void setThreadName(const std::string& name)
	{
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
		//
		// Apple reserves 16 bytes for its thread names
		// Notice that the Apple version of pthread_setname_np
		// does not take a pthread_t argument
		//
		pthread_setname_np(name.substr(0, 63).c_str());
#elif defined(__linux__)
		//
		// Linux only reserves 16 bytes for its thread names
		// See prctl and PR_SET_NAME property in
		// http://man7.org/linux/man-pages/man2/prctl.2.html
		//
		pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
#elif defined(_WIN32)
		SetThreadName(-1, name.c_str());
#elif defined(BSD)
		pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str());
#else
		// ... assert here ?
#endif
	}
}
