-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Description: create a class to manage Envoy lifecycle and allow for graceful teardown. Teardown is now graceful for both iOS and Android. Envoy's codebase expects to have shutdown happen from the main thread. However, in mobile the thread the engine runs in is not the main thread of the process, and thus shutdown/destructors were not getting run from the thread the engine was started on. This PR makes sure that the engine is destructed/shutdown from the thread that it ran in. Risk Level: high. This PR changes state management for the engine, and initializes it on a std::thread instead of a platform thread. iOS thread management is afaict handled gracefully. On Android the native thread has to be attached and detached from the JVM. This PR attaches the native thread, but the work to detach is a bit involved so will come in a subsequent PR. Testing: local device testing. Fixes #492 Co-authored-by: Jose Nino <[email protected]> Signed-off-by: Mike Schore <[email protected]> Signed-off-by: JP Simard <[email protected]>
- Loading branch information
Showing
10 changed files
with
176 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#include "library/common/engine.h" | ||
|
||
#include "common/common/lock_guard.h" | ||
|
||
namespace Envoy { | ||
|
||
// As a server, Envoy's static factory registration happens when main is run. However, when compiled | ||
// as a library, there is no guarantee that such registration will happen before the names are | ||
// needed. The following calls ensure that registration happens before the entities are needed. Note | ||
// that as more registrations are needed, explicit initialization calls will need to be added here. | ||
static void registerFactories() { | ||
Envoy::Extensions::Clusters::DynamicForwardProxy::forceRegisterClusterFactory(); | ||
Envoy::Extensions::HttpFilters::DynamicForwardProxy:: | ||
forceRegisterDynamicForwardProxyFilterFactory(); | ||
Envoy::Extensions::HttpFilters::RouterFilter::forceRegisterRouterFilterConfig(); | ||
Envoy::Extensions::NetworkFilters::HttpConnectionManager:: | ||
forceRegisterHttpConnectionManagerFilterConfigFactory(); | ||
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterDownstreamRawBufferSocketFactory(); | ||
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterUpstreamRawBufferSocketFactory(); | ||
Envoy::Extensions::TransportSockets::Tls::forceRegisterUpstreamSslSocketFactory(); | ||
Envoy::Upstream::forceRegisterLogicalDnsClusterFactory(); | ||
} | ||
|
||
Engine::Engine(const char* config, const char* log_level, | ||
std::atomic<envoy_network_t>& preferred_network) { | ||
// Ensure static factory registration occurs on time. | ||
// TODO: ensure this is only called one time once multiple Engine objects can be allocated. | ||
registerFactories(); | ||
|
||
// Create the Http::Dispatcher first since it contains initial queueing logic. | ||
// TODO: consider centralizing initial queueing in this class. | ||
http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network); | ||
|
||
// Start the Envoy on a dedicated thread. | ||
main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level)); | ||
} | ||
|
||
envoy_status_t Engine::run(std::string config, std::string log_level) { | ||
{ | ||
Thread::LockGuard lock(mutex_); | ||
try { | ||
char* envoy_argv[] = {strdup("envoy"), strdup("--config-yaml"), strdup(config.c_str()), | ||
strdup("-l"), strdup(log_level.c_str()), nullptr}; | ||
|
||
main_common_ = std::make_unique<Envoy::MainCommon>(5, envoy_argv); | ||
event_dispatcher_ = &main_common_->server()->dispatcher(); | ||
cv_.notifyOne(); | ||
} catch (const Envoy::NoServingException& e) { | ||
return ENVOY_FAILURE; | ||
} catch (const Envoy::MalformedArgvException& e) { | ||
std::cerr << e.what() << std::endl; | ||
return ENVOY_FAILURE; | ||
} catch (const Envoy::EnvoyException& e) { | ||
std::cerr << e.what() << std::endl; | ||
return ENVOY_FAILURE; | ||
} | ||
|
||
// Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher. | ||
// This is because we're not simply waiting for its availability and for it to have started, but | ||
// also because we're waiting for clusters to have done their first attempt at DNS resolution. | ||
// When we improve synchronous failure handling and/or move to dynamic forwarding, we only need | ||
// to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it, | ||
// as we did previously). | ||
auto server = main_common_->server(); | ||
postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback( | ||
Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this, server]() -> void { | ||
http_dispatcher_->ready(server->dispatcher(), server->clusterManager()); | ||
}); | ||
} // mutex_ | ||
|
||
// The main run loop must run without holding the mutex, so that the destructor can acquire it. | ||
return TS_UNCHECKED_READ(main_common_)->run() ? ENVOY_SUCCESS : ENVOY_FAILURE; | ||
} | ||
|
||
Engine::~Engine() { | ||
// If we're already on the main thread, it should be safe to simply destruct. | ||
if (!main_thread_.joinable()) { | ||
return; | ||
} | ||
|
||
// If we're not on the main thread, we need to be sure that MainCommon is finished being | ||
// constructed so we can dispatch shutdown. | ||
{ | ||
Thread::LockGuard lock(mutex_); | ||
if (!main_common_) { | ||
cv_.wait(mutex_); | ||
} | ||
ASSERT(main_common_); | ||
// Gracefully shutdown the running envoy instance by resetting the main_common_ unique_ptr. | ||
// Destroying MainCommon's member variables shutsdown things in the correct order and | ||
// gracefully. | ||
event_dispatcher_->post([this]() -> void { TS_UNCHECKED_READ(main_common_).reset(); }); | ||
} // _mutex | ||
|
||
// Now we wait for the main thread to wrap things up. | ||
main_thread_.join(); | ||
} | ||
|
||
Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; } | ||
|
||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#pragma once | ||
|
||
#include "envoy/server/lifecycle_notifier.h" | ||
|
||
#include "common/upstream/logical_dns_cluster.h" | ||
|
||
#include "exe/main_common.h" | ||
|
||
#include "extensions/clusters/dynamic_forward_proxy/cluster.h" | ||
#include "extensions/filters/http/dynamic_forward_proxy/config.h" | ||
#include "extensions/filters/http/router/config.h" | ||
#include "extensions/filters/network/http_connection_manager/config.h" | ||
#include "extensions/transport_sockets/raw_buffer/config.h" | ||
#include "extensions/transport_sockets/tls/config.h" | ||
|
||
#include "absl/base/call_once.h" | ||
#include "library/common/http/dispatcher.h" | ||
#include "library/common/types/c_types.h" | ||
|
||
namespace Envoy { | ||
|
||
class Engine { | ||
public: | ||
Engine(const char* config, const char* log_level, | ||
std::atomic<envoy_network_t>& preferred_network); | ||
|
||
~Engine(); | ||
|
||
envoy_status_t run(std::string config, std::string log_level); | ||
|
||
Http::Dispatcher& httpDispatcher(); | ||
|
||
private: | ||
Thread::MutexBasicLockable mutex_; | ||
Thread::CondVar cv_; | ||
std::thread main_thread_; | ||
std::unique_ptr<Envoy::Http::Dispatcher> http_dispatcher_; | ||
std::unique_ptr<Envoy::MainCommon> main_common_ GUARDED_BY(mutex_); | ||
Envoy::Server::ServerLifecycleNotifier::HandlePtr postinit_callback_handler_; | ||
Event::Dispatcher* event_dispatcher_; | ||
}; | ||
|
||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters