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.
Related
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.
I would like to implement a workaround to use a non-static class as a call-back function. I am working with Eclipse Paho MQTT code. The following type is implemented and used as callback:
typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
MQTTAsync_onSuccess* onSuccess;
onSuccess = myStaticCallback;
void myStaticCallback (void* context, MQTTAsync_successData* response)
{
//...callback actions...
}
I want to wrap this C API (without modifying the existing MQTT C API) and implement non-static / non-centralized callback function that belongs to an object/class.
typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
class myMQTTClass
{
private:
void myCallback (void* context, MQTTAsync_successData* response);
MQTTAsync_onSuccess* onSuccess;
public:
void foo (void)
{
this->onSuccess = this->myCallback;
}
}
As you might guess, the code above causes the error:
cannot convert myCallback from type 'void (myMQTTClass::) (void*, MQTTAsync_successData*)' to type 'void (*)(void*, MQTTAsync_successData*)'.
Any guidance as to how to address this issue or any workaround is greately appreciated. I would be willing to provide any possible missing information. Thanks in advance.
EDIT: Actual code with some omissions
namespace rover
{
typedef struct
{
char * clientID;
char * topic;
char * payload;
int qos; // 1
long int timeout; // Such as 10000L usec
} RoverMQTT_Configure_t;
class RoverPahoMQTT
{
public:
RoverPahoMQTT (char * host_name, int port, RoverMQTT_Configure_t MQTT_Configure);
private:
/**
* #brief Host name used for connecting to the Eclipse Paho MQTT server
*/
char * HOST_NAME;
/**
* #brief Port used for connecting to the Eclipse Paho MQTT server
*/
int PORT;
RoverMQTT_Configure_t rover_MQTT_configure;
/* Internal attributes */
MQTTAsync client;
/**
* #brief Connect options
*/
MQTTAsync_connectOptions conn_opts;
/**
* #brief Disconnect options
*/
MQTTAsync_disconnectOptions disc_opts;
//...
static void onPublisherConnect (void* context, MQTTAsync_successData* response);
void onPublisherConnect_ (MQTTAsync_successData* response);
//...
}
}
int rover::RoverPahoMQTT::publish (void)
{
this->flushFlags ();
this->conn_opts = MQTTAsync_connectOptions_initializer;
this->client = new MQTTAsync;
int rc;
char my_addr[20];
this->constructAddress (my_addr);
printf ("address: %s", my_addr);
MQTTAsync_create ( &(this->client),
my_addr,
this->rover_MQTT_configure.clientID,
MQTTCLIENT_PERSISTENCE_NONE,
NULL);
MQTTAsync_setCallbacks(this->client, NULL, onConnectionLost, NULL, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;
conn_opts.onFailure = onConnectFailure;
conn_opts.context = this->client;
if ((rc = MQTTAsync_connect(this->client, &(this->conn_opts))) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return code %d\n", rc);
return rc;
}
/*printf("Waiting for publication of %s\n"
"on topic %s for client with ClientID: %s\n",
PAYLOAD, TOPIC, CLIENTID);*/
while (!mqtt_finished)
usleep(this->rover_MQTT_configure.timeout);
MQTTAsync_destroy(&client);
return rc;
}
void rover::RoverPahoMQTT::onPublisherConnect_(MQTTAsync_successData* response)
{
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
int rc;
printf("Successful connection\n");
opts.onSuccess = onPublisherSend;
opts.context = client;
pubmsg.payload = &default_MQTT_configure.payload;
pubmsg.payloadlen = strlen(default_MQTT_configure.payload);
pubmsg.qos = default_MQTT_configure.qos;
pubmsg.retained = 0;
deliveredtoken = 0;
if ((rc = MQTTAsync_sendMessage(client, default_MQTT_configure.topic, &pubmsg, &opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start sendMessage, return code %d\n", rc);
exit(EXIT_FAILURE);
}
}
void rover::RoverPahoMQTT::onPublisherConnect (void* context, MQTTAsync_successData* response)
{
rover::RoverPahoMQTT* m = (rover::RoverPahoMQTT*) context;
m->onPublisherConnect_(response);
//((rover::RoverPahoMQTT*)context)->onPublisherConnect_(response);
// ^^^HERE IS THE SEGMENTATION FAULT
}
As clearly stated here, the callback has to be
registered with the client library by passing it as an argument in
MQTTAsync_responseOptions
and the context argument is a
pointer to the context value originally passed to
MQTTAsync_responseOptions, which contains any application-specific
context.
I suggest a common interface for your classes, which provides a static method that matches the callback prototype:
class myMQTTClass
{
public:
static void callback(void* context, MQTTAsync_successData* response)
{
myMQTTClass * m = (myMQTTClass*)context;
m->myCallback(response);
}
protected:
virtual void myCallback(MQTTAsync_successData* response) = 0;
};
You can now implement different behaviours in subclasses:
class myMQTTClassImpl : public myMQTTClass
{
protected:
void myCallback(MQTTAsync_successData *response)
{
std::cout << "success!!!" << std::endl;
}
};
Let's see how to use it:
int main()
{
myMQTTClass * m = new myMQTTClassImpl();
MQTTAsync_responseOptions options;
options.onSuccess = myMQTTClass::callback;
options.context = m;
}
Edit (refers to actual code posted):
In your publish method, this is right:
conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;
this is wrong:
conn_opts.context = this->client;
it should be:
conn_opts.context = this;
If you can use the context parameter to point to the object you want the callback to fire on you could simply make the callback function static and forward to an instance function. If the context parameter is needed for other data then I would use libffi to generate a closure and associate the object pointer with the closure's saved arguments.
There is no correct way to pass an actual instance function to that callback and be sure that it will work (even if you made the instance function be something like void MyCallback(MQTTAsync_successData*) and then forcibly cast that to the callback type you aren't guaranteed that the calling convention would match.
For the first (where you can use the context arg to point to 'this'):
class MyCallback
{
static void CallbackFunc(void * ptr, MQTTAsync_successData* data)
{
((MyCallback*)ptr)->RealCallback(data);
}
void RealCallback(MQTTAsync_successData*)
{}
};
You would then assign &MyCallback::CallbackFunc to the function pointer.
libffi is quite a bit more complicated.
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
I'm try combine Poco::Net::Websocket and Poco::Net::SocketReactor.
using Poco::Net;
class WSConnection: public HTTPRequestHandler{
public:
WSConnection(SocketReactor& reactor):_reactor(reactor){};
void handleRequest(HTTPServerRequest& rqst, HTTPServerResponse& rpns){
this->_ws = new Websocket(rqst, rspns);
this->_reactor.addHandler(this._ws, this._onread_hnd);
this->_reactor.addHandler(this._ws, this._onwrite_hnd);
this->_reactor.addHandler(this._ws, this._onerror_hnd);
}
private:
SocketReactor& _reactor;
};
class WSFactory : public HTTPRequestHandlerFactory{
public:
WSFactory(SocketRactor& reactor): _reactor(reactor){};
void HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request)
{
if (!IsWSRequest(request)){
return 0;
}
return new WSConnection(this->_reactor);
}
private:
bool IsWSRequest(const HTTPServerRequest& request){...}
SocketReactor& _reactor;
}
int WSServerApp::main(...){
Poco::Net::SocketReactor r;
Poco::Thread thrd;
WSFactory fctry(r);
Poco::Net::HTTPServer serv(fctry, 35035);
serv.start();
thrd.start(r);
waitForTerminationRequest();
r.stop();
thrd.join();
serv.stop();
}
But if my code leave handleRequest(), I have call destructor WSConnection();
There are other ways to handle the connections in one thread?
I'm found solution. Need write two classes:
Server(wrapper on ServerSocket) rewrite run method with partial copy Poco::Net::TCPServer::run().
Server::Server(const Poco::UInt16 port, const std::string& serv_name):
Runnable(),
SERVER_NAME(serv_name),
_stop(true),
_sock(port),
_clients(),
_thrd(),
_param(new HTTPServerParams())
{
_thrd.setPriority(Poco::Thread::PRIO_HIGH);
_thrd.setName(SERVER_NAME);
_param->setSoftwareVersion(SERVER_NAME);
}
void Server::run(){
while(!_stop.load()){
Poco::Timespan tm(250000);
if (_sock.poll(tm, Socket::SELECT_READ)){
auto ss = _sock.acceptConnection();
ss.setNoDelay(true);
#if defined(POCO_OS_FAMILY_UNIX)
if (ss.address().family() == AddressFamily::UNIX_LOCAL)
{
//ONLY WEBSOCK
throw Poco::IllegalStateException();
}
#endif
auto conn = std::make_shared<Connection>(ss, _param);
_clients.push_back(conn); //std::vector<std::shared_ptr<Websocket>>
}
}
}
and Connection with partial copy Poco::Net::HTTPServerConnection::run()
Connection::Connection(StreamSocket& ss, HTTPServerParams::Ptr param):
HTTPServerSession(ss, param),
_ws(), //std::unique_ptr<Websocket>
_rqst(), //std::unique_ptr<HTTPServerRequestImpl>
_rspns() //std::unique_ptr<HTTPServerResponseImpl>
{
_rspns.reset(new HTTPServerResponseImpl(*this));
_rqst.reset(new HTTPServerRequestImpl(*_rspns, *this, param));
//may bee need try and cath with send error state
Init(param);
_ws.reset(new WebSocket(*_rqst, *_rspns));
}
void Connection::Init(const HTTPServerParams::Ptr& param){
_rspns->setDate(Poco::Timestamp());
_rspns->setVersion(_rqst->getVersion());
_rspns->set("Server", param->getSoftwareVersion());
_rspns->setKeepAlive(param->getKeepAlive() && _rqst->getKeepAlive());
}
And add SocketReactor or Socket::select call to enother thread.
I'm trying to get familiar with Graphics2D for NaCl, but I'm getting the error "NativeClient: NaCl module crashed" when I try to paint and flush my Graphics2D instance.
Here is the output of my calls to console.log:
View did change
Making graphics context
Drawing
Making blank image data
PaintAndFlush
Flush
I am on main thread
NativeClient: NaCl module crashed
Here is my index.html : https://jsfiddle.net/3c1y4wp9/
And here is my c++ code:
#include <deque>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/graphics_2d.h"
#include "ppapi/cpp/image_data.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array.h"
#include "ppapi/utility/completion_callback_factory.h"
namespace {
const uint32_t kBlue = 0xff4040ffu;
const uint32_t kBlack = 0xff000000u;
}
class GravitySimInstance : public pp::Instance {
public:
explicit GravitySimInstance(PP_Instance instance) : pp::Instance(instance)
{}
virtual ~GravitySimInstance() {}
private:
virtual void HandleMessage(const pp::Var& var_message) {
}
void PaintAndFlush(pp::ImageData* image_data) {
PostMessage("PaintAndFlush");
assert(!flushing_);
graphics_2d_context_.ReplaceContents(image_data);
PostMessage("Flush");
if (pp::Module::Get()->core()->IsMainThread()) {
PostMessage("I am on main thread");
} else {
PostMessage("I am NOT on main thread");
}
graphics_2d_context_.Flush(
callback_factory_.NewCallback(&GravitySimInstance::DidFlush));
flushing_ = true;
}
void DidFlush(int32_t error_code) {
PostMessage("DidFlush");
flushing_ = false;
}
virtual void DidChangeView(const pp::View& view) {
PostMessage("View did change");
if (size_ != view.GetRect().size()) {
size_ = view.GetRect().size();
const bool is_always_opaque = true;
PostMessage("Making graphics context");
graphics_2d_context_ = pp::Graphics2D(this, view.GetRect().size(),
is_always_opaque);
BindGraphics(graphics_2d_context_);
Draw();
}
}
void Draw() {
PostMessage("Drawing");
pp::ImageData image_data = MakeBlankImageData(size_);
PaintAndFlush(&image_data);
}
pp::ImageData MakeBlankImageData(const pp::Size& size) {
PostMessage("Making blank image data");
const bool init_to_zero = false;
pp::ImageData image_data = pp::ImageData(this,
PP_IMAGEDATAFORMAT_BGRA_PREMUL,
size,
init_to_zero);
uint32_t* image_buffer = static_cast<uint32_t*>(image_data.data());
for (int i = 0; i < size.GetArea(); ++i)
image_buffer[i] = kBlack;
return image_data;
}
pp::Graphics2D graphics_2d_context_;
pp::CompletionCallbackFactory<GravitySimInstance> callback_factory_;
/// The size of our rectangle in the DOM, as of the last time DidChangeView
/// was called.
pp::Size size_;
/// true iff we are flushing.
bool flushing_;
/// Stores the most recent histogram so that we can re-draw it if we get
/// resized.
double histogram_[10];
};
class GravitySimModule : public pp::Module {
public:
GravitySimModule() : pp::Module() {}
virtual ~GravitySimModule() {}
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new GravitySimInstance(instance);
}
};
namespace pp {
Module* CreateModule() {
return new GravitySimModule();
}
} // namespace pp
Nothing looks obviously wrong, except where you initialize the image buffer. You need to take the stride into consideration when working with the image data. However, it doesn't look like you're writing out of bounds. Have you tried making init_to_zero true and using the ImageData as is to see if it still crashes?
Try calling of Flush function only in main thread. If you want to call such functions (which should be called in main thread) from working thread call them throuth pp::Core::CallOnMainThread function.