<commit_before>// Copyright (c) 2011 Zeex
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstdio>
#include <string>

#include <sampgdk.h>

#include "callbacks.h"
#include "jump-x86.h"

extern void *pAMXFunctions;

// Gamemode's AMX
static AMX *gamemode = 0;

// Curently Exec()'ing public
static std::string currentPublic;

// AMX hooks
static JumpX86 amx_FindPublicHook;
static JumpX86 amx_ExecHook;
static JumpX86 amx_RegisterHook;
static JumpX86 amx_CallbackHook;

static cell AMX_NATIVE_CALL fixed_funcidx(AMX *amx, cell *params) {
	char *funcname;
	amx_StrParam(amx, params[1], funcname);
	int index;
	int error = amx_FindPublic(amx, funcname, &index);
	if (error != AMX_ERR_NONE || (error == AMX_ERR_NONE && index == AMX_EXEC_GDK)) {
		return -1;
	}
	return index;
}

static int AMXAPI amx_RegisterHookProc(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) {
	amx_RegisterHook.Remove();

	int error = amx_Register(amx, nativelist, number);

	for (int i = 0; nativelist[i].name != 0 && (i < number || number == -1); ++i) {
		sampgdk::Wrapper::GetInstance().SetNative(nativelist[i].name, nativelist[i].func);
		// Fix for funcidx() issue
		if (strcmp(nativelist[i].name, "funcidx") == 0) {
			new JumpX86((void*)nativelist[i].func, (void*)fixed_funcidx);
		}
	}

	amx_RegisterHook.Install();
	return error;
}

static int AMXAPI amx_FindPublicHookProc(AMX *amx, const char *name, int *index) {
	amx_FindPublicHook.Remove();

	int error = amx_FindPublic(amx, name, index);

	if (amx == gamemode) {
		if (error != AMX_ERR_NONE) {
			*index = AMX_EXEC_GDK;
			error = AMX_ERR_NONE;
		}
		currentPublic = name;
	}

	amx_FindPublicHook.Install();
	return error;
}

static int AMXAPI amx_ExecHookProc(AMX *amx, cell *retval, int index) {
	amx_ExecHook.Remove();
	amx_CallbackHook.Install();

	bool canDoExec = true;
	if (index == AMX_EXEC_MAIN) {
		gamemode = amx;
		sampgdk::Wrapper::GetInstance().CallPublicHook(amx, retval, "OnGameModeInit");
	} else {
		if (amx == gamemode && index != AMX_EXEC_CONT) {
			canDoExec = sampgdk::Wrapper::GetInstance().CallPublicHook(amx, retval, currentPublic.c_str());
		}
	}

	int error = AMX_ERR_NONE;
	if (canDoExec && index != AMX_EXEC_GDK) {
		error = amx_Exec(amx, retval, index);
	} else {
		// Pop parameters from stack
		amx->stk += amx->paramcount * sizeof(cell);
	}

	// Reset parameter count
	// <weird>
	// Sometimes it is not auto-reset by amx_Exec though it normally should.
	// </weird>
	amx->paramcount = 0;

	amx_CallbackHook.Remove();
	amx_ExecHook.Install();

	return error;
}

static int AMXAPI amx_CallbackHookProc(AMX *amx, cell index, cell *result, cell *params) {
	amx_CallbackHook.Remove();
	amx_ExecHook.Install();

	// Forbid SYSREQ.D
	amx->sysreq_d = 0;

	int error = amx_Callback(amx, index, result, params);

	amx_ExecHook.Remove();
	amx_CallbackHook.Install();

	return error;
}

namespace sampgdk {

Wrapper::Wrapper() {}

Wrapper &Wrapper::GetInstance() {
	static Wrapper wrapper;
	return wrapper;
}

void Wrapper::Initialize(void **ppPluginData) {
	pAMXFunctions = ppPluginData[PLUGIN_DATA_AMX_EXPORTS];
	void **amxExports = static_cast<void**>(pAMXFunctions);

	amx_RegisterHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Register],
		(void*)amx_RegisterHookProc);

	amx_FindPublicHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_FindPublic],
		(void*)amx_FindPublicHookProc);

	amx_ExecHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Exec],
		(void*)amx_ExecHookProc);

	amx_CallbackHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Callback],
		(void*)amx_CallbackHookProc);

	SetupSampCallbackHooks();
}

void Wrapper::SetNative(const char *name, AMX_NATIVE native) {
	natives_[std::string(name)] = native;
}

AMX_NATIVE Wrapper::GetNative(const char *name) const {
	std::map<std::string, AMX_NATIVE>::const_iterator it = natives_.find(name);
	if (it != natives_.end()) {
		return it->second;
	}
	return 0;
}

void Wrapper::SetPublicHook(const char *name, PublicHook hook) {
	publicHooks_.insert(std::make_pair(std::string(name), hook));
}

bool Wrapper::CallPublicHook(AMX *amx, cell *retval, const char *name) const {
	std::map<std::string, PublicHook>::const_iterator it = publicHooks_.find(name);
	if (it != publicHooks_.end()) {
		return it->second.Call(amx, retval);
	}
	return true;
}

} // namespace sampgdk

<commit_msg>Add missing #include <cstring><commit_after>// Copyright (c) 2011 Zeex
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstdio>
#include <cstring>
#include <string>

#include <sampgdk.h>

#include "callbacks.h"
#include "jump-x86.h"

extern void *pAMXFunctions;

// Gamemode's AMX
static AMX *gamemode = 0;

// Curently Exec()'ing public
static std::string currentPublic;

// AMX hooks
static JumpX86 amx_FindPublicHook;
static JumpX86 amx_ExecHook;
static JumpX86 amx_RegisterHook;
static JumpX86 amx_CallbackHook;

static cell AMX_NATIVE_CALL fixed_funcidx(AMX *amx, cell *params) {
	char *funcname;
	amx_StrParam(amx, params[1], funcname);
	int index;
	int error = amx_FindPublic(amx, funcname, &index);
	if (error != AMX_ERR_NONE || (error == AMX_ERR_NONE && index == AMX_EXEC_GDK)) {
		return -1;
	}
	return index;
}

static int AMXAPI amx_RegisterHookProc(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) {
	amx_RegisterHook.Remove();

	int error = amx_Register(amx, nativelist, number);

	for (int i = 0; nativelist[i].name != 0 && (i < number || number == -1); ++i) {
		sampgdk::Wrapper::GetInstance().SetNative(nativelist[i].name, nativelist[i].func);
		// Fix for funcidx() issue
		if (strcmp(nativelist[i].name, "funcidx") == 0) {
			new JumpX86((void*)nativelist[i].func, (void*)fixed_funcidx);
		}
	}

	amx_RegisterHook.Install();
	return error;
}

static int AMXAPI amx_FindPublicHookProc(AMX *amx, const char *name, int *index) {
	amx_FindPublicHook.Remove();

	int error = amx_FindPublic(amx, name, index);

	if (amx == gamemode) {
		if (error != AMX_ERR_NONE) {
			*index = AMX_EXEC_GDK;
			error = AMX_ERR_NONE;
		}
		currentPublic = name;
	}

	amx_FindPublicHook.Install();
	return error;
}

static int AMXAPI amx_ExecHookProc(AMX *amx, cell *retval, int index) {
	amx_ExecHook.Remove();
	amx_CallbackHook.Install();

	bool canDoExec = true;
	if (index == AMX_EXEC_MAIN) {
		gamemode = amx;
		sampgdk::Wrapper::GetInstance().CallPublicHook(amx, retval, "OnGameModeInit");
	} else {
		if (amx == gamemode && index != AMX_EXEC_CONT) {
			canDoExec = sampgdk::Wrapper::GetInstance().CallPublicHook(amx, retval, currentPublic.c_str());
		}
	}

	int error = AMX_ERR_NONE;
	if (canDoExec && index != AMX_EXEC_GDK) {
		error = amx_Exec(amx, retval, index);
	} else {
		// Pop parameters from stack
		amx->stk += amx->paramcount * sizeof(cell);
	}

	// Reset parameter count
	// <weird>
	// Sometimes it is not auto-reset by amx_Exec though it normally should.
	// </weird>
	amx->paramcount = 0;

	amx_CallbackHook.Remove();
	amx_ExecHook.Install();

	return error;
}

static int AMXAPI amx_CallbackHookProc(AMX *amx, cell index, cell *result, cell *params) {
	amx_CallbackHook.Remove();
	amx_ExecHook.Install();

	// Forbid SYSREQ.D
	amx->sysreq_d = 0;

	int error = amx_Callback(amx, index, result, params);

	amx_ExecHook.Remove();
	amx_CallbackHook.Install();

	return error;
}

namespace sampgdk {

Wrapper::Wrapper() {}

Wrapper &Wrapper::GetInstance() {
	static Wrapper wrapper;
	return wrapper;
}

void Wrapper::Initialize(void **ppPluginData) {
	pAMXFunctions = ppPluginData[PLUGIN_DATA_AMX_EXPORTS];
	void **amxExports = static_cast<void**>(pAMXFunctions);

	amx_RegisterHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Register],
		(void*)amx_RegisterHookProc);

	amx_FindPublicHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_FindPublic],
		(void*)amx_FindPublicHookProc);

	amx_ExecHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Exec],
		(void*)amx_ExecHookProc);

	amx_CallbackHook.Install(
		amxExports[PLUGIN_AMX_EXPORT_Callback],
		(void*)amx_CallbackHookProc);

	SetupSampCallbackHooks();
}

void Wrapper::SetNative(const char *name, AMX_NATIVE native) {
	natives_[std::string(name)] = native;
}

AMX_NATIVE Wrapper::GetNative(const char *name) const {
	std::map<std::string, AMX_NATIVE>::const_iterator it = natives_.find(name);
	if (it != natives_.end()) {
		return it->second;
	}
	return 0;
}

void Wrapper::SetPublicHook(const char *name, PublicHook hook) {
	publicHooks_.insert(std::make_pair(std::string(name), hook));
}

bool Wrapper::CallPublicHook(AMX *amx, cell *retval, const char *name) const {
	std::map<std::string, PublicHook>::const_iterator it = publicHooks_.find(name);
	if (it != publicHooks_.end()) {
		return it->second.Call(amx, retval);
	}
	return true;
}

} // namespace sampgdk

<|endoftext|>


<commit_before>#include <stdlib.h>
#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/uri.hxx>
#include <osl/thread.h>
#include <osl/file.hxx>

#include <cppunit/simpleheader.hxx>

// -----------------------------------------------------------------------------

namespace Stringtest
{
    rtl::OString toHex(unsigned char _c)
    {
        rtl::OStringBuffer sStrBuf;
        static char cHex[] = "0123456789ABCDEF";

        int nhigh = int(_c) >> 4 & 0xf;
        int nlow  = int(_c) & 0xf;
        sStrBuf.append( cHex[nhigh] );
        sStrBuf.append( cHex[nlow] );
        return sStrBuf.makeStringAndClear();
    }

