<commit_before>/******************************************************************************
 * This file is part of dirtsand.                                             *
 *                                                                            *
 * dirtsand is free software: you can redistribute it and/or modify           *
 * it under the terms of the GNU General Public License as published by       *
 * the Free Software Foundation, either version 3 of the License, or          *
 * (at your option) any later version.                                        *
 *                                                                            *
 * dirtsand is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU General Public License for more details.                               *
 *                                                                            *
 * You should have received a copy of the GNU General Public License          *
 * along with dirtsand.  If not, see <http://www.gnu.org/licenses/>.          *
 ******************************************************************************/

#include "AuthServer_Private.h"
#include "encodings.h"
#include "settings.h"
#include "errors.h"
#include <unistd.h>

pthread_t s_authDaemonThread;
DS::MsgChannel s_authChannel;
PGconn* s_postgres;

#define SEND_REPLY(msg, result) \
    msg->m_client->m_channel.putMessage(result)

static inline void check_postgres()
{
    if (PQstatus(s_postgres) == CONNECTION_BAD)
        PQreset(s_postgres);
    DS_DASSERT(PQstatus(s_postgres) == CONNECTION_OK);
}

bool dm_auth_init()
{
    s_postgres = PQconnectdb(DS::String::Format(
                    "host='%s' port='%s' user='%s' password='%s' dbname='%s'",
                    DS::Settings::DbHostname(), DS::Settings::DbPort(),
                    DS::Settings::DbUsername(), DS::Settings::DbPassword(),
                    DS::Settings::DbDbaseName()).c_str());
    if (PQstatus(s_postgres) != CONNECTION_OK) {
        fprintf(stderr, "Error connecting to postgres: %s", PQerrorMessage(s_postgres));
        PQfinish(s_postgres);
        return false;
    }

    init_vault();
    return true;
}

void dm_auth_shutdown()
{
    pthread_mutex_lock(&s_authClientMutex);
    std::list<AuthServer_Private*>::iterator client_iter;
    for (client_iter = s_authClients.begin(); client_iter != s_authClients.end(); ++client_iter)
        DS::CloseSock((*client_iter)->m_sock);
    pthread_mutex_unlock(&s_authClientMutex);

    bool complete = false;
    for (int i=0; i<50 && !complete; ++i) {
        pthread_mutex_lock(&s_authClientMutex);
        size_t alive = s_authClients.size();
        pthread_mutex_unlock(&s_authClientMutex);
        if (alive == 0)
            complete = true;
        usleep(100000);
    }
    if (!complete)
        fprintf(stderr, "[Auth] Clients didn't die after 5 seconds!\n");

    pthread_mutex_destroy(&s_authClientMutex);
    PQfinish(s_postgres);
}

void dm_auth_login(Auth_LoginInfo* info)
{
    check_postgres();

#ifdef DEBUG
    printf("[Auth] Login U:%s P:%s T:%s O:%s\n",
           info->m_acctName.c_str(), info->m_passHash.toString().c_str(),
           info->m_token.c_str(), info->m_os.c_str());
#endif

    // Reset UUID in case authentication fails
    info->m_client->m_acctUuid = DS::Uuid();

    PostgresStrings<1> parm;
    parm.set(0, info->m_acctName);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT \"PassHash\", \"AcctUuid\", \"AcctFlags\", \"BillingType\""
            "    FROM auth.\"Accounts\""
            "    WHERE LOWER(\"Login\")=LOWER($1)",
            1, 0, parm.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(info, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) == 0) {
        printf("[Auth] %s: Account %s does not exist\n",
               DS::SockIpAddress(info->m_client->m_sock).c_str(),
               info->m_acctName.c_str());
        PQclear(result);
        // This should be NetAccountNotFound, but that's technically a
        // security flaw...
        SEND_REPLY(info, DS::e_NetAuthenticationFailed);
        return;
    }
#ifdef DEBUG
    if (PQntuples(result) != 1) {
        PQclear(result);
        DS_PASSERT(0);
    }
#endif

    DS::ShaHash passhash = PQgetvalue(result, 0, 0);
    if (info->m_acctName.find("@") != -1 && info->m_acctName.find("@gametap") == -1) {
        DS::ShaHash challengeHash = DS::BuggyHashLogin(passhash,
                info->m_client->m_serverChallenge, info->m_clientChallenge);
        if (challengeHash != info->m_passHash) {
            printf("[Auth] %s: Failed login to account %s\n",
                   DS::SockIpAddress(info->m_client->m_sock).c_str(),
                   info->m_acctName.c_str());
            PQclear(result);
            SEND_REPLY(info, DS::e_NetAuthenticationFailed);
            return;
        }
    } else {
        // In this case, the Sha1 hash is Big Endian...  Yeah, really...
        info->m_passHash.swapBytes();
        if (passhash != info->m_passHash) {
            printf("[Auth] %s: Failed login to account %s\n",
                   DS::SockIpAddress(info->m_client->m_sock).c_str(),
                   info->m_acctName.c_str());
            PQclear(result);
            SEND_REPLY(info, DS::e_NetAuthenticationFailed);
            return;
        }
    }

    info->m_client->m_acctUuid = DS::Uuid(PQgetvalue(result, 0, 1));
    info->m_acctFlags = strtoul(PQgetvalue(result, 0, 2), 0, 10);
    info->m_billingType = strtoul(PQgetvalue(result, 0, 3), 0, 10);
    printf("[Auth] %s logged in as %s {%s}\n",
           DS::SockIpAddress(info->m_client->m_sock).c_str(),
           info->m_acctName.c_str(), info->m_client->m_acctUuid.toString().c_str());
    PQclear(result);

    // Get list of players
    DS::String uuidString = info->m_client->m_acctUuid.toString();
    parm.m_values[0] = uuidString.c_str();
    result = PQexecParams(s_postgres,
            "SELECT \"PlayerIdx\", \"PlayerName\", \"AvatarShape\", \"Explorer\""
            "    FROM auth.\"Players\""
            "    WHERE \"AcctUuid\"=$1",
            1, 0, parm.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(info, DS::e_NetInternalError);
        return;
    }
    info->m_players.resize(PQntuples(result));
    for (size_t i = 0; i < info->m_players.size(); ++i) {
        info->m_players[i].m_playerId = strtoul(PQgetvalue(result, i, 0), 0, 10);
        info->m_players[i].m_playerName = PQgetvalue(result, i, 1);
        info->m_players[i].m_avatarModel = PQgetvalue(result, i, 2);
        info->m_players[i].m_explorer = strtoul(PQgetvalue(result, i, 3), 0, 10);
    }
    PQclear(result);

    SEND_REPLY(info, DS::e_NetSuccess);
}

void dm_auth_setPlayer(Auth_ClientMessage* msg)
{
    check_postgres();

    PostgresStrings<2> parms;
    parms.set(0, msg->m_client->m_acctUuid.toString());
    parms.set(1, msg->m_client->m_player.m_playerId);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT \"PlayerName\", \"AvatarShape\", \"Explorer\""
            "    FROM auth.\"Players\""
            "    WHERE \"AcctUuid\"=$1 AND \"PlayerIdx\"=$2",
            2, 0, parms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) == 0) {
        printf("[Auth] {%s} requested invalid player ID (%u)\n",
               msg->m_client->m_acctUuid.toString().c_str(),
               msg->m_client->m_player.m_playerId);
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        SEND_REPLY(msg, DS::e_NetPlayerNotFound);
        return;
    }

#ifdef DEBUG
    if (PQntuples(result) != 1) {
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        DS_PASSERT(0);
    }
#endif

    msg->m_client->m_player.m_playerName = PQgetvalue(result, 0, 0);
    msg->m_client->m_player.m_avatarModel = PQgetvalue(result, 0, 1);
    msg->m_client->m_player.m_explorer = strtoul(PQgetvalue(result, 0, 2), 0, 10);
    PQclear(result);

    printf("[Auth] {%s} signed in as %s (%u)\n",
           msg->m_client->m_acctUuid.toString().c_str(),
           msg->m_client->m_player.m_playerName.c_str(),
           msg->m_client->m_player.m_playerId);
    SEND_REPLY(msg, DS::e_NetSuccess);
}

void dm_auth_createPlayer(Auth_PlayerCreate* msg)
{
    if (msg->m_avatarShape != "male" && msg->m_avatarShape != "female") {
        // Cheater!
        msg->m_avatarShape = "male";
    }

    // Check for existing player
    PostgresStrings<1> sparms;
    sparms.set(0, msg->m_playerName);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT idx FROM auth.\"Players\""
            "    WHERE \"PlayerName\"=$1",
            1, 0, sparms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) != 0) {
        fprintf(stderr, "[Auth] %s: Player %s already exists!\n",
                DS::SockIpAddress(msg->m_client->m_sock).c_str(),
                msg->m_playerName.c_str());
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetPlayerAlreadyExists);
        return;
    }
    PQclear(result);

    DS::Uuid playerId = gen_uuid();
    msg->m_playerNode = v_create_player(playerId, msg->m_playerName,
                                        msg->m_avatarShape, true).first;
    if (msg->m_playerNode == 0)
        SEND_REPLY(msg, DS::e_NetInternalError);

    PostgresStrings<5> iparms;
    iparms.set(0, playerId.toString());
    iparms.set(1, msg->m_playerNode);
    iparms.set(2, msg->m_playerName);
    iparms.set(3, msg->m_avatarShape);
    iparms.set(4, 1);
    result = PQexecParams(s_postgres,
            "INSERT INTO auth.\"Players\""
            "    (\"AcctUuid\", \"PlayerIdx\", \"PlayerName\", \"AvatarShape\", \"Explorer\")"
            "    VALUES ($1, $2, $3, $4, $5)"
            "    RETURNING idx",
            5, 0, iparms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres INSERT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    DS_DASSERT(PQntuples(result) == 1);
    PQclear(result);
    SEND_REPLY(msg, DS::e_NetSuccess);
}

void* dm_authDaemon(void*)
{
    for ( ;; ) {
        try {
            DS::FifoMessage msg = s_authChannel.getMessage();
            switch (msg.m_messageType) {
            case e_AuthShutdown:
                dm_auth_shutdown();
                return 0;
            case e_AuthClientLogin:
                dm_auth_login(reinterpret_cast<Auth_LoginInfo*>(msg.m_payload));
                break;
            case e_AuthSetPlayer:
                dm_auth_setPlayer(reinterpret_cast<Auth_ClientMessage*>(msg.m_payload));
                break;
            case e_AuthCreatePlayer:
                dm_auth_createPlayer(reinterpret_cast<Auth_PlayerCreate*>(msg.m_payload));
                break;
            default:
                /* Invalid message...  This shouldn't happen */
                DS_DASSERT(0);
                break;
            }
        } catch (DS::AssertException ex) {
            fprintf(stderr, "[Auth] Assertion failed at %s:%ld:  %s\n",
                    ex.m_file, ex.m_line, ex.m_cond);
        }
    }

    dm_auth_shutdown();
    return 0;
}
<commit_msg>Use the player's Uuid instead of generating a random useless one<commit_after>/******************************************************************************
 * This file is part of dirtsand.                                             *
 *                                                                            *
 * dirtsand is free software: you can redistribute it and/or modify           *
 * it under the terms of the GNU General Public License as published by       *
 * the Free Software Foundation, either version 3 of the License, or          *
 * (at your option) any later version.                                        *
 *                                                                            *
 * dirtsand is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU General Public License for more details.                               *
 *                                                                            *
 * You should have received a copy of the GNU General Public License          *
 * along with dirtsand.  If not, see <http://www.gnu.org/licenses/>.          *
 ******************************************************************************/

