2017-12-21 14 views
2

먼저 모든 관련 질문을 읽었습니다.shared_from_this는 boost :: asio로 bad_weak_ptr을 발생시킵니다.

그들은 "shared_from_this를 사용하려면 먼저 shared_ptr이 있어야합니다."라고 말합니다. 내가 볼 수있는 한, 그 조건을 위반하는 방법은 없습니다. 나는 Foo의 인스턴스를 shared_ptr로 생성하고 항상 shared_ptr로 생성되도록 강제합니다. 그런 다음 shared_ptr을 콜렉션에 저장했습니다. 그러나 shared_from_this가 호출 될 때 여전히 bad_weak_ptr 예외가 발생합니다.

#pragma once 

#include <memory> 
#include <vector> 

//-------------------------------------------------------------------- 
class Foo : std::enable_shared_from_this<Foo> 
{ 
public: 

    typedef std::shared_ptr<Foo> SharedPtr; 

    // Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this 
    static Foo::SharedPtr Create() 
    { 
     return Foo::SharedPtr(new Foo()); 
    }; 

    Foo(const Foo &) = delete; 
    Foo(Foo &&) = delete; 
    Foo & operator = (const Foo &) = delete; 
    Foo & operator = (Foo &&) = delete; 
    ~Foo() {}; 

    // We have to defer the start until we are fully constructed because we share_from_this() 
    void Start() 
    { 
     DoStuff(); 
    } 

private: 

    Foo() {} 

    void DoStuff() 
    { 
     auto self(shared_from_this()); 
    } 
}; 

//-------------------------------------------------------------------- 
int main() 
{ 
    std::vector<Foo::SharedPtr> foos; 
    Foo::SharedPtr foo = Foo::Create(); 
    foos.emplace_back(foo); 
    foo->Start(); 

    return 0; 
} 
+2

는 [mcve] 만듭니다. – user2079303

+1

나는 그것이 최소한이라고 생각하지 않는다. 문제의 재생산을 막지 않고 제거 할 수없는 단일 표현이 없다고 제안 하시겠습니까? 그러한 모든 표현식을 반드시 제거해야하는 것은 아니지만, 예가 커질수록 가능한 한 많이 제거하는 것이 더 중요합니다. 작은 예제로 축소 할 수없는 프로그래밍 문제는 극히 적습니다. 나는 항상 긴 프로그램 목록에 MCVE를 요구하지 않지만, 나는 종종 그렇게한다. – user2079303

답변

2

당신은 공개적으로 표준에서 상속

에 따라 public 지정자 enable_shared_from_this을 상속해야합니다 :: enable_shared_from_this는 멤버 함수 shared_from_this와 유형 (T)를 제공합니다.

http://en.cppreference.com/w/cpp/memory/enable_shared_from_this.

그래서

class Foo : public std::enable_shared_from_this<Foo> 
1

첫째, 당신이 이제까지 게시 작업하기 전에 스레드를 시작 쓰기 때문에 io_service::run() 실제로 수행 DoAccept 전에 완료하는 경향이있다.

class Connection : public std::enable_shared_from_this<Connection> { 

자체에 포함 된 코드를 사용 : : enable_shared_from_this가 작동하려면

다음으로, 기본 클래스는 PUBLIC해야

#include <iostream> 
#include <mutex> 
namespace SomeNamespace{ 
struct Logger { 
    enum { LOGGER_SEVERITY_INFO }; 
    void Log(std::string const& msg, std::string const& file, unsigned line, int level) const { 
     static std::mutex mx; 
     std::lock_guard<std::mutex> lk(mx); 
     std::cout << file << ":" << line << " level:" << level << " " << msg << "\n"; 
    } 
    template <typename... Args> 
    void LogF(std::string const& msg, Args const&... args) const { 
     static std::mutex mx; 
     std::lock_guard<std::mutex> lk(mx); 
     static char buf[2048]; 
     snprintf(buf, sizeof(buf)-1, msg.c_str(), args...); 
     std::cout << buf << "\n"; 
    } 
    static Logger &GetInstance() { 
     static Logger This; 
     return This; 
    } 
}; 
} // namespace Somenamespace 

#include <boost/asio.hpp> 

#include <atomic> 
#include <condition_variable> 
#include <memory> 

//-------------------------------------------------------------------- 
class ConnectionManager; 

//-------------------------------------------------------------------- 
class Connection : public std::enable_shared_from_this<Connection> { 
    public: 
    typedef std::shared_ptr<Connection> SharedPtr; 

    // Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this 
    static Connection::SharedPtr Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket); 

    Connection(const Connection &) = delete; 
    Connection(Connection &&) = delete; 
    Connection &operator=(const Connection &) = delete; 
    Connection &operator=(Connection &&) = delete; 
    ~Connection(); 

