<commit_before>//$Id$

#include <streambuf>
#include <sstream>
#include <cstdlib>

#include <iostream>
#include <vector>
#include "zmq.hpp"
#include <map>
#include <chrono>

#include "Messages.h"
#include "MessageUtilities.h"

#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#else
#include <unistd.h>
#include <signal.h>
#include <spawn.h>
#include <sys/wait.h>
#endif

using namespace std;
using namespace std::chrono;

extern char **environ;

class WorkerInfo
{
public:
    int numThreads;
#ifdef _WIN32
    PROCESS_INFORMATION pi;
#else
    pid_t pid;
#endif

};

class ServerConfig
{
public:
    int mControlPort = 23300;
    string mControlPortStr = "23300";
    int mMaxNumSlots = 4;
};

ServerConfig gServerConfig;
size_t nTakenSlots=0;
#ifdef _WIN32
zmq::context_t gContext(1, 63);
#else
zmq::context_t gContext(1);
#endif

#define PRINTSERVER "HopsanServer@"+gServerConfig.mControlPortStr+"; "
std::string nowDateTime()
{
    std::time_t now_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    char buff[64];
    std::strftime(buff, sizeof(buff), "%b %d %H:%M:%S", std::localtime(&now_time));
    return string(&buff[0]);
}

bool receiveAckNackMessage(zmq::socket_t &rSocket, long timeout, string &rNackReason)
{
    zmq::message_t response;
    if(receiveWithTimeout(rSocket, timeout, response))
    {
        size_t offset=0;
        bool parseOK;
        size_t id = getMessageId(response, offset, parseOK);
        //cout << "id: " << id << endl;
        if (id == Ack)
        {
            return true;
        }
        else if (id == NotAck)
        {
            rNackReason = unpackMessage<std::string>(response, offset, parseOK);
        }
        else if (!parseOK)
        {
            rNackReason = "Exception in msgpack:unpack!";
        }
        else
        {
            rNackReason = "Got neither Ack nor Nack";
        }
    }
    else
    {
        rNackReason = "Got either timeout or exception in zmq::recv()";
    }
    return false;
}

void reportToAddressServer(std::string addressIP, std::string addressPort, std::string myIP, std::string myPort, std::string myDescription, bool isOnline)
{
    try
    {
        zmq::socket_t addressServerSocket (gContext, ZMQ_REQ);
        int linger_ms = 1000;
        addressServerSocket.setsockopt(ZMQ_LINGER, &linger_ms, sizeof(int));
        addressServerSocket.connect(makeZMQAddress(addressIP, addressPort).c_str());

        //! @todo check if ok
        infomsg_Available_t message;
        message.ip = myIP;
        message.port = myPort;
        message.description = myDescription;
        message.numTotalSlots = gServerConfig.mMaxNumSlots;
        message.identity = 0; // Identity should allways be 0 when a server starts, relay servers may set other values

        if (isOnline)
        {
            sendMessage(addressServerSocket, Available, message);
            std::string nackreason;
            bool ack = receiveAckNackMessage(addressServerSocket, 5000, nackreason);
            if (ack)
            {
                cout << PRINTSERVER << nowDateTime() << " Successfully registered in address server" << endl;
            }
            else
            {

                cout << PRINTSERVER << nowDateTime() << " Error: Could not register in master server: " << nackreason << endl;
            }
        }
        else
        {
            sendMessage(addressServerSocket, ServerClosing, message);
            std::string nackreason;
            bool ack = receiveAckNackMessage(addressServerSocket, 5000, nackreason);
            if (ack)
            {
                cout << PRINTSERVER << nowDateTime() << " Successfully unregistered in address server" << endl;
            }
            else
            {
                cout << PRINTSERVER << nowDateTime() << " Error: Could not unregister in master server: " << nackreason << endl;
            }
        }
        addressServerSocket.disconnect(makeZMQAddress(addressIP, addressPort).c_str());
    }
    catch(zmq::error_t e)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: Preparing addressServerSocket: " << e.what() << endl;
    }
}

static int s_interrupted = 0;
#ifdef _WIN32
BOOL WINAPI consoleCtrlHandler( DWORD dwCtrlType )
{
    // what to do here?
    s_interrupted = 1;
    return TRUE;
}
#else
static void s_signal_handler(int signal_value)
{
    s_interrupted = 1;
}

static void s_catch_signals(void)
{
    struct sigaction action;
    action.sa_handler = s_signal_handler;
    action.sa_flags = 0;
    sigemptyset (&action.sa_mask);
    sigaction (SIGINT, &action, NULL);
    sigaction (SIGTERM, &action, NULL);
}
#endif

map<int, WorkerInfo> workerMap;

int main(int argc, char* argv[])
{
    if (argc < 3)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: you must specify what number of threads to use and what base port to use!" << endl;
        return 1;
    }

    string numThreadsToUse = argv[1];
    gServerConfig.mMaxNumSlots = atoi(numThreadsToUse.c_str());

    string myPort = argv[2];
    gServerConfig.mControlPortStr = myPort;
    gServerConfig.mControlPort = atoi(myPort.c_str());

    std::string masterserverip, masterserverport;
    steady_clock::time_point lastStatusRequestTime;
    if (argc >= 5)
    {
        masterserverip = argv[3];
        masterserverport = argv[4];
    }

    string myExternalIP = "127.0.0.1";
    if (argc >= 6)
    {
        myExternalIP = argv[5];
    }

    string myDescription;
    if (argc >= 7)
    {
        myDescription = argv[6];
    }

    cout << PRINTSERVER << nowDateTime() << " Starting with: " << gServerConfig.mMaxNumSlots << " slots, Listening on port: " << gServerConfig.mControlPort  << endl;

    // Prepare our context and socket
    try
    {
        zmq::socket_t socket (gContext, ZMQ_REP);
        int linger_ms = 1000;
        socket.setsockopt(ZMQ_LINGER, &linger_ms, sizeof(int));
        socket.bind( makeZMQAddress("*", myPort).c_str() );

        if (!masterserverip.empty())
        {
            //! @todo somehow automatically figure out my ip
            reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, true);
        }

#ifdef _WIN32
        SetConsoleCtrlHandler( consoleCtrlHandler, TRUE );
#else
        s_catch_signals();
