Skip to content

Calling SocketReactor's run() method in a program's main thread yields a lot of null pointer exceptions #3417

@bassdscho

Description

@bassdscho

Calling SocketReactor's run() method in main thread yields a lot of null pointer exceptions.

This is because the SocketReactor's run method uses Thread::trySleep(...), which calls Thread::current()to obtain the current thread and do a try sleep on that if no socket handlers are installed. If called on the main thread, Thread::current() returns a nullptr (which is a documented behavior):

from SocketReactor.cpp:

// ...
try
{
    if (!hasSocketHandlers())
    {
        onIdle();
        Thread::trySleep(static_cast<long>(_timeout.totalMilliseconds()));
    }
    // ...
}
catch (Exception& exc)
{
	ErrorHandler::handle(exc);
}
// ...

Here's a minimal example:

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <chrono>
#include <future>

#include <Poco/Net/SocketReactor.h>
#include <Poco/Thread.h>

using namespace std::chrono_literals;

TEST(Poco, SocketReactorRunOnMainThread)
{
    Poco::Net::SocketReactor reactor;

    auto stopper = std::async(std::launch::async, [&reactor] {
        std::this_thread::sleep_for(1ms);
        reactor.stop();
    });

    ASSERT_EQ(Poco::Thread::current(), static_cast<Poco::Thread*>(0));
    reactor.run();
    stopper.get();
}

TEST(Poco, SocketReactorRunThreadOtherThanMain)
{
    Poco::Net::SocketReactor reactor;

    Poco::Thread runner;
    runner.startFunc([&reactor] {
        ASSERT_NE(Poco::Thread::current(), static_cast<Poco::Thread*>(0));
        reactor.run();
    });

    std::this_thread::sleep_for(1ms);
    reactor.stop();
    runner.join();
}

Possible output:

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from Poco
[ RUN      ] Poco.SocketReactorRunOnMainThread

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
NULL pointer: pT [in file "/workspace/deps/build/external-poco-prefix/src/external-poco/Foundation/src/Thread.cpp", line 167]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Null pointer [in file "/workspace/deps/build/external-poco-prefix/src/external-poco/Foundation/src/ErrorHandler.cpp", line 38]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

<<<< a lot more of those loggings >>>>

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
NULL pointer: pT [in file "/workspace/deps/build/external-poco-prefix/src/external-poco/Foundation/src/Thread.cpp", line 167]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Null pointer [in file "/workspace/deps/build/external-poco-prefix/src/external-poco/Foundation/src/ErrorHandler.cpp", line 38]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[       OK ] Poco.SocketReactorRunOnMainThread (2 ms)
[ RUN      ] Poco.SocketReactorRunThreadOtherThanMain
[       OK ] Poco.SocketReactorRunThreadOtherThanMain (256 ms)
[----------] 2 tests from Poco (258 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (258 ms total)
[  PASSED  ] 2 tests.

Consequently, wakeUp() will also not work.

Is it not intended to use the SocketReactor on a main thread for a reason that I have not found in the docs (yet?). It is quite usual to have a select loop running in the main thread to handle socket connections in a single threaded application.

The tests where built against poco-1.11.0-release and built with gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions