<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

/* ************************************************************************** */
/* Image Pool                                                                 */
/* ************************************************************************** */

#include "ImagePool.h"
#include "AuthManager.h"
#include "Nebula.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
string ImagePool::_default_type;
string ImagePool::_default_dev_prefix;

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

ImagePool::ImagePool(SqlDB *       db,
                     const string& __default_type,
                     const string& __default_dev_prefix):
                        PoolSQL(db,Image::table)
{
    ostringstream sql;

    // Init static defaults
    _default_type       = __default_type;
    _default_dev_prefix = __default_dev_prefix;

    // Set default type
    if (_default_type != "OS"       &&
        _default_type != "CDROM"    &&
        _default_type != "DATABLOCK" )
    {
        NebulaLog::log("IMG", Log::ERROR, "Bad default for type, setting OS");
        _default_type = "OS";
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImagePool::allocate (
        int            uid,
        int            gid,
        const string&  uname,
        const string&  gname,
        ImageTemplate* img_template,
        int *          oid,
        string&        error_str)
{
    Image *         img;
    Image *         img_aux = 0;
    string          name;
    ostringstream   oss;

    img = new Image(uid, gid, uname, gname, img_template);

    // Check name
    img->get_template_attribute("NAME", name);

    if ( name.empty() )
    {
        goto error_name;
    }

    // Check for duplicates
    img_aux = get(name,uid,false);

    if( img_aux != 0 )
    {
        goto error_duplicated;
    }

    // ---------------------------------------------------------------------
    // Insert the Object in the pool & Register the image in the repository
    // ---------------------------------------------------------------------
    *oid = PoolSQL::allocate(img, error_str);

    if ( *oid != -1 )
    {
        Nebula&        nd     = Nebula::instance();
        ImageManager * imagem = nd.get_imagem();

        if ( imagem->register_image(*oid) == -1 )
        {
            error_str = "Failed to copy image to repository. "
                        "Image left in ERROR state.";
            return -1;
        }
    }

    return *oid;

error_name:
    oss << "NAME cannot be empty.";

    goto error_common;

error_duplicated:
    oss << "NAME is already taken by IMAGE "
        << img_aux->get_oid() << ".";

error_common:
    delete img;

    *oid = -1;
    error_str = oss.str();

    return *oid;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImagePool::disk_attribute(VectorAttribute *  disk,
                              int                disk_id,
                              int *              index,
                              Image::ImageType * img_type,
                              int                uid)
{
    string  source;
    Image * img = 0;
    int     rc;

    ostringstream oss;

    Nebula&        nd     = Nebula::instance();
    ImageManager * imagem = nd.get_imagem();

    istringstream   is;
    int             image_id;

    source = disk->vector_value("IMAGE");

    if (!source.empty())
    {
        return -3;
    }

    source = disk->vector_value("IMAGE_ID");

    if (!source.empty())
    {
        is.str(source);
        is >> image_id;

        if( !is.fail() )
        {
            img = imagem->acquire_image(image_id);

            if (img == 0)
            {
                return -1;
            }
        }
    }

    if (img == 0)
    {
        string type = disk->vector_value("TYPE");

        transform(type.begin(),type.end(),type.begin(),(int(*)(int))toupper);

        if( type == "SWAP" )
        {
            string target = disk->vector_value("TARGET");

            if ( target.empty() )
            {
                string  dev_prefix = _default_dev_prefix;

                dev_prefix += "d";

                disk->replace("TARGET", dev_prefix);
            }
        }

        rc = -2;
    }
    else
    {
        img->disk_attribute(disk, index, img_type);

        update(img);

        img->unlock();
    }

    oss << disk_id;
    disk->replace("DISK_ID",oss.str());

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImagePool::authorize_disk(VectorAttribute * disk,int uid, AuthRequest * ar)
{
    string  source;
    Image * img = 0;

    istringstream   is;
    int             image_id;

    source = disk->vector_value("IMAGE_ID");

    if (source.empty())
    {
        return;
    }

    is.str(source);
    is >> image_id;

    if( !is.fail() )
    {
        img = get(image_id,true);
    }

    if (img == 0)
    {
        return;
    }

    ar->add_auth(AuthRequest::IMAGE,
                 img->get_oid(),
                 img->get_gid(),
                 AuthRequest::USE,
                 img->get_uid(),
                 img->isPublic());

    img->unlock();
}
<commit_msg>Bug #806: Fix rc variable initialization in ImagePool::disk_attribute<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

/* ************************************************************************** */
/* Image Pool                                                                 */
/* ************************************************************************** */

#include "ImagePool.h"
#include "AuthManager.h"
#include "Nebula.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
string ImagePool::_default_type;
string ImagePool::_default_dev_prefix;

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

ImagePool::ImagePool(SqlDB *       db,
                     const string& __default_type,
                     const string& __default_dev_prefix):
                        PoolSQL(db,Image::table)
{
    ostringstream sql;

    // Init static defaults
    _default_type       = __default_type;
    _default_dev_prefix = __default_dev_prefix;

    // Set default type
    if (_default_type != "OS"       &&
        _default_type != "CDROM"    &&
        _default_type != "DATABLOCK" )
    {
        NebulaLog::log("IMG", Log::ERROR, "Bad default for type, setting OS");
        _default_type = "OS";
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImagePool::allocate (
        int            uid,
        int            gid,
        const string&  uname,
        const string&  gname,
        ImageTemplate* img_template,
        int *          oid,
        string&        error_str)
{
    Image *         img;
    Image *         img_aux = 0;
    string          name;
    ostringstream   oss;

    img = new Image(uid, gid, uname, gname, img_template);

    // Check name
    img->get_template_attribute("NAME", name);

    if ( name.empty() )
    {
        goto error_name;
    }

    // Check for duplicates
    img_aux = get(name,uid,false);

    if( img_aux != 0 )
    {
        goto error_duplicated;
    }

    // ---------------------------------------------------------------------
    // Insert the Object in the pool & Register the image in the repository
    // ---------------------------------------------------------------------
    *oid = PoolSQL::allocate(img, error_str);

    if ( *oid != -1 )
    {
        Nebula&        nd     = Nebula::instance();
        ImageManager * imagem = nd.get_imagem();

        if ( imagem->register_image(*oid) == -1 )
        {
            error_str = "Failed to copy image to repository. "
                        "Image left in ERROR state.";
            return -1;
        }
    }

    return *oid;

error_name:
    oss << "NAME cannot be empty.";

    goto error_common;

error_duplicated:
    oss << "NAME is already taken by IMAGE "
        << img_aux->get_oid() << ".";

error_common:
    delete img;

    *oid = -1;
    error_str = oss.str();

    return *oid;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImagePool::disk_attribute(VectorAttribute *  disk,
                              int                disk_id,
                              int *              index,
                              Image::ImageType * img_type,
                              int                uid)
{
    string  source;
    Image * img = 0;
    int     rc  = 0;

    ostringstream oss;

    Nebula&        nd     = Nebula::instance();
    ImageManager * imagem = nd.get_imagem();

    istringstream   is;
    int             image_id;

    source = disk->vector_value("IMAGE");

    if (!source.empty())
    {
        return -3;
    }

    source = disk->vector_value("IMAGE_ID");

    if (!source.empty())
    {
        is.str(source);
        is >> image_id;

        if( !is.fail() )
        {
            img = imagem->acquire_image(image_id);

            if (img == 0)
            {
                return -1;
            }
        }
    }

    if (img == 0)
    {
        string type = disk->vector_value("TYPE");

        transform(type.begin(),type.end(),type.begin(),(int(*)(int))toupper);

        if( type == "SWAP" )
        {
            string target = disk->vector_value("TARGET");

            if ( target.empty() )
            {
                string  dev_prefix = _default_dev_prefix;

                dev_prefix += "d";

                disk->replace("TARGET", dev_prefix);
            }
        }

        rc = -2;
    }
    else
    {
        img->disk_attribute(disk, index, img_type);

        update(img);

        img->unlock();
    }

    oss << disk_id;
    disk->replace("DISK_ID",oss.str());

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImagePool::authorize_disk(VectorAttribute * disk,int uid, AuthRequest * ar)
{
    string  source;
    Image * img = 0;

    istringstream   is;
    int             image_id;

    source = disk->vector_value("IMAGE_ID");

    if (source.empty())
    {
        return;
    }

    is.str(source);
    is >> image_id;

    if( !is.fail() )
    {
        img = get(image_id,true);
    }

    if (img == 0)
    {
        return;
    }

    ar->add_auth(AuthRequest::IMAGE,
                 img->get_oid(),
                 img->get_gid(),
                 AuthRequest::USE,
                 img->get_uid(),
                 img->isPublic());

    img->unlock();
}
<|endoftext|>


<commit_before>/*
 * Copyright 1999-2000,2004-2005 The Apache Software Foundation.
 *
 * 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.
 */

/*
 * $Id$
 */

#include <pthread.h>

#include <xercesc/util/MutexManagers/PosixMutexMgr.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/PanicHandler.hpp>

XERCES_CPP_NAMESPACE_BEGIN

//	Wrap up the mutex with XMemory
class PosixMutexWrap : public XMemory {
public:
	pthread_mutex_t	m;
};

PosixMutexMgr::PosixMutexMgr()
{
}

PosixMutexMgr::~PosixMutexMgr()
{
}

XMLMutexHandle
PosixMutexMgr::create(MemoryManager* const manager)
{
    PosixMutexWrap* mutex = new (manager) PosixMutexWrap;

    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    if (pthread_mutex_init(&mutex->m, &attr))
        XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);

    pthread_mutexattr_destroy(&attr);

    return (void*)(mutex);
}

void
PosixMutexMgr::destroy(XMLMutexHandle mtx, MemoryManager* const manager)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_destroy(&mutex->m))
        {
            ThrowXMLwithMemMgr(XMLPlatformUtilsException,
                     XMLExcepts::Mutex_CouldNotDestroy, manager);
        }
        delete mutex;
    }
}

void
PosixMutexMgr::lock(XMLMutexHandle mtx)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_lock(&mutex->m))
            XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);
    }
}