    // We have to defer the start until we are fully constructed because we share_from_this() 
    void Start(); 
    void Stop(); 

    void Send(const std::vector<char> &data); 

    private: 
    ConnectionManager *m_owner; 
    boost::asio::ip::tcp::socket m_socket; 
    std::atomic<bool> m_stopped; 
    boost::asio::streambuf m_receiveBuffer; 
    mutable std::mutex m_sendMutex; 
    std::shared_ptr<std::vector<boost::asio::const_buffer> > m_sendBuffers; 
    bool m_sending; 

    std::vector<char> m_allReadData; // for testing 

    Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket); 

    void DoReceive(); 
    void DoSend(); 
}; 

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

//#include "Connection.h" 
//#include "ConnectionManager.h" 
//**ConnectionManager.h ** 

//#pragma once 

//#include "Connection.h" 

// Boost Includes 
#include <boost/asio.hpp> 

// Standard Includes 
#include <thread> 
#include <vector> 

//-------------------------------------------------------------------- 
class ConnectionManager { 
    public: 
    ConnectionManager(unsigned port, size_t numThreads); 
    ConnectionManager(const ConnectionManager &) = delete; 
    ConnectionManager(ConnectionManager &&) = delete; 
    ConnectionManager &operator=(const ConnectionManager &) = delete; 
    ConnectionManager &operator=(ConnectionManager &&) = delete; 
    ~ConnectionManager(); 

    void Start(); 
    void Stop(); 

    void OnConnectionClosed(Connection::SharedPtr connection); 

    protected: 
    boost::asio::io_service m_io_service; 
    boost::asio::ip::tcp::acceptor m_acceptor; 
    boost::asio::ip::tcp::socket m_listenSocket; 
    std::vector<std::thread> m_threads; 

    mutable std::mutex m_connectionsMutex; 
    std::vector<Connection::SharedPtr> m_connections; 

    void IoServiceThreadProc(); 

    void DoAccept(); 
}; 

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

#include <boost/bind.hpp> 

#include <algorithm> 

//-------------------------------------------------------------------- 
Connection::SharedPtr Connection::Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket) { 
    return Connection::SharedPtr(new Connection(connectionManager, std::move(socket))); 
} 

//-------------------------------------------------------------------- 
Connection::Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket) 
     : m_owner(connectionManager), m_socket(std::move(socket)), m_stopped(false), m_receiveBuffer(), m_sendMutex(), 
      m_sendBuffers(), m_sending(false), m_allReadData() {} 

//-------------------------------------------------------------------- 
Connection::~Connection() { 
    // Boost uses RAII, so we don't have anything to do. Let thier destructors take care of business 
} 

//-------------------------------------------------------------------- 
void Connection::Start() { DoReceive(); } 

//-------------------------------------------------------------------- 
void Connection::Stop() { 
    // The entire connection class is only kept alive, because it is a shared pointer and always has a ref count 
    // as a consequence of the outstanding async receive call that gets posted every time we receive. 
    // Once we stop posting another receive in the receive handler and once our owner release any references to 
    // us, we will get destroyed. 
    m_stopped = true; 
    m_owner->OnConnectionClosed(shared_from_this()); 
} 

//-------------------------------------------------------------------- 
void Connection::Send(const std::vector<char> &data) { 
    std::lock_guard<std::mutex> lock(m_sendMutex); 

    // If the send buffers do not exist, then create them 
    if (!m_sendBuffers) { 
     m_sendBuffers = std::make_shared<std::vector<boost::asio::const_buffer> >(); 
    } 

    // Copy the data to be sent to the send buffers 
    m_sendBuffers->emplace_back(boost::asio::buffer(data)); 

    DoSend(); 
} 

//-------------------------------------------------------------------- 
void Connection::DoSend() { 
    // According to the boost documentation, we cannot issue an async_write while one is already outstanding 
    // 
    // If that is the case, it is OK, because we've added the data to be sent to a new set of buffers back in 
    // the Send method. Notice how the original buffer is moved, so therefore will be null below and how Send 
    // will create new buffers and accumulate data to be sent until we complete in the lamda 
    // 
    // When we complete in the lamda, if we have any new data to be sent, we call DoSend once again. 
    // 
    // It is important though, that DoSend is only called from the lambda below and the Send method. 

    if (!m_sending && m_sendBuffers) { 
     m_sending = true; 
     auto copy = std::move(m_sendBuffers); 
     auto self(shared_from_this()); 

     boost::asio::async_write(m_socket, *copy, 
      [self, copy](const boost::system::error_code &errorCode, size_t bytes_transferred) { 
       std::lock_guard<std::mutex> lock(self->m_sendMutex); 
       self->m_sending = false; 

       if (errorCode) { 
        // An error occurred 
        return; 
       } 

       self->DoSend(); 
      }); 
    } 
} 