#include "AuthServer_Private.h"
#include "encodings.h"
#include "settings.h"
#include "errors.h"
#include <unistd.h>

pthread_t s_authDaemonThread;
DS::MsgChannel s_authChannel;
PGconn* s_postgres;

#define SEND_REPLY(msg, result) \
    msg->m_client->m_channel.putMessage(result)

static inline void check_postgres()
{
    if (PQstatus(s_postgres) == CONNECTION_BAD)
        PQreset(s_postgres);
    DS_DASSERT(PQstatus(s_postgres) == CONNECTION_OK);
}

bool dm_auth_init()
{
    s_postgres = PQconnectdb(DS::String::Format(
                    "host='%s' port='%s' user='%s' password='%s' dbname='%s'",
                    DS::Settings::DbHostname(), DS::Settings::DbPort(),
                    DS::Settings::DbUsername(), DS::Settings::DbPassword(),
                    DS::Settings::DbDbaseName()).c_str());
    if (PQstatus(s_postgres) != CONNECTION_OK) {
        fprintf(stderr, "Error connecting to postgres: %s", PQerrorMessage(s_postgres));
        PQfinish(s_postgres);
        return false;
    }

    init_vault();
    return true;
}

void dm_auth_shutdown()
{
    pthread_mutex_lock(&s_authClientMutex);
    std::list<AuthServer_Private*>::iterator client_iter;
    for (client_iter = s_authClients.begin(); client_iter != s_authClients.end(); ++client_iter)
        DS::CloseSock((*client_iter)->m_sock);
    pthread_mutex_unlock(&s_authClientMutex);

    bool complete = false;
    for (int i=0; i<50 && !complete; ++i) {
        pthread_mutex_lock(&s_authClientMutex);
        size_t alive = s_authClients.size();
        pthread_mutex_unlock(&s_authClientMutex);
        if (alive == 0)
            complete = true;
        usleep(100000);
    }
    if (!complete)
        fprintf(stderr, "[Auth] Clients didn't die after 5 seconds!\n");

    pthread_mutex_destroy(&s_authClientMutex);
    PQfinish(s_postgres);
}

void dm_auth_login(Auth_LoginInfo* info)
{
    check_postgres();

#ifdef DEBUG
    printf("[Auth] Login U:%s P:%s T:%s O:%s\n",
           info->m_acctName.c_str(), info->m_passHash.toString().c_str(),
           info->m_token.c_str(), info->m_os.c_str());
#endif

    // Reset UUID in case authentication fails
    info->m_client->m_acctUuid = DS::Uuid();

    PostgresStrings<1> parm;
    parm.set(0, info->m_acctName);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT \"PassHash\", \"AcctUuid\", \"AcctFlags\", \"BillingType\""
            "    FROM auth.\"Accounts\""
            "    WHERE LOWER(\"Login\")=LOWER($1)",
            1, 0, parm.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(info, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) == 0) {
        printf("[Auth] %s: Account %s does not exist\n",
               DS::SockIpAddress(info->m_client->m_sock).c_str(),
               info->m_acctName.c_str());
        PQclear(result);
        // This should be NetAccountNotFound, but that's technically a
        // security flaw...
        SEND_REPLY(info, DS::e_NetAuthenticationFailed);
        return;
    }
#ifdef DEBUG
    if (PQntuples(result) != 1) {
        PQclear(result);
        DS_PASSERT(0);
    }
#endif

    DS::ShaHash passhash = PQgetvalue(result, 0, 0);
    if (info->m_acctName.find("@") != -1 && info->m_acctName.find("@gametap") == -1) {
        DS::ShaHash challengeHash = DS::BuggyHashLogin(passhash,
                info->m_client->m_serverChallenge, info->m_clientChallenge);
        if (challengeHash != info->m_passHash) {
            printf("[Auth] %s: Failed login to account %s\n",
                   DS::SockIpAddress(info->m_client->m_sock).c_str(),
                   info->m_acctName.c_str());
            PQclear(result);
            SEND_REPLY(info, DS::e_NetAuthenticationFailed);
            return;
        }
    } else {
        // In this case, the Sha1 hash is Big Endian...  Yeah, really...
        info->m_passHash.swapBytes();
        if (passhash != info->m_passHash) {
            printf("[Auth] %s: Failed login to account %s\n",
                   DS::SockIpAddress(info->m_client->m_sock).c_str(),
                   info->m_acctName.c_str());
            PQclear(result);
            SEND_REPLY(info, DS::e_NetAuthenticationFailed);
            return;
        }
    }

    info->m_client->m_acctUuid = DS::Uuid(PQgetvalue(result, 0, 1));
    info->m_acctFlags = strtoul(PQgetvalue(result, 0, 2), 0, 10);
    info->m_billingType = strtoul(PQgetvalue(result, 0, 3), 0, 10);
    printf("[Auth] %s logged in as %s {%s}\n",
           DS::SockIpAddress(info->m_client->m_sock).c_str(),
           info->m_acctName.c_str(), info->m_client->m_acctUuid.toString().c_str());
    PQclear(result);

    // Get list of players
    DS::String uuidString = info->m_client->m_acctUuid.toString();
    parm.m_values[0] = uuidString.c_str();
    result = PQexecParams(s_postgres,
            "SELECT \"PlayerIdx\", \"PlayerName\", \"AvatarShape\", \"Explorer\""
            "    FROM auth.\"Players\""
            "    WHERE \"AcctUuid\"=$1",
            1, 0, parm.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(info, DS::e_NetInternalError);
        return;
    }
    info->m_players.resize(PQntuples(result));
    for (size_t i = 0; i < info->m_players.size(); ++i) {
        info->m_players[i].m_playerId = strtoul(PQgetvalue(result, i, 0), 0, 10);
        info->m_players[i].m_playerName = PQgetvalue(result, i, 1);
        info->m_players[i].m_avatarModel = PQgetvalue(result, i, 2);
        info->m_players[i].m_explorer = strtoul(PQgetvalue(result, i, 3), 0, 10);
    }
    PQclear(result);

    SEND_REPLY(info, DS::e_NetSuccess);
}

void dm_auth_setPlayer(Auth_ClientMessage* msg)
{
    check_postgres();

    PostgresStrings<2> parms;
    parms.set(0, msg->m_client->m_acctUuid.toString());
    parms.set(1, msg->m_client->m_player.m_playerId);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT \"PlayerName\", \"AvatarShape\", \"Explorer\""
            "    FROM auth.\"Players\""
            "    WHERE \"AcctUuid\"=$1 AND \"PlayerIdx\"=$2",
            2, 0, parms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) == 0) {
        printf("[Auth] {%s} requested invalid player ID (%u)\n",
               msg->m_client->m_acctUuid.toString().c_str(),
               msg->m_client->m_player.m_playerId);
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        SEND_REPLY(msg, DS::e_NetPlayerNotFound);
        return;
    }

#ifdef DEBUG
    if (PQntuples(result) != 1) {
        PQclear(result);
        msg->m_client->m_player.m_playerId = 0;
        DS_PASSERT(0);
    }
#endif

    msg->m_client->m_player.m_playerName = PQgetvalue(result, 0, 0);
    msg->m_client->m_player.m_avatarModel = PQgetvalue(result, 0, 1);
    msg->m_client->m_player.m_explorer = strtoul(PQgetvalue(result, 0, 2), 0, 10);
    PQclear(result);

    printf("[Auth] {%s} signed in as %s (%u)\n",
           msg->m_client->m_acctUuid.toString().c_str(),
           msg->m_client->m_player.m_playerName.c_str(),
           msg->m_client->m_player.m_playerId);
    SEND_REPLY(msg, DS::e_NetSuccess);
}

void dm_auth_createPlayer(Auth_PlayerCreate* msg)
{
    if (msg->m_avatarShape != "male" && msg->m_avatarShape != "female") {
        // Cheater!
        msg->m_avatarShape = "male";
    }

    // Check for existing player
    PostgresStrings<1> sparms;
    sparms.set(0, msg->m_playerName);
    PGresult* result = PQexecParams(s_postgres,
            "SELECT idx FROM auth.\"Players\""
            "    WHERE \"PlayerName\"=$1",
            1, 0, sparms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres SELECT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    if (PQntuples(result) != 0) {
        fprintf(stderr, "[Auth] %s: Player %s already exists!\n",
                DS::SockIpAddress(msg->m_client->m_sock).c_str(),
                msg->m_playerName.c_str());
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetPlayerAlreadyExists);
        return;
    }
    PQclear(result);

    msg->m_playerNode = v_create_player(msg->m_client->m_acctUuid, msg->m_playerName,
                                        msg->m_avatarShape, true).first;
    if (msg->m_playerNode == 0)
        SEND_REPLY(msg, DS::e_NetInternalError);

    PostgresStrings<5> iparms;
    iparms.set(0, msg->m_client->m_acctUuid.toString());
    iparms.set(1, msg->m_playerNode);
    iparms.set(2, msg->m_playerName);
    iparms.set(3, msg->m_avatarShape);
    iparms.set(4, 1);
    result = PQexecParams(s_postgres,
            "INSERT INTO auth.\"Players\""
            "    (\"AcctUuid\", \"PlayerIdx\", \"PlayerName\", \"AvatarShape\", \"Explorer\")"
            "    VALUES ($1, $2, $3, $4, $5)"
            "    RETURNING idx",
            5, 0, iparms.m_values, 0, 0, 0);
    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
        fprintf(stderr, "%s:%d:\n    Postgres INSERT error: %s\n",
                __FILE__, __LINE__, PQerrorMessage(s_postgres));
        PQclear(result);
        SEND_REPLY(msg, DS::e_NetInternalError);
        return;
    }
    DS_DASSERT(PQntuples(result) == 1);
    PQclear(result);
    SEND_REPLY(msg, DS::e_NetSuccess);
}