void
PosixMutexMgr::unlock(XMLMutexHandle mtx)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_unlock(&mutex->m))
            XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);
    }
}

XERCES_CPP_NAMESPACE_END

<commit_msg>Define _GNU_SOURCE<commit_after>/*
 * Copyright 1999-2000,2004-2005 The Apache Software Foundation.
 *
 * 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.
 */

/*
 * $Id$
 */

// on some platforms, THREAD_MUTEX_RECURSIVE is defined only if _GNU_SOURCE is defined
#define _GNU_SOURCE

#include <pthread.h>

#include <xercesc/util/MutexManagers/PosixMutexMgr.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/PanicHandler.hpp>

XERCES_CPP_NAMESPACE_BEGIN

//	Wrap up the mutex with XMemory
class PosixMutexWrap : public XMemory {
public:
	pthread_mutex_t	m;
};

PosixMutexMgr::PosixMutexMgr()
{
}

PosixMutexMgr::~PosixMutexMgr()
{
}

XMLMutexHandle
PosixMutexMgr::create(MemoryManager* const manager)
{
    PosixMutexWrap* mutex = new (manager) PosixMutexWrap;

    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    if (pthread_mutex_init(&mutex->m, &attr))
        XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);

    pthread_mutexattr_destroy(&attr);

    return (void*)(mutex);
}

void
PosixMutexMgr::destroy(XMLMutexHandle mtx, MemoryManager* const manager)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_destroy(&mutex->m))
        {
            ThrowXMLwithMemMgr(XMLPlatformUtilsException,
                     XMLExcepts::Mutex_CouldNotDestroy, manager);
        }
        delete mutex;
    }
}

void
PosixMutexMgr::lock(XMLMutexHandle mtx)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_lock(&mutex->m))
            XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);
    }
}

void
PosixMutexMgr::unlock(XMLMutexHandle mtx)
{
	PosixMutexWrap* mutex = (PosixMutexWrap*)(mtx);
    if (mutex != NULL)
    {
        if (pthread_mutex_unlock(&mutex->m))
            XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr);
    }
}

XERCES_CPP_NAMESPACE_END

<|endoftext|>


<commit_before>
// ServerHandleImpl.cpp

// Implements the cServerHandleImpl class implementing the TCP server functionality

#include "Globals.h"
#include "ServerHandleImpl.h"
#include "TCPLinkImpl.h"
#include "NetworkSingleton.h"

////////////////////////////////////////////////////////////////////////////////
// Globals:

static bool IsValidSocket(evutil_socket_t a_Socket)
{
	#ifdef _WIN32
		return (a_Socket != INVALID_SOCKET);
	#else  // _WIN32
		return (a_Socket >= 0);
	#endif  // else _WIN32
}

////////////////////////////////////////////////////////////////////////////////
// cServerHandleImpl:

cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
	m_ListenCallbacks(a_ListenCallbacks),
	m_ConnListener(nullptr),
	m_SecondaryConnListener(nullptr),
	m_IsListening(false),
	m_ErrorCode(0)
{
}

cServerHandleImpl::~cServerHandleImpl()
{
	if (m_ConnListener != nullptr)
	{
		evconnlistener_free(m_ConnListener);
	}
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_free(m_SecondaryConnListener);
	}
}

void cServerHandleImpl::Close(void)
{
	// Stop the listener sockets:
	evconnlistener_disable(m_ConnListener);
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_disable(m_SecondaryConnListener);
	}
	m_IsListening = false;

	// Shutdown all connections:
	cTCPLinkImplPtrs Conns;
	{
		cCSLock Lock(m_CS);
		std::swap(Conns, m_Connections);
	}
	for (auto conn: Conns)
	{
		conn->Shutdown();
	}

	// Remove the ptr to self, so that the object may be freed:
	m_SelfPtr.reset();
}

cServerHandleImplPtr cServerHandleImpl::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
	res->m_SelfPtr = res;
	if (res->Listen(a_Port))
	{
		cNetworkSingleton::Get().AddServer(res);
	}
	else
	{
		a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
		res->m_SelfPtr.reset();
	}
	return res;
}

bool cServerHandleImpl::Listen(UInt16 a_Port)
{
	// Make sure the cNetwork internals are innitialized:
	cNetworkSingleton::Get();

	// Set up the main socket:
	// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
	bool NeedsTwoSockets = false;
	int err;
	evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(MainSock))
	{
		// Failed to create IPv6 socket, create an IPv4 one instead:
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
		MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (!IsValidSocket(MainSock))
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			return false;
		}

		// Bind to all interfaces:
		sockaddr_in name;
		memset(&name, 0, sizeof(name));
		name.sin_family = AF_INET;
		name.sin_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	else
	{
		// IPv6 socket created, switch it into "dualstack" mode:
		UInt32 Zero = 0;
		#ifdef _WIN32
			// WinXP doesn't support this feature, so if the setting fails, create another socket later on:
			int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
			err = EVUTIL_SOCKET_ERROR();
			NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
			LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s",
				res, err, evutil_socket_error_to_string(err),
				NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed"
			);
		#else
			setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
		#endif

		// Bind to all interfaces:
		sockaddr_in6 name;
		memset(&name, 0, sizeof(name));
		name.sin6_family = AF_INET6;
		name.sin6_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	if (evutil_make_socket_nonblocking(MainSock) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	if (listen(MainSock, 0) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
	m_IsListening = true;
	if (!NeedsTwoSockets)
	{
		return true;
	}

	// If a secondary socket is required (WinXP dual-stack), create it here:
	LOGD("Creating a second socket for IPv4");
	evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(SecondSock))
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		return true;  // Report as success, the primary socket is working
	}

	// Make the secondary socket nonblocking:
	if (evutil_make_socket_nonblocking(SecondSock) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	// Bind to all IPv4 interfaces:
	sockaddr_in name;
	memset(&name, 0, sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = ntohs(a_Port);
	if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	if (listen(SecondSock, 0) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
	return true;
}

void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
{
	// Cast to true self:
	cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
	ASSERT(Self != nullptr);
	ASSERT(Self->m_SelfPtr != nullptr);

	// Get the textual IP address and port number out of a_Addr:
	char IPAddress[128];
	UInt16 Port = 0;
	switch (a_Addr->sa_family)
	{
		case AF_INET:
		{
			sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin->sin_port);
			break;
		}
		case AF_INET6:
		{
			sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin6, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin6->sin6_port);
			break;
		}
	}

	// Call the OnIncomingConnection callback to get the link callbacks to use:
	cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
	if (LinkCallbacks == nullptr)
	{
		// Drop the connection:
		evutil_closesocket(a_Socket);
		return;
	}

	// Create a new cTCPLink for the incoming connection:
	cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
	{
		cCSLock Lock(Self->m_CS);
		Self->m_Connections.push_back(Link);
	}  // Lock(m_CS)
	LinkCallbacks->OnLinkCreated(Link);
	Link->Enable(Link);

	// Call the OnAccepted callback:
	Self->m_ListenCallbacks->OnAccepted(*Link);
}

void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
{
	cCSLock Lock(m_CS);
	for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
	{
		if (itr->get() == a_Link)
		{
			m_Connections.erase(itr);
			return;
		}
	}  // for itr - m_Connections[]
}

////////////////////////////////////////////////////////////////////////////////
// cNetwork API:

cServerHandlePtr cNetwork::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
}

<commit_msg>ServerHandleImpl: Removed needless diagnostic output.<commit_after>
// ServerHandleImpl.cpp

// Implements the cServerHandleImpl class implementing the TCP server functionality

#include "Globals.h"
#include "ServerHandleImpl.h"
#include "TCPLinkImpl.h"
#include "NetworkSingleton.h"

////////////////////////////////////////////////////////////////////////////////
// Globals:

static bool IsValidSocket(evutil_socket_t a_Socket)
{
	#ifdef _WIN32
		return (a_Socket != INVALID_SOCKET);
	#else  // _WIN32
		return (a_Socket >= 0);
	#endif  // else _WIN32
}

////////////////////////////////////////////////////////////////////////////////
// cServerHandleImpl:

cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
	m_ListenCallbacks(a_ListenCallbacks),
	m_ConnListener(nullptr),
	m_SecondaryConnListener(nullptr),
	m_IsListening(false),
	m_ErrorCode(0)
{
}