#endif
        while (true)
        {
            // Wait for next request from client
            zmq::message_t request;
            if(receiveWithTimeout(socket, 30000, request))
            {
                size_t offset=0;
                bool idParseOK;
                size_t msg_id = getMessageId(request, offset, idParseOK);
                //cout << PRINTSERVER << nowDateTime() << " Received message with length: " << request.size() << " msg_id: " << msg_id << endl;

                if (msg_id == RequestServerSlots)
                {
                    bool parseOK;
                    reqmsg_ReqServerSlots_t msg = unpackMessage<reqmsg_ReqServerSlots_t>(request, offset, parseOK);
                    int requestNumThreads = msg.numThreads;

                    cout << PRINTSERVER << nowDateTime() << " Client is requesting: " << requestNumThreads << " slots... " << endl;
                    if (nTakenSlots+requestNumThreads <= gServerConfig.mMaxNumSlots)
                    {
                        size_t workerPort = gServerConfig.mControlPort+nTakenSlots+1;

                        // Generate unique worker Id
                        int uid = rand();
                        while (workerMap.count(uid) != 0)
                        {
                            uid = rand();
                        }

#ifdef _WIN32
                        PROCESS_INFORMATION processInformation;
                        STARTUPINFO startupInfo;
                        memset(&processInformation, 0, sizeof(processInformation));
                        memset(&startupInfo, 0, sizeof(startupInfo));
                        startupInfo.cb = sizeof(startupInfo);

                        string scport = to_string(gServerConfig.mControlPort);
                        string swport = to_string(workerPort);
                        string nthreads = to_string(requestNumThreads);
                        string uidstr = to_string(uid);

                        std::string appName("HopsanServerWorker.exe");
                        std::string cmdLine("HopsanServerWorker "+uidstr+" "+scport+" "+swport+" "+nthreads);
                        TCHAR tempCmdLine[cmdLine.size()*2];
                        strcpy_s(tempCmdLine, cmdLine.size()*2, cmdLine.c_str());

                        BOOL result = CreateProcess(appName.c_str(), tempCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInformation);
                        if (result == 0)
                        {
                            std::cout << PRINTSERVER << "Error: Failed to launch worker process!"<<endl;
                            sendMessage(socket, "Failed to launch worker process!");
                        }
                        else
                        {
                            std::cout << PRINTSERVER << "Launched Worker Process, pid: "<< processInformation.dwProcessId << " port: " << workerPort << " uid: " << uid << " nThreads: " << requestNumThreads  << endl;
                            workerMap.insert({uid, {requestNumThreads, processInformation}});

                            SM_ReqSlot_Reply_t msg = {workerPort};
                            sendMessage<SM_ReqSlot_Reply_t>(socket, ReplyServerSlots, msg);
                            nTakenSlots++;
                        }

#else
                        char sport_buff[64], wport_buff[64], thread_buff[64], uid_buff[64];
                        // Write port as char in buffer
                        sprintf(sport_buff, "%d", gServerConfig.mControlPort);
                        sprintf(wport_buff, "%d", int(workerPort));
                        // Write num threads as char in buffer
                        sprintf(thread_buff, "%d", requestNumThreads);
                        // Write id as char in buffer
                        sprintf(uid_buff, "%d", uid);

                        char *argv[] = {"HopsanServerWorker", uid_buff, sport_buff, wport_buff, thread_buff, nullptr};

                        pid_t pid;
                        int status = posix_spawn(&pid,"./HopsanServerWorker",nullptr,nullptr,argv,environ);
                        if(status == 0)
                        {
                            std::cout << PRINTSERVER << nowDateTime() << " Launched Worker Process, pid: "<< pid << " port: " << workerPort << " uid: " << uid << " nThreads: " << requestNumThreads << endl;
                            workerMap.insert({uid,{requestNumThreads,pid}});

                            replymsg_ReplyServerSlots_t msg = {workerPort};
                            sendMessage(socket, ReplyServerSlots, msg);
                            nTakenSlots+=requestNumThreads;
                            std::cout << PRINTSERVER << nowDateTime() << " Remaining slots: " << gServerConfig.mMaxNumSlots-nTakenSlots << endl;
                        }
                        else
                        {
                            std::cout << PRINTSERVER << nowDateTime() << " Error: Failed to launch worker process!"<<endl;
                            sendMessage(socket, NotAck, "Failed to launch worker process!");
                        }
#endif
                    }
                    else if (nTakenSlots == gServerConfig.mMaxNumSlots)
                    {
                        sendMessage(socket, NotAck, "All slots taken");
                        cout << PRINTSERVER << nowDateTime() << " Denied! All slots taken." << endl;
                    }
                    else
                    {
                        sendMessage(socket, NotAck, "To few free slots");
                        cout << PRINTSERVER << nowDateTime() << " Denied! To few free slots." << endl;
                    }
                }
                else if (msg_id == WorkerFinished)
                {
                    bool parseOK;
                    string id_string = unpackMessage<std::string>(request,offset,parseOK);
                    if (parseOK)
                    {
                        int id = atoi(id_string.c_str());
                        cout << PRINTSERVER << nowDateTime() << " Worker " << id_string << " Finished!" << endl;

                        auto it = workerMap.find(id);
                        if (it != workerMap.end())
                        {
                            sendShortMessage(socket, Ack);

                            // Wait for process to stop (to avoid zombies)
#ifdef _WIN32
                            PROCESS_INFORMATION pi = it->second.pi;
                            WaitForSingleObject( pi.hProcess, INFINITE );
                            CloseHandle( pi.hProcess );
                            CloseHandle( pi.hThread );
#else
                            pid_t pid = it->second.pid;
                            int stat_loc;
                            pid_t status = waitpid(pid, &stat_loc, WUNTRACED);
#endif
                            int nThreads = it->second.numThreads;

                            //! @todo check returncodes maybe
                            workerMap.erase(it);
                            nTakenSlots -= nThreads;
                            std::cout << PRINTSERVER << nowDateTime() << " Open slots: " << gServerConfig.mMaxNumSlots-nTakenSlots << endl;
                        }
                        else
                        {
                            sendMessage(socket, NotAck, "Wrong worker id specified");
                        }
                    }
                    else
                    {
                        cout << PRINTSERVER << nowDateTime() << " Error: Could not server id string" << endl;
                    }
                }
                else if (msg_id == RequestServerStatus)
                {
                    cout << PRINTSERVER << nowDateTime() << " Client is requesting status" << endl;
                    replymsg_ReplyServerStatus_t status;
                    status.numTotalSlots = gServerConfig.mMaxNumSlots;
                    status.numFreeSlots = gServerConfig.mMaxNumSlots-nTakenSlots;
                    status.isReady = true;

                    sendMessage(socket, ReplyServerStatus, status);
                    lastStatusRequestTime = chrono::steady_clock::now();
                }
                // Ignore the following messages silently
                else if (msg_id == ClientClosing)
                {
                    sendShortMessage(socket, Ack);
                }
                else if (!idParseOK)
                {
                    cout << PRINTSERVER << nowDateTime() << " Error: Could not parse message id" << endl;
                }
                else
                {
                    stringstream ss;
                    ss << PRINTSERVER << nowDateTime() << " Warning: Unhandled message id " << msg_id << endl;
                    cout << ss.str() << endl;
                    sendMessage(socket, NotAck, ss.str());
                }
            }
            else
            {
                // Handle timeout / exception
            }

            duration<double> time_span = duration_cast<duration<double>>(steady_clock::now() - lastStatusRequestTime);
            if (time_span.count() > 180)
            {
                cout << PRINTSERVER << nowDateTime() << " Too long since status check: " << time_span.count() << endl;

                // If noone has requested status for this long (and we have a master server) we assume that the master server
                // has gone down, lets reconnect to it to make sure it knows that we still exist
                //! @todo maybe this should be handled in the server by saving known servers to file instead
                if (!masterserverip.empty())
                {
                    reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, true);
                }
            }

            // Do some 'work'
#ifndef _WIN32
            //sleep(1);
#else
            //Sleep (1);
#endif

            if (s_interrupted)
            {
                cout << PRINTSERVER << nowDateTime() << " Interrupt signal received, killing server" << std::endl;
                break;
            }
        }

        // Tell master server we are closing
        if (!masterserverip.empty())
        {
            //! @todo somehow automatically figure out my ip
            reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, false);
        }

    }
    catch(zmq::error_t e)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: Preparing our context and socket: " << e.what() << endl;
    }

    cout << PRINTSERVER << nowDateTime() << " Closed!" << endl;
}

<commit_msg>Fixed win32 ifdefed code<commit_after>//$Id$

#include <streambuf>
#include <sstream>
#include <cstdlib>

#include <iostream>
#include <vector>
#include "zmq.hpp"
#include <map>
#include <chrono>

#include "Messages.h"
#include "MessageUtilities.h"

#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#else
#include <unistd.h>
#include <signal.h>
#include <spawn.h>
#include <sys/wait.h>
#endif

using namespace std;
using namespace std::chrono;

extern char **environ;

class WorkerInfo
{
public:
    int numThreads;
#ifdef _WIN32
    PROCESS_INFORMATION pi;
#else
    pid_t pid;
#endif

};

class ServerConfig
{
public:
    int mControlPort = 23300;
    string mControlPortStr = "23300";
    int mMaxNumSlots = 4;
};

ServerConfig gServerConfig;
size_t nTakenSlots=0;
#ifdef _WIN32
zmq::context_t gContext(1, 63);
#else
zmq::context_t gContext(1);
#endif

#define PRINTSERVER "HopsanServer@"+gServerConfig.mControlPortStr+"; "
std::string nowDateTime()
{
    std::time_t now_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    char buff[64];
    std::strftime(buff, sizeof(buff), "%b %d %H:%M:%S", std::localtime(&now_time));
    return string(&buff[0]);
}

bool receiveAckNackMessage(zmq::socket_t &rSocket, long timeout, string &rNackReason)
{
    zmq::message_t response;
    if(receiveWithTimeout(rSocket, timeout, response))
    {
        size_t offset=0;
        bool parseOK;
        size_t id = getMessageId(response, offset, parseOK);
        //cout << "id: " << id << endl;
        if (id == Ack)
        {
            return true;
        }
        else if (id == NotAck)
        {
            rNackReason = unpackMessage<std::string>(response, offset, parseOK);
        }
        else if (!parseOK)
        {
            rNackReason = "Exception in msgpack:unpack!";
        }
        else
        {
            rNackReason = "Got neither Ack nor Nack";
        }
    }
    else
    {
        rNackReason = "Got either timeout or exception in zmq::recv()";
    }
    return false;
}

void reportToAddressServer(std::string addressIP, std::string addressPort, std::string myIP, std::string myPort, std::string myDescription, bool isOnline)
{
    try
    {
        zmq::socket_t addressServerSocket (gContext, ZMQ_REQ);
        int linger_ms = 1000;
        addressServerSocket.setsockopt(ZMQ_LINGER, &linger_ms, sizeof(int));
        addressServerSocket.connect(makeZMQAddress(addressIP, addressPort).c_str());

        //! @todo check if ok
        infomsg_Available_t message;
        message.ip = myIP;
        message.port = myPort;
        message.description = myDescription;
        message.numTotalSlots = gServerConfig.mMaxNumSlots;
        message.identity = 0; // Identity should allways be 0 when a server starts, relay servers may set other values

        if (isOnline)
        {
            sendMessage(addressServerSocket, Available, message);
            std::string nackreason;
            bool ack = receiveAckNackMessage(addressServerSocket, 5000, nackreason);
            if (ack)
            {
                cout << PRINTSERVER << nowDateTime() << " Successfully registered in address server" << endl;
            }
            else
            {

                cout << PRINTSERVER << nowDateTime() << " Error: Could not register in master server: " << nackreason << endl;
            }
        }
        else
        {
            sendMessage(addressServerSocket, ServerClosing, message);
            std::string nackreason;
            bool ack = receiveAckNackMessage(addressServerSocket, 5000, nackreason);
            if (ack)
            {
                cout << PRINTSERVER << nowDateTime() << " Successfully unregistered in address server" << endl;
            }
            else
            {
                cout << PRINTSERVER << nowDateTime() << " Error: Could not unregister in master server: " << nackreason << endl;
            }
        }
        addressServerSocket.disconnect(makeZMQAddress(addressIP, addressPort).c_str());
    }
    catch(zmq::error_t e)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: Preparing addressServerSocket: " << e.what() << endl;
    }
}