void* dm_authDaemon(void*)
{
    for ( ;; ) {
        try {
            DS::FifoMessage msg = s_authChannel.getMessage();
            switch (msg.m_messageType) {
            case e_AuthShutdown:
                dm_auth_shutdown();
                return 0;
            case e_AuthClientLogin:
                dm_auth_login(reinterpret_cast<Auth_LoginInfo*>(msg.m_payload));
                break;
            case e_AuthSetPlayer:
                dm_auth_setPlayer(reinterpret_cast<Auth_ClientMessage*>(msg.m_payload));
                break;
            case e_AuthCreatePlayer:
                dm_auth_createPlayer(reinterpret_cast<Auth_PlayerCreate*>(msg.m_payload));
                break;
            default:
                /* Invalid message...  This shouldn't happen */
                DS_DASSERT(0);
                break;
            }
        } catch (DS::AssertException ex) {
            fprintf(stderr, "[Auth] Assertion failed at %s:%ld:  %s\n",
                    ex.m_file, ex.m_line, ex.m_cond);
        }
    }

    dm_auth_shutdown();
    return 0;
}
<|endoftext|>


<commit_before>
#if defined(USE_SDL2)
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#if defined(__APPLE__) /* Brew got the headers wrong here */
# include <SDL.h>
#else
# include <SDL2/SDL.h>
#endif

#include "ifict.h"
#include "utils.h"
#include "debug.h"
#include "fatal.h"
#include "palette.h"

SDL_Window*			sdl_window = NULL;
SDL_Surface*			sdl_window_surface = NULL;
SDL_Surface*			sdl_game_surface = NULL;
SDL_Palette*			sdl_game_palette = NULL;
SDL_Color			sdl_pal[256];
Uint32				sdl_ticks_base = 0; /* use Uint32 type provided by SDL2 here to avoid problems */
bool				sdl_signal_to_quit = false;
ifevidinfo_t			ifevidinfo_sdl2;

static void p_SetPaletteColors(const unsigned int first,const unsigned int count,IFEPaletteEntry *pal) {
	unsigned int i;

	priv_SetPaletteColorsRangeCheck(first,count);

	for (i=0;i < count;i++) {
		sdl_pal[i+first].r = pal[i].r;
		sdl_pal[i+first].g = pal[i].g;
		sdl_pal[i+first].b = pal[i].b;
		sdl_pal[i+first].a = 0xFFu;
	}

	if (SDL_SetPaletteColors(sdl_game_palette,sdl_pal,first,count) != 0)
		IFEFatalError("SDL2 game palette set colors");
}

static uint32_t p_GetTicks(void) {
	return uint32_t(SDL_GetTicks() - sdl_ticks_base);
}

static void p_ResetTicks(const uint32_t base) {
	sdl_ticks_base += base; /* NTS: Use return value of IFEGetTicks() */
}

static void p_UpdateFullScreen(void) {
	if (SDL_BlitSurface(sdl_game_surface,NULL,sdl_window_surface,NULL) != 0)
		IFEFatalError("Game to window BlitSurface");

	if (SDL_UpdateWindowSurface(sdl_window) != 0)
		IFEFatalError("Window surface update");
}

static ifevidinfo_t* p_GetVidInfo(void) {
	return &ifevidinfo_sdl2;
}

static bool p_UserWantsToQuit(void) {
	return sdl_signal_to_quit;
}

static uint32_t SDKKeySymToIFE(const SDL_Scancode k) {
#define MAP(x,y) \
	case x: return y;
#define MSN(x) \
	case SDL_SCANCODE_##x: return IFEKEY_##x;

	switch (k) {
		MSN(RETURN);
		MSN(ESCAPE);
		MSN(BACKSPACE);
		MSN(TAB);
		MSN(SPACE);
		default: break;
	}
#undef MSN
#undef MAP

	return uint32_t(0);
}

static void priv_ProcessKeyboardEvent(const SDL_KeyboardEvent &ev) {
	IFEKeyEvent ke;

	memset(&ke,0,sizeof(ke));
	ke.raw_code = (uint32_t)ev.keysym.scancode;
	ke.code = SDKKeySymToIFE(ev.keysym.scancode);
	ke.flags = (ev.state == SDL_PRESSED) ? IFEKeyEvent_FLAG_DOWN : 0;

	if (!IFEKeyQueue.add(ke))
		IFEDBG("ProcessKeyboardEvent: Queue full");
}

static void priv_ProcessEvent(const SDL_Event &ev) {
	switch (ev.type) {
		case SDL_QUIT:
			sdl_signal_to_quit = true;
			break;
		case SDL_KEYDOWN:
		case SDL_KEYUP:
			priv_ProcessKeyboardEvent(ev.key);
			break;
		default:
			break;
	}
}

static void p_CheckEvents(void) {
	SDL_Event ev;

	if (SDL_PollEvent(&ev))
		priv_ProcessEvent(ev);
}

static void p_WaitEvent(const int wait_ms) {
	SDL_Event ev;

	if (SDL_WaitEventTimeout(&ev,wait_ms))
		priv_ProcessEvent(ev);
}

static bool p_BeginScreenDraw(void) {
	if (SDL_MUSTLOCK(sdl_game_surface) && SDL_LockSurface(sdl_game_surface) != 0)
		return false;
	if (sdl_game_surface->pixels == NULL)
		IFEFatalError("SDL2 game surface pixels == NULL"); /* that's a BUG if this happens! */
	if (sdl_game_surface->pitch < 0)
		IFEFatalError("SDL2 game surface pitch is negative");

	ifevidinfo_sdl2.buf_base = ifevidinfo_sdl2.buf_first_row = (unsigned char*)(sdl_game_surface->pixels);
	return true;
}

static void p_EndScreenDraw(void) {
	if (SDL_MUSTLOCK(sdl_game_surface))
		SDL_UnlockSurface(sdl_game_surface);

	ifevidinfo_sdl2.buf_base = ifevidinfo_sdl2.buf_first_row = NULL;
}

static void p_ShutdownVideo(void) {
	if (sdl_game_surface != NULL) {
		SDL_FreeSurface(sdl_game_surface);
		sdl_game_surface = NULL;
	}
	if (sdl_game_palette != NULL) {
		SDL_FreePalette(sdl_game_palette);
		sdl_game_palette = NULL;
	}
	if (sdl_window != NULL) {
		SDL_DestroyWindow(sdl_window);
		sdl_window_surface = NULL;
		sdl_window = NULL;
	}
	SDL_Quit();
}

static void p_InitVideo(void) {
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
		IFEFatalError("SDL2 failed to initialize");

	memset(&ifevidinfo_sdl2,0,sizeof(ifevidinfo_sdl2));

	ifevidinfo_sdl2.width = 640;
	ifevidinfo_sdl2.height = 480;

	if (sdl_window == NULL && (sdl_window=SDL_CreateWindow("",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,SDL_WINDOW_SHOWN)) == NULL)
		IFEFatalError("SDL2 window creation failed");
	if (sdl_window_surface == NULL && (sdl_window_surface=SDL_GetWindowSurface(sdl_window)) == NULL)
		IFEFatalError("SDL2 window surface failed");
	if (sdl_game_surface == NULL && (sdl_game_surface=SDL_CreateRGBSurfaceWithFormat(0,640,480,8,SDL_PIXELFORMAT_INDEX8)) == NULL)
		IFEFatalError("SDL2 game surface (256-color) failed");
	if (sdl_game_palette == NULL && (sdl_game_palette=SDL_AllocPalette(256)) == NULL)
		IFEFatalError("SDL2 game palette");

	ifevidinfo_sdl2.buf_alloc = ifevidinfo_sdl2.buf_size = sdl_game_surface->pitch * sdl_game_surface->h;
	ifevidinfo_sdl2.buf_pitch = sdl_game_surface->pitch;

	/* first color should be black, SDL2 will init palette to white for some reason */
	memset(sdl_pal,0,sizeof(SDL_Color));
	if (SDL_SetPaletteColors(sdl_game_palette,sdl_pal,0,1) != 0)
		IFEFatalError("SDL2 game palette set colors");

	/* apply palette to surface */
	if (SDL_SetSurfacePalette(sdl_game_surface,sdl_game_palette) != 0)
		IFEFatalError("SDL2 game palette set");

	/* make sure screen is cleared black */
	SDL_FillRect(sdl_game_surface,NULL,0);
	ifeapi->UpdateFullScreen();
	ifeapi->CheckEvents();
}

void p_FlushKeyboardInput(void) {
	IFEKeyQueueEmptyAll();
}

IFEKeyEvent *p_GetRawKeyboardInput(void) {
	return IFEKeyQueue.get();
}

IFECookedKeyEvent *p_GetCookedKeyboardInput(void) {
	return IFECookedKeyQueue.get();
}

ifeapi_t ifeapi_sdl2 = {
	"SDL2",
	p_SetPaletteColors,
	p_GetTicks,
	p_ResetTicks,
	p_UpdateFullScreen,
	p_GetVidInfo,
	p_UserWantsToQuit,
	p_CheckEvents,
	p_WaitEvent,
	p_BeginScreenDraw,
	p_EndScreenDraw,
	p_ShutdownVideo,
	p_InitVideo,
	p_FlushKeyboardInput,
	p_GetRawKeyboardInput,
	p_GetCookedKeyboardInput
};

bool priv_IFEMainInit(int argc,char **argv) {
	struct stat st;

	//not used yet
	(void)argc;
	(void)argv;

	/* if STDERR is a valid handle to something, enable debug */
	if (fstat(2/*STDERR*/,&st) == 0) {
		fprintf(stderr,"Will emit debug info to stderr\n");
		ifedbg_en = true;
	}

	return true;
}
#endif

<commit_msg>Cooked keyboard too, SDL2<commit_after>
#if defined(USE_SDL2)
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#if defined(__APPLE__) /* Brew got the headers wrong here */
# include <SDL.h>
#else
# include <SDL2/SDL.h>
#endif

#include "ifict.h"
#include "utils.h"
#include "debug.h"
#include "fatal.h"
#include "palette.h"

SDL_Window*			sdl_window = NULL;
SDL_Surface*			sdl_window_surface = NULL;
SDL_Surface*			sdl_game_surface = NULL;
SDL_Palette*			sdl_game_palette = NULL;
SDL_Color			sdl_pal[256];
Uint32				sdl_ticks_base = 0; /* use Uint32 type provided by SDL2 here to avoid problems */
bool				sdl_signal_to_quit = false;
ifevidinfo_t			ifevidinfo_sdl2;

static void p_SetPaletteColors(const unsigned int first,const unsigned int count,IFEPaletteEntry *pal) {
	unsigned int i;

	priv_SetPaletteColorsRangeCheck(first,count);

	for (i=0;i < count;i++) {
		sdl_pal[i+first].r = pal[i].r;
		sdl_pal[i+first].g = pal[i].g;
		sdl_pal[i+first].b = pal[i].b;
		sdl_pal[i+first].a = 0xFFu;
	}

	if (SDL_SetPaletteColors(sdl_game_palette,sdl_pal,first,count) != 0)
		IFEFatalError("SDL2 game palette set colors");
}

