Files
flipper/android/android/sonar.cpp
John Knox cebc409da6 Change SonarInitConfig to take two EventBases
Summary:
We currently give sonar one event base from java / obj-c code to use for scheduling tasks.
This causes a problem, because we schedule reconnect tasks on the event base, and then these tasks interact with rsocket which schedules it's own tasks.
The problem is that we're passing rsocket the same event base. So the reconnect code executes and blocks waiting for rsocket to connect.
But rsocket can never connect because it never executes because that thread is blocked, so we get deadlock.

This is the first step which just changes the interface to pass two event bases.
The consumers will be changed to pass in different threads next.

Reviewed By: danielbuechele

Differential Revision: D8748350

fbshipit-source-id: 481d6f23644f28fd0f1c786252605287019c999c
2018-07-09 07:49:07 -07:00

312 lines
9.4 KiB
C++

/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#include <memory>
#ifdef SONAR_OSS
#include <fbjni/fbjni.h>
#else
#include <fb/fbjni.h>
#endif
#include <folly/json.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/EventBaseManager.h>
#include <Sonar/SonarClient.h>
#include <Sonar/SonarWebSocket.h>
#include <Sonar/SonarConnection.h>
#include <Sonar/SonarResponder.h>
using namespace facebook;
using namespace facebook::sonar;
namespace {
class JEventBase : public jni::HybridClass<JEventBase> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/android/EventBase;";
static void registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", JEventBase::initHybrid),
makeNativeMethod("loopForever", JEventBase::loopForever),
});
}
folly::EventBase* eventBase() {
return &eventBase_;
}
private:
friend HybridBase;
JEventBase() {}
void loopForever() {
folly::EventBaseManager::get()->setEventBase(&eventBase_, false);
eventBase_.loopForever();
}
static void initHybrid(jni::alias_ref<jhybridobject> o) {
return setCxxInstance(o);
}
folly::EventBase eventBase_;
};
class JSonarObject : public jni::JavaClass<JSonarObject> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarObject;";
static jni::local_ref<JSonarObject> create(const folly::dynamic& json) {
return newInstance(folly::toJson(json));
}
std::string toJsonString() {
static const auto method = javaClassStatic()->getMethod<std::string()>("toJsonString");
return method(self())->toStdString();
}
};
class JSonarArray : public jni::JavaClass<JSonarArray> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarArray;";
static jni::local_ref<JSonarArray> create(const folly::dynamic& json) {
return newInstance(folly::toJson(json));
}
std::string toJsonString() {
static const auto method = javaClassStatic()->getMethod<std::string()>("toJsonString");
return method(self())->toStdString();
}
};
class JSonarResponder : public jni::JavaClass<JSonarResponder> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarResponder;";
};
class JSonarResponderImpl : public jni::HybridClass<JSonarResponderImpl, JSonarResponder> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/android/SonarResponderImpl;";
static void registerNatives() {
registerHybrid({
makeNativeMethod("successObject", JSonarResponderImpl::successObject),
makeNativeMethod("successArray", JSonarResponderImpl::successArray),
makeNativeMethod("error", JSonarResponderImpl::error),
});
}
void successObject(jni::alias_ref<JSonarObject> json) {
_responder->success(json ? folly::parseJson(json->toJsonString()) : folly::dynamic::object());
}
void successArray(jni::alias_ref<JSonarArray> json) {
_responder->success(json ? folly::parseJson(json->toJsonString()) : folly::dynamic::object());
}
void error(jni::alias_ref<JSonarObject> json) {
_responder->error(json ? folly::parseJson(json->toJsonString()) : folly::dynamic::object());
}
private:
friend HybridBase;
std::shared_ptr<SonarResponder> _responder;
JSonarResponderImpl(std::shared_ptr<SonarResponder> responder): _responder(std::move(responder)) {}
};
class JSonarReceiver : public jni::JavaClass<JSonarReceiver> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarReceiver;";
void receive(const folly::dynamic params, std::shared_ptr<SonarResponder> responder) const {
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JSonarObject::javaobject>, jni::alias_ref<JSonarResponder::javaobject>)>("onReceive");
method(self(), JSonarObject::create(std::move(params)), JSonarResponderImpl::newObjectCxxArgs(responder));
}
};
class JSonarConnection : public jni::JavaClass<JSonarConnection> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarConnection;";
};
class JSonarConnectionImpl : public jni::HybridClass<JSonarConnectionImpl, JSonarConnection> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/android/SonarConnectionImpl;";
static void registerNatives() {
registerHybrid({
makeNativeMethod("sendObject", JSonarConnectionImpl::sendObject),
makeNativeMethod("sendArray", JSonarConnectionImpl::sendArray),
makeNativeMethod("reportError", JSonarConnectionImpl::reportError),
makeNativeMethod("receive", JSonarConnectionImpl::receive),
});
}
void sendObject(const std::string method, jni::alias_ref<JSonarObject> json) {
_connection->send(std::move(method), json ? folly::parseJson(json->toJsonString()) : folly::dynamic::object());
}
void sendArray(const std::string method, jni::alias_ref<JSonarArray> json) {
_connection->send(std::move(method), json ? folly::parseJson(json->toJsonString()) : folly::dynamic::object());
}
void reportError(jni::alias_ref<jni::JThrowable> throwable) {
_connection->error(throwable->toString(), throwable->getStackTrace()->toString());
}
void receive(const std::string method, jni::alias_ref<JSonarReceiver> receiver) {
auto global = make_global(receiver);
_connection->receive(std::move(method), [global] (const folly::dynamic& params, std::unique_ptr<SonarResponder> responder) {
global->receive(params, std::move(responder));
});
}
private:
friend HybridBase;
std::shared_ptr<SonarConnection> _connection;
JSonarConnectionImpl(std::shared_ptr<SonarConnection> connection): _connection(std::move(connection)) {}
};
class JSonarPlugin : public jni::JavaClass<JSonarPlugin> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarPlugin;";
std::string identifier() const {
static const auto method = javaClassStatic()->getMethod<std::string()>("getId");
return method(self())->toStdString();
}
void didConnect(std::shared_ptr<SonarConnection> conn) {
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JSonarConnection::javaobject>)>("onConnect");
method(self(), JSonarConnectionImpl::newObjectCxxArgs(conn));
}
void didDisconnect() {
static const auto method = javaClassStatic()->getMethod<void()>("onDisconnect");
method(self());
}
};
class JSonarPluginWrapper : public SonarPlugin {
public:
jni::global_ref<JSonarPlugin> jplugin;
virtual std::string identifier() const override {
return jplugin->identifier();
}
virtual void didConnect(std::shared_ptr<SonarConnection> conn) override {
jplugin->didConnect(conn);
}
virtual void didDisconnect() override {
jplugin->didDisconnect();
}
JSonarPluginWrapper(jni::global_ref<JSonarPlugin> plugin): jplugin(plugin) {}
};
class JSonarClient : public jni::HybridClass<JSonarClient> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/android/SonarClientImpl;";
static void registerNatives() {
registerHybrid({
makeNativeMethod("init", JSonarClient::init),
makeNativeMethod("getInstance", JSonarClient::getInstance),
makeNativeMethod("start", JSonarClient::start),
makeNativeMethod("stop", JSonarClient::stop),
makeNativeMethod("addPlugin", JSonarClient::addPlugin),
makeNativeMethod("removePlugin", JSonarClient::removePlugin),
makeNativeMethod("getPlugin", JSonarClient::getPlugin),
});
}
static jni::alias_ref<JSonarClient::javaobject> getInstance(jni::alias_ref<jclass>) {
static auto client = make_global(newObjectCxxArgs());
return client;
}
void start() {
SonarClient::instance()->start();
}
void stop() {
SonarClient::instance()->stop();
}
void addPlugin(jni::alias_ref<JSonarPlugin> plugin) {
auto wrapper = std::make_shared<JSonarPluginWrapper>(make_global(plugin));
SonarClient::instance()->addPlugin(wrapper);
}
void removePlugin(jni::alias_ref<JSonarPlugin> plugin) {
auto client = SonarClient::instance();
client->removePlugin(client->getPlugin(plugin->identifier()));
}
jni::alias_ref<JSonarPlugin> getPlugin(const std::string& identifier) {
auto plugin = SonarClient::instance()->getPlugin(identifier);
if (plugin) {
auto wrapper = std::static_pointer_cast<JSonarPluginWrapper>(plugin);
return wrapper->jplugin;
} else {
return nullptr;
}
}
static void init(
jni::alias_ref<jclass>,
JEventBase* callbackWorker,
JEventBase* connectionWorker,
const std::string host,
const std::string os,
const std::string device,
const std::string deviceId,
const std::string app,
const std::string appId,
const std::string privateAppDirectory) {
SonarClient::init({
{
std::move(host),
std::move(os),
std::move(device),
std::move(deviceId),
std::move(app),
std::move(appId),
std::move(privateAppDirectory)
},
callbackWorker->eventBase(),
connectionWorker->eventBase()
});
}
private:
friend HybridBase;
JSonarClient() {}
};
} // namespace
jint JNI_OnLoad(JavaVM* vm, void*) {
return jni::initialize(vm, [] {
JSonarClient::registerNatives();
JSonarConnectionImpl::registerNatives();
JSonarResponderImpl::registerNatives();
JEventBase::registerNatives();
});
}