    rtl::OString escapeString(rtl::OString const& _sStr)
    {
        rtl::OStringBuffer sStrBuf;
        sal_Int32 nLength = _sStr.getLength();
        for(int i=0;i<nLength;++i)
        {
            unsigned char c = (unsigned char)_sStr[i];
            if (c > 127)
            {
                sStrBuf.append("%");
                sStrBuf.append(toHex(c));
            }
            else
            {
                sStrBuf.append((char)c);
            }
        }
        return sStrBuf.makeStringAndClear();
    }

    // -----------------------------------------------------------------------------

    class Convert : public CppUnit::TestFixture
    {
        rtl::OUString m_aStr;
    public:
        void setUp()
            {
                // m_aStr = rtl::OUString::createFromAscii( "/tmp/äˤ" );
            }

        /*
          rtl::OString toUTF8(rtl::OUString const& _suStr)
            {
                rtl::OString sStrAsUTF8 = rtl::OUStringToOString(_suStr, RTL_TEXTENCODING_UTF8);
                t_print("%s\n", escapeString(sStrAsUTF8).getStr());
                return sStrAsUTF8;
            }
        */
        rtl::OUString fromUTF8(rtl::OString const& _suStr)
            {
                rtl::OUString suStr = rtl::OStringToOUString(_suStr, RTL_TEXTENCODING_UTF8);
                return suStr;
            }

        rtl::OString convertToOString(rtl::OUString const& _suStr)
            {
                return rtl::OUStringToOString(_suStr, osl_getThreadTextEncoding()/*RTL_TEXTENCODING_ASCII_US*/);
            }

        void showContent(rtl::OUString const& _suStr)
            {
                rtl::OString sStr = convertToOString(_suStr);
                t_print("%s\n", sStr.getStr());
            }

        void toUTF8_mech(rtl::OUString const& _suStr, rtl_UriEncodeMechanism _eMechanism)
            {
                rtl::OUString suStr;
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassNone,          _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUric,          _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUricNoSlash,   _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassRelSegment,    _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassRegName,       _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUserinfo,      _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassPchar,         _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUnoParamValue, _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
            }

        void toUTF8(rtl::OUString const& _suStr)
            {
                t_print("rtl_UriEncodeIgnoreEscapes \n");
                toUTF8_mech(_suStr, rtl_UriEncodeIgnoreEscapes);
                t_print("\n");
                t_print("# rtl_UriEncodeKeepEscapes\n");
                toUTF8_mech(_suStr, rtl_UriEncodeKeepEscapes);
                t_print("\n");
                t_print("# rtl_UriEncodeCheckEscapes\n");
                toUTF8_mech(_suStr, rtl_UriEncodeCheckEscapes);
                t_print("\n");
            }

        void test_FromUTF8_001()
            {
                // string --> ustring
                rtl::OString sStrUTF8("h%C3%A4llo");
                rtl::OUString suStrUTF8 = rtl::OStringToOUString(sStrUTF8, RTL_TEXTENCODING_ASCII_US);

                // UTF8 --> real ustring
                rtl::OUString suStr_UriDecodeToIuri      = rtl::Uri::decode(suStrUTF8, rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeToIuri);

                // string --> ustring
                rtl::OString sStr("hllo");
                rtl::OUString suString = rtl::OStringToOUString(sStr, RTL_TEXTENCODING_ISO_8859_15);

                CPPUNIT_ASSERT_MESSAGE("Strings must be equal", suString.equals(suStr_UriDecodeToIuri) == sal_True);

                // ustring --> ustring (UTF8)
                rtl::OUString suStr2 = rtl::Uri::encode(suStr_UriDecodeToIuri, rtl_UriCharClassUnoParamValue, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
                showContent(suStr2);

                CPPUNIT_ASSERT_MESSAGE("Strings must be equal", suStr2.equals(suStrUTF8) == sal_True);
                // suStr should be equal to suStr2
            }

        // "%C3%84qypten";
        // testshl2 ../../../unxlngi4.pro/lib/libConvert.so "-onlyerrors"
        // # Type: 'Directory' file name '%E6%89%8B%E6%9C%BA%E5%8F%B7%E7%A0%81'
        // # Type: 'Directory' file name '%E6%9C%AA%E5%91%BD%E5%90%8Dzhgb18030'
        // # Type: 'Regular file' file name '%E5%B7%A5%E4%BD%9C'
        // # Type: 'Regular file' file name '%E4%BA%8C%E6%89%8B%E6%88%BF%E4%B9%B0%E5%8D%96%E5%90%88%E5%90%8C%E8%8D%89%E7%A8%BF.doc'
        // ls
        rtl::OString getFileTypeName(osl::FileStatus const& _aStatus)
            {
                rtl::OString sType;
                if (_aStatus.isValid(osl_FileStatus_Mask_Type))
                {
                    osl::FileStatus::Type aType = _aStatus.getFileType();
                    if (aType == osl::FileStatus::Directory)
                    {
                        sType = "Directory";
                    }
                    else if (aType == osl::FileStatus::Regular)
                    {
                        sType = "Regular file";
                    }
                    else if (aType == osl::FileStatus::Volume)
                    {
                        sType = "Volume";
                    }
                    else if (aType == osl::FileStatus::Fifo)
                    {
                        sType = "Fifo";
                    }
                    else if (aType == osl::FileStatus::Socket)
                    {
                        sType = "Socket";
                    }
                    else if (aType == osl::FileStatus::Link)
                    {
                        sType = "Link";
                    }
                    else if (aType == osl::FileStatus::Special)
                    {
                        sType = "Special";
                    }
                    else if (aType == osl::FileStatus::Unknown)
                    {
                        sType = "Unknown";
                    }
                    else
                    {
                        sType = "Not handled yet";
                    }
                }
                else
                {
                    sType = "ERROR: osl_FileStatus_Mask_Type not set for FileStatus!";
                }
                return sType;
            }

        void test_UTF8_files()
            {
#ifdef UNX
                rtl::OUString suDirURL(rtl::OUString::createFromAscii("file:///tmp/atestdir"));
#else /* Windows */
                rtl::OUString suDirURL(rtl::OUString::createFromAscii("file:///c:/temp/atestdir"));
#endif
                osl::Directory aDir(suDirURL);
                aDir.open();
                if (aDir.isOpen())
                {
                    osl::DirectoryItem aItem;
                    osl::FileStatus aStatus(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type);
                    while (aDir.getNextItem(aItem) == ::osl::FileBase::E_None)
                    {
                        if (osl::FileBase::E_None == aItem.getFileStatus(aStatus) &&
                            aStatus.isValid(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes))
                        {
                            rtl::OString sType = getFileTypeName(aStatus);

                            rtl::OUString suFilename = aStatus.getFileName();
                            // rtl::OUString suFullFileURL;

                            rtl::OUString suStrUTF8 = rtl::Uri::encode(suFilename, rtl_UriCharClassUnoParamValue, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
                            rtl::OString sStrUTF8 = convertToOString(suStrUTF8);
                            t_print("Type: '%s' file name '%s'\n", sType.getStr(), sStrUTF8.getStr());
                        }
                    }
                    aDir.close();
                }
                else
                {
                    rtl::OString sStr;
                    sStr = rtl::OUStringToOString(suDirURL, osl_getThreadTextEncoding());
                    t_print("can't open dir:'%s'\n", sStr.getStr());
                }
            }

        void test_FromUTF8()
            {
                rtl::OString sStr("h%C3%A4llo");
                // rtl::OString sStr("hllo");
                // rtl::OUString suStr = rtl::OStringToOUString(sStr, RTL_TEXTENCODING_ASCII_US);
                rtl::OUString suStr = rtl::OStringToOUString(sStr, osl_getThreadTextEncoding());
                // rtl::OString  sStr = escapeString( rtl::OString("/tmp/äˤ") );
                // rtl::OUString suStr = rtl::OUString::createFromAscii( sStr.getStr() );

//    rtl_UriEncodeIgnoreEscapes,
//    rtl_UriEncodeKeepEscapes,
//     rtl_UriEncodeCheckEscapes,
//                rtl::OUString suStr2 = rtl::Uri::encode(suStr, rtl_UriCharClassRegName, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
                rtl::OUString suStr_UriDecodeNone        = rtl::Uri::decode(suStr, rtl_UriDecodeNone, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeNone);
                toUTF8(suStr_UriDecodeNone);

                rtl::OUString suStr_UriDecodeToIuri      = rtl::Uri::decode(suStr, rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeToIuri);
                toUTF8(suStr_UriDecodeToIuri);

                rtl::OUString suStr_UriDecodeWithCharset = rtl::Uri::decode(suStr, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeWithCharset);
                toUTF8(suStr_UriDecodeWithCharset);
            }

        void test_UTF8()
            {
                rtl::OUString aStr = rtl::OUString::createFromAscii( "/tmp/äˤ" );

            }

/*
        void UTF8()
            {
                rtl::OString sSysPath5_AsUTF8 = rtl::OUStringToOString(m_aStr, RTL_TEXTENCODING_UTF8);
                t_print("aSysPath5 as UTF8 '%s'\n", sSysPath5_AsUTF8.getStr());

            {
                rtl::OUString suSysPath5Str = rtl::OStringToOUString(sSysPath5_AsUTF8, RTL_TEXTENCODING_UTF8);
                if (suSysPath5Str.equals(m_aStr))
                {
                    t_print("1. convert works.\n");
                }
            }

            sal_Char aSysPath5Str[] = "/tmp/￣ﾺﾤﾳﾤﾤﾤﾤ";
            rtl::OString aStr (aSysPath5Str);
            rtl::OUString suSysPath5Str = rtl::OStringToOUString(aSysPath5Str, RTL_TEXTENCODING_UTF8);
            if (suSysPath5Str.equals(m_aStr))
            {
                t_print("2. convert works.\n");
            }
            }
*/

        CPPUNIT_TEST_SUITE( Convert );
        CPPUNIT_TEST( test_FromUTF8_001 );
//        CPPUNIT_TEST( test_UTF8_files );
//      CPPUNIT_TEST( test_FromUTF8 );
        CPPUNIT_TEST_SUITE_END( );
    };

}

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( Stringtest::Convert, "Stringtest" );

// LLA: doku anpassen!!!

NOADDITIONAL;
<commit_msg>INTEGRATION: CWS fwkpostbeta02 (1.4.112); FILE MERGED 2005/02/14 16:46:47 sb 1.4.112.1: #i42746# Merged in 1.4.108.2; removed commented-out code that contained non-ASCII characters.<commit_after>#include <stdlib.h>
#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/uri.hxx>
#include <osl/thread.h>
#include <osl/file.hxx>

#include <cppunit/simpleheader.hxx>

// -----------------------------------------------------------------------------

namespace Stringtest
{
    rtl::OString toHex(unsigned char _c)
    {
        rtl::OStringBuffer sStrBuf;
        static char cHex[] = "0123456789ABCDEF";

        int nhigh = int(_c) >> 4 & 0xf;
        int nlow  = int(_c) & 0xf;
        sStrBuf.append( cHex[nhigh] );
        sStrBuf.append( cHex[nlow] );
        return sStrBuf.makeStringAndClear();
    }