static uint32_t p_GetTicks(void) {
	return uint32_t(SDL_GetTicks() - sdl_ticks_base);
}

static void p_ResetTicks(const uint32_t base) {
	sdl_ticks_base += base; /* NTS: Use return value of IFEGetTicks() */
}

static void p_UpdateFullScreen(void) {
	if (SDL_BlitSurface(sdl_game_surface,NULL,sdl_window_surface,NULL) != 0)
		IFEFatalError("Game to window BlitSurface");

	if (SDL_UpdateWindowSurface(sdl_window) != 0)
		IFEFatalError("Window surface update");
}

static ifevidinfo_t* p_GetVidInfo(void) {
	return &ifevidinfo_sdl2;
}

static bool p_UserWantsToQuit(void) {
	return sdl_signal_to_quit;
}

static void SDLKeySymToFill(IFEKeyEvent &ke,IFECookedKeyEvent &cke,const SDL_KeyboardEvent &ev) {
#define MAP(x,y,c) \
	case x: ke.code = (uint32_t)y; cke.code = (uint32_t)c; break;
#define MSN(x,c) \
	case SDL_SCANCODE_##x: ke.code = (uint32_t)(IFEKEY_##x); cke.code = (uint32_t)c; break;

	switch (ev.keysym.scancode) {
		MSN(RETURN,		13);
		MSN(ESCAPE,		27);
		MSN(BACKSPACE,		8);
		MSN(TAB,		9);
		MSN(SPACE,		' ');
		default: break;
	}
#undef MSN
#undef MAP
}

static void priv_ProcessKeyboardEvent(const SDL_KeyboardEvent &ev) {
	IFECookedKeyEvent cke;
	IFEKeyEvent ke;

	memset(&ke,0,sizeof(ke));
	memset(&cke,0,sizeof(cke));
	ke.raw_code = (uint32_t)ev.keysym.scancode;
	ke.flags = (ev.state == SDL_PRESSED) ? IFEKeyEvent_FLAG_DOWN : 0;
	SDLKeySymToFill(ke,cke,ev);

	if (!IFEKeyQueue.add(ke))
		IFEDBG("ProcessKeyboardEvent: Queue full");
	if (cke.code != (uint32_t)0 && !IFECookedKeyQueue.add(cke))
		IFEDBG("ProcessKeyboardEvent: Cooked queue full");
}

static void priv_ProcessEvent(const SDL_Event &ev) {
	switch (ev.type) {
		case SDL_QUIT:
			sdl_signal_to_quit = true;
			break;
		case SDL_KEYDOWN:
		case SDL_KEYUP:
			priv_ProcessKeyboardEvent(ev.key);
			break;
		default:
			break;
	}
}

static void p_CheckEvents(void) {
	SDL_Event ev;

	if (SDL_PollEvent(&ev))
		priv_ProcessEvent(ev);
}

static void p_WaitEvent(const int wait_ms) {
	SDL_Event ev;

	if (SDL_WaitEventTimeout(&ev,wait_ms))
		priv_ProcessEvent(ev);
}

static bool p_BeginScreenDraw(void) {
	if (SDL_MUSTLOCK(sdl_game_surface) && SDL_LockSurface(sdl_game_surface) != 0)
		return false;
	if (sdl_game_surface->pixels == NULL)
		IFEFatalError("SDL2 game surface pixels == NULL"); /* that's a BUG if this happens! */
	if (sdl_game_surface->pitch < 0)
		IFEFatalError("SDL2 game surface pitch is negative");

	ifevidinfo_sdl2.buf_base = ifevidinfo_sdl2.buf_first_row = (unsigned char*)(sdl_game_surface->pixels);
	return true;
}

static void p_EndScreenDraw(void) {
	if (SDL_MUSTLOCK(sdl_game_surface))
		SDL_UnlockSurface(sdl_game_surface);

	ifevidinfo_sdl2.buf_base = ifevidinfo_sdl2.buf_first_row = NULL;
}

static void p_ShutdownVideo(void) {
	if (sdl_game_surface != NULL) {
		SDL_FreeSurface(sdl_game_surface);
		sdl_game_surface = NULL;
	}
	if (sdl_game_palette != NULL) {
		SDL_FreePalette(sdl_game_palette);
		sdl_game_palette = NULL;
	}
	if (sdl_window != NULL) {
		SDL_DestroyWindow(sdl_window);
		sdl_window_surface = NULL;
		sdl_window = NULL;
	}
	SDL_Quit();
}

static void p_InitVideo(void) {
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
		IFEFatalError("SDL2 failed to initialize");

	memset(&ifevidinfo_sdl2,0,sizeof(ifevidinfo_sdl2));

	ifevidinfo_sdl2.width = 640;
	ifevidinfo_sdl2.height = 480;

	if (sdl_window == NULL && (sdl_window=SDL_CreateWindow("",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,SDL_WINDOW_SHOWN)) == NULL)
		IFEFatalError("SDL2 window creation failed");
	if (sdl_window_surface == NULL && (sdl_window_surface=SDL_GetWindowSurface(sdl_window)) == NULL)
		IFEFatalError("SDL2 window surface failed");
	if (sdl_game_surface == NULL && (sdl_game_surface=SDL_CreateRGBSurfaceWithFormat(0,640,480,8,SDL_PIXELFORMAT_INDEX8)) == NULL)
		IFEFatalError("SDL2 game surface (256-color) failed");
	if (sdl_game_palette == NULL && (sdl_game_palette=SDL_AllocPalette(256)) == NULL)
		IFEFatalError("SDL2 game palette");

	ifevidinfo_sdl2.buf_alloc = ifevidinfo_sdl2.buf_size = sdl_game_surface->pitch * sdl_game_surface->h;
	ifevidinfo_sdl2.buf_pitch = sdl_game_surface->pitch;

	/* first color should be black, SDL2 will init palette to white for some reason */
	memset(sdl_pal,0,sizeof(SDL_Color));
	if (SDL_SetPaletteColors(sdl_game_palette,sdl_pal,0,1) != 0)
		IFEFatalError("SDL2 game palette set colors");

	/* apply palette to surface */
	if (SDL_SetSurfacePalette(sdl_game_surface,sdl_game_palette) != 0)
		IFEFatalError("SDL2 game palette set");

	/* make sure screen is cleared black */
	SDL_FillRect(sdl_game_surface,NULL,0);
	ifeapi->UpdateFullScreen();
	ifeapi->CheckEvents();
}

void p_FlushKeyboardInput(void) {
	IFEKeyQueueEmptyAll();
}

IFEKeyEvent *p_GetRawKeyboardInput(void) {
	return IFEKeyQueue.get();
}

IFECookedKeyEvent *p_GetCookedKeyboardInput(void) {
	return IFECookedKeyQueue.get();
}

ifeapi_t ifeapi_sdl2 = {
	"SDL2",
	p_SetPaletteColors,
	p_GetTicks,
	p_ResetTicks,
	p_UpdateFullScreen,
	p_GetVidInfo,
	p_UserWantsToQuit,
	p_CheckEvents,
	p_WaitEvent,
	p_BeginScreenDraw,
	p_EndScreenDraw,
	p_ShutdownVideo,
	p_InitVideo,
	p_FlushKeyboardInput,
	p_GetRawKeyboardInput,
	p_GetCookedKeyboardInput
};

bool priv_IFEMainInit(int argc,char **argv) {
	struct stat st;

	//not used yet
	(void)argc;
	(void)argv;

	/* if STDERR is a valid handle to something, enable debug */
	if (fstat(2/*STDERR*/,&st) == 0) {
		fprintf(stderr,"Will emit debug info to stderr\n");
		ifedbg_en = true;
	}

	return true;
}
#endif

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <kernel/userver.hh>

#include <urbi/object/global.hh>
#include <object/ioservice.hh>
#include <object/server.hh>
#include <object/socket.hh>
#include <object/symbols.hh>

namespace urbi
{
  namespace object
  {
        IoService::IoService()
      : CxxObject()
    {
      proto_add(Object::proto);
      // FIXME: call slots_create here when uobjects load order is fixed
    }

    IoService::IoService(rIoService)
      : CxxObject()
    {
      proto_add(proto);
    }

    void IoService::initialize(CxxObject::Binder<IoService>& bind)
    {
      bind(SYMBOL(pollFor), &IoService::pollFor);
      bind(SYMBOL(pollOneFor), &IoService::pollOneFor);
      bind(SYMBOL(poll), &IoService::poll);
      bind(SYMBOL(makeServer), &IoService::makeServer);
      bind(SYMBOL(makeSocket), &IoService::makeSocket);
    }

    void IoService::pollFor(double d)
    {
      libport::pollFor(d*1000000.0, false, *this);
    }

    void IoService::pollOneFor(double d)
    {
      libport::pollFor(d*1000000.0, true, *this);
    }

    void IoService::poll()
    {
      boost::asio::io_service::reset();
      boost::asio::io_service::poll();
    }

    rSocket IoService::makeSocket()
    {
      return rSocket(new Socket(this));
    }

    rServer IoService::makeServer()
    {
      return rServer(new Server(this));
    }

