Я пытаюсь создать оболочку вокруг этого класса, чтобы сделать вызов Start синхронным, т. е. блокировать до тех пор, пока не будет выполнено Маршрутизатор сигнализирует, что соединение установлено. Таким образом, для потребителя это выглядит как один синхронный вызов.
Для этого я создаю обещание для каждого вызова Start, а затем блокирую вызов до тех пор, пока обещание не будет выполнено. Я выполняю эти обещания, завернутые в std::shared_ptr, в std::vector. Когда Маршрутизатор сигнализирует, что соединение установлено, вектор будет пройден и все обещания установлены.
Вот слегка отредактированный код из моего проекта:
class Wrapper {
private:
Router* m_router; // this is a custom class... details do not matter
std::vector m_pendingPromises;
std::recursive_mutex m_startStopMutex;
public:
// this gets called by the consumer to start the connection
void Start(){
if(m_router->IsRunning() == false) {
std::unique_lock lock(m_startStopMutex);
if(m_router->IsRunning() == false) {
// create the promise and put it in the pending list
auto promise = std::make_shared();
m_pendingPromises.emplace_back(promise);
// call on the router to start the connection
m_router->Start();
// unlock the mutex to allow the OnStarted handler to run
lock.unlock();
// wait for the promise to be fulfilled
promise->get_future().get();
}
}
}
// this will get called by the Router once the connection has started
void OnStarted() {
{
// lock the mutex to ensure that no more pending promises are created
std::lock_guard lock(m_startStopMutex);
// fulfill all the pending promises
for(int i = 0; i < m_pendingPromises.size(); i++) {
m_pendingPromises->set_value();
}
// clear the list of pending promises
m_pendingPromises.clear();
}
}
Проблема, с которой я столкнулся, связана с m_pendingPromises->set_value(); в методе OnStarted(). Когда я отлаживаю и приближаюсь к моменту, когда вектор начинает повторяться, но до того, как будет установлено какое-либо обещание, я вижу ожидаемое состояние m_pendingPromises как размер = 1 и емкость = 1:

Однако момент что я установил обещаю, размер вектора станет нулевым, а емкость достигнет безумно высокого значения!
[img]https://i.sstatic. net/o3KukEA4.png[/img]
Кто-нибудь знает, почему это происходит? Альтернативно, является ли это реальной ошибкой или просто отвлекающим маневром, вызванным отладчиком CLion?
К вашему сведению, я разрабатываю на C++ 17, используя CLion 2024.2.2:

Обновление: я изменил свое устройство тест, чтобы поместить два обещания в вектор. То же самое происходит, но только после установки последнего обещания. При настройке первого обещания я не вижу размер или емкость изменения вектора.
Обновление 2: изначально я опустил m_pendingPromises из конструктор, поскольку, насколько я понимаю, конструктор по умолчанию будет вызываться при его объявлении. Однако, просто чтобы попробовать, я добавил конструктор для Wrapper и инициализировал вектор значением 0:
void Wrapper() : m_pendingPromises(0) {}
Когда я это сделал, я получил аналогичное поведение, но с другими значениями размера и емкости:

Обновление 3. Публикация модульного теста, демонстрирующего такое поведение:
TEST_F(WrapperTestFixture, Start_RouterStartCalledOnce) {
// arrange
// set up router.IsRunning to return the running flag
EXPECT_CALL(this->router, IsRunning())
.Times(::testing::AnyNumber()) // EXPECT_CALL.Times(AnyNumber()) is preferable over ON_CALL because it doesn't raise "Uninteresting call" warnings
.WillRepeatedly([this]() -> bool { return this->connectionRunning; });
// set up router.Start to start a thread that will invoke the callback unless the running flag is toggled
EXPECT_CALL(this->router, Start())
.Times(::testing::AnyNumber()) // EXPECT_CALL.Times(AnyNumber()) is preferable over ON_CALL because it doesn't raise "Uninteresting call" warnings
.WillRepeatedly(::testing::Invoke([this]() {
this->m_numTimesStartCalled++;
auto future = std::async(std::launch::async, [this]() {
// toggle the running flag and raise the event that signals this connection is now running
this->connectionRunning = true;
this->wrapper.OnStarted();
});
// grab the future from the async so that the test fixture can make sure to wait until this thread is completed
this->m_futureFromRouterStart = std::make_shared(std::move(future));
}));
this->wrapper = std::move(Wrapper(this->router));
// act
this->wrapper.Start();
// assert
ASSERT_EQ(this->m_numTimesStartCalled, 1);
// make sure the Start thread is finished
this->m_futureFromRouterStart->get();
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... ro-and-cap