cServerHandleImpl::~cServerHandleImpl()
{
	if (m_ConnListener != nullptr)
	{
		evconnlistener_free(m_ConnListener);
	}
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_free(m_SecondaryConnListener);
	}
}

void cServerHandleImpl::Close(void)
{
	// Stop the listener sockets:
	evconnlistener_disable(m_ConnListener);
	if (m_SecondaryConnListener != nullptr)
	{
		evconnlistener_disable(m_SecondaryConnListener);
	}
	m_IsListening = false;

	// Shutdown all connections:
	cTCPLinkImplPtrs Conns;
	{
		cCSLock Lock(m_CS);
		std::swap(Conns, m_Connections);
	}
	for (auto conn: Conns)
	{
		conn->Shutdown();
	}

	// Remove the ptr to self, so that the object may be freed:
	m_SelfPtr.reset();
}

cServerHandleImplPtr cServerHandleImpl::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
	res->m_SelfPtr = res;
	if (res->Listen(a_Port))
	{
		cNetworkSingleton::Get().AddServer(res);
	}
	else
	{
		a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
		res->m_SelfPtr.reset();
	}
	return res;
}

bool cServerHandleImpl::Listen(UInt16 a_Port)
{
	// Make sure the cNetwork internals are innitialized:
	cNetworkSingleton::Get();

	// Set up the main socket:
	// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
	bool NeedsTwoSockets = false;
	int err;
	evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(MainSock))
	{
		// Failed to create IPv6 socket, create an IPv4 one instead:
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
		MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (!IsValidSocket(MainSock))
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			return false;
		}

		// Bind to all interfaces:
		sockaddr_in name;
		memset(&name, 0, sizeof(name));
		name.sin_family = AF_INET;
		name.sin_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	else
	{
		// IPv6 socket created, switch it into "dualstack" mode:
		UInt32 Zero = 0;
		#ifdef _WIN32
			// WinXP doesn't support this feature, so if the setting fails, create another socket later on:
			int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
			err = EVUTIL_SOCKET_ERROR();
			NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
		#else
			setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
		#endif

		// Bind to all interfaces:
		sockaddr_in6 name;
		memset(&name, 0, sizeof(name));
		name.sin6_family = AF_INET6;
		name.sin6_port = ntohs(a_Port);
		if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
		{
			m_ErrorCode = EVUTIL_SOCKET_ERROR();
			Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
			evutil_closesocket(MainSock);
			return false;
		}
	}
	if (evutil_make_socket_nonblocking(MainSock) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	if (listen(MainSock, 0) != 0)
	{
		m_ErrorCode = EVUTIL_SOCKET_ERROR();
		Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
		evutil_closesocket(MainSock);
		return false;
	}
	m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
	m_IsListening = true;
	if (!NeedsTwoSockets)
	{
		return true;
	}

	// If a secondary socket is required (WinXP dual-stack), create it here:
	LOGD("Creating a second socket for IPv4");
	evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (!IsValidSocket(SecondSock))
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		return true;  // Report as success, the primary socket is working
	}

	// Make the secondary socket nonblocking:
	if (evutil_make_socket_nonblocking(SecondSock) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	// Bind to all IPv4 interfaces:
	sockaddr_in name;
	memset(&name, 0, sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = ntohs(a_Port);
	if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	if (listen(SecondSock, 0) != 0)
	{
		err = EVUTIL_SOCKET_ERROR();
		LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
		evutil_closesocket(SecondSock);
		return true;  // Report as success, the primary socket is working
	}

	m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
	return true;
}

void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
{
	// Cast to true self:
	cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
	ASSERT(Self != nullptr);
	ASSERT(Self->m_SelfPtr != nullptr);

	// Get the textual IP address and port number out of a_Addr:
	char IPAddress[128];
	UInt16 Port = 0;
	switch (a_Addr->sa_family)
	{
		case AF_INET:
		{
			sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin->sin_port);
			break;
		}
		case AF_INET6:
		{
			sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
			evutil_inet_ntop(AF_INET, sin6, IPAddress, ARRAYCOUNT(IPAddress));
			Port = ntohs(sin6->sin6_port);
			break;
		}
	}

	// Call the OnIncomingConnection callback to get the link callbacks to use:
	cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
	if (LinkCallbacks == nullptr)
	{
		// Drop the connection:
		evutil_closesocket(a_Socket);
		return;
	}

	// Create a new cTCPLink for the incoming connection:
	cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
	{
		cCSLock Lock(Self->m_CS);
		Self->m_Connections.push_back(Link);
	}  // Lock(m_CS)
	LinkCallbacks->OnLinkCreated(Link);
	Link->Enable(Link);

	// Call the OnAccepted callback:
	Self->m_ListenCallbacks->OnAccepted(*Link);
}

void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
{
	cCSLock Lock(m_CS);
	for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
	{
		if (itr->get() == a_Link)
		{
			m_Connections.erase(itr);
			return;
		}
	}  // for itr - m_Connections[]
}

////////////////////////////////////////////////////////////////////////////////
// cNetwork API:

cServerHandlePtr cNetwork::Listen(
	UInt16 a_Port,
	cNetwork::cListenCallbacksPtr a_ListenCallbacks
)
{
	return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
}

<|endoftext|>


<commit_before>#include "yael/NetworkSocketListener.h"
#include "yael/EventLoop.h"

using namespace yael;

NetworkSocketListener::NetworkSocketListener()
    : EventListener(EventListener::Mode::ReadOnly), m_socket(nullptr), m_fileno(-1)
{
}

NetworkSocketListener::NetworkSocketListener(std::unique_ptr<network::Socket> &&socket, SocketType type)
    : EventListener(EventListener::Mode::ReadOnly), m_socket(nullptr), m_socket_type(SocketType::None), m_fileno(-1)
{
    if(socket)
    {
        NetworkSocketListener::set_socket(std::forward<std::unique_ptr<network::Socket>>(socket), type);

        m_socket->wait_connection_established();
    }
}

std::unique_ptr<network::Socket> NetworkSocketListener::release_socket()
{
    std::unique_lock lock(m_mutex);

    // Move socket before we unregistered so socket doesn't get closed
    auto sock = std::move(m_socket);

    ///FIXME we should tell event listener to remap the socket
    // the current approach can cause race conditions...
    auto &el = EventLoop::get_instance();
    el.unregister_event_listener(std::dynamic_pointer_cast<EventListener>(shared_from_this()));

    return sock;
}

void NetworkSocketListener::set_socket(std::unique_ptr<network::Socket> &&socket, SocketType type)
{
    std::unique_lock lock(m_mutex);

    if(m_socket)
    {
        throw std::runtime_error("There is already a socket assigned to this listener!");
    }

    if(!socket->is_valid())
    {
        throw std::runtime_error("Not a valid socket!");
    }

    m_socket = std::move(socket);
    m_socket_type = type;
    m_fileno = m_socket->get_fileno();
}

bool NetworkSocketListener::is_valid()
{
    std::unique_lock lock(m_mutex);

    if(!m_socket)
    {
        return false;
    }

    return m_socket->is_valid();
}

bool NetworkSocketListener::is_connected()
{
    std::unique_lock lock(m_mutex);

    if(!m_socket)
    {
        return false;
    }

    return m_socket->is_connected();
}

void NetworkSocketListener::on_write_ready()
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    try {
        has_more = m_socket->do_send();
    } catch(const network::socket_error &e) {
        LOG(WARNING) << "Failed to send data to " << m_socket->get_remote_address() << e.what();

        has_more = false;

        lock.unlock();
        close_socket();
    }

    if(!has_more && is_valid())
    {
        this->set_mode(EventListener::Mode::ReadOnly);
    }
}

void NetworkSocketListener::send(std::unique_ptr<uint8_t[]> &&data, size_t length, bool blocking)
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    while(true) {
        try {
            has_more = m_socket->send(std::move(data), length);
            break;
        } catch(const network::socket_error &e) {
            LOG(ERROR) << e.what();
            has_more = false;
            break;
        } catch(const network::send_queue_full&) {
            if(blocking)
            {
                LOG(WARNING) << "Send queue to " << m_socket->get_remote_address() << " is full. Thread is blocking...";
                m_socket->wait_send_queue_empty();
            }
            else
            {
                LOG(ERROR) << "Failed to send data to " << m_socket->get_remote_address() << ": send queue is full";
                has_more = false;
                close_socket();
                break;
            }
        }
    }

    if(is_valid())
    {
        if(has_more)
        {
            set_mode(Mode::ReadWrite);
        }
        else
        {
            set_mode(Mode::ReadOnly);
        }
    }
    else
    {
        // Tell event loop connection was lost or terminated
        auto &el = EventLoop::get_instance();
        el.unregister_event_listener(shared_from_this());
    }
}

