views:

164

answers:

1

I am currently writing a multi-threaded C++ server using Poco and am now at the point where I need to be keeping information on which users are connected, how many connections each of them have, and given it is a proxy server, where each of those connections are proxying through to.

For this purpose I have created a ServerStats class which holds an STL list of ServerUser objects. The ServerStats class includes functions which can add and remove objects from the list as well as find a user in the list an return a pointer to them so I can access member functions within any given ServerUser object in the list.

The ServerUser class contains an STL list of ServerConnection objects and much like the ServerStats class it contains functions to add, remove and find elements within this list.

Now all of the above is working but I am now trying to make it threadsafe.

I have defined a Poco::FastMutex within the ServerStats class and can lock/unlock this in the appropriate places so that STL containers are not modified at the same time as being searched for example. I am however having an issue setting up mutexes within the ServerUser class and am getting the following compiler error:

/root/poco/Foundation/include/Poco/Mutex.h: In copy constructor âServerUser::ServerUser(const ServerUser&)â: src/SocksServer.cpp:185:
instantiated from âvoid __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = ServerUser]â /usr/include/c++/4.4/bits/stl_list.h:464: instantiated from âstd::_List_node<_Tp>* std::list<_Tp, _Alloc>::_M_create_node(const _Tp&) [with _Tp = ServerUser, _Alloc = std::allocator]â /usr/include/c++/4.4/bits/stl_list.h:1407: instantiated from âvoid std::list<_Tp, _Alloc>::_M_insert(std::_List_iterator<_Tp>, const _Tp&) [with _Tp = ServerUser, _Alloc = std::allocator]â /usr/include/c++/4.4/bits/stl_list.h:920: instantiated from âvoid std::list<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = ServerUser, _Alloc = std::allocator]â src/SocksServer.cpp:301:
instantiated from here /root/poco/Foundation/include/Poco/Mutex.h:164: error: âPoco::FastMutex::FastMutex(const Poco::FastMutex&)â is private src/SocksServer.cpp:185: error: within this context In file included from /usr/include/c++/4.4/x86_64-linux-gnu/bits/c++allocator.h:34, from /usr/include/c++/4.4/bits/allocator.h:48, from /usr/include/c++/4.4/string:43, from /root/poco/Foundation/include/Poco/Bugcheck.h:44, from /root/poco/Foundation/include/Poco/Foundation.h:147, from /root/poco/Net/include/Poco/Net/Net.h:45, from /root/poco/Net/include/Poco/Net/TCPServerParams.h:43, from src/SocksServer.cpp:1: /usr/include/c++/4.4/ext/new_allocator.h: In member function âvoid __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = ServerUser]â: /usr/include/c++/4.4/ext/new_allocator.h:105: note: synthesized method âServerUser::ServerUser(const ServerUser&)â first required here src/SocksServer.cpp: At global scope: src/SocksServer.cpp:118: warning: âstd::string getWord(std::string)â defined but not used make: * [/root/poco/SocksServer/obj/Linux/x86_64/debug_shared/SocksServer.o] Error 1

The code for the ServerStats, ServerUser and ServerConnection classes is below:

class ServerConnection
{
public:
    bool continue_connection;
    int bytes_in;
    int bytes_out;
    string source_address;
    string destination_address;

    ServerConnection()
    {
        continue_connection = true;
    }

    ~ServerConnection()
    {
    }
};

class ServerUser
{
public:
    string username;
    int connection_count;
    string client_ip;

    ServerUser()
    {
    }

    ~ServerUser()
    {
    }

    ServerConnection* addConnection(string source_address, string destination_address)
    {
        //FastMutex::ScopedLock lock(_connection_mutex);

        ServerConnection connection;
        connection.source_address = source_address;
        connection.destination_address = destination_address;
        client_ip = getWord(source_address, ":");

        _connections.push_back(connection);
        connection_count++;

        return &_connections.back();
    }

    void removeConnection(string source_address)
    {
        //FastMutex::ScopedLock lock(_connection_mutex);

        for(list<ServerConnection>::iterator it = _connections.begin(); it != _connections.end(); it++)
        {
            if(it->source_address == source_address)
            {
                it = _connections.erase(it);
                connection_count--;
            }
        }
    }

    void disconnect()
    {    
        //FastMutex::ScopedLock lock(_connection_mutex);

        for(list<ServerConnection>::iterator it = _connections.begin(); it != _connections.end(); it++)
        {
            it->continue_connection = false;
        }
    }

    list<ServerConnection>* getConnections()
    {
        return &_connections;
    }

private:
    list<ServerConnection> _connections;

    //UNCOMMENTING THIS LINE BREAKS IT:
    //mutable FastMutex _connection_mutex;
};

class ServerStats
{
public:
    int current_users;

ServerStats()
{
    current_users = 0;
}

~ServerStats()
{
}

ServerUser* addUser(string username)
{
    FastMutex::ScopedLock lock(_user_mutex);

    for(list<ServerUser>::iterator it = _users.begin(); it != _users.end(); it++)
    {
        if(it->username == username)
        {
            return &(*it);
        }
    }

    ServerUser newUser;
    newUser.username = username;
    _users.push_back(newUser);
    current_users++;

    return &_users.back();
}

void removeUser(string username)
{
    FastMutex::ScopedLock lock(_user_mutex);

    for(list<ServerUser>::iterator it = _users.begin(); it != _users.end(); it++)
    {
        if(it->username == username)
        {
            _users.erase(it);
            current_users--;
            break;
        }
    }
}

ServerUser* getUser(string username)
{
    FastMutex::ScopedLock lock(_user_mutex);

    for(list<ServerUser>::iterator it = _users.begin(); it != _users.end(); it++)
    {
        if(it->username == username)
        {
            return &(*it);
        }
    }
    return NULL;
}

private:
    list<ServerUser> _users;
    mutable FastMutex _user_mutex;
};

Now I have never used C++ for a project of this size or mutexes for that matter so go easy please :)

Firstly, can anyone tell me why the above is causing a compiler error?

Secondly, can anyone suggest a better way of storing the information I require? Bear in mind that I need to update this info whenever connections come or go and it needs to be global to the whole server.

+1  A: 

The problem is that FastMutex isn't copyable, and consequently ServerUser isn't copyable. When you insert objects into an STL container, they have to be copied. I think you will have to change the design of your classes.

Also, you have to be really careful with returning pointers to objects which are stored in an STL container, because they can become invalid due to objects being reshuffled as you insert and remove things from the container.

Alex - Aotea Studios
@Genesis: You might consider making the mutex a static member unless there is a chance of making your threads to serial.
stefaanv
He can just store (smart) pointers of its object in the container, as boost/tr1::shared_ptr for instance
Nikko
@Nikko: yes, shared_ptr will work, but whether it's a good choice has to be considered in conjunction with how these classes are going to work with the rest of the system (and the issue of returning pointers that I mentioned should be considered at the same time). I don't really know enough about the rest of the code to propose a solution here.
Alex - Aotea Studios
@Alex: Thanks that makes sense now. Re lists: Are memory addresses of list elements not static once an element is assigned? My understanding is that whenever elements are added or removed from a list, those elements on either side of them have their pointers modified to point to the new element. I realise that this is not however the case for something like a vector where the elements are adjacent to eachother and need to be re-shuffled when any change is made.
Genesis
@Genesis: You're right about the list but using pointers still feels like relying on an implementation detail to me. STL generally supplants pointers with iterators, so why not return an iterator instead of a pointer?
Alex - Aotea Studios