    URBI_CXX_OBJECT_REGISTER(IoService)
    {
    }
  }
}
<commit_msg>Fix a warning casting a double to ulong.<commit_after>/*
 * Copyright (C) 2010, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <kernel/userver.hh>

#include <urbi/object/global.hh>
#include <object/ioservice.hh>
#include <object/server.hh>
#include <object/socket.hh>
#include <object/symbols.hh>

namespace urbi
{
  namespace object
  {
        IoService::IoService()
      : CxxObject()
    {
      proto_add(Object::proto);
      // FIXME: call slots_create here when uobjects load order is fixed
    }

    IoService::IoService(rIoService)
      : CxxObject()
    {
      proto_add(proto);
    }

    void IoService::initialize(CxxObject::Binder<IoService>& bind)
    {
      bind(SYMBOL(pollFor), &IoService::pollFor);
      bind(SYMBOL(pollOneFor), &IoService::pollOneFor);
      bind(SYMBOL(poll), &IoService::poll);
      bind(SYMBOL(makeServer), &IoService::makeServer);
      bind(SYMBOL(makeSocket), &IoService::makeSocket);
    }

    void IoService::pollFor(double d)
    {
      libport::pollFor(static_cast<useconds_t>(d*1000000.0), false, *this);
    }

    void IoService::pollOneFor(double d)
    {
      libport::pollFor(static_cast<useconds_t>(d*1000000.0), true, *this);
    }

    void IoService::poll()
    {
      boost::asio::io_service::reset();
      boost::asio::io_service::poll();
    }

    rSocket IoService::makeSocket()
    {
      return rSocket(new Socket(this));
    }

    rServer IoService::makeServer()
    {
      return rServer(new Server(this));
    }

    URBI_CXX_OBJECT_REGISTER(IoService)
    {
    }
  }
}
<|endoftext|>


<commit_before>#include "object/prototype.h"

namespace grok {
namespace obj {

void AddPropertiesFromPrototype(JSObject *ctor, JSObject *obj)
{
    std::shared_ptr<Object> proto_wrapped = ctor->GetProperty("prototype");

    // if proto is undefined then we are done
    if (IsUndefined(proto_wrapped)) {
        return; // nothing to be done
    }

    std::shared_ptr<JSObject> proto = proto_wrapped->as<JSObject>();

    for (auto &property : *proto) {
        obj->AddProperty(property.first, CreateCopy(property.second));
    }
}

}
}
<commit_msg>Don't copy properties for built-in objects<commit_after>#include "object/prototype.h"

namespace grok {
namespace obj {

void AddPropertiesFromPrototype(JSObject *ctor, JSObject *obj)
{
    std::shared_ptr<Object> proto_wrapped = ctor->GetProperty("prototype");

    // if proto is undefined then we are done
    if (IsUndefined(proto_wrapped)) {
        return; // nothing to be done
    }

    if (ctor->GetType() == ObjectType::_array
        || ctor->GetType() == ObjectType::_bool
        || ctor->GetType() == ObjectType::_number)
        return;

    std::shared_ptr<JSObject> proto = proto_wrapped->as<JSObject>();

    for (auto &property : *proto) {
        obj->AddProperty(property.first, CreateCopy(property.second));
    }
}

}
}
<|endoftext|>


<commit_before>/* LICENSE NOTICE
	Copyright (c) 2009, Frederick Emmott <mail@fredemmott.co.uk>

	Permission to use, copy, modify, and/or distribute this software for any
	purpose with or without fee is hereby granted, provided that the above
	copyright notice and this permission notice appear in all copies.

	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "RequestWorker.h"

#include "InputDevice.h"
#include "OutputDevice.h"
#include "Request_Backend.h"

#include <QDebug>
#include <QTimer>

namespace FastCgiQt
{
	RequestWorker::RequestWorker(ClientIODevice* device, Responder::Generator generator, QObject* parent)
	: Worker(parent)
	, m_device(device)
	, m_generator(generator)
	, m_inputDevice(0)
	, m_request(new Request::Backend())
	{
		QTimer::singleShot(0, this, SLOT(start()));
	}

	void RequestWorker::copyPostData()
	{
		const QByteArray data = m_device->readAll();
		// eww
		m_inputDevice->appendData(data);
		m_request.backend()->appendContent(data);
	}

	RequestWorker::~RequestWorker()
	{
	}

	void RequestWorker::start()
	{
		connect(
			m_device,
			SIGNAL(readyRead()),
			SLOT(copyPostData())
		);
		m_inputDevice = new InputDevice(this);
		// FIXME
		// - Request::Backend expects QString/QString headers
		// - ClientIODevice expects QByteArray/QByteArray headers
		//
		// Want to switch to QByteArray, but API compatibility?
		const ClientIODevice::HeaderMap byteHeaders = m_device->requestHeaders();
		QHash<QString, QString> stringHeaders;
		for(
			ClientIODevice::HeaderMap::ConstIterator it = byteHeaders.constBegin();
			it != byteHeaders.constEnd();
			++it
		)
		{
			stringHeaders.insertMulti(
				QString::fromUtf8(it.key()),
				QString::fromUtf8(it.value())
			);
		}
		m_request.backend()->addServerData(stringHeaders);
		// Must be done after the server data is added, otherwise confusion happens due to
		// CONTENT_LENGTH header not being set.
		copyPostData();

		Responder* responder = (*m_generator)(
			m_request,
			new OutputDevice(m_device),
			m_inputDevice,
			this
		);

		connect(
			responder,
			SIGNAL(finished(Responder*)),
			SLOT(cleanupResponder(Responder*))
		);

		responder->start();
	}

	void RequestWorker::cleanupResponder(Responder* responder)
	{
		delete responder;
		delete m_device;
		emit finished(thread());
		deleteLater();
	}
};
<commit_msg>comment clarification<commit_after>/* LICENSE NOTICE
	Copyright (c) 2009, Frederick Emmott <mail@fredemmott.co.uk>

	Permission to use, copy, modify, and/or distribute this software for any
	purpose with or without fee is hereby granted, provided that the above
	copyright notice and this permission notice appear in all copies.

	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "RequestWorker.h"

#include "InputDevice.h"
#include "OutputDevice.h"
#include "Request_Backend.h"

#include <QDebug>
#include <QTimer>

namespace FastCgiQt
{
	RequestWorker::RequestWorker(ClientIODevice* device, Responder::Generator generator, QObject* parent)
	: Worker(parent)
	, m_device(device)
	, m_generator(generator)
	, m_inputDevice(0)
	, m_request(new Request::Backend())
	{
		QTimer::singleShot(0, this, SLOT(start()));
	}

	void RequestWorker::copyPostData()
	{
		const QByteArray data = m_device->readAll();
		// eww
		m_inputDevice->appendData(data);
		m_request.backend()->appendContent(data);
	}

	RequestWorker::~RequestWorker()
	{
	}

	void RequestWorker::start()
	{
		connect(
			m_device,
			SIGNAL(readyRead()),
			SLOT(copyPostData())
		);
		m_inputDevice = new InputDevice(this);
		// @todo Completely switchover to QByteArray headers
		// - Request::Backend expects QString/QString headers
		// - ClientIODevice expects QByteArray/QByteArray headers
		//
		// Want to switch to QByteArray, but API compatibility?
		const ClientIODevice::HeaderMap byteHeaders = m_device->requestHeaders();
		QHash<QString, QString> stringHeaders;
		for(
			ClientIODevice::HeaderMap::ConstIterator it = byteHeaders.constBegin();
			it != byteHeaders.constEnd();
			++it
		)
		{
			stringHeaders.insertMulti(
				QString::fromUtf8(it.key()),
				QString::fromUtf8(it.value())
			);
		}
		m_request.backend()->addServerData(stringHeaders);
		// Must be done after the server data is added, otherwise confusion happens due to
		// CONTENT_LENGTH header not being set.
		copyPostData();

		Responder* responder = (*m_generator)(
			m_request,
			new OutputDevice(m_device),
			m_inputDevice,
			this
		);

		connect(
			responder,
			SIGNAL(finished(Responder*)),
			SLOT(cleanupResponder(Responder*))
		);

		responder->start();
	}

	void RequestWorker::cleanupResponder(Responder* responder)
	{
		delete responder;
		delete m_device;
		emit finished(thread());
		deleteLater();
	}
};
<|endoftext|>


<commit_before>/*
 * reference_resolver.cpp
 *
 *  Created on: Oct 3, 2016
 *      Author: zmij
 */

#include <wire/core/detail/reference_resolver.hpp>
#include <wire/core/detail/configuration_options.hpp>
#include <wire/core/connector.hpp>
#include <wire/core/reference.hpp>
#include <wire/core/proxy.hpp>
#include <wire/core/locator.hpp>

#include <wire/util/make_unique.hpp>
#include <wire/util/timed_cache.hpp>

#include <tbb/concurrent_hash_map.h>
#include <mutex>
#if DEBUG_OUTPUT >= 1
#include <iostream>
#endif

namespace wire {
namespace core {
namespace detail {

struct reference_resolver::impl {
    using mutex_type                = ::std::mutex;
    using lock_guard                = ::std::lock_guard<mutex_type>;
    using endpoint_rotation_type    = endpoint_rotation< endpoint_list >;
    using endpoint_rotation_ptr     = ::std::shared_ptr< endpoint_rotation_type >;
    using endpoint_cache_item       = util::timed_cache< endpoint_rotation_type, 10 >;
    using hash_value_type           = ::std::size_t;
    using endpoint_cache            = ::tbb::concurrent_hash_map<hash_value_type, endpoint_cache_item>;
    using endpoint_accessor         = endpoint_cache::accessor;
    using endpoint_const_accessor   = endpoint_cache::const_accessor;
    using shared_count              = ::std::shared_ptr< ::std::size_t >;

    connector_weak_ptr          connector_;

    mutex_type                  locator_mtx_;

    reference_data              locator_ref_;

    locator_prx                 locator_;
    locator_registry_prx        locator_reg_;

    endpoint_cache              endpoints_;

    void
    set_owner(connector_ptr c)
    {
        connector_ = c;
        auto const& options = c->options();
        locator_ref_ = options.locator_ref;
    }

    connector_ptr
    get_connector()
    {
        auto cnctr = connector_.lock();
        if (!cnctr) {
            throw errors::connector_destroyed{"Connector was already destroyed"};
        }
        return cnctr;
    }

    void
    get_locator_async(functional::callback< locator_prx >   result,
            functional::exception_callback                  exception,
            context_type const&                             ctx,
            invocation_options const&                       opts)
    {
        using functional::report_exception;

        auto cnctr = get_connector();
        locator_prx loc;
        {
            lock_guard lock{locator_mtx_};
            loc = locator_;
        }

        if (locator_ref_.object_id.empty()) {
            auto const& options = cnctr->options();
            locator_ref_ = options.locator_ref;
        }

        if (!loc && !locator_ref_.object_id.empty()) {
            #if DEBUG_OUTPUT >= 1
            ::std::cerr <<::getpid() << " Connecting to locator "
                    << locator_ref_ << "\n";
            #endif
            try {
                if (locator_ref_.endpoints.empty())
                    throw errors::runtime_error{ "Locator reference must have endpoints" };
                object_prx prx = cnctr->make_proxy(locator_ref_);
                checked_cast_async< locator_proxy >(
                    prx,
                    [this, result](locator_prx loc)
                    {
                        if (loc) {
                            set_locator(loc);
                        }
                        try {
                            result(loc);
                        } catch (...) {}
                    },
                    [exception](::std::exception_ptr ex)
                    {
                        #if DEBUG_OUTPUT >= 1
                        ::std::cerr <<::getpid() << " Exception in checked_cast<locator_proxy>\n";
                        #endif
                        report_exception(exception, ex);
                    }, nullptr, ctx, opts
                );
            } catch (...) {
                report_exception(exception, ::std::current_exception());
            }
        } else {
            try {
                result(loc);
            } catch (...) {}
        }
    }

