SpeakX
SpeakX

Reputation: 385

Beast Socket from Boost Python Package Kills Jupyter Kernel

For some reason when exposing the below to python using Boost Python, the unique pointer constructor (the obj constructor) fails. This used to work for me before, not sure why it stopped.

#pragma once
#include <memory>
#include <boost/asio/ssl.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast.hpp>

//Socket
class Contextable
{
public:
    Contextable() : m_ssl(boost::asio::ssl::context::sslv23_client) {}

protected:
    boost::asio::io_context    m_ctx;
    boost::asio::ssl::context  m_ssl;
};

class SocketExample : protected Contextable
{
    using SocketImp = boost::beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>;
public:
    SocketExample()
    {
    }

    void constructSocket()
    {
        m_ws = std::make_unique<SocketImp>(m_ctx, m_ssl); //kills jupyter kernel
        std::cout << "DONE. OPEN FLAG: " << m_ws->is_open() << std::endl;
    }

private:
    std::unique_ptr<SocketImp> m_ws;
};

//ExampleModule
#include <boost/python.hpp>

BOOST_PYTHON_MODULE(ExampleModule)
{
    boost::python::class_<SocketExample, std::shared_ptr<SocketExample>, boost::noncopyable>("SocketExample")
        .def(boost::python::init<>())
        .def("constructSocket", &SocketExample::constructSocket)
        ;
}
##Jupyter Notebook
import ExampleModule as m
socket = m.SocketExample()
socket.constructSocket() ##kills kernel

Any idea what I am doing wrong? When running this using c++ as follows does not fail.

SocketExample sk;
sk.constructSocket(); //works fine when run inside main.cpp

Upvotes: 1

Views: 62

Answers (1)

sehe
sehe

Reputation: 392833

You're most likely pulling in indirect dependencies that are missing from the Jupyter environment.

I've compiled your source (with minor adaptations)

#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <iostream>
#include <memory>

// Socket
class Contextable {
  public:
    Contextable() : m_ssl(boost::asio::ssl::context::sslv23_client) {}

  protected:
    boost::asio::io_context   m_ctx;
    boost::asio::ssl::context m_ssl;
};

class SocketExample : protected Contextable {
    using SocketImp = boost::beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>;

  public:
    SocketExample() { std::cout << "SocketExample constructor" << std::endl; }
    ~SocketExample() { std::cout << "SocketExample destructor" << std::endl; }

    void constructSocket() {
        m_ws = std::make_unique<SocketImp>(m_ctx, m_ssl);
        std::cout << "DONE. OPEN FLAG: " << m_ws->is_open() << std::endl;
    }

  private:
    std::unique_ptr<SocketImp> m_ws;
};

// ExampleModule
#include <boost/python.hpp>

BOOST_PYTHON_MODULE(ExampleModule) {
    boost::python::class_<SocketExample, std::shared_ptr<SocketExample>, boost::noncopyable>("SocketExample")
        .def(boost::python::init<>())
        .def("constructSocket", &SocketExample::constructSocket);
}

A simple CLI test using python:

enter image description here

Starting a Jupyter notebook¹:

enter image description here

Trouble In Paradise

As an example of why things would break, let me enable ASAN/UBSAN on the C++ module, by adding

-fsanitize=undefined,address

to the compiler flags. Now, the python CLI test will actually show what's wrong:

enter image description here

We can tell python to load ASAN prior²:

LD_PRELOAD=/nix/store/jfilhsiqdgm4nks2z6labx3iq9qd077a-gcc-13.3.0-lib/lib/libasan.so.8 python ./test.py

And then it works, giving some expectable memory leak reports:

enter image description here

However, if you try to load this into a Jupyter notebook, it won't know how to load the ASAN dependency and fail:

Summary

Hopefully this gives you some ideas of what technical issues might be at play, and some ideas of how to start diagnosing them (at least on a Linux-like system as I used).

In general, try to reduce the number of (non-standard) compiler/linker flags. Inspect the shared library (loadable module) for any unexpected/unknown/unavailable dependencies. On Windows, you might use Dependency Walker or - more advanced - Fusion Logging to inspect.


¹ Disclaimer I had never used Jupyter, so I installed it fresh and just winged it...

² I got that path using ldd ExampleModule.so

Upvotes: 1

Related Questions