-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dispatcher: keeps a stack of tracked objects. #14573
Changes from all commits
07e671b
918383d
41d85bf
cb43d8a
98d318c
0940f3a
0fe46db
3f8ba3e
265b3a7
3434c7b
7182e95
3885c7a
0724016
be52ed4
984ffc0
e760c57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,12 @@ | |
#include <vector> | ||
|
||
#include "envoy/api/api.h" | ||
#include "envoy/common/scope_tracker.h" | ||
#include "envoy/network/listen_socket.h" | ||
#include "envoy/network/listener.h" | ||
|
||
#include "common/buffer/buffer_impl.h" | ||
#include "common/common/assert.h" | ||
#include "common/common/lock_guard.h" | ||
#include "common/common/thread.h" | ||
#include "common/event/file_event_impl.h" | ||
|
@@ -287,6 +289,16 @@ void DispatcherImpl::runPostCallbacks() { | |
} | ||
} | ||
|
||
void DispatcherImpl::onFatalError(std::ostream& os) const { | ||
// Dump the state of the tracked objects in the dispatcher if thread safe. This generally | ||
// results in dumping the active state only for the thread which caused the fatal error. | ||
if (isThreadSafe()) { | ||
for (auto iter = tracked_object_stack_.rbegin(); iter != tracked_object_stack_.rend(); ++iter) { | ||
(*iter)->dumpState(os); | ||
} | ||
} | ||
} | ||
|
||
void DispatcherImpl::runFatalActionsOnTrackedObject( | ||
const FatalAction::FatalActionPtrList& actions) const { | ||
// Only run if this is the dispatcher of the current thread and | ||
|
@@ -296,7 +308,7 @@ void DispatcherImpl::runFatalActionsOnTrackedObject( | |
} | ||
|
||
for (const auto& action : actions) { | ||
action->run(current_object_); | ||
action->run(tracked_object_stack_); | ||
} | ||
} | ||
|
||
|
@@ -306,5 +318,23 @@ void DispatcherImpl::touchWatchdog() { | |
} | ||
} | ||
|
||
void DispatcherImpl::pushTrackedObject(const ScopeTrackedObject* object) { | ||
KBaichoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ASSERT(isThreadSafe()); | ||
ASSERT(object != nullptr); | ||
tracked_object_stack_.push_back(object); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
ASSERT(tracked_object_stack_.size() <= ExpectedMaxTrackedObjectStackDepth); | ||
} | ||
|
||
void DispatcherImpl::popTrackedObject(const ScopeTrackedObject* expected_object) { | ||
antoniovicente marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ASSERT(isThreadSafe()); | ||
ASSERT(expected_object != nullptr); | ||
RELEASE_ASSERT(!tracked_object_stack_.empty(), "Tracked Object Stack is empty, nothing to pop!"); | ||
|
||
const ScopeTrackedObject* top = tracked_object_stack_.back(); | ||
tracked_object_stack_.pop_back(); | ||
ASSERT(top == expected_object, | ||
"Popped the top of the tracked object stack, but it wasn't the expected object!"); | ||
} | ||
|
||
} // namespace Event | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#include <iostream> | ||
|
||
#include "common/api/api_impl.h" | ||
#include "common/common/scope_tracker.h" | ||
#include "common/event/dispatcher_impl.h" | ||
|
||
#include "test/mocks/common.h" | ||
#include "test/test_common/utility.h" | ||
|
||
#include "gmock/gmock.h" | ||
#include "gtest/gtest.h" | ||
|
||
namespace Envoy { | ||
namespace { | ||
|
||
using testing::_; | ||
|
||
TEST(ScopeTrackerScopeStateTest, ShouldManageTrackedObjectOnDispatcherStack) { | ||
Api::ApiPtr api(Api::createApiForTest()); | ||
Event::DispatcherPtr dispatcher(api->allocateDispatcher("test_thread")); | ||
MockScopedTrackedObject tracked_object; | ||
{ | ||
ScopeTrackerScopeState scope(&tracked_object, *dispatcher); | ||
// Check that the tracked_object is on the tracked object stack | ||
dispatcher->popTrackedObject(&tracked_object); | ||
|
||
// Restore it to the top, it should be removed in the dtor of scope. | ||
dispatcher->pushTrackedObject(&tracked_object); | ||
} | ||
|
||
// Check nothing is tracked now. | ||
EXPECT_CALL(tracked_object, dumpState(_, _)).Times(0); | ||
static_cast<Event::DispatcherImpl*>(dispatcher.get())->onFatalError(std::cerr); | ||
} | ||
|
||
} // namespace | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
#include <functional> | ||
|
||
#include "envoy/common/scope_tracker.h" | ||
#include "envoy/thread/thread.h" | ||
|
||
#include "common/api/api_impl.h" | ||
#include "common/api/os_sys_calls_impl.h" | ||
#include "common/common/lock_guard.h" | ||
#include "common/common/scope_tracker.h" | ||
#include "common/common/utility.h" | ||
#include "common/event/deferred_task.h" | ||
#include "common/event/dispatcher_impl.h" | ||
#include "common/event/timer_impl.h" | ||
|
@@ -533,9 +536,71 @@ TEST_F(DispatcherImplTest, IsThreadSafe) { | |
EXPECT_FALSE(dispatcher_->isThreadSafe()); | ||
} | ||
|
||
TEST_F(DispatcherImplTest, ShouldDumpNothingIfNoTrackedObjects) { | ||
std::array<char, 1024> buffer; | ||
OutputBufferStream ostream{buffer.data(), buffer.size()}; | ||
|
||
// Call on FatalError to trigger dumps of tracked objects. | ||
dispatcher_->post([this, &ostream]() { | ||
Thread::LockGuard lock(mu_); | ||
static_cast<DispatcherImpl*>(dispatcher_.get())->onFatalError(ostream); | ||
work_finished_ = true; | ||
cv_.notifyOne(); | ||
}); | ||
|
||
Thread::LockGuard lock(mu_); | ||
while (!work_finished_) { | ||
cv_.wait(mu_); | ||
} | ||
|
||
// Check ostream still empty. | ||
EXPECT_EQ(ostream.contents(), ""); | ||
} | ||
|
||
class MessageTrackedObject : public ScopeTrackedObject { | ||
public: | ||
MessageTrackedObject(absl::string_view sv) : sv_(sv) {} | ||
void dumpState(std::ostream& os, int /*indent_level*/) const override { os << sv_; } | ||
|
||
private: | ||
absl::string_view sv_; | ||
}; | ||
|
||
TEST_F(DispatcherImplTest, ShouldDumpTrackedObjectsInFILO) { | ||
std::array<char, 1024> buffer; | ||
OutputBufferStream ostream{buffer.data(), buffer.size()}; | ||
|
||
// Call on FatalError to trigger dumps of tracked objects. | ||
dispatcher_->post([this, &ostream]() { | ||
Thread::LockGuard lock(mu_); | ||
|
||
// Add several tracked objects to the dispatcher | ||
MessageTrackedObject first{"first"}; | ||
ScopeTrackerScopeState first_state{&first, *dispatcher_}; | ||
MessageTrackedObject second{"second"}; | ||
ScopeTrackerScopeState second_state{&second, *dispatcher_}; | ||
MessageTrackedObject third{"third"}; | ||
ScopeTrackerScopeState third_state{&third, *dispatcher_}; | ||
|
||
static_cast<DispatcherImpl*>(dispatcher_.get())->onFatalError(ostream); | ||
work_finished_ = true; | ||
cv_.notifyOne(); | ||
}); | ||
|
||
Thread::LockGuard lock(mu_); | ||
while (!work_finished_) { | ||
cv_.wait(mu_); | ||
} | ||
|
||
// Check the dump includes and registered objects in a FILO order. | ||
EXPECT_EQ(ostream.contents(), "thirdsecondfirst"); | ||
} | ||
|
||
class TestFatalAction : public Server::Configuration::FatalAction { | ||
public: | ||
void run(const ScopeTrackedObject* /*current_object*/) override { ++times_ran_; } | ||
void run(absl::Span<const ScopeTrackedObject* const> /*tracked_objects*/) override { | ||
++times_ran_; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add some tests that cover 0, 1, >1 entries in tracked_objects ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
bool isAsyncSignalSafe() const override { return true; } | ||
int getNumTimesRan() { return times_ran_; } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test that covers dumping of multiple tracked objects, including relative ordering of the dumps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.