-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
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