void NetworkSocketListener::send(const uint8_t *data, size_t length, bool blocking)
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    while(true) {
        try {
            has_more = m_socket->send(data, length);
            break;
        }
        catch(const network::socket_error &e)
        {
            LOG(ERROR) << e.what();
            has_more = false;
            break;
        }
        catch(const network::send_queue_full&)
        {
            if(blocking)
            {
                LOG(WARNING) << "Send queue to " << m_socket->get_remote_address() << " is full. Thread is blocking...";

                lock.unlock();
                m_socket->wait_send_queue_empty();
                lock.lock();
            }
            else
            {
                LOG(ERROR) << "Failed to send data to " << m_socket->get_remote_address() << ": send queue is full";
                has_more = false;
                close_socket();
                break;
            }
        }
    }

    if(is_valid())
    {
        if(has_more)
        {
            set_mode(Mode::ReadWrite);
        }
        else
        {
            set_mode(Mode::ReadOnly);
        }
    }
    else
    {
        // Tell event loop connection was lost or terminated
        auto &el = EventLoop::get_instance();
        el.unregister_event_listener(shared_from_this());
    }
}

void NetworkSocketListener::wait_for_connection()
{
    while(!is_connected())
    {
        if(!m_socket)
        {
            // busy wait while socket doesn't exist
            continue;
        }

        if(m_socket->is_listening())
        {
            throw std::runtime_error("Cannot wait for connection. Is listening.");
        }

        m_socket->wait_connection_established();
    }
}

void NetworkSocketListener::on_error()
{
    LOG(WARNING) << "Got error; closing socket";
    close_socket();
}

void NetworkSocketListener::on_read_ready()
{
    std::unique_lock lock(m_mutex);

    switch(m_socket_type)
    {
    case SocketType::Acceptor:
    {
        auto result = m_socket->accept();
        lock.unlock();

        for(auto &s: result)
        {
            this->on_new_connection(std::move(s));
        }

        break;
    }
    case SocketType::Connection:
    {
        try
        {
            while(m_socket)
            {
                auto message = m_socket->receive();

                if(message)
                {
                    lock.unlock();
                    this->on_network_message(*message);
                    lock.lock();
                }
                else
                {
                    // no more data
                    break;
                }
            }
        }
        catch (const network::socket_error &e)
        {
            LOG(WARNING) << e.what();
        }

        // After processing the last message we will notify the user that the socket is closed
        if(m_socket && !m_socket->is_valid())
        {
            close_socket_internal(lock);
        }

        break;
    }
    default:
        throw std::runtime_error("Unknown socket type!");
    }

}

void NetworkSocketListener::close_socket_internal(std::unique_lock<std::mutex> &lock)
{
    // make sure we invoke the callback at most once
    if(m_has_disconnected)
    {
        //ignore
        return;
    }

    m_has_disconnected = true;

    if(m_socket)
    {
        bool done = m_socket->close();
        lock.unlock();

        if(done)
        {
            if(m_socket_type == SocketType::Connection)
            {
                this->on_disconnect();
            }
            else if(EventLoop::is_initialized())
            {
                try {
                    auto &el = EventLoop::get_instance();
                    el.unregister_event_listener(shared_from_this());
                } catch(const std::runtime_error&) {
                    // can happen during shutdown
                    // ignore...
                }
            }
        }
    }
}

int32_t NetworkSocketListener::get_fileno() const
{
    return m_fileno;
}
<commit_msg>Better error logging<commit_after>#include "yael/NetworkSocketListener.h"
#include "yael/EventLoop.h"

using namespace yael;

NetworkSocketListener::NetworkSocketListener()
    : EventListener(EventListener::Mode::ReadOnly), m_socket(nullptr), m_fileno(-1)
{
}

NetworkSocketListener::NetworkSocketListener(std::unique_ptr<network::Socket> &&socket, SocketType type)
    : EventListener(EventListener::Mode::ReadOnly), m_socket(nullptr), m_socket_type(SocketType::None), m_fileno(-1)
{
    if(socket)
    {
        NetworkSocketListener::set_socket(std::forward<std::unique_ptr<network::Socket>>(socket), type);

        m_socket->wait_connection_established();
    }
}

std::unique_ptr<network::Socket> NetworkSocketListener::release_socket()
{
    std::unique_lock lock(m_mutex);

    // Move socket before we unregistered so socket doesn't get closed
    auto sock = std::move(m_socket);

    ///FIXME we should tell event listener to remap the socket
    // the current approach can cause race conditions...
    auto &el = EventLoop::get_instance();
    el.unregister_event_listener(std::dynamic_pointer_cast<EventListener>(shared_from_this()));

    return sock;
}

void NetworkSocketListener::set_socket(std::unique_ptr<network::Socket> &&socket, SocketType type)
{
    std::unique_lock lock(m_mutex);

    if(m_socket)
    {
        throw std::runtime_error("There is already a socket assigned to this listener!");
    }

    if(!socket->is_valid())
    {
        throw std::runtime_error("Not a valid socket!");
    }

    m_socket = std::move(socket);
    m_socket_type = type;
    m_fileno = m_socket->get_fileno();
}

bool NetworkSocketListener::is_valid()
{
    std::unique_lock lock(m_mutex);

    if(!m_socket)
    {
        return false;
    }

    return m_socket->is_valid();
}

bool NetworkSocketListener::is_connected()
{
    std::unique_lock lock(m_mutex);

    if(!m_socket)
    {
        return false;
    }

    return m_socket->is_connected();
}

void NetworkSocketListener::on_write_ready()
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    try {
        has_more = m_socket->do_send();
    } catch(const network::socket_error &e) {
        LOG(WARNING) << "Failed to send data to " << m_socket->get_remote_address() << ": " << e.what();

        has_more = false;

        lock.unlock();
        close_socket();
    }

    if(!has_more && is_valid())
    {
        this->set_mode(EventListener::Mode::ReadOnly);
    }
}

void NetworkSocketListener::send(std::unique_ptr<uint8_t[]> &&data, size_t length, bool blocking)
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    while(true) {
        try {
            has_more = m_socket->send(std::move(data), length);
            break;
        } catch(const network::socket_error &e) {
            LOG(WARNING) << "Failed to send data to " << m_socket->get_remote_address() << ": " << e.what();
            has_more = false;
            break;
        } catch(const network::send_queue_full&) {
            if(blocking)
            {
                LOG(WARNING) << "Send queue to " << m_socket->get_remote_address() << " is full. Thread is blocking...";
                m_socket->wait_send_queue_empty();
            }
            else
            {
                LOG(ERROR) << "Failed to send data to " << m_socket->get_remote_address() << ": send queue is full";
                has_more = false;
                close_socket();
                break;
            }
        }
    }

    if(is_valid())
    {
        if(has_more)
        {
            set_mode(Mode::ReadWrite);
        }
        else
        {
            set_mode(Mode::ReadOnly);
        }
    }
    else
    {
        // Tell event loop connection was lost or terminated
        auto &el = EventLoop::get_instance();
        el.unregister_event_listener(shared_from_this());
    }
}

void NetworkSocketListener::send(const uint8_t *data, size_t length, bool blocking)
{
    std::unique_lock lock(m_send_mutex);

    bool has_more;

    while(true) {
        try {
            has_more = m_socket->send(data, length);
            break;
        }
        catch(const network::socket_error &e)
        {
            LOG(ERROR) << "Failed to send data: " << e.what();
            has_more = false;
            break;
        }
        catch(const network::send_queue_full&)
        {
            if(blocking)
            {
                LOG(WARNING) << "Send queue to " << m_socket->get_remote_address() << " is full. Thread is blocking...";

                lock.unlock();
                m_socket->wait_send_queue_empty();
                lock.lock();
            }
            else
            {
                LOG(ERROR) << "Failed to send data to " << m_socket->get_remote_address() << ": send queue is full";
                has_more = false;
                close_socket();
                break;
            }
        }
    }

    if(is_valid())
    {
        if(has_more)
        {
            set_mode(Mode::ReadWrite);
        }
        else
        {
            set_mode(Mode::ReadOnly);
        }
    }
    else
    {
        // Tell event loop connection was lost or terminated
        auto &el = EventLoop::get_instance();
        el.unregister_event_listener(shared_from_this());
    }
}

void NetworkSocketListener::wait_for_connection()
{
    while(!is_connected())
    {
        if(!m_socket)
        {
            // busy wait while socket doesn't exist
            continue;
        }

        if(m_socket->is_listening())
        {
            throw std::runtime_error("Cannot wait for connection. Is listening.");
        }

        m_socket->wait_connection_established();
    }
}

void NetworkSocketListener::on_error()
{
    LOG(WARNING) << "Got error; closing socket";
    close_socket();
}

void NetworkSocketListener::on_read_ready()
{
    std::unique_lock lock(m_mutex);

    switch(m_socket_type)
    {
    case SocketType::Acceptor:
    {
        auto result = m_socket->accept();
        lock.unlock();

        for(auto &s: result)
        {
            this->on_new_connection(std::move(s));
        }

        break;
    }
    case SocketType::Connection:
    {
        try
        {
            while(m_socket)
            {
                auto message = m_socket->receive();

                if(message)
                {
                    lock.unlock();
                    this->on_network_message(*message);
                    lock.lock();
                }
                else
                {
                    // no more data
                    break;
                }
            }
        }
        catch (const network::socket_error &e)
        {
            LOG(WARNING) << e.what();
        }

        // After processing the last message we will notify the user that the socket is closed
        if(m_socket && !m_socket->is_valid())
        {
            close_socket_internal(lock);
        }

        break;
    }
    default:
        throw std::runtime_error("Unknown socket type!");
    }

}