static int s_interrupted = 0;
#ifdef _WIN32
BOOL WINAPI consoleCtrlHandler( DWORD dwCtrlType )
{
    // what to do here?
    s_interrupted = 1;
    return TRUE;
}
#else
static void s_signal_handler(int signal_value)
{
    s_interrupted = 1;
}

static void s_catch_signals(void)
{
    struct sigaction action;
    action.sa_handler = s_signal_handler;
    action.sa_flags = 0;
    sigemptyset (&action.sa_mask);
    sigaction (SIGINT, &action, NULL);
    sigaction (SIGTERM, &action, NULL);
}
#endif

map<int, WorkerInfo> workerMap;

int main(int argc, char* argv[])
{
    if (argc < 3)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: you must specify what number of threads to use and what base port to use!" << endl;
        return 1;
    }

    string numThreadsToUse = argv[1];
    gServerConfig.mMaxNumSlots = atoi(numThreadsToUse.c_str());

    string myPort = argv[2];
    gServerConfig.mControlPortStr = myPort;
    gServerConfig.mControlPort = atoi(myPort.c_str());

    std::string masterserverip, masterserverport;
    steady_clock::time_point lastStatusRequestTime;
    if (argc >= 5)
    {
        masterserverip = argv[3];
        masterserverport = argv[4];
    }

    string myExternalIP = "127.0.0.1";
    if (argc >= 6)
    {
        myExternalIP = argv[5];
    }

    string myDescription;
    if (argc >= 7)
    {
        myDescription = argv[6];
    }

    cout << PRINTSERVER << nowDateTime() << " Starting with: " << gServerConfig.mMaxNumSlots << " slots, Listening on port: " << gServerConfig.mControlPort  << endl;

    // Prepare our context and socket
    try
    {
        zmq::socket_t socket (gContext, ZMQ_REP);
        int linger_ms = 1000;
        socket.setsockopt(ZMQ_LINGER, &linger_ms, sizeof(int));
        socket.bind( makeZMQAddress("*", myPort).c_str() );

        if (!masterserverip.empty())
        {
            //! @todo somehow automatically figure out my ip
            reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, true);
        }

#ifdef _WIN32
        SetConsoleCtrlHandler( consoleCtrlHandler, TRUE );
#else
        s_catch_signals();
#endif
        while (true)
        {
            // Wait for next request from client
            zmq::message_t request;
            if(receiveWithTimeout(socket, 30000, request))
            {
                size_t offset=0;
                bool idParseOK;
                size_t msg_id = getMessageId(request, offset, idParseOK);
                //cout << PRINTSERVER << nowDateTime() << " Received message with length: " << request.size() << " msg_id: " << msg_id << endl;

                if (msg_id == RequestServerSlots)
                {
                    bool parseOK;
                    reqmsg_ReqServerSlots_t msg = unpackMessage<reqmsg_ReqServerSlots_t>(request, offset, parseOK);
                    int requestNumThreads = msg.numThreads;

                    cout << PRINTSERVER << nowDateTime() << " Client is requesting: " << requestNumThreads << " slots... " << endl;
                    if (nTakenSlots+requestNumThreads <= gServerConfig.mMaxNumSlots)
                    {
                        size_t workerPort = gServerConfig.mControlPort+nTakenSlots+1;

                        // Generate unique worker Id
                        int uid = rand();
                        while (workerMap.count(uid) != 0)
                        {
                            uid = rand();
                        }

#ifdef _WIN32
                        PROCESS_INFORMATION processInformation;
                        STARTUPINFO startupInfo;
                        memset(&processInformation, 0, sizeof(processInformation));
                        memset(&startupInfo, 0, sizeof(startupInfo));
                        startupInfo.cb = sizeof(startupInfo);

                        string scport = to_string(gServerConfig.mControlPort);
                        string swport = to_string(workerPort);
                        string nthreads = to_string(requestNumThreads);
                        string uidstr = to_string(uid);

                        std::string appName("HopsanServerWorker.exe");
                        std::string cmdLine("HopsanServerWorker "+uidstr+" "+scport+" "+swport+" "+nthreads);
                        TCHAR tempCmdLine[cmdLine.size()*2];
                        strcpy_s(tempCmdLine, cmdLine.size()*2, cmdLine.c_str());

                        BOOL result = CreateProcess(appName.c_str(), tempCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInformation);
                        if (result == 0)
                        {
                            std::cout << PRINTSERVER << "Error: Failed to launch worker process!"<<endl;
                            sendMessage(socket, NotAck, "Failed to launch worker process!");
                        }
                        else
                        {
                            std::cout << PRINTSERVER << "Launched Worker Process, pid: "<< processInformation.dwProcessId << " port: " << workerPort << " uid: " << uid << " nThreads: " << requestNumThreads  << endl;
                            workerMap.insert({uid, {requestNumThreads, processInformation}});

                            replymsg_ReplyServerSlots_t msg = {workerPort};
                            sendMessage<replymsg_ReplyServerSlots_t>(socket, ReplyServerSlots, msg);
                            nTakenSlots++;
                        }

#else
                        char sport_buff[64], wport_buff[64], thread_buff[64], uid_buff[64];
                        // Write port as char in buffer
                        sprintf(sport_buff, "%d", gServerConfig.mControlPort);
                        sprintf(wport_buff, "%d", int(workerPort));
                        // Write num threads as char in buffer
                        sprintf(thread_buff, "%d", requestNumThreads);
                        // Write id as char in buffer
                        sprintf(uid_buff, "%d", uid);

                        char *argv[] = {"HopsanServerWorker", uid_buff, sport_buff, wport_buff, thread_buff, nullptr};

                        pid_t pid;
                        int status = posix_spawn(&pid,"./HopsanServerWorker",nullptr,nullptr,argv,environ);
                        if(status == 0)
                        {
                            std::cout << PRINTSERVER << nowDateTime() << " Launched Worker Process, pid: "<< pid << " port: " << workerPort << " uid: " << uid << " nThreads: " << requestNumThreads << endl;
                            workerMap.insert({uid,{requestNumThreads,pid}});

                            replymsg_ReplyServerSlots_t msg = {workerPort};
                            sendMessage(socket, ReplyServerSlots, msg);
                            nTakenSlots+=requestNumThreads;
                            std::cout << PRINTSERVER << nowDateTime() << " Remaining slots: " << gServerConfig.mMaxNumSlots-nTakenSlots << endl;
                        }
                        else
                        {
                            std::cout << PRINTSERVER << nowDateTime() << " Error: Failed to launch worker process!"<<endl;
                            sendMessage(socket, NotAck, "Failed to launch worker process!");
                        }
#endif
                    }
                    else if (nTakenSlots == gServerConfig.mMaxNumSlots)
                    {
                        sendMessage(socket, NotAck, "All slots taken");
                        cout << PRINTSERVER << nowDateTime() << " Denied! All slots taken." << endl;
                    }
                    else
                    {
                        sendMessage(socket, NotAck, "To few free slots");
                        cout << PRINTSERVER << nowDateTime() << " Denied! To few free slots." << endl;
                    }
                }
                else if (msg_id == WorkerFinished)
                {
                    bool parseOK;
                    string id_string = unpackMessage<std::string>(request,offset,parseOK);
                    if (parseOK)
                    {
                        int id = atoi(id_string.c_str());
                        cout << PRINTSERVER << nowDateTime() << " Worker " << id_string << " Finished!" << endl;

                        auto it = workerMap.find(id);
                        if (it != workerMap.end())
                        {
                            sendShortMessage(socket, Ack);

                            // Wait for process to stop (to avoid zombies)
#ifdef _WIN32
                            PROCESS_INFORMATION pi = it->second.pi;
                            WaitForSingleObject( pi.hProcess, INFINITE );
                            CloseHandle( pi.hProcess );
                            CloseHandle( pi.hThread );
#else
                            pid_t pid = it->second.pid;
                            int stat_loc;
                            pid_t status = waitpid(pid, &stat_loc, WUNTRACED);
#endif
                            int nThreads = it->second.numThreads;

                            //! @todo check returncodes maybe
                            workerMap.erase(it);
                            nTakenSlots -= nThreads;
                            std::cout << PRINTSERVER << nowDateTime() << " Open slots: " << gServerConfig.mMaxNumSlots-nTakenSlots << endl;
                        }
                        else
                        {
                            sendMessage(socket, NotAck, "Wrong worker id specified");
                        }
                    }
                    else
                    {
                        cout << PRINTSERVER << nowDateTime() << " Error: Could not server id string" << endl;
                    }
                }
                else if (msg_id == RequestServerStatus)
                {
                    cout << PRINTSERVER << nowDateTime() << " Client is requesting status" << endl;
                    replymsg_ReplyServerStatus_t status;
                    status.numTotalSlots = gServerConfig.mMaxNumSlots;
                    status.numFreeSlots = gServerConfig.mMaxNumSlots-nTakenSlots;
                    status.isReady = true;

                    sendMessage(socket, ReplyServerStatus, status);
                    lastStatusRequestTime = chrono::steady_clock::now();
                }
                // Ignore the following messages silently
                else if (msg_id == ClientClosing)
                {
                    sendShortMessage(socket, Ack);
                }
                else if (!idParseOK)
                {
                    cout << PRINTSERVER << nowDateTime() << " Error: Could not parse message id" << endl;
                }
                else
                {
                    stringstream ss;
                    ss << PRINTSERVER << nowDateTime() << " Warning: Unhandled message id " << msg_id << endl;
                    cout << ss.str() << endl;
                    sendMessage(socket, NotAck, ss.str());
                }
            }
            else
            {
                // Handle timeout / exception
            }

            duration<double> time_span = duration_cast<duration<double>>(steady_clock::now() - lastStatusRequestTime);
            if (time_span.count() > 180)
            {
                cout << PRINTSERVER << nowDateTime() << " Too long since status check: " << time_span.count() << endl;

                // If noone has requested status for this long (and we have a master server) we assume that the master server
                // has gone down, lets reconnect to it to make sure it knows that we still exist
                //! @todo maybe this should be handled in the server by saving known servers to file instead
                if (!masterserverip.empty())
                {
                    reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, true);
                }
            }

            // Do some 'work'
