-
Notifications
You must be signed in to change notification settings - Fork 84
core: envoy engine #498
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
core: envoy engine #498
Changes from all commits
f3c8612
952f2bc
51eaa99
dab5e45
372c4a1
c177ab4
9a1130a
7ecafeb
5750ba2
258efb4
1cf968a
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 |
|---|---|---|
| @@ -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 |
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,11 +30,9 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr | |
| return init_engine(); | ||
| } | ||
|
|
||
| extern "C" JNIEXPORT jint JNICALL | ||
| Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine(JNIEnv* env, | ||
| jclass, // class | ||
| jstring config, jstring log_level) { | ||
| return run_engine(env->GetStringUTFChars(config, nullptr), | ||
| extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine( | ||
| JNIEnv* env, jclass, jlong engine, jstring config, jstring log_level) { | ||
| return run_engine(engine, env->GetStringUTFChars(config, nullptr), | ||
| env->GetStringUTFChars(log_level, nullptr)); | ||
| } | ||
|
|
||
|
|
@@ -121,6 +119,11 @@ static JNIEnv* get_env() { | |
| int get_env_res = static_jvm->GetEnv((void**)&env, JNI_VERSION); | ||
| if (get_env_res == JNI_EDETACHED) { | ||
| __android_log_write(ANDROID_LOG_ERROR, "jni_lib", "equals JNI_EDETACHED"); | ||
| // Note: the only thread that should need to be attached is Envoy's engine std::thread. | ||
| // TODO: harden this piece of code to make sure that we are only needing to attach Envoy | ||
| // engine's std::thread, and that we detach it successfully. | ||
| static_jvm->AttachCurrentThread(&env, NULL); | ||
|
Member
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. nit: s/NULL/nullptr
Member
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. will do in subsequent PR to avoid re-approval and re-running CI
Member
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. |
||
| static_jvm->GetEnv((void**)&env, JNI_VERSION); | ||
| } | ||
| return env; | ||
| } | ||
|
|
||
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.
I am going to add thread detachment in a subsequent PR, as it requires introducing engine level callbacks. I don't want to make this PR any bigger.