void NetworkSocketListener::close_socket_internal(std::unique_lock<std::mutex> &lock)
{
    // make sure we invoke the callback at most once
    if(m_has_disconnected)
    {
        //ignore
        return;
    }

    m_has_disconnected = true;

    if(m_socket)
    {
        bool done = m_socket->close();
        lock.unlock();

        if(done)
        {
            if(m_socket_type == SocketType::Connection)
            {
                this->on_disconnect();
            }
            else if(EventLoop::is_initialized())
            {
                try {
                    auto &el = EventLoop::get_instance();
                    el.unregister_event_listener(shared_from_this());
                } catch(const std::runtime_error&) {
                    // can happen during shutdown
                    // ignore...
                }
            }
        }
    }
}

int32_t NetworkSocketListener::get_fileno() const
{
    return m_fileno;
}
<|endoftext|>


<commit_before>#include "feature.h"

#include "../core/Point.h"

namespace ncv {

  namespace imgproc {

    void FeatureInit(Local<Object> &target) {
      // line segment detector modes
      DEFINE_CONST_ENUM(LSD_REFINE_NONE);
      DEFINE_CONST_ENUM(LSD_REFINE_STD);
      DEFINE_CONST_ENUM(LSD_REFINE_ADV);

      // hough modes
      DEFINE_CONST_ENUM(HOUGH_STANDARD);
      DEFINE_CONST_ENUM(HOUGH_PROBABILISTIC);
      DEFINE_CONST_ENUM(HOUGH_MULTI_SCALE);
      DEFINE_CONST_ENUM(HOUGH_GRADIENT);

      Nan::SetMethod(target, "canny", Canny);
      Nan::SetMethod(target, "cornerEigenValsAndVecs", CornerEigenValsAndVecs);
      Nan::SetMethod(target, "cornerHarris", CornerHarris);
      Nan::SetMethod(target, "cornerMinEigenVal", CornerMinEigenVal);
      Nan::SetMethod(target, "cornerSubPix", CornerSubPix);
      Nan::SetMethod(target, "createLineSegmentDetector", CreateLineSegmentDetector);
      Nan::SetMethod(target, "goodFeaturesToTrack", GoodFeaturesToTrack);
      Nan::SetMethod(target, "houghCircles", HoughCircles);
      Nan::SetMethod(target, "houghLines", HoughLines);
      Nan::SetMethod(target, "houghLinesP", HoughLinesP);
      Nan::SetMethod(target, "preCornerDetect", PreCornerDetect);
    }

    NAN_METHOD(Canny) {
      FUNCTION_REQUIRE_ARGUMENTS_RANGE(4, 6);
      Nan::HandleScope scope;
      ASSERT_INPUTARRAY_FROM_ARGS(m1, 0);
      int argumentOffset = 0;
      cv::_InputArray m2;
      cv::_OutputArray out;
      try {
        out = ReadOutputArray(info[2]);
        try {
          ASSERT_INPUTARRAY_FROM_ARGS(dy, 1);
          m2 = dy;
        } catch (char const*) {
          return Nan::ThrowError(ERROR_INVALID_ARGUMENTS);
        }
        argumentOffset = 1;
      } catch (char const*) {
        ASSERT_OUTPUTARRAY_FROM_ARGS(dst, 1);
        out = dst;
      }
      ASSERT_DOUBLE_FROM_ARGS(threshold1, 2 + argumentOffset);
      ASSERT_DOUBLE_FROM_ARGS(threshold2, 3 + argumentOffset);
      DEFAULT_BOOLEAN_FROM_ARGS(L2gradient, 5, false);

      if (argumentOffset == 0) {
        DEFAULT_INT_FROM_ARGS(apertureSize, 4, 3);
        TRY_CATCH_THROW_OPENCV(cv::Canny(m1, out, threshold1, threshold2, apertureSize, L2gradient));
      } else {
        TRY_CATCH_THROW_OPENCV(cv::Canny(m1, m2, out, threshold1, threshold2, L2gradient));
      }
    }

    NAN_METHOD(CornerEigenValsAndVecs) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerHarris) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerMinEigenVal) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerSubPix) {
      NotImplemented(info);
    }

    NAN_METHOD(CreateLineSegmentDetector) {
      NotImplemented(info);
    }

    NAN_METHOD(GoodFeaturesToTrack) {
      NotImplemented(info);
    }

    NAN_METHOD(HoughCircles) {
      ASSERT_INPUTARRAY_FROM_ARGS(img, 0);
      ASSERT_INT_FROM_ARGS(method, 1);
      ASSERT_DOUBLE_FROM_ARGS(dp, 2);
      ASSERT_DOUBLE_FROM_ARGS(minDist, 3);
      DEFAULT_DOUBLE_FROM_ARGS(param1, 4, 100);
      DEFAULT_DOUBLE_FROM_ARGS(param2, 5, 100);
      DEFAULT_DOUBLE_FROM_ARGS(minRadius, 5, 0);
      DEFAULT_DOUBLE_FROM_ARGS(maxRadius, 6, 0);
      std::vector<cv::Vec3f> circles;
      TRY_CATCH_THROW_OPENCV(cv::HoughCircles(img, circles, method, dp, minDist, param1, param2, minRadius, maxRadius));

      Local<String> center = Nan::New<String>("center").ToLocalChecked();
      Local<String> radius = Nan::New<String>("radius").ToLocalChecked();

      Local<Array> out = Nan::New<Array>();
      for (cv::Vec3f circle : circles) {
        Local<Object> circleObj = Nan::New<Object>();

        Nan::Set(circleObj, center, Point::NewInstance(circle[0], circle[1]));
        Nan::Set(circleObj, radius, Nan::New<Number>(circle[2]));

        Nan::Set(out, out->Length(), circleObj);
      }

      info.GetReturnValue().Set(out);
    }

    NAN_METHOD(HoughLines) {
      NotImplemented(info);
    }

    NAN_METHOD(HoughLinesP) {
      FUNCTION_REQUIRE_ARGUMENTS_RANGE(4, 6);
      Nan::HandleScope scope;
      ASSERT_INPUTARRAY_FROM_ARGS(src, 0);
      ASSERT_DOUBLE_FROM_ARGS(rho, 1);
      ASSERT_DOUBLE_FROM_ARGS(theta, 2);
      ASSERT_INT_FROM_ARGS(threshold, 3);
      DEFAULT_DOUBLE_FROM_ARGS(minLineLength, 4, 0);
      DEFAULT_DOUBLE_FROM_ARGS(maxLineGap, 5, 0);

      std::vector<cv::Vec4i> lines;
      TRY_CATCH_THROW_OPENCV(cv::HoughLinesP(src, lines, rho, theta, threshold, minLineLength, maxLineGap));

      v8::Local<v8::Array> out = Nan::New<Array>(lines.size());
      for (unsigned i = 0; i < lines.size(); ++i) {
        v8::Local<v8::Array> arr = Nan::New<Array>(4);
        Nan::Set(arr, 0, Nan::New<Number>(lines[i][0]));
        Nan::Set(arr, 1, Nan::New<Number>(lines[i][1]));
        Nan::Set(arr, 2, Nan::New<Number>(lines[i][2]));
        Nan::Set(arr, 3, Nan::New<Number>(lines[i][3]));

        Nan::Set(out, i, arr);
      }

      info.GetReturnValue().Set(out);
    }

    NAN_METHOD(PreCornerDetect) {
      NotImplemented(info);
    }

  }

}
<commit_msg>fixed `houghCircles`<commit_after>#include "feature.h"

#include "../core/Point.h"

namespace ncv {

  namespace imgproc {

    void FeatureInit(Local<Object> &target) {
      // line segment detector modes
      DEFINE_CONST_ENUM(LSD_REFINE_NONE);
      DEFINE_CONST_ENUM(LSD_REFINE_STD);
      DEFINE_CONST_ENUM(LSD_REFINE_ADV);

      // hough modes
      DEFINE_CONST_ENUM(HOUGH_STANDARD);
      DEFINE_CONST_ENUM(HOUGH_PROBABILISTIC);
      DEFINE_CONST_ENUM(HOUGH_MULTI_SCALE);
      DEFINE_CONST_ENUM(HOUGH_GRADIENT);

      Nan::SetMethod(target, "canny", Canny);
      Nan::SetMethod(target, "cornerEigenValsAndVecs", CornerEigenValsAndVecs);
      Nan::SetMethod(target, "cornerHarris", CornerHarris);
      Nan::SetMethod(target, "cornerMinEigenVal", CornerMinEigenVal);
      Nan::SetMethod(target, "cornerSubPix", CornerSubPix);
      Nan::SetMethod(target, "createLineSegmentDetector", CreateLineSegmentDetector);
      Nan::SetMethod(target, "goodFeaturesToTrack", GoodFeaturesToTrack);
      Nan::SetMethod(target, "houghCircles", HoughCircles);
      Nan::SetMethod(target, "houghLines", HoughLines);
      Nan::SetMethod(target, "houghLinesP", HoughLinesP);
      Nan::SetMethod(target, "preCornerDetect", PreCornerDetect);
    }

    NAN_METHOD(Canny) {
      FUNCTION_REQUIRE_ARGUMENTS_RANGE(4, 6);
      Nan::HandleScope scope;
      ASSERT_INPUTARRAY_FROM_ARGS(m1, 0);
      int argumentOffset = 0;
      cv::_InputArray m2;
      cv::_OutputArray out;
      try {
        out = ReadOutputArray(info[2]);
        try {
          ASSERT_INPUTARRAY_FROM_ARGS(dy, 1);
          m2 = dy;
        } catch (char const*) {
          return Nan::ThrowError(ERROR_INVALID_ARGUMENTS);
        }
        argumentOffset = 1;
      } catch (char const*) {
        ASSERT_OUTPUTARRAY_FROM_ARGS(dst, 1);
        out = dst;
      }
      ASSERT_DOUBLE_FROM_ARGS(threshold1, 2 + argumentOffset);
      ASSERT_DOUBLE_FROM_ARGS(threshold2, 3 + argumentOffset);
      DEFAULT_BOOLEAN_FROM_ARGS(L2gradient, 5, false);

      if (argumentOffset == 0) {
        DEFAULT_INT_FROM_ARGS(apertureSize, 4, 3);
        TRY_CATCH_THROW_OPENCV(cv::Canny(m1, out, threshold1, threshold2, apertureSize, L2gradient));
      } else {
        TRY_CATCH_THROW_OPENCV(cv::Canny(m1, m2, out, threshold1, threshold2, L2gradient));
      }
    }

    NAN_METHOD(CornerEigenValsAndVecs) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerHarris) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerMinEigenVal) {
      NotImplemented(info);
    }

    NAN_METHOD(CornerSubPix) {
      NotImplemented(info);
    }

    NAN_METHOD(CreateLineSegmentDetector) {
      NotImplemented(info);
    }

    NAN_METHOD(GoodFeaturesToTrack) {
      NotImplemented(info);
    }

    NAN_METHOD(HoughCircles) {
      FUNCTION_REQUIRE_ARGUMENTS_RANGE(4, 8);
      ASSERT_INPUTARRAY_FROM_ARGS(img, 0);
      ASSERT_INT_FROM_ARGS(method, 1);
      ASSERT_DOUBLE_FROM_ARGS(dp, 2);
      ASSERT_DOUBLE_FROM_ARGS(minDist, 3);
      DEFAULT_DOUBLE_FROM_ARGS(param1, 4, 100);
      DEFAULT_DOUBLE_FROM_ARGS(param2, 5, 100);
      DEFAULT_DOUBLE_FROM_ARGS(minRadius, 6, 0);
      DEFAULT_DOUBLE_FROM_ARGS(maxRadius, 7, 0);
      std::vector<cv::Vec3f> circles;
      TRY_CATCH_THROW_OPENCV(cv::HoughCircles(img, circles, method, dp, minDist, param1, param2, minRadius, maxRadius));

      Local<String> center = Nan::New<String>("center").ToLocalChecked();
      Local<String> radius = Nan::New<String>("radius").ToLocalChecked();

      Local<Array> out = Nan::New<Array>();
      for (cv::Vec3f circle : circles) {
        Local<Object> circleObj = Nan::New<Object>();

        Nan::Set(circleObj, center, Point::NewInstance(circle[0], circle[1]));
        Nan::Set(circleObj, radius, Nan::New<Number>(circle[2]));

        Nan::Set(out, out->Length(), circleObj);
      }

      info.GetReturnValue().Set(out);
    }

    NAN_METHOD(HoughLines) {
      NotImplemented(info);
    }

    NAN_METHOD(HoughLinesP) {
      FUNCTION_REQUIRE_ARGUMENTS_RANGE(4, 6);
      Nan::HandleScope scope;
      ASSERT_INPUTARRAY_FROM_ARGS(src, 0);
      ASSERT_DOUBLE_FROM_ARGS(rho, 1);
      ASSERT_DOUBLE_FROM_ARGS(theta, 2);
      ASSERT_INT_FROM_ARGS(threshold, 3);
      DEFAULT_DOUBLE_FROM_ARGS(minLineLength, 4, 0);
      DEFAULT_DOUBLE_FROM_ARGS(maxLineGap, 5, 0);

      std::vector<cv::Vec4i> lines;
      TRY_CATCH_THROW_OPENCV(cv::HoughLinesP(src, lines, rho, theta, threshold, minLineLength, maxLineGap));

      v8::Local<v8::Array> out = Nan::New<Array>(lines.size());
      for (unsigned i = 0; i < lines.size(); ++i) {
        v8::Local<v8::Array> arr = Nan::New<Array>(4);
        Nan::Set(arr, 0, Nan::New<Number>(lines[i][0]));
        Nan::Set(arr, 1, Nan::New<Number>(lines[i][1]));
        Nan::Set(arr, 2, Nan::New<Number>(lines[i][2]));
        Nan::Set(arr, 3, Nan::New<Number>(lines[i][3]));

        Nan::Set(out, i, arr);
      }

      info.GetReturnValue().Set(out);
    }

    NAN_METHOD(PreCornerDetect) {
      NotImplemented(info);
    }

  }

}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

bool isRaspberryPi2(void)
{
  FILE* fp;
  char buff[256];
  const char* raspberrypi2_device_name = "BCM2709";
  bool raspberrypi2 = false;

  fp = fopen("/proc/cpuinfo","r");
  while(fgets(buff,sizeof(buff),fp) != NULL){
    if(strstr(buff,raspberrypi2_device_name) != NULL){
      raspberrypi2 = true;
    }
  }
  fclose(fp);

  return raspberrypi2;
}

<commit_msg>Add RaspberryPi3 support<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

bool isRaspberryPi2(void)
{
  FILE* fp;
  char buff[256];
  const char* raspberrypi2_device_name = "BCM2709";
  const char* raspberrypi3_device_name = "BCM2835";
  bool newraspberrypi = false;

  fp = fopen("/proc/cpuinfo","r");
  while(fgets(buff,sizeof(buff),fp) != NULL){
    if(strstr(buff,raspberrypi2_device_name) != NULL){
      newraspberrypi = true;
    }
    if(strstr(buff,raspberrypi3_device_name) != NULL){
      newraspberrypi = true;
    }
  }
  fclose(fp);

  //printf("RasPiDetect : %s                ", newraspberrypi ? "True" : "False");

  return newraspberrypi;
}

<|endoftext|>


<commit_before>// Copyright (c) 2021 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "GpuSubmissionTrack.h"

#include <absl/time/time.h>

#include <memory>

#include "App.h"
#include "Batcher.h"
#include "ClientData/TimerChain.h"
#include "DisplayFormats/DisplayFormats.h"
#include "GlUtils.h"
#include "OrbitBase/Logging.h"
#include "OrbitBase/ThreadConstants.h"
#include "TimeGraph.h"
#include "TimeGraphLayout.h"
#include "TriangleToggle.h"
#include "absl/strings/str_format.h"
#include "capture_data.pb.h"

using orbit_client_data::TimerChain;
using orbit_client_protos::TimerInfo;

constexpr const char* kSwQueueString = "sw queue";
constexpr const char* kHwQueueString = "hw queue";
constexpr const char* kHwExecutionString = "hw execution";
constexpr const char* kCmdBufferString = "command buffer";

GpuSubmissionTrack::GpuSubmissionTrack(Track* parent, TimeGraph* time_graph,
                                       orbit_gl::Viewport* viewport, TimeGraphLayout* layout,
                                       uint64_t timeline_hash, OrbitApp* app,
                                       const orbit_client_data::CaptureData* capture_data,
                                       orbit_client_data::TimerData* timer_data)
    : TimerTrack(parent, time_graph, viewport, layout, app, capture_data, timer_data),
      timeline_hash_{timeline_hash},
      string_manager_{app->GetStringManager()},
      parent_{parent} {
  draw_background_ = false;
  text_renderer_ = time_graph->GetTextRenderer();
}

std::string GpuSubmissionTrack::GetName() const {
  return absl::StrFormat(
      "%s_submissions",
      string_manager_->Get(timeline_hash_).value_or(std::to_string(timeline_hash_)));
}

std::string GpuSubmissionTrack::GetTooltip() const {
  return "Shows scheduling and execution times for selected GPU job "
         "submissions";
}

void GpuSubmissionTrack::OnTimer(const orbit_client_protos::TimerInfo& timer_info) {
  // In case of having command buffer timers, we need to double the depth of the GPU timers (as we
  // are drawing the corresponding command buffer timers below them). Therefore, we watch out for
  // those timers.
  if (timer_info.type() == TimerInfo::kGpuCommandBuffer) {
    has_vulkan_layer_command_buffer_timers_ = true;
  }
  TimerTrack::OnTimer(timer_info);
}

bool GpuSubmissionTrack::IsTimerActive(const TimerInfo& timer_info) const {
  bool is_same_tid_as_selected = timer_info.thread_id() == app_->selected_thread_id();
  // We do not properly track the PID for GPU jobs and we still want to show
  // all jobs as active when no thread is selected, so this logic is a bit
  // different than SchedulerTrack::IsTimerActive.
  bool no_thread_selected = app_->selected_thread_id() == orbit_base::kAllProcessThreadsTid;

  return is_same_tid_as_selected || no_thread_selected;
}