#ifndef _WIN32
            //sleep(1);
#else
            //Sleep (1);
#endif

            if (s_interrupted)
            {
                cout << PRINTSERVER << nowDateTime() << " Interrupt signal received, killing server" << std::endl;
                break;
            }
        }

        // Tell master server we are closing
        if (!masterserverip.empty())
        {
            //! @todo somehow automatically figure out my ip
            reportToAddressServer(masterserverip, masterserverport, myExternalIP, myPort, myDescription, false);
        }

    }
    catch(zmq::error_t e)
    {
        cout << PRINTSERVER << nowDateTime() << " Error: Preparing our context and socket: " << e.what() << endl;
    }

    cout << PRINTSERVER << nowDateTime() << " Closed!" << endl;
}

<|endoftext|>


<commit_before>#include "Object.h"
#include "Math.h"

using namespace std;
using namespace Intersection;
using namespace Rendering;
using namespace Math;
using namespace Rendering::Light;
using namespace Core;

#define _child _vector

Object::Object(const std::string& name, Object* parent /* = NULL */) :
	_culled(false), _parent(parent), _use(true), _hasMesh(false), _name(name),
	_radius(0.0f)
{
	_boundBox.SetMinMax(Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));

	_transform = new Transform( this );
	_root = parent ? parent->_root : this;

	if(parent)
		parent->AddChild(this);
}

Object::~Object(void)
{
	SAFE_DELETE(_transform);

	DeleteAllChild();
	DeleteAllComponent();
}

void Object::DeleteAllChild()
{
	for(auto iter = _vector.begin(); iter != _vector.end(); ++iter)
	{
		(*iter)->DeleteAllComponent();
		(*iter)->DeleteAllChild();

		SAFE_DELETE((*iter));
	}

	DeleteAll();
}

void Object::AddChild(Object *child)
{
	ASSERT_COND_MSG( (child->_parent == nullptr) || (child->_parent == this),
		"Error, Invalid parent");

	if(FindChild(child->GetName()) == nullptr)
	{
		Add(child->_name, child);
		child->_parent = this;

		_transform->AddChild(child->GetName(), child->_transform);
	}
	else
	{
		ASSERT_MSG("Duplicated child. plz check your code.");
	}
}

Object* Object::FindChild(const std::string& key)
{
	Object** ret = Find(key, nullptr);
	return ret ? (*ret) : nullptr;
}

bool Object::CompareIsChildOfParent(Object *parent)
{
	return (parent == _parent);
}

void Object::Culling(const Intersection::Frustum *frustum)
{
	if(_use == false) return;

	Vector3 wp;
	_transform->FetchWorldPosition(wp);
	_culled = !frustum->In(wp, _radius);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->Culling(frustum);
}

void Object::Update(float delta)
{
	if(_use == false)
		return;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		(*iter)->OnUpdate(delta);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->Update(delta);
}

void Object::UpdateTransformCB_With_ComputeSceneMinMaxPos(
	const Device::DirectX*& dx, Math::Vector3& refWorldPosMin, Math::Vector3& refWorldPosMax,
	const Math::Matrix& parentWorldMat)
{
	if(_use == false)
		return;

	Math::Matrix localMat;
	_transform->FetchLocalMatrix(localMat);

	Math::Matrix worldMat = localMat * parentWorldMat;

	if(_hasMesh)
	{
		Vector3 extents		= _boundBox.GetExtents();
		Vector3 boxCenter	= _boundBox.GetCenter();

		Vector3 worldPos = Vector3(worldMat._41, worldMat._42, worldMat._43) + boxCenter;

		Vector3 worldScale;
		Transform::FetchWorldScale(worldScale, worldMat);

		Vector3 minPos = (worldPos - extents) * worldScale;
		Vector3 maxPos = (worldPos + extents) * worldScale;

		if(refWorldPosMin.x > minPos.x) refWorldPosMin.x = minPos.x;
		if(refWorldPosMin.y > minPos.y) refWorldPosMin.y = minPos.y;
		if(refWorldPosMin.z > minPos.z) refWorldPosMin.z = minPos.z;

		if(refWorldPosMax.x < maxPos.x) refWorldPosMax.x = maxPos.x;
		if(refWorldPosMax.y < maxPos.y) refWorldPosMax.y = maxPos.y;
		if(refWorldPosMax.z < maxPos.z) refWorldPosMax.z = maxPos.z;
	}

	Rendering::TransformCB transformCB;

	Matrix& transposedWM = transformCB.world;
	Matrix::Transpose(transposedWM, worldMat);

	Matrix& worldInvTranspose = transformCB.worldInvTranspose;
	{
		Matrix::Inverse(worldInvTranspose, transposedWM);
		Matrix::Transpose(worldInvTranspose, worldInvTranspose);
	}

	bool changedWorldMat = memcmp(&_prevWorldMat, &worldMat, sizeof(Math::Matrix)) != 0;
	if(changedWorldMat)
		_prevWorldMat = worldMat;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
	{
		if(changedWorldMat)
			(*iter)->OnUpdateTransformCB(dx, transformCB);

		(*iter)->OnRenderPreview();
	}

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->UpdateTransformCB_With_ComputeSceneMinMaxPos(dx, refWorldPosMin, refWorldPosMax, worldMat);
}

bool Object::Intersects(Intersection::Sphere &sphere)
{
	Vector3 wp;
	_transform->FetchWorldPosition(wp);
	return sphere.Intersects(wp, _radius);
}

void Object::DeleteComponent(Component *component)
{
	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
	{
		if((*iter) == component)
		{
			(*iter)->OnDestroy();
			_components.erase(iter);
			delete (*iter);
			return;
		}
	}

}

void Object::DeleteAllComponent()
{
	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		delete (*iter);

	_components.clear();
}

Object* Object::Clone() const
{
	Object* newObject = new Object(_name + "-Clone", nullptr);
	newObject->_transform->UpdateTransform(*_transform);
	newObject->_hasMesh		= _hasMesh;
	newObject->_use			= _use;
	newObject->_radius		= _radius;
	newObject->_boundBox	= _boundBox;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		newObject->_components.push_back( (*iter)->Clone() );

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		newObject->AddChild( (*iter)->Clone() );

	return newObject;
}

void Object::SetUse(bool b)
{
	_use = b;
	Geometry::Mesh* mesh = GetComponent<Geometry::Mesh>();
	if(mesh)
		mesh->SetShow(b);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->SetUse(b);
}<commit_msg>Object.cpp - _prevWorldMat -> _prevTransposedWorldMat #53<commit_after>#include "Object.h"
#include "Math.h"

using namespace std;
using namespace Intersection;
using namespace Rendering;
using namespace Math;
using namespace Rendering::Light;
using namespace Core;

#define _child _vector

Object::Object(const std::string& name, Object* parent /* = NULL */) :
	_culled(false), _parent(parent), _use(true), _hasMesh(false), _name(name),
	_radius(0.0f)
{
	_boundBox.SetMinMax(Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));

	_transform = new Transform( this );
	_root = parent ? parent->_root : this;

	if(parent)
		parent->AddChild(this);
}

Object::~Object(void)
{
	SAFE_DELETE(_transform);

	DeleteAllChild();
	DeleteAllComponent();
}

void Object::DeleteAllChild()
{
	for(auto iter = _vector.begin(); iter != _vector.end(); ++iter)
	{
		(*iter)->DeleteAllComponent();
		(*iter)->DeleteAllChild();

		SAFE_DELETE((*iter));
	}

	DeleteAll();
}