    rtl::OString escapeString(rtl::OString const& _sStr)
    {
        rtl::OStringBuffer sStrBuf;
        sal_Int32 nLength = _sStr.getLength();
        for(int i=0;i<nLength;++i)
        {
            unsigned char c = (unsigned char)_sStr[i];
            if (c > 127)
            {
                sStrBuf.append("%");
                sStrBuf.append(toHex(c));
            }
            else
            {
                sStrBuf.append((char)c);
            }
        }
        return sStrBuf.makeStringAndClear();
    }

    // -----------------------------------------------------------------------------

    class Convert : public CppUnit::TestFixture
    {
        rtl::OUString m_aStr;
    public:
        /*
          rtl::OString toUTF8(rtl::OUString const& _suStr)
            {
                rtl::OString sStrAsUTF8 = rtl::OUStringToOString(_suStr, RTL_TEXTENCODING_UTF8);
                t_print("%s\n", escapeString(sStrAsUTF8).getStr());
                return sStrAsUTF8;
            }
        */
        rtl::OUString fromUTF8(rtl::OString const& _suStr)
            {
                rtl::OUString suStr = rtl::OStringToOUString(_suStr, RTL_TEXTENCODING_UTF8);
                return suStr;
            }

        rtl::OString convertToOString(rtl::OUString const& _suStr)
            {
                return rtl::OUStringToOString(_suStr, osl_getThreadTextEncoding()/*RTL_TEXTENCODING_ASCII_US*/);
            }

        void showContent(rtl::OUString const& _suStr)
            {
                rtl::OString sStr = convertToOString(_suStr);
                t_print("%s\n", sStr.getStr());
            }

        void toUTF8_mech(rtl::OUString const& _suStr, rtl_UriEncodeMechanism _eMechanism)
            {
                rtl::OUString suStr;
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassNone,          _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUric,          _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUricNoSlash,   _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassRelSegment,    _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassRegName,       _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUserinfo,      _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassPchar,         _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
                suStr = rtl::Uri::encode(_suStr, rtl_UriCharClassUnoParamValue, _eMechanism, RTL_TEXTENCODING_UTF8);
                showContent(suStr);
            }

        void toUTF8(rtl::OUString const& _suStr)
            {
                t_print("rtl_UriEncodeIgnoreEscapes \n");
                toUTF8_mech(_suStr, rtl_UriEncodeIgnoreEscapes);
                t_print("\n");
                t_print("# rtl_UriEncodeKeepEscapes\n");
                toUTF8_mech(_suStr, rtl_UriEncodeKeepEscapes);
                t_print("\n");
                t_print("# rtl_UriEncodeCheckEscapes\n");
                toUTF8_mech(_suStr, rtl_UriEncodeCheckEscapes);
                t_print("\n");
            }