Color GpuSubmissionTrack::GetTimerColor(const TimerInfo& timer_info, bool is_selected,
                                        bool is_highlighted) const {
  const Color kInactiveColor(100, 100, 100, 255);
  const Color kSelectionColor(0, 128, 255, 255);
  if (is_highlighted) {
    return TimerTrack::kHighlightColor;
  }
  if (is_selected) {
    return kSelectionColor;
  }
  if (!IsTimerActive(timer_info)) {
    return kInactiveColor;
  }
  if (timer_info.has_color()) {
    CHECK(timer_info.color().red() < 256);
    CHECK(timer_info.color().green() < 256);
    CHECK(timer_info.color().blue() < 256);
    CHECK(timer_info.color().alpha() < 256);
    return Color(static_cast<uint8_t>(timer_info.color().red()),
                 static_cast<uint8_t>(timer_info.color().green()),
                 static_cast<uint8_t>(timer_info.color().blue()),
                 static_cast<uint8_t>(timer_info.color().alpha()));
  }

  // We color code the timeslices for GPU activity using the color
  // of the CPU thread track that submitted the job.
  Color color = TimeGraph::GetThreadColor(timer_info.thread_id());

  // We disambiguate the different types of GPU activity based on the
  // string that is displayed on their timeslice.
  float coeff = 1.0f;
  std::string gpu_stage = string_manager_->Get(timer_info.user_data_key()).value_or("");
  if (gpu_stage == kSwQueueString) {
    coeff = 0.5f;
  } else if (gpu_stage == kHwQueueString) {
    coeff = 0.75f;
  } else if (gpu_stage == kHwExecutionString) {
    coeff = 1.0f;
  }

  color[0] = static_cast<uint8_t>(coeff * color[0]);
  color[1] = static_cast<uint8_t>(coeff * color[1]);
  color[2] = static_cast<uint8_t>(coeff * color[2]);

  constexpr uint8_t kOddAlpha = 210;
  if ((timer_info.depth() & 0x1) == 0u) {
    color[3] = kOddAlpha;
  }

  return color;
}

float GpuSubmissionTrack::GetYFromTimer(const TimerInfo& timer_info) const {
  auto adjusted_depth = static_cast<float>(timer_info.depth());
  if (IsCollapsed()) {
    adjusted_depth = 0.f;
  }
  CHECK(timer_info.type() == TimerInfo::kGpuActivity ||
        timer_info.type() == TimerInfo::kGpuCommandBuffer);

  // We are drawing a small gap between each depth, for visualization purposes.
  // There won't be a gap between "hw execution"timers and command buffer timers, which
  // is why the gap space needs to be calculated before adjusting the depth further (see below).
  float gap_space = adjusted_depth * layout_->GetSpaceBetweenGpuDepths();

  // Command buffer timers are drawn underneath the matching "hw execution" timer, which has the
  // same depth value as the command buffer timer. Therefore, we need to double the depth in the
  // case that we have command buffer timers.
  if (has_vulkan_layer_command_buffer_timers_) {
    adjusted_depth *= 2.f;
  }

  // Command buffer timers have the same depth value as their matching "hw execution" timer.
  // As we want to draw command buffers underneath the hw execution timers, we need to increase
  // the depth by one.
  if (timer_info.type() == TimerInfo::kGpuCommandBuffer) {
    adjusted_depth += 1.f;
  }
  return GetPos()[1] + GetHeaderHeight() + layout_->GetTextBoxHeight() * adjusted_depth - gap_space;
}

// When track or its parent is collapsed, only draw "hardware execution" timers.
bool GpuSubmissionTrack::TimerFilter(const TimerInfo& timer_info) const {
  if (IsCollapsed()) {
    std::string gpu_stage = string_manager_->Get(timer_info.user_data_key()).value_or("");
    return gpu_stage == kHwExecutionString;
  }
  return true;
}

std::string GpuSubmissionTrack::GetTimesliceText(const TimerInfo& timer_info) const {
  CHECK(timer_info.type() == TimerInfo::kGpuActivity ||
        timer_info.type() == TimerInfo::kGpuCommandBuffer);
  std::string time = GetDisplayTime(timer_info);

  return absl::StrFormat("%s  %s", string_manager_->Get(timer_info.user_data_key()).value_or(""),
                         time);
}

float GpuSubmissionTrack::GetHeight() const {
  bool collapsed = IsCollapsed();
  uint32_t depth = collapsed ? 1 : timer_data_->GetMaxDepth();
  uint32_t num_gaps = depth > 0 ? depth - 1 : 0;
  if (has_vulkan_layer_command_buffer_timers_ && !collapsed) {
    depth *= 2;
  }
  return layout_->GetTrackTabHeight() + layout_->GetTrackContentTopMargin() +
         layout_->GetTextBoxHeight() * depth + (num_gaps * layout_->GetSpaceBetweenGpuDepths()) +
         layout_->GetTrackContentBottomMargin();
}

const TimerInfo* GpuSubmissionTrack::GetLeft(const TimerInfo& timer_info) const {
  if (timer_info.timeline_hash() == timeline_hash_) {
    const TimerChain* chain = timer_data_->GetChain(timer_info.depth());
    if (chain != nullptr) return chain->GetElementBefore(timer_info);
  }
  return nullptr;
}

const TimerInfo* GpuSubmissionTrack::GetRight(const TimerInfo& timer_info) const {
  if (timer_info.timeline_hash() == timeline_hash_) {
    const TimerChain* chain = timer_data_->GetChain(timer_info.depth());
    if (chain != nullptr) return chain->GetElementAfter(timer_info);
  }
  return nullptr;
}

std::string GpuSubmissionTrack::GetBoxTooltip(const Batcher& batcher, PickingId id) const {
  const TimerInfo* timer_info = batcher.GetTimerInfo(id);
  if ((timer_info == nullptr) || timer_info->type() == TimerInfo::kCoreActivity) {
    return "";
  }

  std::string gpu_stage = string_manager_->Get(timer_info->user_data_key()).value_or("");
  if (gpu_stage == kSwQueueString) {
    return GetSwQueueTooltip(*timer_info);
  }
  if (gpu_stage == kHwQueueString) {
    return GetHwQueueTooltip(*timer_info);
  }
  if (gpu_stage == kHwExecutionString) {
    return GetHwExecutionTooltip(*timer_info);
  }
  if (gpu_stage == kCmdBufferString) {
    return GetCommandBufferTooltip(*timer_info);
  }

  return "";
}

std::string GpuSubmissionTrack::GetSwQueueTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Software Queue
"
      "Time between amdgpu_cs_ioctl (job submitted) and "
      "amdgpu_sched_run_job (job scheduled)"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetHwQueueTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Hardware Queue
Time between amdgpu_sched_run_job "
      "(job scheduled) and start of GPU execution"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetHwExecutionTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Harware Execution
