i init CefResourceManager pointer in my handler class constructor,and call SetupResourceManager function immediately.
cefclient_handler::cefclient_handler(CefRefPtr<QCefPage> listner, CefRefPtr<CefRenderHandler> osrenderHandler)
: is_closing_(false), clientListner_(listner), osrenderHandler_(osrenderHandler){
resource_manager_ = new CefResourceManager();
SetupResourceManager(resource_manager_ ,"file:///D:/test/test.zip")
}
void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager,std::string resource_dir) {
if (!CefCurrentlyOn(TID_IO)) {
// Execute on the browser IO thread.
CefPostTask(TID_IO, base::Bind(SetupResourceManager, resource_manager, resource_dir));
return;
}
resource_manager->AddArchiveProvider("https://***.net", resource_dir,"", 0,
std::string());
}
it works well.
But in the current business needs, when the handler is initialized, the resource (zip file) is not ready, so I try to call CreateBrowser first, and then call AddArchiveProvider in my handler class when the resource file is downloaded, like this.
void cefclient_handler::AddArchiveProvider(const std::string& url_path, const std::string& archive_path, const std::string& password, int order, const std::string& identifier)
{
if (!CefCurrentlyOn(TID_IO)) {
// Execute on the browser IO thread.
CefPostTask(TID_IO, base::Bind(&cefclient_handler::AddArchiveProvider, this, url_path, archive_path, password, order, identifier));
return;
}
if (resource_manager_.get())
{
resource_manager_->AddArchiveProvider(url_path, archive_path, "", 0,
std::string());
}
}
It doesn't work anymore.
Related
I have one problem. In c++ app, I am using sd-bus and signal does not call my callback function.
I hooked to org.freedesktop.login1, interface is org.freedesktop.DBus.Properties, member is PropertiesChanged and path is /org/freedesktop/login1/seat/seat0
In my connect method I have this:
sd_bus_add_match(m_bus, NULL, "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/login1/seat/seat0',type='signal'", on_properties_changed, NULL)
On properties changed method is this:
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
printf("got some signal");
}
So, when I ran this program, I also ran following command in cmd:ΒΈ
gdbus monitor --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1/seat/seat0
However, when I switch from userA to userB, I got following line in cmd window:
/org/freedesktop/login1/seat/seat0: org.freedesktop.DBus.Properties.PropertiesChanged ('org.freedesktop.login1.Seat', {'ActiveSession': <('c7', objectpath '/org/freedesktop/login1/session/c7')>}, #as [])
Also when I tried this
busctl --system --match "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/login1/seat/seat0',type='signal' monitor
then I also get proper response
Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=2281
Sender=:1.0 Path=/org/freedesktop/login1/seat/seat0 Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged
UniqueName=:1.0
MESSAGE "sa{sv}as" {
STRING "org.freedesktop.login1.Seat";
ARRAY "{sv}" {
DICT_ENTRY "sv" {
STRING "ActiveSession";
VARIANT "(so)" {
STRUCT "so" {
STRING "c2";
OBJECT_PATH "/org/freedesktop/login1/session/c2";
};
};
};
};
ARRAY "s" {
};
};
But in c++ that callback function is not called. Any idea why is it not called?
I am using Ubuntu 16.04 and my systemd version is 229.
I found a solution to this problem. Problem was that I wasn't hooked to any event loop.
So I created new function run() and in this function I say this:
while(m_running) {
sd_bus_message *m = NULL;
r = sd_bus_process(m_bus, &m);
if (r < 0) {
//error handling
}
r = sd_bus_wait(m_bus, (uint64_t)-1);
if (r < 0) {
//error handling
}
}
and now I call this function after I connect to signal, and callback function of sd_bus_add_match is normally called
This question involves etcd specific stuff, but I think the question more related to work with gRPC in general.
I'm trying to create etcd Watch for some keys, since the documentation is sparse I had a look at Nokia implementation
It was easy to adapt code to my needs and I came up with first version which worked just fine, creating WatchCreateRequest, and firing callback on key update. So far so good. Then I've tried to add more than one key to watch. Fiasco! ClientAsyncReaderWriter is failing to Read/Write in such a case. Now to the question.
If I have following members in my class
Watch::Stub watchStub;
CompletionQueue completionQueue;
ClientContext context;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest, WatchResponse>> stream;
WatchResponse reply;
and I want to support multiple Watches added to my class, I guess I have to hold several variables per watch and not as class members.
First of all, I guess, WatchResponse reply should be one per Watch. I'm less sure about the stream, should I hold one per Watch? I'm almost sure that context could be reused for all Watches and 100% sure the stub and completionQueue can be reused for all Watches.
So the question is my guess-work right? Whats about thread safety? Didnt find any documentation describing what objects are safe to use from multiple thread and where I have to synchronize access.
Any link to documentation (not this one) will be appreciated!
Test code before I split members into single Watch property
(no proper shutdown, I know)
using namespace grpc;
class Watcher
{
public:
using Callback = std::function<void(const std::string&, const std::string&)>;
Watcher(std::shared_ptr<Channel> channel) : watchStub(channel)
{
stream = watchStub.AsyncWatch(&context, &completionQueue, (void*) "create");
eventPoller = std::thread([this]() { WaitForEvent(); });
}
void AddWatch(const std::string& key, Callback callback)
{
AddWatch(key, callback, false);
}
void AddWatches(const std::string& key, Callback callback)
{
AddWatch(key, callback, true);
}
private:
void AddWatch(const std::string& key, Callback callback, bool isRecursive)
{
auto insertionResult = callbacks.emplace(key, callback);
if (!insertionResult.second) {
throw std::runtime_error("Event handle already exist.");
}
WatchRequest watch_req;
WatchCreateRequest watch_create_req;
watch_create_req.set_key(key);
if (isRecursive) {
watch_create_req.set_range_end(key + "\xFF");
}
watch_req.mutable_create_request()->CopyFrom(watch_create_req);
stream->Write(watch_req, (void*) insertionResult.first->first.c_str());
stream->Read(&reply, (void*) insertionResult.first->first.c_str());
}
void WaitForEvent()
{
void* got_tag;
bool ok = false;
while (completionQueue.Next(&got_tag, &ok)) {
if (ok == false) {
break;
}
if (got_tag == (void*) "writes done") {
// Signal shutdown
}
else if (got_tag == (void*) "create") {
}
else if (got_tag == (void*) "write") {
}
else {
auto tag = std::string(reinterpret_cast<char*>(got_tag));
auto findIt = callbacks.find(tag);
if (findIt == callbacks.end()) {
throw std::runtime_error("Key \"" + tag + "\"not found");
}
if (reply.events_size()) {
ParseResponse(findIt->second);
}
stream->Read(&reply, got_tag);
}
}
}
void ParseResponse(Callback& callback)
{
for (int i = 0; i < reply.events_size(); ++i) {
auto event = reply.events(i);
auto key = event.kv().key();
callback(event.kv().key(), event.kv().value());
}
}
Watch::Stub watchStub;
CompletionQueue completionQueue;
ClientContext context;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest, WatchResponse>> stream;
WatchResponse reply;
std::unordered_map<std::string, Callback> callbacks;
std::thread eventPoller;
};
I'm sorry that I'm not very sure about the proper Watch design here. It's not very clear to me whether you want to create a gRPC call for each Watch.
Anyway, each gRPC call will have its own ClientContext, ClientAsyncReaderWriter. But stub and CompletionQueue is not per-call thing.
As far as I know, there is no central place to find the thread-safe classes. You may want to read the API document to have a correct expectation.
When I was writing the async server load reporting service, the only place I add synchronization myself is around CompletionQueue, so that I don't en-queue new tags to the cq if it's shut down.
In order to separate my GUI from the logic (which fetches data from a REST service), I refactored some logic into a controller.
Now, only part of the logic seems to work.
The GUI component looks like this after refactoring (I am using the JUCE framework)
#pragma once
#include "../../JuceLibraryCode/JuceHeader.h"
#include "../../GUI.Controller/includes/ProjectEntryListController.h"
#include "ProjectEntryListComponent.h"
#include "LocalProjectEntryComponent.h"
class ProjectBrowserTabComponent : public TabbedComponent
{
public:
ProjectBrowserTabComponent();
~ProjectBrowserTabComponent();
private:
ProjectEntryListComponent m_remote_proj;
ProjectEntryListComponent m_local_proj;
ProjectEntryListController *pelccont = new ProjectEntryListController(&m_remote_proj);
ProjectEntryListController *pelccont2 = new ProjectEntryListController(&m_local_proj);
};
The GUI controller looks like this:
#define BOOST_THREAD_PROVIDES_FUTURE
#include "../includes/ProjectEntryListController.h"
template<typename R>
bool isReady(std::future<R> const& f)
{
Logger::writeToLog("check future");
return f.wait_for(std::chrono::seconds(-1)) == std::future_status::ready;
}
ProjectEntryListController::ProjectEntryListController(ProjectEntryListComponent *comp) {
m_comp = comp;
requestProjects();
}
void ProjectEntryListController::requestProjects()
{
Logger::writeToLog("requesting projects");
projectsFuture = std::async(std::launch::async, &ProjectsController::getProjects, &pc);
Logger::writeToLog("requested projects");
}
void ProjectEntryListController::backgroundCheckFuture()
{
timer = new boost::asio::deadline_timer(io_service, boost::posix_time::seconds(interval_secs));
timer->async_wait(boost::bind(&ProjectEntryListController::fetchData, this, boost::asio::placeholders::error, timer));
ioSvcFuture = std::async(std::launch::async, static_cast<size_t(boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io_service);
}
void ProjectEntryListController::initData() {
requestProjects();
backgroundCheckFuture();
}
void ProjectEntryListController::fetchData(const boost::system::error_code& /*e*/,
boost::asio::deadline_timer* tmr) {
if (isReady(projectsFuture)) {
projects = projectsFuture.get();
for (auto project : projects)
{
ProjectEntryComponent *pec = new ProjectEntryComponent(std::to_string(project.getId()), "222");
m_comp->addListEntry(pec);
m_comp->repaint();
}
Logger::writeToLog("got projs");
}
else {
tmr->expires_at(tmr->expires_at() + boost::posix_time::seconds(interval_secs));
tmr->async_wait(boost::bind(&ProjectEntryListController::fetchData, this, boost::asio::placeholders::error, tmr));
}
}
The requestProjects method's log messages are appearing in my console, but not the log message of the getProjects method I am calling asynchronously:
std::vector<Project> ProjectsController::getProjects() {
std::vector<Project> result;
if(serviceClient != nullptr) {
try
{
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "controller requested projs\n";
result = serviceClient->getAvailableProjects();
}
catch (const std::exception&)
{
}
}
return result;
}
However, when I debug into the code, the debugger (using VS 2015) also is able to step to the log message.
What am I doing wrong?
Actually I solved this now.
1.) I was calling the wrong method requestProjects instead of initData
2.) I couldn't see the result because the implementation of ProjectEntryComponent::ProjectEntryComponent(std::string name, std::string version) was missing
The purpose of the following code is to have various classes publish data to an observable. Some classes will observe every data, some will observe periodically with buffer_with_time().
This works well until the program exits, then it crashes, probably because the observer using buffer_with_time() is still hanging on to some thread.
struct Data
{
Data() : _subscriber(_subject.get_subscriber()) { }
~Data() { _subscriber.on_completed(); }
void publish(std::string data) { _subscriber.on_next(data); }
rxcpp::observable<std::string> observable() { return _subject.get_observable(); }
private:
rxcpp::subjects::subject<std::string> _subject;
rxcpp::subscriber<std::string> _subscriber;
};
void foo()
{
Data data;
auto period = std::chrono::milliseconds(30);
auto s1 = data.observable()
.buffer_with_time(period , rxcpp::observe_on_new_thread())
.subscribe([](std::vector<std::string>& data)
{ std::cout << data.size() << std::endl; });
data.publish("test 1");
data.publish("test 2");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// hope to call something here so s1's thread can be joined.
// program crashes upon exit
}
I tried calling "s1.unsubscribe()", and various as_blocking(), from(), merge(), but still can't get the program to exit gracefully.
Note that I used "subjects" here because "publish" can then be called from different places (which can be from different threads). I am not sure if this is the best mechanism to do that, I am open to other ways to accomplish that.
Advice?
This is very close to working..
However, having the Data destructor complete the input while also wanting the subscription to block the exit of foo until input is completed makes this more complex.
Here is a way to ensure that foo blocks after Data destructs. This is using the existing Data contract.
void foo1()
{
rxcpp::observable<std::vector<std::string>> buffered;
{
Data data;
auto period = std::chrono::milliseconds(30);
buffered = data.observable()
.buffer_with_time(period , rxcpp::observe_on_new_thread())
.publish().ref_count();
buffered
.subscribe([](const std::vector<std::string>& data)
{ printf("%lu\n", data.size()); },
[](){printf("data complete\n");});
data.publish("test 1");
data.publish("test 2");
// hope to call something here so s1's thread can be joined.
// program crashes upon exit
}
buffered.as_blocking().subscribe();
printf("exit foo1\n");
}
Alternatively, the changing the shape of Data (add a complete method) would allow the following code:
struct Data
{
Data() : _subscriber(_subject.get_subscriber()) { }
~Data() { complete(); }
void publish(std::string data) { _subscriber.on_next(data); }
void complete() {_subscriber.on_completed();}
rxcpp::observable<std::string> observable() { return _subject.get_observable(); }
private:
rxcpp::subjects::subject<std::string> _subject;
rxcpp::subscriber<std::string> _subscriber;
};
void foo2()
{
printf("foo2\n");
Data data;
auto newthread = rxcpp::observe_on_new_thread();
auto period = std::chrono::milliseconds(30);
auto buffered = data.observable()
.buffer_with_time(period , newthread)
.tap([](const std::vector<std::string>& data)
{ printf("%lu\n", data.size()); },
[](){printf("data complete\n");});
auto emitter = rxcpp::sources::timer(std::chrono::milliseconds(0), newthread)
.tap([&](long) {
data.publish("test 1");
data.publish("test 2");
data.complete();
});
// hope to call something here so s1's thread can be joined.
// program crashes upon exit
buffered.combine_latest(newthread, emitter).as_blocking().subscribe();
printf("exit foo2\n");
}
I think that this better expresses the dependencies..
I'm implementing a "wait for WebSocket response before continuation" mechanism on PNaCl plugin through pp::WebSocketAPI in PPAPI. The following is a simplified version which stores the replied data into a global std::string, while the function myecho() sends a string through WebSocket and polling until the global string changes. The driver web page is the same as in the WebSocket example in NaCl SDK.
#include <string>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/utility/websocket/websocket_api.h"
class MyWebSocketReceiveListener
{
public:
virtual void onWebSocketDataReceived(const std::string& data) = 0;
};
class MyWebSocketAPI : protected pp::WebSocketAPI
{
public:
MyWebSocketAPI(pp::Instance* ppinstance, MyWebSocketReceiveListener* recvlistener)
: pp::WebSocketAPI(ppinstance), m_onReceiveListener(recvlistener), m_ppinstance(ppinstance) {}
virtual ~MyWebSocketAPI() {}
bool isConnected() { return pp::WebSocketAPI::GetReadyState() == PP_WEBSOCKETREADYSTATE_OPEN; }
void open(const std::string& url) { pp::WebSocketAPI::Connect(url, NULL, 0); }
void close() { pp::WebSocketAPI::Close(PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, "bye"); }
void sendData(const std::string& data) { pp::WebSocketAPI::Send(data); }
protected:
virtual void WebSocketDidOpen() { m_ppinstance->PostMessage("Connected"); }
virtual void WebSocketDidClose(bool wasClean, uint16_t code, const pp::Var& reason) {}
virtual void HandleWebSocketMessage(const pp::Var& message)
{
if (message.is_array_buffer()) {
pp::VarArrayBuffer vararybuf(message);
char *data = static_cast<char*>(vararybuf.Map());
std::string datastr(data, data + vararybuf.ByteLength());
vararybuf.Unmap();
m_onReceiveListener->onWebSocketDataReceived(datastr);
} else { // is string
m_onReceiveListener->onWebSocketDataReceived(message.AsString());
}
}
virtual void HandleWebSocketError() {}
private:
MyWebSocketAPI(const MyWebSocketAPI&);
MyWebSocketAPI& operator=(const MyWebSocketAPI&);
MyWebSocketReceiveListener* const m_onReceiveListener;
pp::Instance * const m_ppinstance;
};
static std::string g_returnval;
class MyPPPluginInstance : public pp::Instance, public MyWebSocketReceiveListener {
public:
explicit MyPPPluginInstance(PP_Instance instance)
: pp::Instance(instance), rpcwebsocket_(this, this) {}
virtual ~MyPPPluginInstance() {}
virtual void HandleMessage(const pp::Var& var_message);
virtual void onWebSocketDataReceived(const std::string& data)
{
g_returnval = data;
}
private:
bool IsConnected() { return rpcwebsocket_.isConnected(); }
void Open(const std::string& url)
{
rpcwebsocket_.open(url);
PostMessage(pp::Var("connecting..."));
}
void Close()
{
if (!IsConnected())
return;
rpcwebsocket_.close();
}
MyWebSocketAPI rpcwebsocket_;
};
std::string myecho(pp::Instance* inst, MyWebSocketAPI& ws, const std::string& in)
{
ws.sendData(in);
while (g_returnval.empty()) {
usleep(1000 * 1000); // 1 sec
inst->PostMessage("Waiting for response...");
}
return g_returnval;
}
void MyPPPluginInstance::HandleMessage(const pp::Var& var_message) {
if (!var_message.is_string())
return;
std::string message = var_message.AsString();
// This message must contain a command character followed by ';' and
// arguments like "X;arguments".
if (message.length() < 2 || message[1] != ';')
return;
switch (message[0]) {
case 'o':
// The command 'o' requests to open the specified URL.
// URL is passed as an argument like "o;URL".
Open(message.substr(2));
break;
case 'c':
// The command 'c' requests to close without any argument like "c;"
Close();
break;
case 'b':
case 't':
PostMessage(std::string("Calling remote echo for ") + message.substr(2));
std::string ret(myecho(this, rpcwebsocket_, message.substr(2)));
PostMessage(ret);
break;
}
}
// Creates MyPPPluginInstance objects when invoked.
class MyPPPluginModule : public pp::Module {
public:
MyPPPluginModule() : pp::Module() {}
virtual ~MyPPPluginModule() {}
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new MyPPPluginInstance(instance);
}
};
// Implement the required pp::CreateModule function that creates our specific
// kind of Module.
namespace pp {
Module* CreateModule() { return new MyPPPluginModule(); }
} // namespace pp
However, this approach didn't work. After connecting to the echo test server ws://echo.websocket.org and send "hello", I just get
connecting...
Connected
Calling remote echo for hello
Waiting for response...
Waiting for response...
Waiting for response...
Waiting for response...
Waiting for response...
(never replies)
I used another hand-crafted WebSocket server to test, and the message was sent to server successfully. And in addition to the usleep() polling as in my attached snippet, I've also tried to use pthread_cond_wait() and pthread_cond_signal() to wait for and notify about received message.
What should I do to "wait pp::WebSocketAPI receive data" correctly?
The function myecho() blocks MyPPPluginInstance::HandleMessage() and somehow in turn blocks receiving from WebSocket.
I added a pp::SimpleThread as a new data member of class MyPPPluginInstance and dispatch myecho() to another thread through pp::SimpleThread::message_loop().PostWork(). It works smoothly.