@@ -63,6 +63,8 @@ DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api,
6363 ? watermark_factory
6464 : std::make_shared<Buffer::WatermarkBufferFactory>()),
6565 scheduler_(time_system.createScheduler(base_scheduler_, base_scheduler_)),
66+ thread_local_delete_cb_(
67+ base_scheduler_.createSchedulableCallback([this ]() -> void { runThreadLocalDelete (); })),
6668 deferred_delete_cb_ (base_scheduler_.createSchedulableCallback(
6769 [this ]() -> void { clearDeferredDeleteList (); })),
6870 post_cb_ (base_scheduler_.createSchedulableCallback([this ]() -> void { runPostCallbacks (); })),
@@ -74,7 +76,12 @@ DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api,
7476 std::bind (&DispatcherImpl::updateApproximateMonotonicTime, this ));
7577}
7678
77- DispatcherImpl::~DispatcherImpl () { FatalErrorHandler::removeFatalErrorHandler (*this ); }
79+ DispatcherImpl::~DispatcherImpl () {
80+ ENVOY_LOG (debug, " destroying dispatcher {}" , name_);
81+ FatalErrorHandler::removeFatalErrorHandler (*this );
82+ // TODO(lambdai): Resolve https://github.com/envoyproxy/envoy/issues/15072 and enable
83+ // ASSERT(deletable_in_dispatcher_thread_.empty())
84+ }
7885
7986void DispatcherImpl::registerWatchdog (const Server::WatchDogSharedPtr& watchdog,
8087 std::chrono::milliseconds min_touch_interval) {
@@ -265,9 +272,23 @@ void DispatcherImpl::post(std::function<void()> callback) {
265272 }
266273}
267274
275+ void DispatcherImpl::deleteInDispatcherThread (DispatcherThreadDeletableConstPtr deletable) {
276+ bool need_schedule;
277+ {
278+ Thread::LockGuard lock (thread_local_deletable_lock_);
279+ need_schedule = deletables_in_dispatcher_thread_.empty ();
280+ deletables_in_dispatcher_thread_.emplace_back (std::move (deletable));
281+ // TODO(lambdai): Enable below after https://github.com/envoyproxy/envoy/issues/15072
282+ // ASSERT(!shutdown_called_, "inserted after shutdown");
283+ }
284+
285+ if (need_schedule) {
286+ thread_local_delete_cb_->scheduleCallbackCurrentIteration ();
287+ }
288+ }
289+
268290void DispatcherImpl::run (RunType type) {
269291 run_tid_ = api_.threadFactory ().currentThreadId ();
270-
271292 // Flush all post callbacks before we run the event loop. We do this because there are post
272293 // callbacks that have to get run before the initial event loop starts running. libevent does
273294 // not guarantee that events are run in any particular order. So even if we post() and call
@@ -280,12 +301,56 @@ MonotonicTime DispatcherImpl::approximateMonotonicTime() const {
280301 return approximate_monotonic_time_;
281302}
282303
304+ void DispatcherImpl::shutdown () {
305+ // TODO(lambdai): Resolve https://github.com/envoyproxy/envoy/issues/15072 and loop delete below
306+ // below 3 lists until all lists are empty. The 3 lists are list of deferred delete objects, post
307+ // callbacks and dispatcher thread deletable objects.
308+ ASSERT (isThreadSafe ());
309+ auto deferred_deletables_size = current_to_delete_->size ();
310+ std::list<std::function<void ()>>::size_type post_callbacks_size;
311+ {
312+ Thread::LockGuard lock (post_lock_);
313+ post_callbacks_size = post_callbacks_.size ();
314+ }
315+
316+ std::list<DispatcherThreadDeletableConstPtr> local_deletables;
317+ {
318+ Thread::LockGuard lock (thread_local_deletable_lock_);
319+ local_deletables = std::move (deletables_in_dispatcher_thread_);
320+ }
321+ auto thread_local_deletables_size = local_deletables.size ();
322+ while (!local_deletables.empty ()) {
323+ local_deletables.pop_front ();
324+ }
325+ ASSERT (!shutdown_called_);
326+ shutdown_called_ = true ;
327+ ENVOY_LOG (
328+ trace,
329+ " {} destroyed {} thread local objects. Peek {} deferred deletables, {} post callbacks. " ,
330+ __FUNCTION__, deferred_deletables_size, post_callbacks_size, thread_local_deletables_size);
331+ }
332+
283333void DispatcherImpl::updateApproximateMonotonicTime () { updateApproximateMonotonicTimeInternal (); }
284334
285335void DispatcherImpl::updateApproximateMonotonicTimeInternal () {
286336 approximate_monotonic_time_ = api_.timeSource ().monotonicTime ();
287337}
288338
339+ void DispatcherImpl::runThreadLocalDelete () {
340+ std::list<DispatcherThreadDeletableConstPtr> to_be_delete;
341+ {
342+ Thread::LockGuard lock (thread_local_deletable_lock_);
343+ to_be_delete = std::move (deletables_in_dispatcher_thread_);
344+ ASSERT (deletables_in_dispatcher_thread_.empty ());
345+ }
346+ while (!to_be_delete.empty ()) {
347+ // Touch the watchdog before deleting the objects to avoid spurious watchdog miss events when
348+ // executing complicated destruction.
349+ touchWatchdog ();
350+ // Delete in FIFO order.
351+ to_be_delete.pop_front ();
352+ }
353+ }
289354void DispatcherImpl::runPostCallbacks () {
290355 // Clear the deferred delete list before running post callbacks to reduce non-determinism in
291356 // callback processing, and more easily detect if a scheduled post callback refers to one of the
0 commit comments