//-------------------------------------------------------------------- 
void Connection::DoReceive() { 
    SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO); 
    auto self(shared_from_this()); // ***EXCEPTION HERE**** 

    boost::asio::async_read_until(m_socket, m_receiveBuffer, '#', 
     [self](const boost::system::error_code &errorCode, size_t bytesRead) { 
      if (errorCode) { 
       // Notify our masters that we are ready to be destroyed 
       self->m_owner->OnConnectionClosed(self); 

       // An error occured 
       return; 
      } 

      // Grab the read data 
      std::istream stream(&self->m_receiveBuffer); 
      std::string data; 
      std::getline(stream, data, '#'); 

      // Issue the next receive 
      if (!self->m_stopped) { 
       self->DoReceive(); 
      } 
     }); 
} 

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

//**ConnectionManager.cpp ** 

//#include "ConnectionManager.h" 

//#include "Logger.h" 

#include <boost/bind.hpp> 

#include <system_error> 

//------------------------------------------------------------------------------ 
ConnectionManager::ConnectionManager(unsigned port, size_t numThreads) 
     : m_io_service(), m_acceptor(m_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), 
      m_listenSocket(m_io_service), m_threads(numThreads) {} 

//------------------------------------------------------------------------------ 
ConnectionManager::~ConnectionManager() { Stop(); } 

//------------------------------------------------------------------------------ 
void ConnectionManager::Start() { 
    if (m_io_service.stopped()) { 
     m_io_service.reset(); 
    } 

    DoAccept(); 

    for (auto &thread : m_threads) { 
     if (!thread.joinable()) { 
      thread = std::thread(&ConnectionManager::IoServiceThreadProc, this); 
     } 
    } 
} 

//------------------------------------------------------------------------------ 
void ConnectionManager::Stop() { 
    { 
     std::lock_guard<std::mutex> lock(m_connectionsMutex); 
     m_connections.clear(); 
    } 

    // TODO - Will the stopping of the io_service be enough to kill all the connections and ultimately have them get 
    // destroyed? 
    //  Because remember they have outstanding ref count to thier shared_ptr in the async handlers 
    m_io_service.stop(); 

    for (auto &thread : m_threads) { 
     if (thread.joinable()) { 
      thread.join(); 
     } 
    } 
} 

//------------------------------------------------------------------------------ 
void ConnectionManager::IoServiceThreadProc() { 
    try { 
     // Log that we are starting the io_service thread 
     { 
      const std::string msg("io_service socket thread starting."); 
      SomeNamespace::Logger::GetInstance().Log(msg, __FILE__, __LINE__, 
                 SomeNamespace::Logger::LOGGER_SEVERITY_INFO); 
     } 

     // Run the asynchronous callbacks from the socket on this thread 
     // Until the io_service is stopped from another thread 
     m_io_service.run(); 
    } catch (std::system_error &e) { 
     SomeNamespace::Logger::GetInstance().LogF("System error caught in io_service socket thread. Error Code: %d", e.code().value()); 
    } catch (std::exception &e) { 
     SomeNamespace::Logger::GetInstance().LogF("Standard exception caught in io_service socket thread. Exception: %s", e.what()); 
    } catch (...) { 
     SomeNamespace::Logger::GetInstance().LogF("Unhandled exception caught in io_service socket thread."); 
    } 

    SomeNamespace::Logger::GetInstance().LogF("io_service socket thread exiting."); 
} 

//------------------------------------------------------------------------------ 
void ConnectionManager::DoAccept() { 
    SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO); 

    m_acceptor.async_accept(m_listenSocket, [this](const boost::system::error_code errorCode) { 
     if (errorCode) { 
      return; 
     } 

     { 
      // Create the connection from the connected socket 
      Connection::SharedPtr connection = Connection::Create(this, m_listenSocket); 
      { 
       std::lock_guard<std::mutex> lock(m_connectionsMutex); 
       m_connections.push_back(connection); 
       connection->Start(); 
      } 
     } 

     DoAccept(); 
    }); 
} 

//------------------------------------------------------------------------------ 
void ConnectionManager::OnConnectionClosed(Connection::SharedPtr connection) { 
    std::lock_guard<std::mutex> lock(m_connectionsMutex); 

    auto itConnection = std::find(m_connections.begin(), m_connections.end(), connection); 
    if (itConnection != m_connections.end()) { 
     m_connections.erase(itConnection); 
    } 
} 

//------------------------------------------------------------------------------ 
//**main.cpp** 
//#include "ConnectionManager.h" 

#include <cstring> 
#include <iostream> 
#include <string> 

int main() { 
    ConnectionManager connectionManager(4000, 2); 
    connectionManager.Start(); 

    std::this_thread::sleep_for(std::chrono::minutes(1)); 

    connectionManager.Stop(); 
}