void Object::AddChild(Object *child)
{
	ASSERT_COND_MSG( (child->_parent == nullptr) || (child->_parent == this),
		"Error, Invalid parent");

	if(FindChild(child->GetName()) == nullptr)
	{
		Add(child->_name, child);
		child->_parent = this;

		_transform->AddChild(child->GetName(), child->_transform);
	}
	else
	{
		ASSERT_MSG("Duplicated child. plz check your code.");
	}
}

Object* Object::FindChild(const std::string& key)
{
	Object** ret = Find(key, nullptr);
	return ret ? (*ret) : nullptr;
}

bool Object::CompareIsChildOfParent(Object *parent)
{
	return (parent == _parent);
}

void Object::Culling(const Intersection::Frustum *frustum)
{
	if(_use == false) return;

	Vector3 wp;
	_transform->FetchWorldPosition(wp);
	_culled = !frustum->In(wp, _radius);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->Culling(frustum);
}

void Object::Update(float delta)
{
	if(_use == false)
		return;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		(*iter)->OnUpdate(delta);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->Update(delta);
}

void Object::UpdateTransformCB_With_ComputeSceneMinMaxPos(
	const Device::DirectX*& dx, Math::Vector3& refWorldPosMin, Math::Vector3& refWorldPosMax,
	const Math::Matrix& parentWorldMat)
{
	if(_use == false)
		return;

	Math::Matrix localMat;
	_transform->FetchLocalMatrix(localMat);

	Math::Matrix worldMat = localMat * parentWorldMat;

	if(_hasMesh)
	{
		Vector3 extents		= _boundBox.GetExtents();
		Vector3 boxCenter	= _boundBox.GetCenter();

		Vector3 worldPos = Vector3(worldMat._41, worldMat._42, worldMat._43) + boxCenter;

		Vector3 worldScale;
		Transform::FetchWorldScale(worldScale, worldMat);

		Vector3 minPos = (worldPos - extents) * worldScale;
		Vector3 maxPos = (worldPos + extents) * worldScale;

		if(refWorldPosMin.x > minPos.x) refWorldPosMin.x = minPos.x;
		if(refWorldPosMin.y > minPos.y) refWorldPosMin.y = minPos.y;
		if(refWorldPosMin.z > minPos.z) refWorldPosMin.z = minPos.z;

		if(refWorldPosMax.x < maxPos.x) refWorldPosMax.x = maxPos.x;
		if(refWorldPosMax.y < maxPos.y) refWorldPosMax.y = maxPos.y;
		if(refWorldPosMax.z < maxPos.z) refWorldPosMax.z = maxPos.z;
	}

	Rendering::TransformCB transformCB;

	Matrix& transposedWM = transformCB.world;
	Matrix::Transpose(transposedWM, worldMat);

	Matrix& worldInvTranspose = transformCB.worldInvTranspose;
	{
		Matrix::Inverse(worldInvTranspose, transposedWM);
		Matrix::Transpose(worldInvTranspose, worldInvTranspose);
	}

	bool changedWorldMat = memcmp(&_prevTransposedWorldMat, &transposedWM, sizeof(Math::Matrix)) != 0;
	if(changedWorldMat)
		_prevTransposedWorldMat = transposedWM;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
	{
		if(changedWorldMat)
			(*iter)->OnUpdateTransformCB(dx, transformCB);

		(*iter)->OnRenderPreview();
	}

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->UpdateTransformCB_With_ComputeSceneMinMaxPos(dx, refWorldPosMin, refWorldPosMax, worldMat);
}

bool Object::Intersects(Intersection::Sphere &sphere)
{
	Vector3 wp;
	_transform->FetchWorldPosition(wp);
	return sphere.Intersects(wp, _radius);
}

void Object::DeleteComponent(Component *component)
{
	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
	{
		if((*iter) == component)
		{
			(*iter)->OnDestroy();
			_components.erase(iter);
			delete (*iter);
			return;
		}
	}

}

void Object::DeleteAllComponent()
{
	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		delete (*iter);

	_components.clear();
}

Object* Object::Clone() const
{
	Object* newObject = new Object(_name + "-Clone", nullptr);
	newObject->_transform->UpdateTransform(*_transform);
	newObject->_hasMesh		= _hasMesh;
	newObject->_use			= _use;
	newObject->_radius		= _radius;
	newObject->_boundBox	= _boundBox;

	for(auto iter = _components.begin(); iter != _components.end(); ++iter)
		newObject->_components.push_back( (*iter)->Clone() );

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		newObject->AddChild( (*iter)->Clone() );

	return newObject;
}

void Object::SetUse(bool b)
{
	_use = b;
	Geometry::Mesh* mesh = GetComponent<Geometry::Mesh>();
	if(mesh)
		mesh->SetShow(b);

	for(auto iter = _child.begin(); iter != _child.end(); ++iter)
		(*iter)->SetUse(b);
}
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "ipc/ipc_server.h"

#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_pump_asio.h"
#include "base/strings/string_printf.h"
#include "base/strings/unicode.h"
#include "base/win/scoped_object.h"
#include "base/win/security_helpers.h"
#include "crypto/random.h"
#include "ipc/ipc_channel.h"

#include <asio/post.hpp>
#include <asio/windows/overlapped_ptr.hpp>
#include <asio/windows/stream_handle.hpp>

namespace ipc {

namespace {

const DWORD kAcceptTimeout = 5000; // ms
const DWORD kPipeBufferSize = 512 * 1024; // 512 kB

} // namespace

class Server::Listener : public std::enable_shared_from_this<Listener>
{
public:
    Listener(Server* server, size_t index);

    void dettach() { server_ = nullptr; }

    bool listen(asio::io_context& io_context, std::u16string_view channel_name);
    void onNewConnetion(const std::error_code& error_code, size_t bytes_transferred);

private:
    Server* server_;
    const size_t index_;

    std::unique_ptr<asio::windows::stream_handle> handle_;
    std::unique_ptr<asio::windows::overlapped_ptr> overlapped_;

    DISALLOW_COPY_AND_ASSIGN(Listener);
};

Server::Listener::Listener(Server* server, size_t index)
    : server_(server),
      index_(index)
{
    // Nothing
}

bool Server::Listener::listen(asio::io_context& io_context, std::u16string_view channel_name)
{
    std::wstring user_sid;

    if (!base::win::userSidString(&user_sid))
    {
        LOG(LS_ERROR) << "Failed to query the current user SID";
        return nullptr;
    }

    // Create a security descriptor that gives full access to the caller and authenticated users
    // and denies access by anyone else.
    std::wstring security_descriptor =
        base::stringPrintf(L"O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;AU)",
                           user_sid.c_str(),
                           user_sid.c_str(),
                           user_sid.c_str());

    base::win::ScopedSd sd = base::win::convertSddlToSd(security_descriptor);
    if (!sd.get())
    {
        LOG(LS_ERROR) << "Failed to create a security descriptor";
        return nullptr;
    }

    SECURITY_ATTRIBUTES security_attributes = { 0 };
    security_attributes.nLength = sizeof(security_attributes);
    security_attributes.lpSecurityDescriptor = sd.get();
    security_attributes.bInheritHandle = FALSE;

    base::win::ScopedHandle handle(
        CreateNamedPipeW(reinterpret_cast<const wchar_t*>(channel_name.data()),
                         FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX,
                         PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
                         PIPE_UNLIMITED_INSTANCES,
                         kPipeBufferSize,
                         kPipeBufferSize,
                         kAcceptTimeout,
                         &security_attributes));
    if (!handle.isValid())
    {
        PLOG(LS_WARNING) << "CreateNamedPipeW failed";
        return nullptr;
    }

    handle_ = std::make_unique<asio::windows::stream_handle>(io_context, handle.release());

    overlapped_ = std::make_unique<asio::windows::overlapped_ptr>(
        io_context,
        std::bind(&Server::Listener::onNewConnetion,
                  shared_from_this(),
                  std::placeholders::_1,
                  std::placeholders::_2));

    if (!ConnectNamedPipe(handle_->native_handle(), overlapped_->get()))
    {
        DWORD last_error = GetLastError();

        switch (last_error)
        {
            case ERROR_PIPE_CONNECTED:
                break;

            case ERROR_IO_PENDING:
                overlapped_->release();
                return true;

            default:
                overlapped_->complete(
                    std::error_code(last_error, asio::error::get_system_category()), 0);
                return false;
        }
    }

    overlapped_->complete(std::error_code(), 0);
    return true;
}

void Server::Listener::onNewConnetion(
    const std::error_code& error_code, size_t /* bytes_transferred */)
{
    if (!server_)
        return;

    if (error_code)
    {
        server_->onErrorOccurred(FROM_HERE);
        return;
    }

    std::unique_ptr<Channel> channel =
        std::unique_ptr<Channel>(new Channel(server_->channel_name_, std::move(*handle_)));

    server_->onNewConnection(index_, std::move(channel));
}

Server::Server()
    : io_context_(base::MessageLoop::current()->pumpAsio()->ioContext())
{
    for (size_t i = 0; i < listeners_.size(); ++i)
        listeners_[i] = std::make_shared<Listener>(this, i);
}

Server::~Server()
{
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    stop();
}

// static
std::u16string Server::createUniqueId()
{
    static std::atomic_uint32_t last_channel_id = 0;

    uint32_t channel_id = last_channel_id++;
    uint32_t process_id = GetCurrentProcessId();
    uint32_t random_number = crypto::Random::number32();

    return base::utf16FromAscii(
        base::stringPrintf("%lu.%lu.%lu", process_id, channel_id, random_number));
}

bool Server::start(std::u16string_view channel_id, Delegate* delegate)
{
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    DCHECK(delegate);

    if (channel_id.empty())
    {
        DLOG(LS_ERROR) << "Empty channel id";
        return false;
    }

    channel_name_ = Channel::channelName(channel_id);
    delegate_ = delegate;

    for (size_t i = 0; i < listeners_.size(); ++i)
    {
        if (!runListener(i))
            return false;
    }

    return true;
}

void Server::stop()
{
    delegate_ = nullptr;

    for (size_t i = 0; i < listeners_.size(); ++i)
    {
        if (listeners_[i])
        {
            listeners_[i]->dettach();
            listeners_[i].reset();
        }
    }
}

bool Server::runListener(size_t index)
{
    return listeners_[index]->listen(io_context_, channel_name_);
}

void Server::onNewConnection(size_t index, std::unique_ptr<Channel> channel)
{
    if (delegate_)
    {
        delegate_->onNewConnection(std::move(channel));
        runListener(index);
    }
}

void Server::onErrorOccurred(const base::Location& location)
{
    LOG(LS_WARNING) << "Error in IPC server with name: " << channel_name_
                    << " (" << location.toString() << ")";

    if (delegate_)
        delegate_->onErrorOccurred();
}

} // namespace ipc
<commit_msg>- Added null pointer check. - Other minor changes.<commit_after>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "ipc/ipc_server.h"