        void test_FromUTF8_001()
            {
                // string --> ustring
                rtl::OString sStrUTF8("h%C3%A4llo");
                rtl::OUString suStrUTF8 = rtl::OStringToOUString(sStrUTF8, RTL_TEXTENCODING_ASCII_US);

                // UTF8 --> real ustring
                rtl::OUString suStr_UriDecodeToIuri      = rtl::Uri::decode(suStrUTF8, rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeToIuri);

                // string --> ustring
                rtl::OString sStr("h\xE4llo");
                rtl::OUString suString = rtl::OStringToOUString(sStr, RTL_TEXTENCODING_ISO_8859_15);

                CPPUNIT_ASSERT_MESSAGE("Strings must be equal", suString.equals(suStr_UriDecodeToIuri) == sal_True);

                // ustring --> ustring (UTF8)
                rtl::OUString suStr2 = rtl::Uri::encode(suStr_UriDecodeToIuri, rtl_UriCharClassUnoParamValue, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
                showContent(suStr2);

                CPPUNIT_ASSERT_MESSAGE("Strings must be equal", suStr2.equals(suStrUTF8) == sal_True);
                // suStr should be equal to suStr2
            }

        // "%C3%84qypten";
        // testshl2 ../../../unxlngi4.pro/lib/libConvert.so "-onlyerrors"
        // # Type: 'Directory' file name '%E6%89%8B%E6%9C%BA%E5%8F%B7%E7%A0%81'
        // # Type: 'Directory' file name '%E6%9C%AA%E5%91%BD%E5%90%8Dzhgb18030'
        // # Type: 'Regular file' file name '%E5%B7%A5%E4%BD%9C'
        // # Type: 'Regular file' file name '%E4%BA%8C%E6%89%8B%E6%88%BF%E4%B9%B0%E5%8D%96%E5%90%88%E5%90%8C%E8%8D%89%E7%A8%BF.doc'
        // ls
        rtl::OString getFileTypeName(osl::FileStatus const& _aStatus)
            {
                rtl::OString sType;
                if (_aStatus.isValid(osl_FileStatus_Mask_Type))
                {
                    osl::FileStatus::Type aType = _aStatus.getFileType();
                    if (aType == osl::FileStatus::Directory)
                    {
                        sType = "Directory";
                    }
                    else if (aType == osl::FileStatus::Regular)
                    {
                        sType = "Regular file";
                    }
                    else if (aType == osl::FileStatus::Volume)
                    {
                        sType = "Volume";
                    }
                    else if (aType == osl::FileStatus::Fifo)
                    {
                        sType = "Fifo";
                    }
                    else if (aType == osl::FileStatus::Socket)
                    {
                        sType = "Socket";
                    }
                    else if (aType == osl::FileStatus::Link)
                    {
                        sType = "Link";
                    }
                    else if (aType == osl::FileStatus::Special)
                    {
                        sType = "Special";
                    }
                    else if (aType == osl::FileStatus::Unknown)
                    {
                        sType = "Unknown";
                    }
                    else
                    {
                        sType = "Not handled yet";
                    }
                }
                else
                {
                    sType = "ERROR: osl_FileStatus_Mask_Type not set for FileStatus!";
                }
                return sType;
            }

        void test_UTF8_files()
            {
#ifdef UNX
                rtl::OUString suDirURL(rtl::OUString::createFromAscii("file:///tmp/atestdir"));
#else /* Windows */
                rtl::OUString suDirURL(rtl::OUString::createFromAscii("file:///c:/temp/atestdir"));
#endif
                osl::Directory aDir(suDirURL);
                aDir.open();
                if (aDir.isOpen())
                {
                    osl::DirectoryItem aItem;
                    osl::FileStatus aStatus(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type);
                    while (aDir.getNextItem(aItem) == ::osl::FileBase::E_None)
                    {
                        if (osl::FileBase::E_None == aItem.getFileStatus(aStatus) &&
                            aStatus.isValid(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes))
                        {
                            rtl::OString sType = getFileTypeName(aStatus);

                            rtl::OUString suFilename = aStatus.getFileName();
                            // rtl::OUString suFullFileURL;

                            rtl::OUString suStrUTF8 = rtl::Uri::encode(suFilename, rtl_UriCharClassUnoParamValue, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
                            rtl::OString sStrUTF8 = convertToOString(suStrUTF8);
                            t_print("Type: '%s' file name '%s'\n", sType.getStr(), sStrUTF8.getStr());
                        }
                    }
                    aDir.close();
                }
                else
                {
                    rtl::OString sStr;
                    sStr = rtl::OUStringToOString(suDirURL, osl_getThreadTextEncoding());
                    t_print("can't open dir:'%s'\n", sStr.getStr());
                }
            }

        void test_FromUTF8()
            {
                rtl::OString sStr("h%C3%A4llo");
                rtl::OUString suStr = rtl::OStringToOUString(sStr, osl_getThreadTextEncoding());

//    rtl_UriEncodeIgnoreEscapes,
//    rtl_UriEncodeKeepEscapes,
//     rtl_UriEncodeCheckEscapes,
//                rtl::OUString suStr2 = rtl::Uri::encode(suStr, rtl_UriCharClassRegName, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
                rtl::OUString suStr_UriDecodeNone        = rtl::Uri::decode(suStr, rtl_UriDecodeNone, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeNone);
                toUTF8(suStr_UriDecodeNone);

                rtl::OUString suStr_UriDecodeToIuri      = rtl::Uri::decode(suStr, rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeToIuri);
                toUTF8(suStr_UriDecodeToIuri);

                rtl::OUString suStr_UriDecodeWithCharset = rtl::Uri::decode(suStr, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
                showContent(suStr_UriDecodeWithCharset);
                toUTF8(suStr_UriDecodeWithCharset);
            }

        CPPUNIT_TEST_SUITE( Convert );
        CPPUNIT_TEST( test_FromUTF8_001 );
//        CPPUNIT_TEST( test_UTF8_files );
//      CPPUNIT_TEST( test_FromUTF8 );
        CPPUNIT_TEST_SUITE_END( );
    };

}

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( Stringtest::Convert, "Stringtest" );

// LLA: doku anpassen!!!

NOADDITIONAL;
<|endoftext|>


<commit_before>/*

Copyright (c) 2007-2012, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/config.hpp"

#if (defined TORRENT_DEBUG && !TORRENT_NO_ASSERTS) || defined TORRENT_ASIO_DEBUGGING || TORRENT_RELEASE_ASSERTS

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

#include <string>
#include <cstring>
#include <stdlib.h>

// uClibc++ doesn't have cxxabi.h
#if defined __GNUC__ && __GNUC__ >= 3 \
	&& !defined __UCLIBCXX_MAJOR__

#include <cxxabi.h>

std::string demangle(char const* name)
{
// in case this string comes
	// this is needed on linux
	char const* start = strchr(name, '(');
	if (start != 0)
	{
		++start;
	}
	else
	{
		// this is needed on macos x
		start = strstr(name, "0x");
		if (start != 0)
		{
			start = strchr(start, ' ');
			if (start != 0) ++start;
			else start = name;
		}
		else start = name;
	}

	char const* end = strchr(start, '+');
	if (end) while (*(end-1) == ' ') --end;

	std::string in;
	if (end == 0) in.assign(start);
	else in.assign(start, end);

	size_t len;
	int status;
	char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
	if (unmangled == 0) return in;
	std::string ret(unmangled);
	free(unmangled);
	return ret;
}
#elif defined WIN32

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // XP

#include "windows.h"
#include "dbghelp.h"

std::string demangle(char const* name)
{
	char demangled_name[256];
	if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0)
		demangled_name[0] = 0;
	return demangled_name;
}

#else
std::string demangle(char const* name) { return name; }
#endif

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "libtorrent/version.hpp"

// execinfo.h is available in the MacOS X 10.5 SDK.
#if (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
#include <execinfo.h>

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
{
	void* stack[50];
	int size = backtrace(stack, 50);
	char** symbols = backtrace_symbols(stack, size);

	for (int i = 1; i < size && len > 0; ++i)
	{
		int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str());
		out += ret;
		len -= ret;
		if (i - 1 == max_depth && max_depth > 0) break;
	}

	free(symbols);
}

// visual studio 9 and up appears to support this
#elif defined WIN32 && _MSC_VER >= 1500

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // XP

#include "windows.h"
#include "libtorrent/utf8.hpp"

#include "winbase.h"
#include "dbghelp.h"

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
{
	typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)(
		__in ULONG FramesToSkip,
		__in ULONG FramesToCapture,
		__out PVOID *BackTrace,
		__out_opt PULONG BackTraceHash);

	static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace = 0;

	if (RtlCaptureStackBackTrace == 0)
	{
		// we don't actually have to free this library, everyone has it loaded
		HMODULE lib = LoadLibrary(TEXT("kernel32.dll"));
		RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t)GetProcAddress(lib, "RtlCaptureStackBackTrace");
		if (RtlCaptureStackBackTrace == 0)
		{
			out[0] = 0;
			return;
		}
	}

	int i;
	void* stack[50];
	int size = CaptureStackBackTrace(0, 50, stack, 0);

	SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR), 1);
	symbol->MaxNameLen = MAX_SYM_NAME;
	symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

	HANDLE p = GetCurrentProcess();
	static bool sym_initialized = false;
	if (!sym_initialized)
	{
		sym_initialized = true;
		SymInitialize(p, NULL, true);
	}
	for (i = 0; i < size && len > 0; ++i)
	{
		int ret;
		if (SymFromAddr(p, uintptr_t(stack[i]), 0, symbol))
			ret = snprintf(out, len, "%d: %s\n", i, symbol->Name);
		else
			ret = snprintf(out, len, "%d: <unknown>\n", i);

		out += ret;
		len -= ret;
		if (i == max_depth && max_depth > 0) break;
	}
	free(symbol);
}

#else

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) {}

#endif

#if TORRENT_PRODUCTION_ASSERTS
char const* libtorrent_assert_log = "asserts.log";
#endif

TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file
	, char const* function, char const* value)
{
#if TORRENT_PRODUCTION_ASSERTS
	FILE* out = fopen(libtorrent_assert_log, "a+");
	if (out == 0) out = stderr;
#else
	FILE* out = stderr;
#endif

	char stack[8192];
	print_backtrace(stack, sizeof(stack), 0);

	fprintf(out, "assertion failed. Please file a bugreport at "
		"http://code.google.com/p/libtorrent/issues\n"
		"Please include the following information:\n\n"
		"version: " LIBTORRENT_VERSION "\n"
		"%s\n"
		"file: '%s'\n"
		"line: %d\n"
		"function: %s\n"
		"expression: %s\n"
		"%s%s\n"
		"stack:\n"
		"%s\n"
		, LIBTORRENT_REVISION, file, line, function, expr
		, value ? value : "", value ? "\n" : ""
		, stack);

	// if production asserts are defined, don't abort, just print the error
#if TORRENT_PRODUCTION_ASSERTS
	if (out != stderr) fclose(out);
#else
 	// send SIGINT to the current process
 	// to break into the debugger
 	raise(SIGINT);
 	abort();
#endif
}

#else

TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file, char const* function) {}

#endif

<commit_msg>stop logging production asserts after 500 hits<commit_after>/*

Copyright (c) 2007-2012, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/config.hpp"

#if TORRENT_PRODUCTION_ASSERTS
#include <boost/detail/atomic_count.hpp>
#endif

#if (defined TORRENT_DEBUG && !TORRENT_NO_ASSERTS) || defined TORRENT_ASIO_DEBUGGING || TORRENT_RELEASE_ASSERTS

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

#include <string>
#include <cstring>
#include <stdlib.h>

// uClibc++ doesn't have cxxabi.h
#if defined __GNUC__ && __GNUC__ >= 3 \
	&& !defined __UCLIBCXX_MAJOR__

#include <cxxabi.h>

std::string demangle(char const* name)
{
// in case this string comes
	// this is needed on linux
	char const* start = strchr(name, '(');
	if (start != 0)
	{
		++start;
	}
	else
	{
		// this is needed on macos x
		start = strstr(name, "0x");
		if (start != 0)
		{
			start = strchr(start, ' ');
			if (start != 0) ++start;
			else start = name;
		}
		else start = name;
	}

	char const* end = strchr(start, '+');
	if (end) while (*(end-1) == ' ') --end;

	std::string in;
	if (end == 0) in.assign(start);
	else in.assign(start, end);

	size_t len;
	int status;
	char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
	if (unmangled == 0) return in;
	std::string ret(unmangled);
	free(unmangled);
	return ret;
}
#elif defined WIN32

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // XP

#include "windows.h"
#include "dbghelp.h"

std::string demangle(char const* name)
{
	char demangled_name[256];
	if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0)
		demangled_name[0] = 0;
	return demangled_name;
}

#else
std::string demangle(char const* name) { return name; }
#endif

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "libtorrent/version.hpp"

// execinfo.h is available in the MacOS X 10.5 SDK.
#if (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
#include <execinfo.h>

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
{
	void* stack[50];
	int size = backtrace(stack, 50);
	char** symbols = backtrace_symbols(stack, size);

	for (int i = 1; i < size && len > 0; ++i)
	{
		int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str());
		out += ret;
		len -= ret;
		if (i - 1 == max_depth && max_depth > 0) break;
	}

	free(symbols);
}

// visual studio 9 and up appears to support this
#elif defined WIN32 && _MSC_VER >= 1500

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // XP

#include "windows.h"
#include "libtorrent/utf8.hpp"

#include "winbase.h"
#include "dbghelp.h"

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
{
	typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)(
		__in ULONG FramesToSkip,
		__in ULONG FramesToCapture,
		__out PVOID *BackTrace,
		__out_opt PULONG BackTraceHash);

	static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace = 0;

	if (RtlCaptureStackBackTrace == 0)
	{
		// we don't actually have to free this library, everyone has it loaded
		HMODULE lib = LoadLibrary(TEXT("kernel32.dll"));
		RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t)GetProcAddress(lib, "RtlCaptureStackBackTrace");
		if (RtlCaptureStackBackTrace == 0)
		{
			out[0] = 0;
			return;
		}
	}

	int i;
	void* stack[50];
	int size = CaptureStackBackTrace(0, 50, stack, 0);

	SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR), 1);
	symbol->MaxNameLen = MAX_SYM_NAME;
	symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

	HANDLE p = GetCurrentProcess();
	static bool sym_initialized = false;
	if (!sym_initialized)
	{
		sym_initialized = true;
		SymInitialize(p, NULL, true);
	}
	for (i = 0; i < size && len > 0; ++i)
	{
		int ret;
		if (SymFromAddr(p, uintptr_t(stack[i]), 0, symbol))
			ret = snprintf(out, len, "%d: %s\n", i, symbol->Name);
		else
			ret = snprintf(out, len, "%d: <unknown>\n", i);

		out += ret;
		len -= ret;
		if (i == max_depth && max_depth > 0) break;
	}
	free(symbol);
}

#else

TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) {}

#endif

#if TORRENT_PRODUCTION_ASSERTS
char const* libtorrent_assert_log = "asserts.log";
// the number of asserts we've printed to the log
boost::detail::atomic_count assert_counter(0);
#endif

TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file
	, char const* function, char const* value)
{
#if TORRENT_PRODUCTION_ASSERTS
	// no need to flood the assert log with infinite number of asserts
	if (++assert_counter > 500) return;

	FILE* out = fopen(libtorrent_assert_log, "a+");
	if (out == 0) out = stderr;
#else
	FILE* out = stderr;
#endif

	char stack[8192];
	print_backtrace(stack, sizeof(stack), 0);

	fprintf(out, "assertion failed. Please file a bugreport at "
		"http://code.google.com/p/libtorrent/issues\n"
		"Please include the following information:\n\n"
		"version: " LIBTORRENT_VERSION "\n"
		"%s\n"
		"file: '%s'\n"
		"line: %d\n"
		"function: %s\n"
		"expression: %s\n"
		"%s%s\n"
		"stack:\n"
		"%s\n"
		, LIBTORRENT_REVISION, file, line, function, expr
		, value ? value : "", value ? "\n" : ""
		, stack);

	// if production asserts are defined, don't abort, just print the error
#if TORRENT_PRODUCTION_ASSERTS
	if (out != stderr) fclose(out);
#else
 	// send SIGINT to the current process
 	// to break into the debugger
 	raise(SIGINT);
 	abort();
#endif
}

#else

TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file, char const* function) {}

#endif

<|endoftext|>


<commit_before>/*
 *  CodecAwareConduit.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 12/10/09.
 *  Copyright 2009 Harvard University. All rights reserved.
 *
 */

#include "CodecAwareConduit.h"
#include "SystemEventFactory.h"
#include "StandardVariables.h"

void CodecAwareConduit::transmitCodecEvent(){
    // This is called internally from methods that already hold conduit_mutex

    //std::cerr << "transmitting codec" << endl;
    //mw::Datum codec_datum(local_codec);
    mw::Datum codec_datum = construct_simple_codec_datum_from_map(local_codec);
    sendData(RESERVED_CODEC_CODE, codec_datum);
}

void CodecAwareConduit::receiveCodecEvent(shared_ptr<Event> evt){
    // This is called from handleCallbacks, which already holds conduit_mutex

    remote_codec = extract_simple_codec_map(evt);
    remote_reverse_codec = reverse_simple_codec_map(remote_codec);

    rebuildEventCallbacks();

    remote_codec_cond.notify_all();
}

void CodecAwareConduit::receiveControlEvent(shared_ptr<Event> evt){
    // This is called from handleCallbacks, which already holds conduit_mutex

    Datum payload_type = evt->getData().getElement(M_SYSTEM_PAYLOAD_TYPE);
    if((int)payload_type == M_REQUEST_CODEC){
        transmitCodecEvent();
    }
}

void CodecAwareConduit::addEventCallback(const std::string &evt_name, EventCallback cb) {
    // This is called internally from methods that already hold conduit_mutex

    int incoming_code = M_UNDEFINED_EVENT_CODE;

    if (remote_reverse_codec.find(evt_name) != remote_reverse_codec.end()) {
        incoming_code = remote_reverse_codec[evt_name];
    } else {
        //std::cerr << "Cannot register callback for unknown name: " << evt_name << " (maybe codec hasn't been received yet?)" << endl;
        // send a request, just in case no one else has
        // sendData(SystemEventFactory::requestCodecControl());
        return;
    }

    //std::cerr << "registering callback for code: " << incoming_code << " to name: " << evt_name << " in: " << this << std::endl;

    // find out if that event name has a different code on this side of the conduit
    // if so, we'll want to create a translation callback
    int local_code = M_UNDEFINED_EVENT_CODE;

    if (local_reverse_codec.find(evt_name) != local_reverse_codec.end()) {
        local_code = local_reverse_codec[evt_name];
    }

    if (M_UNDEFINED_EVENT_CODE == local_code) {
        registerCallback(incoming_code, cb, string(name_defined_callback_key));
    } else {
        EventCallback cb2 = boost::bind(&CodecAwareConduit::codeTranslatedCallback, _1, cb, local_code);
        registerCallback(incoming_code, cb2, string(name_defined_callback_key));
    }

    // send a request to the other side of the conduit to begin forwarding this event
    sendData(SystemEventFactory::setEventForwardingControl(evt_name, true));
}

void CodecAwareConduit::rebuildEventCallbacks(){
    // This is called internally from methods that already hold conduit_mutex

    //std::cerr << "rebuildEventCallbacks called in: " << this << std::endl;

    unregisterCallbacks(name_defined_callback_key);

    for (map<string, EventCallback>::iterator i = callbacks_by_name.begin(); i != callbacks_by_name.end(); ++i) {
        string evt_name = (*i).first;
        EventCallback cb = (*i).second;
        addEventCallback(evt_name, cb);
    }

}

void CodecAwareConduit::codeTranslatedCallback(shared_ptr<Event> evt, EventCallback cb, int new_code){
    // This is a static method that does not need to hold conduit_mutex

    //std::cerr << "calling code translated callback; new_code: " << new_code << " (old code: " << evt->getEventCode() << ")" << endl;
    evt->setEventCode(new_code);
    return cb(evt);
}

void CodecAwareConduit::handleCallbacks(shared_ptr<Event> evt) {
    scoped_lock lock(conduit_mutex);
    SimpleConduit::handleCallbacks(evt);
}

bool CodecAwareConduit::initialize(){
    //std::cerr << "initializing: " << this << std::endl;
    //EventCallback cb = boost::bind(&CodecAwareConduit::receiveCodecEvent, this, _1);
    EventCallback cb = boost::bind(&CodecAwareConduit::receiveCodecEvent, shared_from_this(), _1);
    registerCallback(RESERVED_CODEC_CODE, cb);
    registerCallback(RESERVED_SYSTEM_EVENT_CODE, boost::bind(&CodecAwareConduit::receiveControlEvent, shared_from_this(), _1));
    bool okayp = SimpleConduit::initialize();

    if(okayp){
        sendData(SystemEventFactory::requestCodecControl());
    }

    return okayp;
}

void CodecAwareConduit::registerCallbackByName(string event_name, EventCallback cb){
    scoped_lock lock(conduit_mutex);

    if(event_name.size() == 0){
        throw SimpleException("Attempt to register callback for empty event name");
    }

    callbacks_by_name[event_name] = cb;
    addEventCallback(event_name, cb);

    //std::cerr << "Sending event forwarding event for " << event_name << std::endl;
    sendData(SystemEventFactory::setEventForwardingControl(event_name, true));
}

void CodecAwareConduit::registerLocalEventCode(int code, string event_name){
    scoped_lock lock(conduit_mutex);

    //std::cerr << "registering local name: " << event_name << " to code: " << code << endl;
    local_codec[code] = event_name;

    local_reverse_codec = reverse_simple_codec_map(local_codec);
    transmitCodecEvent();

}

map<int, string> CodecAwareConduit::getRemoteCodec() {
    scoped_lock lock(conduit_mutex);
    waitForRemoteCodec(lock);
    return remote_codec;
}

map<string, int> CodecAwareConduit::getRemoteReverseCodec() {
    scoped_lock lock(conduit_mutex);
    waitForRemoteCodec(lock);
    return remote_reverse_codec;
}

void CodecAwareConduit::waitForRemoteCodec(scoped_lock &lock) {
    if (remote_codec.empty()) {
        sendData(SystemEventFactory::requestCodecControl());

        boost::system_time timeout = boost::get_system_time() + boost::posix_time::milliseconds(CODEC_RESEND_TIMEOUT_MS);
        do {
            if (!remote_codec_cond.timed_wait(lock, timeout)) {
                throw SimpleException("Timeout while waiting for remote codec");
            }
        } while (remote_codec.empty());
    }
}

<commit_msg>CodecAwareConduit now ensures that the codec has been received before registering a callback by name<commit_after>/*
 *  CodecAwareConduit.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 12/10/09.
 *  Copyright 2009 Harvard University. All rights reserved.
 *
 */

#include "CodecAwareConduit.h"
#include "SystemEventFactory.h"
#include "StandardVariables.h"

void CodecAwareConduit::transmitCodecEvent(){
    // This is called internally from methods that already hold conduit_mutex

    //std::cerr << "transmitting codec" << endl;
    //mw::Datum codec_datum(local_codec);
    mw::Datum codec_datum = construct_simple_codec_datum_from_map(local_codec);
    sendData(RESERVED_CODEC_CODE, codec_datum);
}

void CodecAwareConduit::receiveCodecEvent(shared_ptr<Event> evt){
    // This is called from handleCallbacks, which already holds conduit_mutex

    remote_codec = extract_simple_codec_map(evt);
    remote_reverse_codec = reverse_simple_codec_map(remote_codec);

    rebuildEventCallbacks();

    remote_codec_cond.notify_all();
}

void CodecAwareConduit::receiveControlEvent(shared_ptr<Event> evt){
    // This is called from handleCallbacks, which already holds conduit_mutex

    Datum payload_type = evt->getData().getElement(M_SYSTEM_PAYLOAD_TYPE);
    if((int)payload_type == M_REQUEST_CODEC){
        transmitCodecEvent();
    }
}

void CodecAwareConduit::addEventCallback(const std::string &evt_name, EventCallback cb) {
    // This is called internally from methods that already hold conduit_mutex

    int incoming_code = M_UNDEFINED_EVENT_CODE;

    if (remote_reverse_codec.find(evt_name) != remote_reverse_codec.end()) {
        incoming_code = remote_reverse_codec[evt_name];
    } else {
        //std::cerr << "Cannot register callback for unknown name: " << evt_name << " (maybe codec hasn't been received yet?)" << endl;
        // send a request, just in case no one else has
        // sendData(SystemEventFactory::requestCodecControl());
        return;
    }

    //std::cerr << "registering callback for code: " << incoming_code << " to name: " << evt_name << " in: " << this << std::endl;

    // find out if that event name has a different code on this side of the conduit
    // if so, we'll want to create a translation callback
    int local_code = M_UNDEFINED_EVENT_CODE;

    if (local_reverse_codec.find(evt_name) != local_reverse_codec.end()) {
        local_code = local_reverse_codec[evt_name];
    }

    if (M_UNDEFINED_EVENT_CODE == local_code) {
        registerCallback(incoming_code, cb, string(name_defined_callback_key));
    } else {
        EventCallback cb2 = boost::bind(&CodecAwareConduit::codeTranslatedCallback, _1, cb, local_code);
        registerCallback(incoming_code, cb2, string(name_defined_callback_key));
    }

    // send a request to the other side of the conduit to begin forwarding this event
    sendData(SystemEventFactory::setEventForwardingControl(evt_name, true));
}

void CodecAwareConduit::rebuildEventCallbacks(){
    // This is called internally from methods that already hold conduit_mutex

    //std::cerr << "rebuildEventCallbacks called in: " << this << std::endl;

    unregisterCallbacks(name_defined_callback_key);

    for (map<string, EventCallback>::iterator i = callbacks_by_name.begin(); i != callbacks_by_name.end(); ++i) {
        string evt_name = (*i).first;
        EventCallback cb = (*i).second;
        addEventCallback(evt_name, cb);
    }

}

void CodecAwareConduit::codeTranslatedCallback(shared_ptr<Event> evt, EventCallback cb, int new_code){
    // This is a static method that does not need to hold conduit_mutex

    //std::cerr << "calling code translated callback; new_code: " << new_code << " (old code: " << evt->getEventCode() << ")" << endl;
    evt->setEventCode(new_code);
    return cb(evt);
}

void CodecAwareConduit::handleCallbacks(shared_ptr<Event> evt) {
    scoped_lock lock(conduit_mutex);
    SimpleConduit::handleCallbacks(evt);
}

bool CodecAwareConduit::initialize(){
    //std::cerr << "initializing: " << this << std::endl;
    //EventCallback cb = boost::bind(&CodecAwareConduit::receiveCodecEvent, this, _1);
    EventCallback cb = boost::bind(&CodecAwareConduit::receiveCodecEvent, shared_from_this(), _1);
    registerCallback(RESERVED_CODEC_CODE, cb);
    registerCallback(RESERVED_SYSTEM_EVENT_CODE, boost::bind(&CodecAwareConduit::receiveControlEvent, shared_from_this(), _1));
    bool okayp = SimpleConduit::initialize();

    if(okayp){
        sendData(SystemEventFactory::requestCodecControl());
    }

    return okayp;
}

void CodecAwareConduit::registerCallbackByName(string event_name, EventCallback cb){
    scoped_lock lock(conduit_mutex);

    if(event_name.size() == 0){
        throw SimpleException("Attempt to register callback for empty event name");
    }

    // We can't register a callback by name until we have the remote codec
    waitForRemoteCodec(lock);

    callbacks_by_name[event_name] = cb;
    addEventCallback(event_name, cb);

    //std::cerr << "Sending event forwarding event for " << event_name << std::endl;
    sendData(SystemEventFactory::setEventForwardingControl(event_name, true));
}

void CodecAwareConduit::registerLocalEventCode(int code, string event_name){
    scoped_lock lock(conduit_mutex);

    //std::cerr << "registering local name: " << event_name << " to code: " << code << endl;
    local_codec[code] = event_name;

    local_reverse_codec = reverse_simple_codec_map(local_codec);
    transmitCodecEvent();

}

map<int, string> CodecAwareConduit::getRemoteCodec() {
    scoped_lock lock(conduit_mutex);
    waitForRemoteCodec(lock);
    return remote_codec;
}

map<string, int> CodecAwareConduit::getRemoteReverseCodec() {
    scoped_lock lock(conduit_mutex);
    waitForRemoteCodec(lock);
    return remote_reverse_codec;
}

void CodecAwareConduit::waitForRemoteCodec(scoped_lock &lock) {
    if (remote_codec.empty()) {
        sendData(SystemEventFactory::requestCodecControl());

        boost::system_time timeout = boost::get_system_time() + boost::posix_time::milliseconds(CODEC_RESEND_TIMEOUT_MS);
        do {
            if (!remote_codec_cond.timed_wait(lock, timeout)) {
                throw SimpleException("Timeout while waiting for remote codec");
            }
        } while (remote_codec.empty());
    }
}

<|endoftext|>


<commit_before>/** message_loop.cc
    Jeremy Barnes, 1 June 2012
    Copyright (c) 2012 Datacratic.  All rights reserved.

*/

#include <thread>
#include <time.h>
#include <limits.h>
#include <sys/epoll.h>

#include "jml/arch/exception.h"
#include "jml/arch/futex.h"
#include "jml/arch/timers.h"
#include "jml/arch/demangle.h"
#include "jml/arch/futex.h"
#include "jml/arch/backtrace.h"
#include "jml/utils/smart_ptr_utils.h"
#include "jml/utils/exc_assert.h"
#include "soa/types/date.h"

#include "message_loop.h"

using namespace std;

namespace Datacratic {

/*****************************************************************************/
/* MESSAGE LOOP                                                              */
/*****************************************************************************/

MessageLoop::
MessageLoop(int numThreads, double maxAddedLatency, int epollTimeout)
    : sourceActions_(32),
      numThreadsCreated(0),
      totalSleepTime_(0.0)
{
    init(numThreads, maxAddedLatency, epollTimeout);
}

MessageLoop::
~MessageLoop()
{
    shutdown();
}

void
MessageLoop::
init(int numThreads, double maxAddedLatency, int epollTimeout)
{
    // std::cerr << "msgloop init: " << this << "\n";
    if (maxAddedLatency == 0 && epollTimeout != -1)
        cerr << "warning: MessageLoop with maxAddedLatency of zero and epollTeimout != -1 will busy wait" << endl;

    // See the comments on processOne below for more details on this assertion.
    ExcAssertEqual(numThreads, 1);

    Epoller::init(16384, epollTimeout);
    maxAddedLatency_ = maxAddedLatency;
    handleEvent = std::bind(&MessageLoop::handleEpollEvent,
                            this,
                            std::placeholders::_1);

    /* Our source action is a source in itself. This enables us to handle
       source operations from the same epoll mechanism as the rest.

       Adding special source named "_shutdown", enable to trigger
       shutdown-related events, without requiring the use of an additional signal
       fd. */
    sourceActions_.onEvent = [&] (SourceAction && action) {
        handleSourceAction(move(action));
    };
    addFd(sourceActions_.selectFd(), &sourceActions_);

    debug_ = false;
}

void
MessageLoop::
start(const OnStop & onStop)
{
    if (numThreadsCreated)
        throw ML::Exception("already have started message loop");

    shutdown_ = false;

    //cerr << "starting thread from " << this << endl;
    //ML::backtrace();

    auto runfn = [&, onStop] () {
        this->runWorkerThread();
        if (onStop) onStop();
    };
    threads.create_thread(runfn);

    ++numThreadsCreated;
}

void
MessageLoop::
startSync()
{
    if (numThreadsCreated)
        throw ML::Exception("already have started message loop");

    ++numThreadsCreated;

    runWorkerThread();
}

void
MessageLoop::
shutdown()
{
    if (shutdown_)
        return;

    shutdown_ = true;
    ML::futex_wake((int &)shutdown_);

    addSource("_shutdown", nullptr);

    threads.join_all();

    numThreadsCreated = 0;
}

bool
MessageLoop::
addSource(const std::string & name,
          AsyncEventSource & source, int priority)
{
    return addSource(name, ML::make_unowned_std_sp(source), priority);
}

bool
MessageLoop::
addSource(const std::string & name,
          const std::shared_ptr<AsyncEventSource> & source,
          int priority)
{
    if (name != "_shutdown") {
        ExcCheck(!source->parent_, "source already has a parent: " + name);
        source->parent_ = this;
    }

    // cerr << "addSource: " << source.get()
    //      << " (" << ML::type_name(*source) << ")"
    //      << " needsPoll: " << source->needsPoll
    //      << " in msg loop: " << this
    //      << " needsPoll: " << needsPoll
    //      << endl;

    SourceEntry entry(name, source, priority);
    SourceAction newAction(SourceAction::ADD, move(entry));

    return sourceActions_.tryPush(move(newAction));
}

bool
MessageLoop::
addPeriodic(const std::string & name,
            double timePeriodSeconds,
            std::function<void (uint64_t)> toRun,
            int priority)
{
    auto newPeriodic
        = make_shared<PeriodicEventSource>(timePeriodSeconds, toRun);
    return addSource(name, newPeriodic, priority);
}

bool
MessageLoop::
removeSource(AsyncEventSource * source)
{
    // When we free the elements in our destructor, they will try to remove themselves.
    // we just make it a nop.
    if (shutdown_) return true;

    SourceEntry entry("", ML::make_unowned_std_sp(*source), 0);
    SourceAction newAction(SourceAction::REMOVE, move(entry));
    return sourceActions_.tryPush(move(newAction));
}

void
MessageLoop::
wakeupMainThread()
{
    // TODO: do
}

void
MessageLoop::
startSubordinateThread(const SubordinateThreadFn & thread)
{
    Guard guard(threadsLock);
    int64_t id = 0;
    threads.create_thread(std::bind(thread, std::ref(shutdown_),
                                    id));
}

void
MessageLoop::
runWorkerThread()
{
    Date lastCheck = Date::now();

    ML::Duty_Cycle_Timer duty;

    while (!shutdown_) {
        Date start = Date::now();

        bool more = true;

        if (debug_) {
            cerr << "handling events from " << sources.size()
                 << " sources with needsPoll " << needsPoll << endl;
            for (unsigned i = 0;  i < sources.size();  ++i)
                cerr << sources[i].name << " " << sources[i].source->needsPoll << endl;
        }

        while (more) {
            more = processOne();
        }

        Date end = Date::now();

        double elapsed = end.secondsSince(start);
        double sleepTime = maxAddedLatency_ - elapsed;

        duty.notifyBeforeSleep();
        if (sleepTime > 0) {
            ML::sleep(sleepTime);
            totalSleepTime_ += sleepTime;
        }
        duty.notifyAfterSleep();

        if (lastCheck.secondsUntil(end) > 10.0) {
            // auto stats = duty.stats();
            //cerr << "message loop: wakeups " << stats.numWakeups
            //     << " duty " << stats.duty_cycle() << endl;
            lastCheck = end;
            duty.clear();
        }
    }
}

bool
MessageLoop::
handleEpollEvent(epoll_event & event)
{
    bool debug = false;

    if (debug) {
        cerr << "handleEvent" << endl;
        int mask = event.events;

        cerr << "events "
             << (mask & EPOLLIN ? "I" : "")
             << (mask & EPOLLOUT ? "O" : "")
             << (mask & EPOLLPRI ? "P" : "")
             << (mask & EPOLLERR ? "E" : "")
             << (mask & EPOLLHUP ? "H" : "")
             << (mask & EPOLLRDHUP ? "R" : "")
             << endl;
    }

    AsyncEventSource * source
        = reinterpret_cast<AsyncEventSource *>(event.data.ptr);

    //cerr << "source = " << source << " of type "
    //     << ML::type_name(*source) << endl;

    source->processOne();

    return false;
}

void
MessageLoop::
handleSourceAction(SourceAction && action)
{
    if (action.action_ == SourceAction::ADD) {
        processAddSource(action.entry_);
    }
    else if (action.action_ == SourceAction::REMOVE) {
        processRemoveSource(action.entry_);
    }
}

void
MessageLoop::
processAddSource(const SourceEntry & entry)
{
    if (entry.name == "_shutdown")
        return;

    // cerr << "processAddSource: " << entry.source.get()
    //      << " (" << ML::type_name(*entry.source) << ")"
    //      << " needsPoll: " << entry.source->needsPoll
    //      << " in msg loop: " << this
    //      << " needsPoll: " << needsPoll
    //      << endl;
    int fd = entry.source->selectFd();
    if (fd != -1)
        addFd(fd, entry.source.get());

    if (!needsPoll && entry.source->needsPoll) {
        needsPoll = true;
        if (parent_) parent_->checkNeedsPoll();
    }

    if (debug_) entry.source->debug(true);
    sources.push_back(entry);

    entry.source->connectionState_ = AsyncEventSource::CONNECTED;
    ML::futex_wake(entry.source->connectionState_);
}

void
MessageLoop::
processRemoveSource(const SourceEntry & rmEntry)
{
    auto pred = [&] (const SourceEntry & entry) {
        return entry.source.get() == rmEntry.source.get();
    };
    auto it = find_if(sources.begin(), sources.end(), pred);

    ExcCheck(it != sources.end(), "couldn't remove source");

    SourceEntry entry = *it;
    sources.erase(it);

    entry.source->parent_ = nullptr;
    int fd = entry.source->selectFd();
    if (fd == -1) return;
    removeFd(fd);

    // Make sure that our and our parent's value of needsPoll is up to date
    bool sourceNeedsPoll = entry.source->needsPoll;
    if (needsPoll && sourceNeedsPoll) {
        bool oldNeedsPoll = needsPoll;
        checkNeedsPoll();
        if (oldNeedsPoll != needsPoll && parent_)
            parent_->checkNeedsPoll();
    }

    entry.source->connectionState_ = AsyncEventSource::DISCONNECTED;
    ML::futex_wake(entry.source->connectionState_);
}

bool
MessageLoop::
poll() const
{
    if (needsPoll) {
        for (auto & s: sources)
            if (s.source->poll())
                return true;
        return false;
    }
    else return Epoller::poll();
}

/** This function assumes that it's called by only a single thread. This
    contradicts the AsyncEventSource documentation for processOne. If
    multi-threading is ever required then accesses to the sources array will
    need to be synchronized in some way.

    Note, that this synchronization mechanism should not hold a lock while
    calling a child's processOne() function. This can easily lead to
    deadlocks. This is the main reason for the existence of the sources queues.

 */
bool
MessageLoop::
processOne()
{
    bool more = false;

    if (needsPoll) {
        more = sourceActions_.processOne();
        for (unsigned i = 0;  i < sources.size();  ++i) {
            try {
                bool hasMore = sources[i].source->processOne();
                if (debug_)
                    cerr << "source " << sources[i].name << " has " << hasMore << endl;
                more = more || hasMore;
            } catch (...) {
                cerr << "exception processing source " << sources[i].name
                     << endl;
                throw;
            }
        }
    }
    else more = Epoller::processOne();

    return more;
}

void
MessageLoop::
debug(bool debugOn)
{
    debug_ = debugOn;
}

void
MessageLoop::
checkNeedsPoll()
{
    bool newNeedsPoll = false;
    for (unsigned i = 0;  i < sources.size() && !newNeedsPoll;  ++i)
        newNeedsPoll = sources[i].source->needsPoll;

    if (newNeedsPoll == needsPoll) return;

    needsPoll = newNeedsPoll;
    if (parent_) parent_->checkNeedsPoll();
}

} // namespace Datacratic
<commit_msg>improved comment<commit_after>/** message_loop.cc
    Jeremy Barnes, 1 June 2012
    Copyright (c) 2012 Datacratic.  All rights reserved.

*/

#include <thread>
#include <time.h>
#include <limits.h>
#include <sys/epoll.h>

#include "jml/arch/exception.h"
#include "jml/arch/futex.h"
#include "jml/arch/timers.h"
#include "jml/arch/demangle.h"
#include "jml/arch/futex.h"
#include "jml/arch/backtrace.h"
#include "jml/utils/smart_ptr_utils.h"
#include "jml/utils/exc_assert.h"
#include "soa/types/date.h"

#include "message_loop.h"

using namespace std;

namespace Datacratic {

/*****************************************************************************/
/* MESSAGE LOOP                                                              */
/*****************************************************************************/

MessageLoop::
MessageLoop(int numThreads, double maxAddedLatency, int epollTimeout)
    : sourceActions_(32),
      numThreadsCreated(0),
      totalSleepTime_(0.0)
{
    init(numThreads, maxAddedLatency, epollTimeout);
}

MessageLoop::
~MessageLoop()
{
    shutdown();
}

void
MessageLoop::
init(int numThreads, double maxAddedLatency, int epollTimeout)
{
    // std::cerr << "msgloop init: " << this << "\n";
    if (maxAddedLatency == 0 && epollTimeout != -1)
        cerr << "warning: MessageLoop with maxAddedLatency of zero and epollTeimout != -1 will busy wait" << endl;

    // See the comments on processOne below for more details on this assertion.
    ExcAssertEqual(numThreads, 1);

    Epoller::init(16384, epollTimeout);
    maxAddedLatency_ = maxAddedLatency;
    handleEvent = std::bind(&MessageLoop::handleEpollEvent,
                            this,
                            std::placeholders::_1);

    /* Our source action queue is a source in itself, which enables us to
       handle source operations from the same epoll mechanism as the rest.

       Adding a special source named "_shutdown" triggers shutdown-related
       events, without requiring the use of an additional signal fd. */
    sourceActions_.onEvent = [&] (SourceAction && action) {
        handleSourceAction(move(action));
    };
    addFd(sourceActions_.selectFd(), &sourceActions_);

    debug_ = false;
}

void
MessageLoop::
start(const OnStop & onStop)
{
    if (numThreadsCreated)
        throw ML::Exception("already have started message loop");

    shutdown_ = false;

    //cerr << "starting thread from " << this << endl;
    //ML::backtrace();

    auto runfn = [&, onStop] () {
        this->runWorkerThread();
        if (onStop) onStop();
    };
    threads.create_thread(runfn);

    ++numThreadsCreated;
}

void
MessageLoop::
startSync()
{
    if (numThreadsCreated)
        throw ML::Exception("already have started message loop");

    ++numThreadsCreated;

    runWorkerThread();
}

void
MessageLoop::
shutdown()
{
    if (shutdown_)
        return;

    shutdown_ = true;
    ML::futex_wake((int &)shutdown_);

    addSource("_shutdown", nullptr);

    threads.join_all();

    numThreadsCreated = 0;
}

bool
MessageLoop::
addSource(const std::string & name,
          AsyncEventSource & source, int priority)
{
    return addSource(name, ML::make_unowned_std_sp(source), priority);
}

bool
MessageLoop::
addSource(const std::string & name,
          const std::shared_ptr<AsyncEventSource> & source,
          int priority)
{
    if (name != "_shutdown") {
        ExcCheck(!source->parent_, "source already has a parent: " + name);
        source->parent_ = this;
    }

    // cerr << "addSource: " << source.get()
    //      << " (" << ML::type_name(*source) << ")"
    //      << " needsPoll: " << source->needsPoll
    //      << " in msg loop: " << this
    //      << " needsPoll: " << needsPoll
    //      << endl;

    SourceEntry entry(name, source, priority);
    SourceAction newAction(SourceAction::ADD, move(entry));

    return sourceActions_.tryPush(move(newAction));
}

bool
MessageLoop::
addPeriodic(const std::string & name,
            double timePeriodSeconds,
            std::function<void (uint64_t)> toRun,
            int priority)
{
    auto newPeriodic
        = make_shared<PeriodicEventSource>(timePeriodSeconds, toRun);
    return addSource(name, newPeriodic, priority);
}

bool
MessageLoop::
removeSource(AsyncEventSource * source)
{
    // When we free the elements in our destructor, they will try to remove themselves.
    // we just make it a nop.
    if (shutdown_) return true;

    SourceEntry entry("", ML::make_unowned_std_sp(*source), 0);
    SourceAction newAction(SourceAction::REMOVE, move(entry));
    return sourceActions_.tryPush(move(newAction));
}

void
MessageLoop::
wakeupMainThread()
{
    // TODO: do
}

void
MessageLoop::
startSubordinateThread(const SubordinateThreadFn & thread)
{
    Guard guard(threadsLock);
    int64_t id = 0;
    threads.create_thread(std::bind(thread, std::ref(shutdown_),
                                    id));
}

void
MessageLoop::
runWorkerThread()
{
    Date lastCheck = Date::now();

    ML::Duty_Cycle_Timer duty;

    while (!shutdown_) {
        Date start = Date::now();

        bool more = true;

        if (debug_) {
            cerr << "handling events from " << sources.size()
                 << " sources with needsPoll " << needsPoll << endl;
            for (unsigned i = 0;  i < sources.size();  ++i)
                cerr << sources[i].name << " " << sources[i].source->needsPoll << endl;
        }

        while (more) {
            more = processOne();
        }

        Date end = Date::now();

        double elapsed = end.secondsSince(start);
        double sleepTime = maxAddedLatency_ - elapsed;

        duty.notifyBeforeSleep();
        if (sleepTime > 0) {
            ML::sleep(sleepTime);
            totalSleepTime_ += sleepTime;
        }
        duty.notifyAfterSleep();

        if (lastCheck.secondsUntil(end) > 10.0) {
            // auto stats = duty.stats();
            //cerr << "message loop: wakeups " << stats.numWakeups
            //     << " duty " << stats.duty_cycle() << endl;
            lastCheck = end;
            duty.clear();
        }
    }
}

bool
MessageLoop::
handleEpollEvent(epoll_event & event)
{
    bool debug = false;

    if (debug) {
        cerr << "handleEvent" << endl;
        int mask = event.events;

        cerr << "events "
             << (mask & EPOLLIN ? "I" : "")
             << (mask & EPOLLOUT ? "O" : "")
             << (mask & EPOLLPRI ? "P" : "")
             << (mask & EPOLLERR ? "E" : "")
             << (mask & EPOLLHUP ? "H" : "")
             << (mask & EPOLLRDHUP ? "R" : "")
             << endl;
    }

    AsyncEventSource * source
        = reinterpret_cast<AsyncEventSource *>(event.data.ptr);

    //cerr << "source = " << source << " of type "
    //     << ML::type_name(*source) << endl;

    source->processOne();

    return false;
}

void
MessageLoop::
handleSourceAction(SourceAction && action)
{
    if (action.action_ == SourceAction::ADD) {
        processAddSource(action.entry_);
    }
    else if (action.action_ == SourceAction::REMOVE) {
        processRemoveSource(action.entry_);
    }
}

void
MessageLoop::
processAddSource(const SourceEntry & entry)
{
    if (entry.name == "_shutdown")
        return;

    // cerr << "processAddSource: " << entry.source.get()
    //      << " (" << ML::type_name(*entry.source) << ")"
    //      << " needsPoll: " << entry.source->needsPoll
    //      << " in msg loop: " << this
    //      << " needsPoll: " << needsPoll
    //      << endl;
    int fd = entry.source->selectFd();
    if (fd != -1)
        addFd(fd, entry.source.get());

    if (!needsPoll && entry.source->needsPoll) {
        needsPoll = true;
        if (parent_) parent_->checkNeedsPoll();
    }

    if (debug_) entry.source->debug(true);
    sources.push_back(entry);

    entry.source->connectionState_ = AsyncEventSource::CONNECTED;
    ML::futex_wake(entry.source->connectionState_);
}

void
MessageLoop::
processRemoveSource(const SourceEntry & rmEntry)
{
    auto pred = [&] (const SourceEntry & entry) {
        return entry.source.get() == rmEntry.source.get();
    };
    auto it = find_if(sources.begin(), sources.end(), pred);

    ExcCheck(it != sources.end(), "couldn't remove source");

    SourceEntry entry = *it;
    sources.erase(it);

    entry.source->parent_ = nullptr;
    int fd = entry.source->selectFd();
    if (fd == -1) return;
    removeFd(fd);

    // Make sure that our and our parent's value of needsPoll is up to date
    bool sourceNeedsPoll = entry.source->needsPoll;
    if (needsPoll && sourceNeedsPoll) {
        bool oldNeedsPoll = needsPoll;
        checkNeedsPoll();
        if (oldNeedsPoll != needsPoll && parent_)
            parent_->checkNeedsPoll();
    }

    entry.source->connectionState_ = AsyncEventSource::DISCONNECTED;
    ML::futex_wake(entry.source->connectionState_);
}

bool
MessageLoop::
poll() const
{
    if (needsPoll) {
        for (auto & s: sources)
            if (s.source->poll())
                return true;
        return false;
    }
    else return Epoller::poll();
}

/** This function assumes that it's called by only a single thread. This
    contradicts the AsyncEventSource documentation for processOne. If
    multi-threading is ever required then accesses to the sources array will
    need to be synchronized in some way.

    Note, that this synchronization mechanism should not hold a lock while
    calling a child's processOne() function. This can easily lead to
    deadlocks. This is the main reason for the existence of the sources queues.

 */
bool
MessageLoop::
processOne()
{
    bool more = false;

    if (needsPoll) {
        more = sourceActions_.processOne();
        for (unsigned i = 0;  i < sources.size();  ++i) {
            try {
                bool hasMore = sources[i].source->processOne();
                if (debug_)
                    cerr << "source " << sources[i].name << " has " << hasMore << endl;
                more = more || hasMore;
            } catch (...) {
                cerr << "exception processing source " << sources[i].name
                     << endl;
                throw;
            }
        }
    }
    else more = Epoller::processOne();

    return more;
}

void
MessageLoop::
debug(bool debugOn)
{
    debug_ = debugOn;
}

void
MessageLoop::
checkNeedsPoll()
{
    bool newNeedsPoll = false;
    for (unsigned i = 0;  i < sources.size() && !newNeedsPoll;  ++i)
        newNeedsPoll = sources[i].source->needsPoll;

    if (newNeedsPoll == needsPoll) return;

    needsPoll = newNeedsPoll;
    if (parent_) parent_->checkNeedsPoll();
}

} // namespace Datacratic
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <functional>
#include <mutex>

#include "IOGroup.hpp"

#include "geopm_plugin.hpp"
#include "MSRIOGroup.hpp"
#include "CpuinfoIOGroup.hpp"
#include "TimeIOGroup.hpp"
#include "SSTIOGroup.hpp"
#include "Helper.hpp"
#include "config.h"
#ifdef GEOPM_CNL_IOGROUP
#include "CNLIOGroup.hpp"
#endif
#ifdef GEOPM_ENABLE_NVML
#include "NVMLIOGroup.hpp"
#endif
#ifdef GEOPM_ENABLE_LEVELZERO
#include "LevelZeroIOGroup.hpp"
#endif
#ifdef GEOPM_DEBUG
#include <iostream>
#endif

namespace geopm
{
    const std::string IOGroup::M_PLUGIN_PREFIX = "libgeopmiogroup_";

    const std::string IOGroup::M_UNITS[IOGroup::M_NUM_UNITS] = {
        "none",
        "seconds",
        "hertz",
        "watts",
        "joules",
        "celsius"
    };

    const std::string IOGroup::M_BEHAVIORS[IOGroup::M_NUM_SIGNAL_BEHAVIOR] = {
        "constant",
        "monotone",
        "variable",
        "label"
    };

    const std::map<std::string, IOGroup::m_units_e> IOGroup::M_UNITS_STRING = {
        {IOGroup::M_UNITS[M_UNITS_NONE], M_UNITS_NONE},
        {IOGroup::M_UNITS[M_UNITS_SECONDS], M_UNITS_SECONDS},
        {IOGroup::M_UNITS[M_UNITS_HERTZ], M_UNITS_HERTZ},
        {IOGroup::M_UNITS[M_UNITS_WATTS], M_UNITS_WATTS},
        {IOGroup::M_UNITS[M_UNITS_JOULES], M_UNITS_JOULES},
        {IOGroup::M_UNITS[M_UNITS_CELSIUS], M_UNITS_CELSIUS}
    };

    const std::map<std::string, IOGroup::m_signal_behavior_e> IOGroup::M_BEHAVIOR_STRING = {
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_CONSTANT], M_SIGNAL_BEHAVIOR_CONSTANT},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_MONOTONE], M_SIGNAL_BEHAVIOR_MONOTONE},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_VARIABLE], M_SIGNAL_BEHAVIOR_VARIABLE},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_LABEL], M_SIGNAL_BEHAVIOR_LABEL},
    };

    IOGroupFactory::IOGroupFactory()
    {
#ifndef GEOPM_SERVICE_BUILD
        // Unless compiling for geopmd add the ServiceIOGroup which
        // will go through D-Bus to access geopmd.  Note this IOGroup
        // is loaded first and provides all signals and controls
        // available from geopmd.  Any signal or control available
        // without using the service will be used preferentially
        // because this IOGroup is loaded first.  Also note that
        // creation of the ServiceIOGroup will open a session with the
        // service enabling save/restore by geopmd.  If the geopm
        // service is not active then loading the ServiceIOGroup will
        // fail.
        register_plugin(ServiceIOGroup::plugin_name(),
                        ServiceIOGroup::make_plugin);
#endif
        register_plugin(MSRIOGroup::plugin_name(),
                        MSRIOGroup::make_plugin);
        register_plugin(TimeIOGroup::plugin_name(),
                        TimeIOGroup::make_plugin);
        register_plugin(CpuinfoIOGroup::plugin_name(),
                        CpuinfoIOGroup::make_plugin);
        register_plugin(SSTIOGroup::plugin_name(),
                        SSTIOGroup::make_plugin);
#ifdef GEOPM_CNL_IOGROUP
        register_plugin(CNLIOGroup::plugin_name(),
                        CNLIOGroup::make_plugin);
#endif
#ifdef GEOPM_ENABLE_NVML
        register_plugin(NVMLIOGroup::plugin_name(),
                        NVMLIOGroup::make_plugin);
#endif
#ifdef GEOPM_ENABLE_LEVELZERO
        register_plugin(LevelZeroIOGroup::plugin_name(),
                        LevelZeroIOGroup::make_plugin);
#endif
    }

    IOGroupFactory &iogroup_factory(void)
    {
        static IOGroupFactory instance;
        static bool is_once = true;
        static std::once_flag flag;
        if (is_once) {
            is_once = false;
            std::call_once(flag, plugin_load, IOGroup::M_PLUGIN_PREFIX);
        }
        return instance;
    }

    std::vector<std::string> IOGroup::iogroup_names(void)
    {
        return iogroup_factory().plugin_names();
    }

    std::unique_ptr<IOGroup> IOGroup::make_unique(const std::string &iogroup_name)
    {
        return iogroup_factory().make_plugin(iogroup_name);
    }

    std::function<std::string(double)> IOGroup::format_function(const std::string &signal_name) const
    {
#ifdef GEOPM_DEBUG
        static bool is_once = true;
        if (is_once) {
            is_once = false;
            std::cerr << "Warning: <geopm> Use of geopm::IOGroup::format_function() is deprecated, each IOGroup will be required implement this method in the future.\n";
        }
#endif
        std::function<std::string(double)> result = string_format_double;
        if (string_ends_with(signal_name, "#")) {
            result = string_format_raw64;
        }
        return result;
    }

    IOGroup::m_units_e IOGroup::string_to_units(const std::string &str)
    {
        auto it = M_UNITS_STRING.find(str);
        if (it == M_UNITS_STRING.end()) {
            throw Exception("IOGroup::string_to_units(): invalid units string",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return it->second;
    }

    std::string IOGroup::units_to_string(int uni)
    {
        if (uni >= IOGroup::M_NUM_UNITS || uni < 0) {
            throw Exception("IOGroup::units_to_string): invalid units value",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return IOGroup::M_UNITS[uni];
    }

    IOGroup::m_signal_behavior_e IOGroup::string_to_behavior(const std::string &str)
    {
        auto it = M_BEHAVIOR_STRING.find(str);
        if (it == M_BEHAVIOR_STRING.end()) {
            throw Exception("IOGroup::string_to_behavior(): invalid behavior string",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return it->second;
    }
}
<commit_msg>Add back in ProfileIOGroup on client side<commit_after>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "config.h"

#include <functional>
#include <mutex>

#include "IOGroup.hpp"

#include "geopm_plugin.hpp"
#include "MSRIOGroup.hpp"
#include "CpuinfoIOGroup.hpp"
#include "TimeIOGroup.hpp"
#include "SSTIOGroup.hpp"
#include "Helper.hpp"
#ifndef GEOPM_SERVICE_BUILD
#include "ProfileIOGroup.hpp"
#endif
#ifdef GEOPM_CNL_IOGROUP
#include "CNLIOGroup.hpp"
#endif
#ifdef GEOPM_ENABLE_NVML
#include "NVMLIOGroup.hpp"
#endif
#ifdef GEOPM_ENABLE_LEVELZERO
#include "LevelZeroIOGroup.hpp"
#endif
#ifdef GEOPM_DEBUG
#include <iostream>
#endif

namespace geopm
{
    const std::string IOGroup::M_PLUGIN_PREFIX = "libgeopmiogroup_";

    const std::string IOGroup::M_UNITS[IOGroup::M_NUM_UNITS] = {
        "none",
        "seconds",
        "hertz",
        "watts",
        "joules",
        "celsius"
    };

    const std::string IOGroup::M_BEHAVIORS[IOGroup::M_NUM_SIGNAL_BEHAVIOR] = {
        "constant",
        "monotone",
        "variable",
        "label"
    };

    const std::map<std::string, IOGroup::m_units_e> IOGroup::M_UNITS_STRING = {
        {IOGroup::M_UNITS[M_UNITS_NONE], M_UNITS_NONE},
        {IOGroup::M_UNITS[M_UNITS_SECONDS], M_UNITS_SECONDS},
        {IOGroup::M_UNITS[M_UNITS_HERTZ], M_UNITS_HERTZ},
        {IOGroup::M_UNITS[M_UNITS_WATTS], M_UNITS_WATTS},
        {IOGroup::M_UNITS[M_UNITS_JOULES], M_UNITS_JOULES},
        {IOGroup::M_UNITS[M_UNITS_CELSIUS], M_UNITS_CELSIUS}
    };

    const std::map<std::string, IOGroup::m_signal_behavior_e> IOGroup::M_BEHAVIOR_STRING = {
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_CONSTANT], M_SIGNAL_BEHAVIOR_CONSTANT},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_MONOTONE], M_SIGNAL_BEHAVIOR_MONOTONE},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_VARIABLE], M_SIGNAL_BEHAVIOR_VARIABLE},
        {IOGroup::M_BEHAVIORS[M_SIGNAL_BEHAVIOR_LABEL], M_SIGNAL_BEHAVIOR_LABEL},
    };

    IOGroupFactory::IOGroupFactory()
    {
#ifndef GEOPM_SERVICE_BUILD
        // Unless compiling for geopmd add the ServiceIOGroup which
        // will go through D-Bus to access geopmd.  Note this IOGroup
        // is loaded first and provides all signals and controls
        // available from geopmd.  Any signal or control available
        // without using the service will be used preferentially
        // because this IOGroup is loaded first.  Also note that
        // creation of the ServiceIOGroup will open a session with the
        // service enabling save/restore by geopmd.  If the geopm
        // service is not active then loading the ServiceIOGroup will
        // fail.
        register_plugin(ServiceIOGroup::plugin_name(),
                        ServiceIOGroup::make_plugin);
        // Enable application profiling if not compiling the service.
        register_plugin(ProfileIOGroup::plugin_name(),
                        ProfileIOGroup::make_plugin);
#endif
        register_plugin(MSRIOGroup::plugin_name(),
                        MSRIOGroup::make_plugin);
        register_plugin(TimeIOGroup::plugin_name(),
                        TimeIOGroup::make_plugin);
        register_plugin(CpuinfoIOGroup::plugin_name(),
                        CpuinfoIOGroup::make_plugin);
        register_plugin(SSTIOGroup::plugin_name(),
                        SSTIOGroup::make_plugin);
#ifdef GEOPM_CNL_IOGROUP
        register_plugin(CNLIOGroup::plugin_name(),
                        CNLIOGroup::make_plugin);
#endif
#ifdef GEOPM_ENABLE_NVML
        register_plugin(NVMLIOGroup::plugin_name(),
                        NVMLIOGroup::make_plugin);
#endif
#ifdef GEOPM_ENABLE_LEVELZERO
        register_plugin(LevelZeroIOGroup::plugin_name(),
                        LevelZeroIOGroup::make_plugin);
#endif
    }

    IOGroupFactory &iogroup_factory(void)
    {
        static IOGroupFactory instance;
        static bool is_once = true;
        static std::once_flag flag;
        if (is_once) {
            is_once = false;
            std::call_once(flag, plugin_load, IOGroup::M_PLUGIN_PREFIX);
        }
        return instance;
    }

    std::vector<std::string> IOGroup::iogroup_names(void)
    {
        return iogroup_factory().plugin_names();
    }

    std::unique_ptr<IOGroup> IOGroup::make_unique(const std::string &iogroup_name)
    {
        return iogroup_factory().make_plugin(iogroup_name);
    }

    std::function<std::string(double)> IOGroup::format_function(const std::string &signal_name) const
    {
#ifdef GEOPM_DEBUG
        static bool is_once = true;
        if (is_once) {
            is_once = false;
            std::cerr << "Warning: <geopm> Use of geopm::IOGroup::format_function() is deprecated, each IOGroup will be required implement this method in the future.\n";
        }
#endif
        std::function<std::string(double)> result = string_format_double;
        if (string_ends_with(signal_name, "#")) {
            result = string_format_raw64;
        }
        return result;
    }

    IOGroup::m_units_e IOGroup::string_to_units(const std::string &str)
    {
        auto it = M_UNITS_STRING.find(str);
        if (it == M_UNITS_STRING.end()) {
            throw Exception("IOGroup::string_to_units(): invalid units string",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return it->second;
    }

    std::string IOGroup::units_to_string(int uni)
    {
        if (uni >= IOGroup::M_NUM_UNITS || uni < 0) {
            throw Exception("IOGroup::units_to_string): invalid units value",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return IOGroup::M_UNITS[uni];
    }

    IOGroup::m_signal_behavior_e IOGroup::string_to_behavior(const std::string &str)
    {
        auto it = M_BEHAVIOR_STRING.find(str);
        if (it == M_BEHAVIOR_STRING.end()) {
            throw Exception("IOGroup::string_to_behavior(): invalid behavior string",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        return it->second;
    }
}
<|endoftext|>