    void
    get_locator_registry_async(
            functional::callback< locator_registry_prx >    result,
            functional::exception_callback                  exception,
            context_type const&                             ctx,
            invocation_options const&                       opts)
    {
        using functional::report_exception;
        locator_registry_prx reg;
        {
            lock_guard lock{locator_mtx_};
            reg = locator_reg_;
        }
        if (!reg) {
            get_locator_async(
                [this, result, exception, ctx, opts](locator_prx loc)
                {
                    if (loc) {
                        #if DEBUG_OUTPUT >= 1
                        ::std::cerr <<::getpid() << " Try to obtain locator registry proxy from locator "
                                << *loc << "\n";
                        #endif
                        loc->get_registry_async(
                            [this, result](locator_registry_prx reg)
                            {
                                if (reg) {
                                    lock_guard lock{locator_mtx_};
                                    locator_reg_ = reg;
                                }
                                try {
                                    result(locator_reg_);
                                } catch (...) {}
                            }, exception, nullptr, ctx, opts);
                    } else {
                        try {
                            result(locator_reg_);
                        } catch (...) {}
                    }
                },
                #if DEBUG_OUTPUT >= 1
                [exception](::std::exception_ptr ex)
                {
                    ::std::cerr <<::getpid() << " Exception in get_locator_registry\n";
                    report_exception(exception, ex);
                },
                #else
                exception,
                #endif
                ctx, opts);
        } else {
            try {
                result(reg);
            } catch (...) {}
        }
    }

    void
    set_locator(locator_prx loc)
    {
        lock_guard lock{locator_mtx_};
        locator_ref_    = loc->wire_get_reference()->data();
        locator_        = loc;
    }

    // TODO Make the member static and pass connector_ptr as argument
    static void
    get_connection(
            connector_ptr                       connector,
            endpoint_rotation_ptr               ep_rot,
            functional::callback<connection_ptr> result,
            functional::exception_callback      exception,
            shared_count                        retries,
            invocation_options const&           opts)
    {
        using functional::report_exception;
        if (!connector) {
            report_exception(exception,
                    errors::connector_destroyed{"Connector was already destroyed"});
        }

        ++(*retries);
        connector->get_outgoing_connection_async(
                ep_rot->next(), result,
                [connector, ep_rot, result, exception, retries, opts](::std::exception_ptr ex)
                {
                    // Check retry count
                    if (*retries < ep_rot->size()) {
                        // Rethrow the exception
                        try {
                            ::std::rethrow_exception(ex);
                        } catch (errors::connection_refused const& ex) {
                            // Retry if the exception is connection_refused
                            // and retry count doesn't exceed retry count
                            get_connection(connector, ep_rot, result,
                                    exception, retries, opts);
                        } catch (...) {
                            // Other exceptions are not retryable at this level
                            report_exception(exception, ::std::current_exception());
                        }
                    } else {
                        // Out of endpoint variants
                        report_exception(exception, ex);
                    }
                },
                opts);
    }
    void
    get_connection(endpoint_rotation_ptr            ep_rot,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts)

    {
        auto retries = ::std::make_shared<::std::size_t>(0);
        get_connection(connector_.lock(), ep_rot, result, exception, retries, opts);
    }
    bool
    get_connection(endpoint_const_accessor const&   eps,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts )
    {
        if (!eps.empty() && eps->second && !eps->second.stale()) {
            endpoint_rotation_ptr ep_rot = eps->second;
            if (!ep_rot)
                return false;
            auto retries = ::std::make_shared<::std::size_t>(0);
            get_connection(eps->second, result, exception, opts);
            return true;
        }
        return false;
    }

    void
    cache_store(hash_value_type key, endpoint_rotation_ptr const& eps)
    {
        endpoint_accessor f;
        endpoints_.insert(f, key);
        f->second = eps;
    }
    void
    cache_store(reference_data const& ref, endpoint_rotation_ptr const& eps)
    {
        cache_store(hash(*eps), eps);
        cache_store(id_facet_hash(ref), eps);
        if (ref.adapter.is_initialized()) {
            cache_store(hash(ref.adapter.get()), eps);
        }
    }

    bool
    cache_lookup(reference_data const&              ref,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts)
    {
        endpoint_accessor eps;
        if (!ref.endpoints.empty()) {
            // Fixed endpoints list, return one
            auto eps_hash = hash(ref.endpoints);

            if (endpoints_.insert(eps, eps_hash) || !eps->second) {
                eps->second = endpoint_cache_item{ ref.endpoints };
            }
            return get_connection(eps, result, exception, opts);
        }

        // Find endpoints by object_id
        auto id_hash = id_facet_hash(ref);
        if (endpoints_.find(eps, id_hash) && get_connection(eps, result, exception, opts)) {
            return true;
        }

        if (ref.adapter.is_initialized()) {
            // Find endpoints by adapter id
            auto adapter_hash = hash(ref.adapter.get());
            if (endpoints_.find(eps, adapter_hash)
                    && get_connection(eps, result, exception, opts)) {
                // Cache to object id
                endpoint_accessor oid_eps;
                endpoints_.insert(oid_eps, id_hash);
                oid_eps->second = eps->second;
                return true;
            }
        }
        return false;
    }

    void
    resolve_reference_async(reference_data const&   ref,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts
        )
    {
        using functional::report_exception;
        if (!cache_lookup(ref, result, exception, opts)) {
            // Don't check endpoints - they are checked by lookup function
            get_locator_async(
            [this, ref, result, exception, opts](locator_prx loc)
            {
                if (!loc) {
                    report_exception( exception, errors::no_locator{} );
                    return;
                }
                if (ref.adapter.is_initialized()) {
                    loc->find_adapter_async(ref.adapter.get(),
                    [this, ref, result, exception, opts](object_prx obj)
                    {
                        auto const& objref = obj->wire_get_reference()->data();
                        auto eps = make_enpoint_rotation(objref);
                        cache_store(ref, eps);
                        get_connection(eps, result, exception, opts);
                    },
                    exception, nullptr, no_context, opts);
                } else {
                    // Lookup by object id
                    loc->find_object_async(ref.object_id,
                    [this, ref, result, exception, opts](object_prx obj)
                    {
                        auto const& objref = obj->wire_get_reference()->data();
                        // Well-known object can have no endpoints initialized, only adapter set
                        if (objref.endpoints.empty()) {
                            // Resolve by adapter, if any
                            if (objref.adapter.is_initialized()) {
                                resolve_reference_async(objref, result, exception, opts);
                            } else {
                                // This is an error situation - locator returned
                                // a proxy containing only object id
                                report_exception( exception, object_not_found{ ref.object_id } );
                            }
                        } else {
                            auto eps = make_enpoint_rotation(objref);
                            cache_store(ref, eps);
                            get_connection(eps, result, exception, opts);
                        }
                    }, exception, nullptr, no_context, opts);
                }
            }, exception, no_context, opts);
        }
    }

    endpoint_rotation_ptr
    make_enpoint_rotation(
            const wire::core::reference_data& objref)
    {
        return ::std::make_shared<endpoint_rotation_type>(objref.endpoints);
    }
};

reference_resolver::reference_resolver()
    : pimpl_{ util::make_unique<impl>() }
{
}

reference_resolver::reference_resolver(connector_ptr cnctr, reference_data loc_ref)
    : pimpl_{ util::make_unique<impl>() }
{
    pimpl_->connector_      = cnctr;
    pimpl_->locator_ref_    = loc_ref;
}

reference_resolver::reference_resolver(reference_resolver&& rhs)
    : pimpl_{ ::std::move(rhs.pimpl_) }
{
}

reference_resolver::~reference_resolver() = default;

void
reference_resolver::set_owner(connector_ptr c)
{
    pimpl_->set_owner(c);
}

void
reference_resolver::set_locator(locator_prx loc)
{
    pimpl_->set_locator(loc);
}

void
reference_resolver::get_locator_async(functional::callback<locator_prx>   result,
        functional::exception_callback      exception,
        context_type const&                 ctx,
        invocation_options const&           opts) const
{
    pimpl_->get_locator_async(result, exception, ctx, opts);
}

void
reference_resolver::get_locator_registry_async(
    functional::callback<locator_registry_prx> result,
    functional::exception_callback      exception,
    context_type const&                 ctx,
    invocation_options const&           opts) const
{
    pimpl_->get_locator_registry_async(result, exception, ctx, opts);
}

void
reference_resolver::resolve_reference_async(reference_data const& ref,
        functional::callback<connection_ptr> result,
        functional::exception_callback      exception,
        invocation_options const&           opts
    ) const
{
    pimpl_->resolve_reference_async(ref, result, exception, opts);
}

} /* namespace detail */
} /* namespace core */
} /* namespace wire */
<commit_msg>Fix reference resolve crash when endpoint data is at the brink of timing out<commit_after>/*
 * reference_resolver.cpp
 *
 *  Created on: Oct 3, 2016
 *      Author: zmij
 */

#include <wire/core/detail/reference_resolver.hpp>
#include <wire/core/detail/configuration_options.hpp>
#include <wire/core/connector.hpp>
#include <wire/core/reference.hpp>
#include <wire/core/proxy.hpp>
#include <wire/core/locator.hpp>

#include <wire/util/make_unique.hpp>
#include <wire/util/timed_cache.hpp>

#include <tbb/concurrent_hash_map.h>
#include <mutex>
#if DEBUG_OUTPUT >= 1
#include <iostream>
#endif

namespace wire {
namespace core {
namespace detail {

struct reference_resolver::impl {
    using mutex_type                = ::std::mutex;
    using lock_guard                = ::std::lock_guard<mutex_type>;
    using endpoint_rotation_type    = endpoint_rotation< endpoint_list >;
    using endpoint_rotation_ptr     = ::std::shared_ptr< endpoint_rotation_type >;
    using endpoint_cache_item       = util::timed_cache< endpoint_rotation_type, 10 >;
    using hash_value_type           = ::std::size_t;
    using endpoint_cache            = ::tbb::concurrent_hash_map<hash_value_type, endpoint_cache_item>;
    using endpoint_accessor         = endpoint_cache::accessor;
    using endpoint_const_accessor   = endpoint_cache::const_accessor;
    using shared_count              = ::std::shared_ptr< ::std::size_t >;

    connector_weak_ptr          connector_;

    mutex_type                  locator_mtx_;

    reference_data              locator_ref_;

    locator_prx                 locator_;
    locator_registry_prx        locator_reg_;

    endpoint_cache              endpoints_;

    void
    set_owner(connector_ptr c)
    {
        connector_ = c;
        auto const& options = c->options();
        locator_ref_ = options.locator_ref;
    }

    connector_ptr
    get_connector()
    {
        auto cnctr = connector_.lock();
        if (!cnctr) {
            throw errors::connector_destroyed{"Connector was already destroyed"};
        }
        return cnctr;
    }