#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_pump_asio.h"
#include "base/strings/string_printf.h"
#include "base/strings/unicode.h"
#include "base/win/scoped_object.h"
#include "base/win/security_helpers.h"
#include "crypto/random.h"
#include "ipc/ipc_channel.h"

#include <asio/post.hpp>
#include <asio/windows/overlapped_ptr.hpp>
#include <asio/windows/stream_handle.hpp>

namespace ipc {

namespace {

const DWORD kAcceptTimeout = 5000; // ms
const DWORD kPipeBufferSize = 512 * 1024; // 512 kB

} // namespace

class Server::Listener : public std::enable_shared_from_this<Listener>
{
public:
    Listener(Server* server, size_t index);
    ~Listener();

    void dettach() { server_ = nullptr; }

    bool listen(asio::io_context& io_context, std::u16string_view channel_name);
    void onNewConnetion(const std::error_code& error_code, size_t bytes_transferred);

private:
    Server* server_;
    const size_t index_;

    std::unique_ptr<asio::windows::stream_handle> handle_;
    std::unique_ptr<asio::windows::overlapped_ptr> overlapped_;

    DISALLOW_COPY_AND_ASSIGN(Listener);
};

Server::Listener::Listener(Server* server, size_t index)
    : server_(server),
      index_(index)
{
    // Nothing
}

Server::Listener::~Listener() = default;

bool Server::Listener::listen(asio::io_context& io_context, std::u16string_view channel_name)
{
    std::wstring user_sid;

    if (!base::win::userSidString(&user_sid))
    {
        LOG(LS_ERROR) << "Failed to query the current user SID";
        return false;
    }

    // Create a security descriptor that gives full access to the caller and authenticated users
    // and denies access by anyone else.
    std::wstring security_descriptor =
        base::stringPrintf(L"O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;AU)",
                           user_sid.c_str(),
                           user_sid.c_str(),
                           user_sid.c_str());

    base::win::ScopedSd sd = base::win::convertSddlToSd(security_descriptor);
    if (!sd.get())
    {
        LOG(LS_ERROR) << "Failed to create a security descriptor";
        return false;
    }

    SECURITY_ATTRIBUTES security_attributes = { 0 };
    security_attributes.nLength = sizeof(security_attributes);
    security_attributes.lpSecurityDescriptor = sd.get();
    security_attributes.bInheritHandle = FALSE;

    base::win::ScopedHandle handle(
        CreateNamedPipeW(reinterpret_cast<const wchar_t*>(channel_name.data()),
                         FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX,
                         PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
                         PIPE_UNLIMITED_INSTANCES,
                         kPipeBufferSize,
                         kPipeBufferSize,
                         kAcceptTimeout,
                         &security_attributes));
    if (!handle.isValid())
    {
        PLOG(LS_WARNING) << "CreateNamedPipeW failed";
        return false;
    }

    handle_ = std::make_unique<asio::windows::stream_handle>(io_context, handle.release());

    overlapped_ = std::make_unique<asio::windows::overlapped_ptr>(
        io_context,
        std::bind(&Server::Listener::onNewConnetion,
                  shared_from_this(),
                  std::placeholders::_1,
                  std::placeholders::_2));

    if (!ConnectNamedPipe(handle_->native_handle(), overlapped_->get()))
    {
        DWORD last_error = GetLastError();

        switch (last_error)
        {
            case ERROR_PIPE_CONNECTED:
                break;

            case ERROR_IO_PENDING:
                overlapped_->release();
                return true;

            default:
                overlapped_->complete(
                    std::error_code(last_error, asio::error::get_system_category()), 0);
                return false;
        }
    }

    overlapped_->complete(std::error_code(), 0);
    return true;
}

void Server::Listener::onNewConnetion(
    const std::error_code& error_code, size_t /* bytes_transferred */)
{
    if (!server_)
        return;

    if (error_code)
    {
        server_->onErrorOccurred(FROM_HERE);
        return;
    }

    std::unique_ptr<Channel> channel =
        std::unique_ptr<Channel>(new Channel(server_->channel_name_, std::move(*handle_)));

    server_->onNewConnection(index_, std::move(channel));
}

Server::Server()
    : io_context_(base::MessageLoop::current()->pumpAsio()->ioContext())
{
    for (size_t i = 0; i < listeners_.size(); ++i)
        listeners_[i] = std::make_shared<Listener>(this, i);
}

Server::~Server()
{
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    stop();
}

// static
std::u16string Server::createUniqueId()
{
    static std::atomic_uint32_t last_channel_id = 0;

    uint32_t channel_id = last_channel_id++;
    uint32_t process_id = GetCurrentProcessId();
    uint32_t random_number = crypto::Random::number32();

    return base::utf16FromAscii(
        base::stringPrintf("%lu.%lu.%lu", process_id, channel_id, random_number));
}

bool Server::start(std::u16string_view channel_id, Delegate* delegate)
{
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    DCHECK(delegate);

    if (channel_id.empty())
    {
        DLOG(LS_ERROR) << "Empty channel id";
        return false;
    }

    channel_name_ = Channel::channelName(channel_id);
    delegate_ = delegate;

    for (size_t i = 0; i < listeners_.size(); ++i)
    {
        if (!runListener(i))
            return false;
    }

    return true;
}

void Server::stop()
{
    delegate_ = nullptr;

    for (size_t i = 0; i < listeners_.size(); ++i)
    {
        if (listeners_[i])
        {
            listeners_[i]->dettach();
            listeners_[i].reset();
        }
    }
}

bool Server::runListener(size_t index)
{
    std::shared_ptr<Listener> listener = listeners_[index];
    if (!listener)
        return false;

    return listener->listen(io_context_, channel_name_);
}

void Server::onNewConnection(size_t index, std::unique_ptr<Channel> channel)
{
    if (delegate_)
    {
        delegate_->onNewConnection(std::move(channel));
        runListener(index);
    }
}

void Server::onErrorOccurred(const base::Location& location)
{
    LOG(LS_WARNING) << "Error in IPC server with name: " << channel_name_
                    << " (" << location.toString() << ")";

    if (delegate_)
        delegate_->onErrorOccurred();
}

} // namespace ipc
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2016 The DarkNet developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "transactionrecord.h"

#include "base58.h"
#include "timedata.h"
#include "wallet.h"
#include "obfuscation.h"
#include "swifttx.h"