"
      "End is marked by \"dma_fence_signaled\" event for this command "
      "buffer submission"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetCommandBufferTooltip(
    const orbit_client_protos::TimerInfo& timer_info) const {
  return absl::StrFormat(
      "Command Buffer Execution
"
      "At `vkBeginCommandBuffer` and `vkEndCommandBuffer` `vkCmdWriteTimestamp`s have been "
      "inserted. The GPU timestamps get aligned with the corresponding hardware execution of the "
      "submission."
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}
<commit_msg>Adapt space_between_gpu_track to the new coordinate system (#2806)<commit_after>// Copyright (c) 2021 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "GpuSubmissionTrack.h"

#include <absl/time/time.h>

#include <memory>

#include "App.h"
#include "Batcher.h"
#include "ClientData/TimerChain.h"
#include "DisplayFormats/DisplayFormats.h"
#include "GlUtils.h"
#include "OrbitBase/Logging.h"
#include "OrbitBase/ThreadConstants.h"
#include "TimeGraph.h"
#include "TimeGraphLayout.h"
#include "TriangleToggle.h"
#include "absl/strings/str_format.h"
#include "capture_data.pb.h"

using orbit_client_data::TimerChain;
using orbit_client_protos::TimerInfo;

constexpr const char* kSwQueueString = "sw queue";
constexpr const char* kHwQueueString = "hw queue";
constexpr const char* kHwExecutionString = "hw execution";
constexpr const char* kCmdBufferString = "command buffer";

GpuSubmissionTrack::GpuSubmissionTrack(Track* parent, TimeGraph* time_graph,
                                       orbit_gl::Viewport* viewport, TimeGraphLayout* layout,
                                       uint64_t timeline_hash, OrbitApp* app,
                                       const orbit_client_data::CaptureData* capture_data,
                                       orbit_client_data::TimerData* timer_data)
    : TimerTrack(parent, time_graph, viewport, layout, app, capture_data, timer_data),
      timeline_hash_{timeline_hash},
      string_manager_{app->GetStringManager()},
      parent_{parent} {
  draw_background_ = false;
  text_renderer_ = time_graph->GetTextRenderer();
}

std::string GpuSubmissionTrack::GetName() const {
  return absl::StrFormat(
      "%s_submissions",
      string_manager_->Get(timeline_hash_).value_or(std::to_string(timeline_hash_)));
}

std::string GpuSubmissionTrack::GetTooltip() const {
  return "Shows scheduling and execution times for selected GPU job "
         "submissions";
}

void GpuSubmissionTrack::OnTimer(const orbit_client_protos::TimerInfo& timer_info) {
  // In case of having command buffer timers, we need to double the depth of the GPU timers (as we
  // are drawing the corresponding command buffer timers below them). Therefore, we watch out for
  // those timers.
  if (timer_info.type() == TimerInfo::kGpuCommandBuffer) {
    has_vulkan_layer_command_buffer_timers_ = true;
  }
  TimerTrack::OnTimer(timer_info);
}

bool GpuSubmissionTrack::IsTimerActive(const TimerInfo& timer_info) const {
  bool is_same_tid_as_selected = timer_info.thread_id() == app_->selected_thread_id();
  // We do not properly track the PID for GPU jobs and we still want to show
  // all jobs as active when no thread is selected, so this logic is a bit
  // different than SchedulerTrack::IsTimerActive.
  bool no_thread_selected = app_->selected_thread_id() == orbit_base::kAllProcessThreadsTid;

  return is_same_tid_as_selected || no_thread_selected;
}

Color GpuSubmissionTrack::GetTimerColor(const TimerInfo& timer_info, bool is_selected,
                                        bool is_highlighted) const {
  const Color kInactiveColor(100, 100, 100, 255);
  const Color kSelectionColor(0, 128, 255, 255);
  if (is_highlighted) {
    return TimerTrack::kHighlightColor;
  }
  if (is_selected) {
    return kSelectionColor;
  }
  if (!IsTimerActive(timer_info)) {
    return kInactiveColor;
  }
  if (timer_info.has_color()) {
    CHECK(timer_info.color().red() < 256);
    CHECK(timer_info.color().green() < 256);
    CHECK(timer_info.color().blue() < 256);
    CHECK(timer_info.color().alpha() < 256);
    return Color(static_cast<uint8_t>(timer_info.color().red()),
                 static_cast<uint8_t>(timer_info.color().green()),
                 static_cast<uint8_t>(timer_info.color().blue()),
                 static_cast<uint8_t>(timer_info.color().alpha()));
  }

  // We color code the timeslices for GPU activity using the color
  // of the CPU thread track that submitted the job.
  Color color = TimeGraph::GetThreadColor(timer_info.thread_id());

  // We disambiguate the different types of GPU activity based on the
  // string that is displayed on their timeslice.
  float coeff = 1.0f;
  std::string gpu_stage = string_manager_->Get(timer_info.user_data_key()).value_or("");
  if (gpu_stage == kSwQueueString) {
    coeff = 0.5f;
  } else if (gpu_stage == kHwQueueString) {
    coeff = 0.75f;
  } else if (gpu_stage == kHwExecutionString) {
    coeff = 1.0f;
  }

  color[0] = static_cast<uint8_t>(coeff * color[0]);
  color[1] = static_cast<uint8_t>(coeff * color[1]);
  color[2] = static_cast<uint8_t>(coeff * color[2]);

  constexpr uint8_t kOddAlpha = 210;
  if ((timer_info.depth() & 0x1) == 0u) {
    color[3] = kOddAlpha;
  }

  return color;
}

float GpuSubmissionTrack::GetYFromTimer(const TimerInfo& timer_info) const {
  auto adjusted_depth = static_cast<float>(timer_info.depth());
  if (IsCollapsed()) {
    adjusted_depth = 0.f;
  }
  CHECK(timer_info.type() == TimerInfo::kGpuActivity ||
        timer_info.type() == TimerInfo::kGpuCommandBuffer);

  // We are drawing a small gap between each depth, for visualization purposes.
  // There won't be a gap between "hw execution"timers and command buffer timers, which
  // is why the gap space needs to be calculated before adjusting the depth further (see below).
  float gap_space = adjusted_depth * layout_->GetSpaceBetweenGpuDepths();

  // Command buffer timers are drawn underneath the matching "hw execution" timer, which has the
  // same depth value as the command buffer timer. Therefore, we need to double the depth in the
  // case that we have command buffer timers.
  if (has_vulkan_layer_command_buffer_timers_) {
    adjusted_depth *= 2.f;
  }

  // Command buffer timers have the same depth value as their matching "hw execution" timer.
  // As we want to draw command buffers underneath the hw execution timers, we need to increase
  // the depth by one.
  if (timer_info.type() == TimerInfo::kGpuCommandBuffer) {
    adjusted_depth += 1.f;
  }
  return GetPos()[1] + GetHeaderHeight() + layout_->GetTextBoxHeight() * adjusted_depth + gap_space;
}

// When track or its parent is collapsed, only draw "hardware execution" timers.
bool GpuSubmissionTrack::TimerFilter(const TimerInfo& timer_info) const {
  if (IsCollapsed()) {
    std::string gpu_stage = string_manager_->Get(timer_info.user_data_key()).value_or("");
    return gpu_stage == kHwExecutionString;
  }
  return true;
}

std::string GpuSubmissionTrack::GetTimesliceText(const TimerInfo& timer_info) const {
  CHECK(timer_info.type() == TimerInfo::kGpuActivity ||
        timer_info.type() == TimerInfo::kGpuCommandBuffer);
  std::string time = GetDisplayTime(timer_info);

  return absl::StrFormat("%s  %s", string_manager_->Get(timer_info.user_data_key()).value_or(""),
                         time);
}

float GpuSubmissionTrack::GetHeight() const {
  bool collapsed = IsCollapsed();
  uint32_t depth = collapsed ? 1 : timer_data_->GetMaxDepth();
  uint32_t num_gaps = depth > 0 ? depth - 1 : 0;
  if (has_vulkan_layer_command_buffer_timers_ && !collapsed) {
    depth *= 2;
  }
  return layout_->GetTrackTabHeight() + layout_->GetTrackContentTopMargin() +
         layout_->GetTextBoxHeight() * depth + (num_gaps * layout_->GetSpaceBetweenGpuDepths()) +
         layout_->GetTrackContentBottomMargin();
}

const TimerInfo* GpuSubmissionTrack::GetLeft(const TimerInfo& timer_info) const {
  if (timer_info.timeline_hash() == timeline_hash_) {
    const TimerChain* chain = timer_data_->GetChain(timer_info.depth());
    if (chain != nullptr) return chain->GetElementBefore(timer_info);
  }
  return nullptr;
}

const TimerInfo* GpuSubmissionTrack::GetRight(const TimerInfo& timer_info) const {
  if (timer_info.timeline_hash() == timeline_hash_) {
    const TimerChain* chain = timer_data_->GetChain(timer_info.depth());
    if (chain != nullptr) return chain->GetElementAfter(timer_info);
  }
  return nullptr;
}

std::string GpuSubmissionTrack::GetBoxTooltip(const Batcher& batcher, PickingId id) const {
  const TimerInfo* timer_info = batcher.GetTimerInfo(id);
  if ((timer_info == nullptr) || timer_info->type() == TimerInfo::kCoreActivity) {
    return "";
  }

  std::string gpu_stage = string_manager_->Get(timer_info->user_data_key()).value_or("");
  if (gpu_stage == kSwQueueString) {
    return GetSwQueueTooltip(*timer_info);
  }
  if (gpu_stage == kHwQueueString) {
    return GetHwQueueTooltip(*timer_info);
  }
  if (gpu_stage == kHwExecutionString) {
    return GetHwExecutionTooltip(*timer_info);
  }
  if (gpu_stage == kCmdBufferString) {
    return GetCommandBufferTooltip(*timer_info);
  }

  return "";
}

std::string GpuSubmissionTrack::GetSwQueueTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Software Queue
"
      "Time between amdgpu_cs_ioctl (job submitted) and "
      "amdgpu_sched_run_job (job scheduled)"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetHwQueueTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Hardware Queue
Time between amdgpu_sched_run_job "
      "(job scheduled) and start of GPU execution"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetHwExecutionTooltip(const TimerInfo& timer_info) const {
  CHECK(capture_data_ != nullptr);
  return absl::StrFormat(
      "Harware Execution
"
      "End is marked by \"dma_fence_signaled\" event for this command "
      "buffer submission"
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}

std::string GpuSubmissionTrack::GetCommandBufferTooltip(
    const orbit_client_protos::TimerInfo& timer_info) const {
  return absl::StrFormat(
      "Command Buffer Execution
"
      "At `vkBeginCommandBuffer` and `vkEndCommandBuffer` `vkCmdWriteTimestamp`s have been "
      "inserted. The GPU timestamps get aligned with the corresponding hardware execution of the "
      "submission."
      "
"
      "
"
      "Submitted from process: %s [%d]
"
      "Submitted from thread: %s [%d]
"
      "Time: %s",
      capture_data_->GetThreadName(timer_info.process_id()), timer_info.process_id(),
      capture_data_->GetThreadName(timer_info.thread_id()), timer_info.thread_id(),
      orbit_display_formats::GetDisplayTime(TicksToDuration(timer_info.start(), timer_info.end()))
          .c_str());
}
<|endoftext|>