    void
    get_locator_async(functional::callback< locator_prx >   result,
            functional::exception_callback                  exception,
            context_type const&                             ctx,
            invocation_options const&                       opts)
    {
        using functional::report_exception;

        auto cnctr = get_connector();
        locator_prx loc;
        {
            lock_guard lock{locator_mtx_};
            loc = locator_;
        }

        if (locator_ref_.object_id.empty()) {
            auto const& options = cnctr->options();
            locator_ref_ = options.locator_ref;
        }

        if (!loc && !locator_ref_.object_id.empty()) {
            #if DEBUG_OUTPUT >= 1
            ::std::cerr <<::getpid() << " Connecting to locator "
                    << locator_ref_ << "\n";
            #endif
            try {
                if (locator_ref_.endpoints.empty())
                    throw errors::runtime_error{ "Locator reference must have endpoints" };
                object_prx prx = cnctr->make_proxy(locator_ref_);
                checked_cast_async< locator_proxy >(
                    prx,
                    [this, result](locator_prx loc)
                    {
                        if (loc) {
                            set_locator(loc);
                        }
                        try {
                            result(loc);
                        } catch (...) {}
                    },
                    [exception](::std::exception_ptr ex)
                    {
                        #if DEBUG_OUTPUT >= 1
                        ::std::cerr <<::getpid() << " Exception in checked_cast<locator_proxy>\n";
                        #endif
                        report_exception(exception, ex);
                    }, nullptr, ctx, opts
                );
            } catch (...) {
                report_exception(exception, ::std::current_exception());
            }
        } else {
            try {
                result(loc);
            } catch (...) {}
        }
    }

    void
    get_locator_registry_async(
            functional::callback< locator_registry_prx >    result,
            functional::exception_callback                  exception,
            context_type const&                             ctx,
            invocation_options const&                       opts)
    {
        using functional::report_exception;
        locator_registry_prx reg;
        {
            lock_guard lock{locator_mtx_};
            reg = locator_reg_;
        }
        if (!reg) {
            get_locator_async(
                [this, result, exception, ctx, opts](locator_prx loc)
                {
                    if (loc) {
                        #if DEBUG_OUTPUT >= 1
                        ::std::cerr <<::getpid() << " Try to obtain locator registry proxy from locator "
                                << *loc << "\n";
                        #endif
                        loc->get_registry_async(
                            [this, result](locator_registry_prx reg)
                            {
                                if (reg) {
                                    lock_guard lock{locator_mtx_};
                                    locator_reg_ = reg;
                                }
                                try {
                                    result(locator_reg_);
                                } catch (...) {}
                            }, exception, nullptr, ctx, opts);
                    } else {
                        try {
                            result(locator_reg_);
                        } catch (...) {}
                    }
                },
                #if DEBUG_OUTPUT >= 1
                [exception](::std::exception_ptr ex)
                {
                    ::std::cerr <<::getpid() << " Exception in get_locator_registry\n";
                    report_exception(exception, ex);
                },
                #else
                exception,
                #endif
                ctx, opts);
        } else {
            try {
                result(reg);
            } catch (...) {}
        }
    }

    void
    set_locator(locator_prx loc)
    {
        lock_guard lock{locator_mtx_};
        locator_ref_    = loc->wire_get_reference()->data();
        locator_        = loc;
    }

    // TODO Make the member static and pass connector_ptr as argument
    static void
    get_connection(
            connector_ptr                       connector,
            endpoint_rotation_ptr               ep_rot,
            functional::callback<connection_ptr> result,
            functional::exception_callback      exception,
            shared_count                        retries,
            invocation_options const&           opts)
    {
        using functional::report_exception;
        if (!connector) {
            report_exception(exception,
                    errors::connector_destroyed{"Connector was already destroyed"});
        }

        ++(*retries);
        connector->get_outgoing_connection_async(
                ep_rot->next(), result,
                [connector, ep_rot, result, exception, retries, opts](::std::exception_ptr ex)
                {
                    // Check retry count
                    if (*retries < ep_rot->size()) {
                        // Rethrow the exception
                        try {
                            ::std::rethrow_exception(ex);
                        } catch (errors::connection_refused const& ex) {
                            // Retry if the exception is connection_refused
                            // and retry count doesn't exceed retry count
                            get_connection(connector, ep_rot, result,
                                    exception, retries, opts);
                        } catch (...) {
                            // Other exceptions are not retryable at this level
                            report_exception(exception, ::std::current_exception());
                        }
                    } else {
                        // Out of endpoint variants
                        report_exception(exception, ex);
                    }
                },
                opts);
    }
    void
    get_connection(endpoint_rotation_ptr            ep_rot,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts)

    {
        auto retries = ::std::make_shared<::std::size_t>(0);
        get_connection(connector_.lock(), ep_rot, result, exception, retries, opts);
    }
    bool
    get_connection(endpoint_const_accessor const&   eps,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts )
    {
        if (!eps.empty() && eps->second && !eps->second.stale()) {
            endpoint_rotation_ptr ep_rot = eps->second;
            if (!ep_rot)
                return false;
            auto retries = ::std::make_shared<::std::size_t>(0);
            get_connection(ep_rot, result, exception, opts);
            return true;
        }
        return false;
    }

    void
    cache_store(hash_value_type key, endpoint_rotation_ptr const& eps)
    {
        endpoint_accessor f;
        endpoints_.insert(f, key);
        f->second = eps;
    }
    void
    cache_store(reference_data const& ref, endpoint_rotation_ptr const& eps)
    {
        cache_store(hash(*eps), eps);
        cache_store(id_facet_hash(ref), eps);
        if (ref.adapter.is_initialized()) {
            cache_store(hash(ref.adapter.get()), eps);
        }
    }

    bool
    cache_lookup(reference_data const&              ref,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts)
    {
        endpoint_accessor eps;
        if (!ref.endpoints.empty()) {
            // Fixed endpoints list, return one
            auto eps_hash = hash(ref.endpoints);

            if (endpoints_.insert(eps, eps_hash) || !eps->second) {
                eps->second = endpoint_cache_item{ ref.endpoints };
            }
            return get_connection(eps, result, exception, opts);
        }

        // Find endpoints by object_id
        auto id_hash = id_facet_hash(ref);
        if (endpoints_.find(eps, id_hash) && get_connection(eps, result, exception, opts)) {
            return true;
        }

        if (ref.adapter.is_initialized()) {
            // Find endpoints by adapter id
            auto adapter_hash = hash(ref.adapter.get());
            if (endpoints_.find(eps, adapter_hash)
                    && get_connection(eps, result, exception, opts)) {
                // Cache to object id
                endpoint_accessor oid_eps;
                endpoints_.insert(oid_eps, id_hash);
                oid_eps->second = eps->second;
                return true;
            }
        }
        return false;
    }

    void
    resolve_reference_async(reference_data const&   ref,
            functional::callback<connection_ptr>    result,
            functional::exception_callback          exception,
            invocation_options const&               opts
        )
    {
        using functional::report_exception;
        if (!cache_lookup(ref, result, exception, opts)) {
            // Don't check endpoints - they are checked by lookup function
            get_locator_async(
            [this, ref, result, exception, opts](locator_prx loc)
            {
                if (!loc) {
                    report_exception( exception, errors::no_locator{} );
                    return;
                }
                if (ref.adapter.is_initialized()) {
                    loc->find_adapter_async(ref.adapter.get(),
                    [this, ref, result, exception, opts](object_prx obj)
                    {
                        auto const& objref = obj->wire_get_reference()->data();
                        auto eps = make_enpoint_rotation(objref);
                        cache_store(ref, eps);
                        get_connection(eps, result, exception, opts);
                    },
                    exception, nullptr, no_context, opts);
                } else {
                    // Lookup by object id
                    loc->find_object_async(ref.object_id,
                    [this, ref, result, exception, opts](object_prx obj)
                    {
                        auto const& objref = obj->wire_get_reference()->data();
                        // Well-known object can have no endpoints initialized, only adapter set
                        if (objref.endpoints.empty()) {
                            // Resolve by adapter, if any
                            if (objref.adapter.is_initialized()) {
                                resolve_reference_async(objref, result, exception, opts);
                            } else {
                                // This is an error situation - locator returned
                                // a proxy containing only object id
                                report_exception( exception, object_not_found{ ref.object_id } );
                            }
                        } else {
                            auto eps = make_enpoint_rotation(objref);
                            cache_store(ref, eps);
                            get_connection(eps, result, exception, opts);
                        }
                    }, exception, nullptr, no_context, opts);
                }
            }, exception, no_context, opts);
        }
    }

    endpoint_rotation_ptr
    make_enpoint_rotation(
            const wire::core::reference_data& objref)
    {
        return ::std::make_shared<endpoint_rotation_type>(objref.endpoints);
    }
};

reference_resolver::reference_resolver()
    : pimpl_{ util::make_unique<impl>() }
{
}

reference_resolver::reference_resolver(connector_ptr cnctr, reference_data loc_ref)
    : pimpl_{ util::make_unique<impl>() }
{
    pimpl_->connector_      = cnctr;
    pimpl_->locator_ref_    = loc_ref;
}

reference_resolver::reference_resolver(reference_resolver&& rhs)
    : pimpl_{ ::std::move(rhs.pimpl_) }
{
}

reference_resolver::~reference_resolver() = default;

void
reference_resolver::set_owner(connector_ptr c)
{
    pimpl_->set_owner(c);
}

void
reference_resolver::set_locator(locator_prx loc)
{
    pimpl_->set_locator(loc);
}

void
reference_resolver::get_locator_async(functional::callback<locator_prx>   result,
        functional::exception_callback      exception,
        context_type const&                 ctx,
        invocation_options const&           opts) const
{
    pimpl_->get_locator_async(result, exception, ctx, opts);
}

void
reference_resolver::get_locator_registry_async(
    functional::callback<locator_registry_prx> result,
    functional::exception_callback      exception,
    context_type const&                 ctx,
    invocation_options const&           opts) const
{
    pimpl_->get_locator_registry_async(result, exception, ctx, opts);
}

void
reference_resolver::resolve_reference_async(reference_data const& ref,
        functional::callback<connection_ptr> result,
        functional::exception_callback      exception,
        invocation_options const&           opts
    ) const
{
    pimpl_->resolve_reference_async(ref, result, exception, opts);
}

} /* namespace detail */
} /* namespace core */
} /* namespace wire */
<|endoftext|>


<commit_before>// Copyright (c) 2015 kamyu. All rights reserved.

#include <iostream>
#include <unordered_set>
#include <thread>
#include <memory>
#include <mutex>
#include <chrono>
#include <cassert>

using std::cout;
using std::endl;
using std::unordered_set;
using std::thread;
using std::shared_ptr;
using std::enable_shared_from_this;
using std::mutex;
using std::lock_guard;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;

class Request;
typedef shared_ptr<Request> RequestPtr;

class Inventory {
 public:
    Inventory() : requests_(new RequestList) {}

    // Copy on write.
    void add(const RequestPtr& req) {
        lock_guard<mutex> lock{mtx_};
        if (!requests_.unique()) {
            requests_.reset(new RequestList(*requests_));
            cout << "Inventory::add() copy on write" << endl;
        }
        assert(requests_.unique());
        requests_->insert(req);
    }

    // Copy on write.
    void remove(const RequestPtr& req) {
        lock_guard<mutex> lock(mtx_);
        if (!requests_.unique()) {
            requests_.reset(new RequestList(*requests_));
            cout << "Inventory::remove() copy on write" << endl;
        }
        assert(requests_.unique());
        requests_->erase(req);
    }

