admin管理员组文章数量:1123049
I have a project that uses a custom class, Router, that has an asynchronous call to Start a connection. How this works is that the consumer will call into Start(), which will return immediately. At a later time, then, the Router will signal that the connection has started.
I am trying to put a wrapper around this class to make the Start call synchronous, i.e. will block until the Router signals back that the connection has started. So to the consumer it looks like a single, synchronous call.
To accomplish this, I create a promise for every call to Start and then block the call until the promise gets fulfilled. I keep these promises, wrapped in std::shared_ptr<>, in a std::vector. When the Router signals that the connection has started, the vector will be iterated over and all promises are set.
Here is some lightly edited code from my project:
class Wrapper {
private:
Router* m_router; // this is a custom class... details do not matter
std::vector<std::shared_ptr<std::promise<void>>> 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<std::promise<void>>();
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[i]->set_value();
}
// clear the list of pending promises
m_pendingPromises.clear();
}
}
The issue that I'm running into is at the m_pendingPromises[i]->set_value();
in the OnStarted()
method. When I debug and I step up to the point where the vector starts to get iterated, but before any promise is set, I see the expected state of m_pendingPromises
as size = 1 and capacity = 1:
However, the instant that I set the promise, the vector's size goes to zero and the capacity goes to some crazy high number!
Does anyone know why this is happening? Alternatively, is this a real error or just a red herring caused by the CLion debugger?
FYI I am developing in C++ 17 using CLion 2024.2.2:
Update: I modified my unit test to put two promises into the vector. The same behavior occurs but only after the last promise is set. When setting the first promise, I do not see the size or capacity of the vector change.
Update 2: I originally omitted m_pendingPromises
from the constructor since, as I understand it, the default constructor will be called when it gets declared. However, just to try, I did add a constructor for the Wrapper and initialized the vector to 0:
void Wrapper() : m_pendingPromises(0) {}
When I did this, I got a similar behavior but with different values for size and capacity:
Update 3: Posting the unit test that exhibits this behavior:
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; });
auto wrapper = Wrapper(this->router);
// 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, wrapper]() {
this->m_numTimesStartCalled++;
auto future = std::async(std::launch::async, [this, wrapper]() {
// toggle the running flag and raise the event that signals this connection is now running
this->connectionRunning = true;
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::future<void>>(std::move(future));
}));
// act
wrapper.Start();
// assert
ASSERT_EQ(this->m_numTimesStartCalled, 1);
// make sure the Start thread is finished
this->m_futureFromRouterStart->get();
}
本文标签:
版权声明:本文标题:c++ - Setting a std::promise inside a vector changes the vector's size to zero and capacity to a nonsensical value - Sta 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736543708a1944418.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论