#include <stdint.h>

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash();
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (wtx.IsCoinStake())
    {
        CTxDestination address;
        if (!ExtractDestination(wtx.vout[1].scriptPubKey, address))
            return parts;

        if(!IsMine(*wallet, address)) //if the address is not yours then it means you have a tx sent to you in someone elses coinstake tx
        {
            for(unsigned int i = 0; i < wtx.vout.size(); i++)
            {
                if(i == 0)
                    continue; // first tx is blank
                CTxDestination outAddress;
                if(ExtractDestination(wtx.vout[i].scriptPubKey, outAddress))
                {
                    if(IsMine(*wallet, outAddress))
                    {
                        TransactionRecord txrMasternodeRec = TransactionRecord(hash, nTime, TransactionRecord::MNReward, CBitcoinAddress(outAddress).ToString(), wtx.vout[i].nValue, 0);
                        parts.append(txrMasternodeRec);
                    }
                }
            }
        }
        else
        {
            //stake reward
            TransactionRecord txrCoinStake = TransactionRecord(hash, nTime, TransactionRecord::StakeMint, CBitcoinAddress(address).ToString(), nNet, 0);
            parts.append(txrCoinStake);

        }
    }
    else if (nNet > 0 || wtx.IsCoinBase())
    {
        //
        // Credit
        //
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if(mine)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY;
                if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
                {
                    // Received by DarkNet Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }
                if (wtx.IsCoinBase())
                {
                    // Generated
                    sub.type = TransactionRecord::Generated;
                }

                parts.append(sub);
            }
        }
    }
    else
    {
        bool fAllFromMeDenom = true;
        int nFromMe = 0;
        bool involvesWatchAddress = false;
        isminetype fAllFromMe = ISMINE_SPENDABLE;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
        {
            if(wallet->IsMine(txin)) {
                fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin);
                nFromMe++;
            }
            isminetype mine = wallet->IsMine(txin);
            if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
            if(fAllFromMe > mine) fAllFromMe = mine;
        }

        isminetype fAllToMe = ISMINE_SPENDABLE;
        bool fAllToMeDenom = true;
        int nToMe = 0;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout) {
            if(wallet->IsMine(txout)) {
                fAllToMeDenom = fAllToMeDenom && wallet->IsDenominatedAmount(txout.nValue);
                nToMe++;
            }
            isminetype mine = wallet->IsMine(txout);
            if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
            if(fAllToMe > mine) fAllToMe = mine;
        }

        if(fAllFromMeDenom && fAllToMeDenom && nFromMe * nToMe) {
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::ObfuscationDenominate, "", -nDebit, nCredit));
            parts.last().involvesWatchAddress = false;   // maybe pass to TransactionRecord as constructor argument
        }
        else if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            // TODO: this section still not accurate but covers most cases,
            // might need some additional work however

            TransactionRecord sub(hash, nTime);
            // Payment to self by default
            sub.type = TransactionRecord::SendToSelf;
            sub.address = "";

            if(mapValue["DS"] == "1")
            {
                sub.type = TransactionRecord::Obfuscated;
                CTxDestination address;
                if (ExtractDestination(wtx.vout[0].scriptPubKey, address))
                {
                    // Sent to DarkNet Address
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.address = mapValue["to"];
                }
            }
            else
            {
                for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
                {
                    const CTxOut& txout = wtx.vout[nOut];
                    sub.idx = parts.size();

                    if(wallet->IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::ObfuscationMakeCollaterals;
                    if(wallet->IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::ObfuscationCreateDenominations;
                    if(nDebit - wtx.GetValueOut() == OBFUSCATION_COLLATERAL) sub.type = TransactionRecord::ObfuscationCollateralPayment;
                }
            }

            CAmount nChange = wtx.GetChange();

            sub.debit = -(nDebit - nChange);
            sub.credit = nCredit - nChange;
            parts.append(sub);
            parts.last().involvesWatchAddress = involvesWatchAddress;   // maybe pass to TransactionRecord as constructor argument
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            CAmount nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();
                sub.involvesWatchAddress = involvesWatchAddress;

                if(wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to DarkNet Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                if(mapValue["DS"] == "1")
                {
                    sub.type = TransactionRecord::Obfuscated;
                }

                CAmount nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = chainActive.Height();
    status.cur_num_ix_locks = nCompleteTXLocks;

    if (!IsFinalTx(wtx, chainActive.Height() + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - chainActive.Height();
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }
    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }

}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != chainActive.Height() || status.cur_num_ix_locks != nCompleteTXLocks;
}

QString TransactionRecord::getTxID() const
{
    return formatSubTxId(hash, idx);
}

QString TransactionRecord::formatSubTxId(const uint256 &hash, int vout)
{
    return QString::fromStdString(hash.ToString() + strprintf("-%03d", vout));
}

<commit_msg>fix masternode rewards showing as confirmed prematurely in transaction tab<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2016 The DarkNet developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "transactionrecord.h"

#include "base58.h"
#include "timedata.h"
#include "wallet.h"
#include "obfuscation.h"
#include "swifttx.h"

#include <stdint.h>

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash();
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (wtx.IsCoinStake())
    {
        CTxDestination address;
        if (!ExtractDestination(wtx.vout[1].scriptPubKey, address))
            return parts;

        if(!IsMine(*wallet, address)) //if the address is not yours then it means you have a tx sent to you in someone elses coinstake tx
        {
            for(unsigned int i = 0; i < wtx.vout.size(); i++)
            {
                if(i == 0)
                    continue; // first tx is blank
                CTxDestination outAddress;
                if(ExtractDestination(wtx.vout[i].scriptPubKey, outAddress))
                {
                    if(IsMine(*wallet, outAddress))
                    {
                        TransactionRecord txrMasternodeRec = TransactionRecord(hash, nTime, TransactionRecord::MNReward, CBitcoinAddress(outAddress).ToString(), wtx.vout[i].nValue, 0);
                        parts.append(txrMasternodeRec);
                    }
                }
            }
        }
        else
        {
            //stake reward
            TransactionRecord txrCoinStake = TransactionRecord(hash, nTime, TransactionRecord::StakeMint, CBitcoinAddress(address).ToString(), nNet, 0);
            parts.append(txrCoinStake);

        }
    }
    else if (nNet > 0 || wtx.IsCoinBase())
    {
        //
        // Credit
        //
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if(mine)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY;
                if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
                {
                    // Received by DarkNet Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }
                if (wtx.IsCoinBase())
                {
                    // Generated
                    sub.type = TransactionRecord::Generated;
                }

                parts.append(sub);
            }
        }
    }
    else
    {
        bool fAllFromMeDenom = true;
        int nFromMe = 0;
        bool involvesWatchAddress = false;
        isminetype fAllFromMe = ISMINE_SPENDABLE;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
        {
            if(wallet->IsMine(txin)) {
                fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin);
                nFromMe++;
            }
            isminetype mine = wallet->IsMine(txin);
            if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
            if(fAllFromMe > mine) fAllFromMe = mine;
        }

        isminetype fAllToMe = ISMINE_SPENDABLE;
        bool fAllToMeDenom = true;
        int nToMe = 0;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout) {
            if(wallet->IsMine(txout)) {
                fAllToMeDenom = fAllToMeDenom && wallet->IsDenominatedAmount(txout.nValue);
                nToMe++;
            }
            isminetype mine = wallet->IsMine(txout);
            if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
            if(fAllToMe > mine) fAllToMe = mine;
        }

        if(fAllFromMeDenom && fAllToMeDenom && nFromMe * nToMe) {
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::ObfuscationDenominate, "", -nDebit, nCredit));
            parts.last().involvesWatchAddress = false;   // maybe pass to TransactionRecord as constructor argument
        }
        else if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            // TODO: this section still not accurate but covers most cases,
            // might need some additional work however

            TransactionRecord sub(hash, nTime);
            // Payment to self by default
            sub.type = TransactionRecord::SendToSelf;
            sub.address = "";

            if(mapValue["DS"] == "1")
            {
                sub.type = TransactionRecord::Obfuscated;
                CTxDestination address;
                if (ExtractDestination(wtx.vout[0].scriptPubKey, address))
                {
                    // Sent to DarkNet Address
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.address = mapValue["to"];
                }
            }
            else
            {
                for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
                {
                    const CTxOut& txout = wtx.vout[nOut];
                    sub.idx = parts.size();

                    if(wallet->IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::ObfuscationMakeCollaterals;
                    if(wallet->IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::ObfuscationCreateDenominations;
                    if(nDebit - wtx.GetValueOut() == OBFUSCATION_COLLATERAL) sub.type = TransactionRecord::ObfuscationCollateralPayment;
                }
            }

            CAmount nChange = wtx.GetChange();

            sub.debit = -(nDebit - nChange);
            sub.credit = nCredit - nChange;
            parts.append(sub);
            parts.last().involvesWatchAddress = involvesWatchAddress;   // maybe pass to TransactionRecord as constructor argument
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            CAmount nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();
                sub.involvesWatchAddress = involvesWatchAddress;

                if(wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to DarkNet Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                if(mapValue["DS"] == "1")
                {
                    sub.type = TransactionRecord::Obfuscated;
                }

                CAmount nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = chainActive.Height();
    status.cur_num_ix_locks = nCompleteTXLocks;

    if (!IsFinalTx(wtx, chainActive.Height() + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - chainActive.Height();
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }
    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint || type == TransactionRecord::MNReward)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }

}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != chainActive.Height() || status.cur_num_ix_locks != nCompleteTXLocks;
}

QString TransactionRecord::getTxID() const
{
    return formatSubTxId(hash, idx);
}

QString TransactionRecord::formatSubTxId(const uint256 &hash, int vout)
{
    return QString::fromStdString(hash.ToString() + strprintf("-%03d", vout));
}

<|endoftext|>


<commit_before>#include "buffer_cache/alt/evicter.hpp"

#include "buffer_cache/alt/page.hpp"
#include "buffer_cache/alt/page_cache.hpp"
#include "buffer_cache/alt/cache_balancer.hpp"