    void printAll() const;

 private:
    typedef unordered_set<RequestPtr> RequestList;
    typedef shared_ptr<RequestList> RequestListPtr;

    RequestListPtr getData() const {  // 0.5s in timeline.
        lock_guard<mutex> lock{mtx_};
        return requests_;
    }

    mutable mutex mtx_;  // mutable is due to const method.
    RequestListPtr requests_;
};

class Request : public enable_shared_from_this<Request> {
 public:
    explicit Request(Inventory *inventory) : val_(0), inventory_(*inventory) {}

    ~Request() {
        val_ = -1;
    }

    void process() {
        lock_guard<mutex> lock(mtx_);
        cout << "Request::process()" << endl;  // 0s in timeline.
        inventory_.add(shared_from_this());
        // ...
    }

    void cancel() {
        lock_guard<mutex> lock{mtx_};
        val_ = 1;
        sleep_for(milliseconds(1000));
        cout << "Request::cancel()" << endl;  // 1s in timeline.
        inventory_.remove(shared_from_this());
        // ...
    }

    void print() const {  // 1.5s in timeline.
        lock_guard<mutex> lock{mtx_};
        cout << "Request::print() " << this << ", val_: " << val_ << endl;
        // ...
    }

 private:
    mutable mutex mtx_;  // mutable is due to const method.
    int val_;
    Inventory& inventory_;
};

void Inventory::printAll() const {
    RequestListPtr requests = getData();  // Copy a list.
    cout << "Inventory::printAll()" << endl;
    sleep_for(milliseconds(1000));
    for (auto it = requests->cbegin(); it != requests->cend(); ++it) {
        (*it)->print();  // Every request ptr is shared_ptr.
    }
}

void threadFunc(Inventory *inventory) {
    RequestPtr req{new Request{inventory}};
    req->process();
    req->cancel();
}

int main() {
    Inventory inventory;
    thread thrd(threadFunc, &inventory);

    sleep_for(milliseconds(500));
    inventory.printAll();

    thrd.join();

    return 0;
}
<commit_msg>update<commit_after>// Copyright (c) 2015 kamyu. All rights reserved.

#include <iostream>
#include <unordered_set>
#include <thread>
#include <memory>
#include <mutex>
#include <chrono>
#include <cassert>

using std::cout;
using std::endl;
using std::unordered_set;
using std::thread;
using std::shared_ptr;
using std::enable_shared_from_this;
using std::mutex;
using std::lock_guard;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;

class Request;
typedef shared_ptr<Request> RequestPtr;

class Inventory {
 public:
    Inventory() : requests_(new RequestList) {}

    // Copy on write.
    void add(const RequestPtr& req) {
        lock_guard<mutex> lock{mtx_};
        if (!requests_.unique()) {
            requests_.reset(new RequestList(*requests_));
            cout << "Inventory::add() copy on write" << endl;
        }
        assert(requests_.unique());
        requests_->emplace(req);
    }

    // Copy on write.
    void remove(const RequestPtr& req) {
        lock_guard<mutex> lock(mtx_);
        if (!requests_.unique()) {
            requests_.reset(new RequestList(*requests_));
            cout << "Inventory::remove() copy on write" << endl;
        }
        assert(requests_.unique());
        requests_->erase(req);
    }

    void printAll() const;

 private:
    typedef unordered_set<RequestPtr> RequestList;
    typedef shared_ptr<RequestList> RequestListPtr;

    RequestListPtr getData() const {  // 0.5s in timeline.
        lock_guard<mutex> lock{mtx_};
        return requests_;
    }

    mutable mutex mtx_;  // mutable is due to const method.
    RequestListPtr requests_;
};

class Request : public enable_shared_from_this<Request> {
 public:
    explicit Request(Inventory *inventory) : val_(0), inventory_(*inventory) {}

    ~Request() {
        val_ = -1;
    }

    void process() {
        lock_guard<mutex> lock(mtx_);
        cout << "Request::process()" << endl;  // 0s in timeline.
        inventory_.add(shared_from_this());
        // ...
    }

    void cancel() {
        lock_guard<mutex> lock{mtx_};
        val_ = 1;
        sleep_for(milliseconds(1000));
        cout << "Request::cancel()" << endl;  // 1s in timeline.
        inventory_.remove(shared_from_this());
        // ...
    }

    void print() const {  // 1.5s in timeline.
        lock_guard<mutex> lock{mtx_};
        cout << "Request::print() " << this << ", val_: " << val_ << endl;
        // ...
    }

 private:
    mutable mutex mtx_;  // mutable is due to const method.
    int val_;
    Inventory& inventory_;
};

void Inventory::printAll() const {
    RequestListPtr requests = getData();  // Copy a list.
    cout << "Inventory::printAll()" << endl;
    sleep_for(milliseconds(1000));
    for (auto it = requests->cbegin(); it != requests->cend(); ++it) {
        (*it)->print();  // Every request ptr is shared_ptr.
    }
}

void threadFunc(Inventory *inventory) {
    RequestPtr req{new Request{inventory}};
    req->process();
    req->cancel();
}

int main() {
    Inventory inventory;
    thread thrd(threadFunc, &inventory);

    sleep_for(milliseconds(500));
    inventory.printAll();

    thrd.join();

    return 0;
}
<|endoftext|>


<commit_before>/* -*-c++-*- */
/**
* OsgAL - OpenSceneGraph Audio Library
* Copyright (C) 2004 VRlab, Ume University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/

#include "openalpp/Sample"
#include <sstream>
#include <alut.h>

using namespace openalpp;

  Sample::Sample(const std::string& filename) throw (FileError)
  : SoundData(),filename_(filename) {
  ALuint success = alutCreateBufferFromFile (filename.c_str());

  if(success!=AL_NONE) {
    buffer_ = new SoundBuffer(success);

  } else {
    ALenum error = alutGetError ();
    const char *error_str = alutGetErrorString (error);
    std::ostringstream str;
    str << "Error loading file: " << filename << ": " << error_str << std::ends;
    throw FileError(str.str().c_str());
  }
}

Sample::Sample(const Sample &sample)
  : SoundData(sample), filename_(sample.filename_) {
}

Sample::Sample(ALenum format,ALvoid* data,ALsizei size,ALsizei freq) throw (FileError)
   : SoundData() {
   ALenum error;

   alBufferData(buffer_->getName(),format,data,size,freq);
   if((error=alGetError())!=AL_FALSE)
     throw FileError("Error buffering sound");
 }

std::string Sample::getFileName() const {
  return filename_;
}

Sample::~Sample()
{

}

Sample &Sample::operator=(const Sample &sample) {
  if(this!=&sample) {
    SoundData::operator=(sample);
    filename_=sample.filename_;
  }
  return *this;
}

// A couple of utility functions. Might move them to their own file later...
unsigned int openalpp::sampleSize(SampleFormat format) {
  switch(format) {
    case(Mono8):
      return 1;
      break;
    case(Stereo8):
    case(Mono16):
      return 2;
      break;
    case(Stereo16):
      return 4;
      break;
    default:
      return 0;
  }
}

unsigned int openalpp::sampleSize(ALenum format) {
  switch(format) {
    case(AL_FORMAT_MONO8):
      return 1;
      break;
    case(AL_FORMAT_STEREO8):
    case(AL_FORMAT_MONO16):
      return 2;
      break;
    case(AL_FORMAT_STEREO16):
      return 4;
      break;
    default:
      return 0;
  }
}

<commit_msg>GNU/Linux specific includes<commit_after>/* -*-c++-*- */
/**
* OsgAL - OpenSceneGraph Audio Library
* Copyright (C) 2004 VRlab, Ume University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/

#include "openalpp/Sample"
#include <sstream>
#ifdef _WIN32
#include <alut.h>
#else // _WIN32
#include <AL/alut.h>
#endif // _WIN32

using namespace openalpp;

  Sample::Sample(const std::string& filename) throw (FileError)
  : SoundData(),filename_(filename) {
#if OPENAL_VERSION < 2007
  ALsizei size;

	ALsizei freq;
#  if OPENAL_VERSION < 2005
  ALsizei bits;
  ALboolean success;
#  endif // OPENAL_VERSION < 2005
  ALenum format,error;
  ALvoid* data;
  ALboolean loop;

#  if OPENAL_VERSION < 2005
  success=alutLoadWAV(filename.c_str(),&data,&format,&size,&bits,&freq);
  if(success==AL_TRUE && data ) {
#  else // OPENAL_VERSION < 2005
  alutLoadWAVFile(const_cast<ALbyte *>(filename.c_str()),&format,&data,&size,&freq,&loop);
  if(data) {
#  endif // OPENAL_VERSION < 2005
    alBufferData(buffer_->buffername_,format,data,size,freq);
    if((error=alGetError())!=AL_FALSE)
      throw FileError("Error buffering sound");
    free(data);
  } else {
    std::ostringstream str;
    str << "Unable to load file: " << filename << std::ends;
    throw FileError(str.str().c_str());
  }
#else // OPENAL_VERSION < 2007
  ALuint success = alutCreateBufferFromFile (filename.c_str());

  if(success!=AL_NONE) {
    buffer_ = new SoundBuffer(success);

  } else {
    ALenum error = alutGetError ();
    const char *error_str = alutGetErrorString (error);
    std::ostringstream str;
    str << "Error loading file: " << filename << ": " << error_str << std::ends;
    throw FileError(str.str().c_str());
  }
#endif // OPENAL_VERSION < 2007
}

Sample::Sample(const Sample &sample)
  : SoundData(sample), filename_(sample.filename_) {
}

Sample::Sample(ALenum format,ALvoid* data,ALsizei size,ALsizei freq) throw (FileError)
   : SoundData() {
   ALenum error;

   alBufferData(buffer_->getName(),format,data,size,freq);
   if((error=alGetError())!=AL_FALSE)
     throw FileError("Error buffering sound");
 }

std::string Sample::getFileName() const {
  return filename_;
}

Sample::~Sample()
{

}

Sample &Sample::operator=(const Sample &sample) {
  if(this!=&sample) {
    SoundData::operator=(sample);
    filename_=sample.filename_;
  }
  return *this;
}

// A couple of utility functions. Might move them to their own file later...
unsigned int openalpp::sampleSize(SampleFormat format) {
  switch(format) {
    case(Mono8):
      return 1;
      break;
    case(Stereo8):
    case(Mono16):
      return 2;
      break;
    case(Stereo16):
      return 4;
      break;
    default:
      return 0;
  }
}

unsigned int openalpp::sampleSize(ALenum format) {
  switch(format) {
    case(AL_FORMAT_MONO8):
      return 1;
      break;
    case(AL_FORMAT_STEREO8):
    case(AL_FORMAT_MONO16):
      return 2;
      break;
    case(AL_FORMAT_STEREO16):
      return 4;
      break;
    default:
      return 0;
  }
}

<|endoftext|>