namespace alt {

evicter_t::evicter_t(cache_balancer_t *balancer)
    : balancer_(balancer),
      bytes_loaded_counter_(0),
      access_time_counter_(INITIAL_ACCESS_TIME)
{
    guarantee(balancer_ != NULL);
    memory_limit_ = balancer_->base_mem_per_store();
    balancer_->add_evicter(this);
}

evicter_t::~evicter_t() {
    assert_thread();
    balancer_->remove_evicter(this);
}

void evicter_t::update_memory_limit(uint64_t new_memory_limit,
                                    uint64_t bytes_loaded_accounted_for) {
    assert_thread();
    __sync_sub_and_fetch(&bytes_loaded_counter_, bytes_loaded_accounted_for);
    memory_limit_ = new_memory_limit;
    evict_if_necessary();
}

uint64_t evicter_t::get_clamped_bytes_loaded() const {
    __sync_synchronize();
    int64_t res = bytes_loaded_counter_;
    __sync_synchronize();
    return std::max<int64_t>(res, 0);
}

void evicter_t::notify_bytes_loading(int64_t in_memory_buf_change) {
    assert_thread();
    __sync_add_and_fetch(&bytes_loaded_counter_, in_memory_buf_change);
    balancer_->notify_access();
}

void evicter_t::add_deferred_loaded(page_t *page) {
    assert_thread();
    evicted_.add(page, page->hypothetical_memory_usage());
}

void evicter_t::catch_up_deferred_load(page_t *page) {
    assert_thread();
    rassert(evicted_.has_page(page));
    evicted_.remove(page, page->hypothetical_memory_usage());
    unevictable_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::add_not_yet_loaded(page_t *page) {
    assert_thread();
    unevictable_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::reloading_page(page_t *page) {
    assert_thread();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

bool evicter_t::page_is_in_unevictable_bag(page_t *page) const {
    assert_thread();
    return unevictable_.has_page(page);
}

bool evicter_t::page_is_in_evicted_bag(page_t *page) const {
    assert_thread();
    return evicted_.has_page(page);
}

void evicter_t::add_to_evictable_unbacked(page_t *page) {
    assert_thread();
    evictable_unbacked_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::add_to_evictable_disk_backed(page_t *page) {
    assert_thread();
    evictable_disk_backed_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::move_unevictable_to_evictable(page_t *page) {
    assert_thread();
    rassert(unevictable_.has_page(page));
    unevictable_.remove(page, page->hypothetical_memory_usage());
    eviction_bag_t *new_bag = correct_eviction_category(page);
    rassert(new_bag == &evictable_disk_backed_
            || new_bag == &evictable_unbacked_);
    new_bag->add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
}

void evicter_t::change_to_correct_eviction_bag(eviction_bag_t *current_bag,
                                               page_t *page) {
    assert_thread();
    rassert(current_bag->has_page(page));
    current_bag->remove(page, page->hypothetical_memory_usage());
    eviction_bag_t *new_bag = correct_eviction_category(page);
    new_bag->add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
}

eviction_bag_t *evicter_t::correct_eviction_category(page_t *page) {
    assert_thread();
    if (page->is_loading() || page->has_waiters()) {
        return &unevictable_;
    } else if (page->is_not_loaded()) {
        return &evicted_;
    } else if (page->is_disk_backed()) {
        return &evictable_disk_backed_;
    } else {
        return &evictable_unbacked_;
    }
}

void evicter_t::remove_page(page_t *page) {
    assert_thread();
    eviction_bag_t *bag = correct_eviction_category(page);
    bag->remove(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(-static_cast<int64_t>(page->hypothetical_memory_usage()));
}

uint64_t evicter_t::in_memory_size() const {
    assert_thread();
    return unevictable_.size()
        + evictable_disk_backed_.size()
        + evictable_unbacked_.size();
}

void evicter_t::evict_if_necessary() {
    assert_thread();
    // KSI: Implement eviction of unbacked evictables too.  When flushing, you
    // could use the page_t::eviction_index_ field to identify pages that are
    // currently in the process of being evicted, to avoid reflushing a page
    // currently being written for the purpose of eviction.

    page_t *page;
    while (in_memory_size() > memory_limit_
           && evictable_disk_backed_.remove_oldish(&page, access_time_counter_)) {
        evicted_.add(page, page->hypothetical_memory_usage());
        page->evict_self();
    }
}

}  // namespace alt
<commit_msg>Made catch_up_deferred_load assert page is unevictable, rather than evicted...<commit_after>#include "buffer_cache/alt/evicter.hpp"

#include "buffer_cache/alt/page.hpp"
#include "buffer_cache/alt/page_cache.hpp"
#include "buffer_cache/alt/cache_balancer.hpp"

namespace alt {

evicter_t::evicter_t(cache_balancer_t *balancer)
    : balancer_(balancer),
      bytes_loaded_counter_(0),
      access_time_counter_(INITIAL_ACCESS_TIME)
{
    guarantee(balancer_ != NULL);
    memory_limit_ = balancer_->base_mem_per_store();
    balancer_->add_evicter(this);
}

evicter_t::~evicter_t() {
    assert_thread();
    balancer_->remove_evicter(this);
}

void evicter_t::update_memory_limit(uint64_t new_memory_limit,
                                    uint64_t bytes_loaded_accounted_for) {
    assert_thread();
    __sync_sub_and_fetch(&bytes_loaded_counter_, bytes_loaded_accounted_for);
    memory_limit_ = new_memory_limit;
    evict_if_necessary();
}

uint64_t evicter_t::get_clamped_bytes_loaded() const {
    __sync_synchronize();
    int64_t res = bytes_loaded_counter_;
    __sync_synchronize();
    return std::max<int64_t>(res, 0);
}

void evicter_t::notify_bytes_loading(int64_t in_memory_buf_change) {
    assert_thread();
    __sync_add_and_fetch(&bytes_loaded_counter_, in_memory_buf_change);
    balancer_->notify_access();
}

void evicter_t::add_deferred_loaded(page_t *page) {
    assert_thread();
    evicted_.add(page, page->hypothetical_memory_usage());
}

void evicter_t::catch_up_deferred_load(page_t *page) {
    assert_thread();
    rassert(unevictable_.has_page(page));
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::add_not_yet_loaded(page_t *page) {
    assert_thread();
    unevictable_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::reloading_page(page_t *page) {
    assert_thread();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

bool evicter_t::page_is_in_unevictable_bag(page_t *page) const {
    assert_thread();
    return unevictable_.has_page(page);
}

bool evicter_t::page_is_in_evicted_bag(page_t *page) const {
    assert_thread();
    return evicted_.has_page(page);
}

void evicter_t::add_to_evictable_unbacked(page_t *page) {
    assert_thread();
    evictable_unbacked_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::add_to_evictable_disk_backed(page_t *page) {
    assert_thread();
    evictable_disk_backed_.add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(page->hypothetical_memory_usage());
}

void evicter_t::move_unevictable_to_evictable(page_t *page) {
    assert_thread();
    rassert(unevictable_.has_page(page));
    unevictable_.remove(page, page->hypothetical_memory_usage());
    eviction_bag_t *new_bag = correct_eviction_category(page);
    rassert(new_bag == &evictable_disk_backed_
            || new_bag == &evictable_unbacked_);
    new_bag->add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
}

void evicter_t::change_to_correct_eviction_bag(eviction_bag_t *current_bag,
                                               page_t *page) {
    assert_thread();
    rassert(current_bag->has_page(page));
    current_bag->remove(page, page->hypothetical_memory_usage());
    eviction_bag_t *new_bag = correct_eviction_category(page);
    new_bag->add(page, page->hypothetical_memory_usage());
    evict_if_necessary();
}

eviction_bag_t *evicter_t::correct_eviction_category(page_t *page) {
    assert_thread();
    if (page->is_loading() || page->has_waiters()) {
        return &unevictable_;
    } else if (page->is_not_loaded()) {
        return &evicted_;
    } else if (page->is_disk_backed()) {
        return &evictable_disk_backed_;
    } else {
        return &evictable_unbacked_;
    }
}

void evicter_t::remove_page(page_t *page) {
    assert_thread();
    eviction_bag_t *bag = correct_eviction_category(page);
    bag->remove(page, page->hypothetical_memory_usage());
    evict_if_necessary();
    notify_bytes_loading(-static_cast<int64_t>(page->hypothetical_memory_usage()));
}

uint64_t evicter_t::in_memory_size() const {
    assert_thread();
    return unevictable_.size()
        + evictable_disk_backed_.size()
        + evictable_unbacked_.size();
}

void evicter_t::evict_if_necessary() {
    assert_thread();
    // KSI: Implement eviction of unbacked evictables too.  When flushing, you
    // could use the page_t::eviction_index_ field to identify pages that are
    // currently in the process of being evicted, to avoid reflushing a page
    // currently being written for the purpose of eviction.

    page_t *page;
    while (in_memory_size() > memory_limit_
           && evictable_disk_backed_.remove_oldish(&page, access_time_counter_)) {
        evicted_.add(page, page->hypothetical_memory_usage());
        page->evict_self();
    }
}

}  // namespace alt
<|endoftext|>