Updates: I found out if I call contactRemoteDone() inside of the contactRemote(), it does process. But if I call it outside of contactRemote(),but right after it, it throws a seg fault.So
for(int x=0; x<10; x++){
c1->contactRemote(x,request,response[x]);
c1->contactRemoteDone(x,response[x]);
}
doesn't work.
I tried to write a small program to test my implementation of gRPC asynchronous service. It throws a seg fault when I tried to access the response and when I used GDB to debug, I could not understand what the backtrace actually means and I did not find anything doing a Google search. The following is my code.
grpc_async_client.h
#include "sundial_grpc.grpc.pb.h"
#include "sundial_grpc.pb.h"
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using sundial_rpc::SundialRequest;
using sundial_rpc::SundialResponse;
using sundial_rpc::Sundial_GRPC_ASYNC;
#ifndef SAC
#define SAC
class TxnManager;
class Sundial_Async_Client{
public:
Sundial_Async_Client(std::string* channel);
Status contactRemote(uint64_t node_id,SundialRequest& request, SundialResponse* response);
Status contactRemoteDone(uint64_t node_id, SundialResponse* response);
private:
//std::unique_ptr<Sundial_GRPC_ASYNC::Stub> stub_[8];
std::unique_ptr<Sundial_GRPC_ASYNC::Stub> stub_;
CompletionQueue cq;
};
#endif
grpc_async_client.cpp
#include "sundial_grpc.grpc.pb.h"
#include "sundial_grpc.pb.h"
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "grpc_async_client.h"
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using sundial_rpc::SundialRequest;
using sundial_rpc::SundialResponse;
using sundial_rpc::Sundial_GRPC_ASYNC;
Sundial_Async_Client::Sundial_Async_Client(std::string* channel){
for(int i=0; i<2;i++){
if(i==1)
continue;
std::string server_address = channel[i];
printf("async client is connecting to server %s\n",server_address.c_str());
stub_=Sundial_GRPC_ASYNC::NewStub(grpc::CreateChannel(
server_address, grpc::InsecureChannelCredentials()));
}
};
//toDo: more than 2 nodes
Status Sundial_Async_Client:: contactRemote(uint64_t node_id,SundialRequest& request, SundialResponse* response){
ClientContext context;
Status status;
std::unique_ptr<ClientAsyncResponseReader<SundialResponse>> rpc(stub_->PrepareAsynccontactRemote(&context,request,&cq));
rpc->StartCall();
rpc->Finish(response, &status, (void*)1);
printf("sends a request %d \n",node_id);
return status;
}
Status Sundial_Async_Client::contactRemoteDone(uint64_t node_id, SundialResponse* response){
void* got_tag;
bool ok = false;
// Block until the next result is available in the completion queue "cq".
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or the cq_ is shutting down.
GPR_ASSERT(cq.Next(&got_tag, &ok));
// Verify that the result from "cq" corresponds, by its tag, our previous
// request.
GPR_ASSERT(got_tag == (void*)1);
// ... and that the request was completed successfully. Note that "ok"
// corresponds solely to the request for updates introduced by Finish().
GPR_ASSERT(ok);
printf("node %d is done\n",node_id);
return Status::OK;
}
grpc_async_server.h
#include "sundial_grpc.grpc.pb.h"
#include "sundial_grpc.pb.h"
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using sundial_rpc::SundialRequest;
using sundial_rpc::SundialResponse;
using sundial_rpc::Sundial_GRPC_ASYNC;
#ifndef SAS
#define SAS
class SundialAsyncServiceImp {
public:
~SundialAsyncServiceImp();
void run();
std::mutex mtx;
private:
// Class encompasing the state and logic needed to serve a request.
class CallData {
public:
// Take in the "service" instance (in this case representing an asynchronous
// server) and the completion queue "cq" used for asynchronous communication
// with the gRPC runtime.
CallData(Sundial_GRPC_ASYNC::AsyncService* service, ServerCompletionQueue* cq);
void Proceed();
private:
// The means of communication with the gRPC runtime for an asynchronous
// server.
Sundial_GRPC_ASYNC::AsyncService* service_;
// The producer-consumer queue where for asynchronous server notifications.
ServerCompletionQueue* cq_;
// Context for the rpc, allowing to tweak aspects of it such as the use
// of compression, authentication, as well as to send metadata back to the
// client.
ServerContext ctx_;
// What we get from the client.
SundialRequest request_;
// What we send back to the client.
SundialResponse response_;
// The means to get back to the client.
ServerAsyncResponseWriter<SundialResponse> responder_;
// Let's implement a tiny state machine with the following states.
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_; // The current serving state.
};
void HandleRpcs();
std::unique_ptr<ServerCompletionQueue> cq_;
Sundial_GRPC_ASYNC::AsyncService service_;
std::unique_ptr<Server> server_;
};
#endif
grpc_aynsc_server.cpp
#include "sundial_grpc.grpc.pb.h"
#include "sundial_grpc.pb.h"
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "grpc_async_server.h"
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using sundial_rpc::SundialRequest;
using sundial_rpc::SundialResponse;
using sundial_rpc::Sundial_GRPC_ASYNC;
SundialAsyncServiceImp::~SundialAsyncServiceImp(){
server_->Shutdown();
// Always shutdown the completion queue after the server.
cq_->Shutdown();
}
void SundialAsyncServiceImp::run(){
uint32_t num_nodes = 0;
std::string port("0.0.0.0:50051");
ServerBuilder builder;
builder.AddListeningPort(port, grpc::InsecureServerCredentials());
builder.RegisterService(&service_);
cq_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << port << std::endl<<"\n";
HandleRpcs();
}
SundialAsyncServiceImp::CallData::CallData(Sundial_GRPC_ASYNC::AsyncService* service, ServerCompletionQueue* cq):
service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
Proceed();
}
int processRequest(SundialRequest* request, SundialResponse* response){
if (request->request_type() == SundialRequest::SYS_REQ) {
response->set_response_type( SundialResponse::SYS_RESP );
return 1;
}
int a =0;
//some processing
while(a<100000){
a++;
}
return 1;
}
void SundialAsyncServiceImp::CallData::Proceed(){
if (status_ == CREATE) {
status_ = PROCESS;
service_->RequestcontactRemote(&ctx_, &request_, &responder_, cq_, cq_,
this);
} else if (status_ == PROCESS) {
new CallData(service_, cq_);
int a = processRequest(&request_ , &response_);
status_ = FINISH;
responder_.Finish(response_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
delete this;
}
}
void SundialAsyncServiceImp::HandleRpcs(){
new CallData(&service_, cq_.get());
void* tag;
bool ok;
mtx.unlock();
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
}
main.cpp
#include "grpc_async_server.h"
#include "grpc_async_client.h"
Sundial_Async_Client* c1;
SundialAsyncServiceImp* s1 ;
void * start_sync_rpc_server(void* input){
s1->run();
return NULL;
}
int main(){
std::string server_address("0.0.0.0:50051");
std::string channel_async[2];
channel_async[0]=server_address;
s1 = new SundialAsyncServiceImp();
pthread_t * pthread_rpc1 = new pthread_t;
s1->mtx.lock();
pthread_create(pthread_rpc1, NULL, start_sync_rpc_server,NULL);
s1->mtx.lock();
c1=new Sundial_Async_Client(channel_async);
SundialRequest request;
SundialResponse* response[10];
for(int i=0; i<10; i++){
SundialResponse r;
response[i]=&r;
}
for(int x=0; x<10; x++){
c1->contactRemote(x,request,response[x]);
}
printf("sends all requests out\n");
int y=0;
while(y<10){
c1->contactRemoteDone(y,response[y]);
}
}
It runs smoothly until it reaches the while loop in main. It throws a seg fault. If I use GDB, I get the following:
Thread 1 "rundb" received signal SIGSEGV, Segmentation fault.
grpc::internal::InterceptorBatchMethodsImpl::RunInterceptors (
this=this#entry=0x7ffff6a9ec58)
at /usr/local/include/grpcpp/impl/codegen/interceptor_common.h:264
264 RunServerInterceptors();
(gdb) backtrace
#0 grpc::internal::InterceptorBatchMethodsImpl::RunInterceptors (
this=this#entry=0x7ffff6a9ec58)
at /usr/local/include/grpcpp/impl/codegen/interceptor_common.h:264
#1 0x000000000041c8aa in grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata, grpc::internal::CallOpSendMessage, grpc::internal::CallOpClientSendClose, grpc::internal::CallOpRecvInitialMetadata, grpc::internal::CallOpRecvMessage<sundial_rpc::SundialResponse>, grpc::internal::CallOpClientRecvStatus>::RunInterceptorsPostRecv (this=0x7ffff6a9eb18)
at /usr/local/include/grpcpp/impl/codegen/call_op_set.h:826
#2 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata, grpc::internal::CallOpSendMessage, grpc::internal::CallOpClientSendClose, grpc::internal::CallOpRecvInitialMetadata, grpc::internal::CallOpRecvMessage<sundial_rpc::SundialResponse>, grpc::internal::CallOpClientRecvStatus>::FinalizeResult (
this=0x7ffff6a9eb18, tag=0x7fffffffdd18, status=<optimized out>)
at /usr/local/include/grpcpp/impl/codegen/call_op_set.h:920
#3 0x00007ffff7c95f23 in grpc_impl::CompletionQueue::AsyncNextInternal(void**, bool*, gpr_timespec) () from /usr/local/lib/libgrpc++.so.1
#4 0x000000000040ab9c in grpc_impl::CompletionQueue::Next (ok=0x7fffffffdd17,
tag=0x7fffffffdd18, this=0x7ffff6a42078)
at /usr/local/include/grpcpp/impl/codegen/completion_queue_impl.h:179
#5 Sundial_Async_Client::contactRemoteDone (this=0x7ffff6a42070,
node_id=node_id#entry=0, response=response#entry=0x7fffffffdd70)
at grpc/grpc_async_client.cpp:93
--Type <RET> for more, q to quit, c to continue without paging--ret
#6 0x000000000040a6ac in main () at grpc/main.cpp:38
main.cpp:38 is c1->contactRemoteDone(y,response[y]); and grpc_async_client.cpp:93 is GPR_ASSERT(cq.Next(&got_tag, &ok));. I'm really lost on this error message and really wish someone could help me out.
Here:
for(int i=0; i<10; i++){
SundialResponse r;
response[i]=&r;
}
Each SundialResponse is being destructed per iteration. Do this instead:
for(int i=0; i<10; i++){
SundialResponse* r = new SundialResponse();
response[i]=r;
}
Related
Im trying to make multithreaded proxy checker in c++, when I start the threads and lock it all threads wait till the request is finished. I tried to remove the locks but that doesn't help either. Im using the cpr library to make the requests, the documentation can be found here: https://whoshuu.github.io/cpr/advanced-usage.html.
Reproduceable example:
#include <stdio.h>
#include <pthread.h>
#include <iostream>
#include <queue>
#include <mutex>
#include <cpr/cpr.h>
#include <fmt/format.h>
#define NUMT 10
using namespace std;
using namespace fmt;
std::mutex mut;
std::queue<std::string> q;
void* Checker(void* arg) {
while (!q.empty()) {
mut.lock();
//get a webhook at https://webhook.site
string protocol = "socks4";
string proxyformatted = format("{0}://{1}", protocol, q.front());
auto r = cpr::Get(cpr::Url{ "<webhook url>" },
cpr::Proxies{ {"http", proxyformatted}, {"https", proxyformatted} });
q.pop();
mut.unlock();
}
return NULL;
}
int main(int argc, char** argv) {
q.push("138.201.134.206:5678");
q.push("185.113.7.87:5678");
q.push("5.9.16.126:5678");
q.push("88.146.196.181:4153");
pthread_t tid[NUMT]; int i;
int thread_args[NUMT];
for (i = 0; i < NUMT; i++) {
thread_args[i] = i;
pthread_create(&tid[i], NULL, Checker, (void*) &thread_args);
}
for (i = 0; i < NUMT; i++) {
pthread_join(tid[i], NULL);
fprintf(stderr, "Thread %d terminated\n", i);
}
return 0;
}
Thanks in advance.
I suggest to implement a wrapper class for your queue that will hide the mutex.
That class can provide push(std::string s) and bool pop(std::string& s) that returns true and populate s if the queue wasn't empty or false othervise. Then your worker threads can simply loop
std::string s;
while(q.pop(s)) {
...
}
I created sample app, for sending/receiving messages between node.js app which is running as socket.io server and C++ client, Below is my code of C++ client side:
sio::client io;
socket::ptr current_socket;
string w = "harshil";
io.set_open_listener([&]() {
io.socket()->emit("message", w); // Can able to send message to server
});
io.socket()->on("server", sio::socket::event_listener([&](event &e)
{
cout << __LINE__ << endl; // Can not print line :(
}));
io.connect("http://127.0.0.1:8081");
Over here you can see, that client can able to send message to server, but it can not receive message based on "server" event name, Can some one help me on it?
To those of you who stuck as me, then below sample code will be helpful for them.
Created different sample code for node.js acts as socket.io server and main.cpp file acts as client,
'use strict';
const express = require('express');
const app = express();
const serverHttp = require('http').Server(app);
const io = require('socket.io')(serverHttp);
const port = 8081;
io.on('connection', function (socket) {
socket.on('message', function (data) {
console.log("key received!!!" + data);
socket.emit('server', 'hello socket io');
console.log("sent server msg");
});
});
serverHttp.listen(port, function() {
console.log("init!!!");
});
Sample server app, which receives request from client and emit message to client.
#include "sio_client.h"
#include <unistd.h>
#include <functional>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <string>
#define HIGHLIGHT(__O__) std::cout<<"\e[1;31m"<<__O__<<"\e[0m"<<std::endl
#define EM(__O__) std::cout<<"\e[1;30;1m"<<__O__<<"\e[0m"<<std::endl
#define MAIN_FUNC int main(int argc ,const char* args[])
using namespace sio;
using namespace std;
std::mutex _lock;
std::condition_variable_any _cond;
bool connect_finish = false;
class connection_listener
{
sio::client &handler;
public:
connection_listener(sio::client& h):
handler(h)
{
}
void on_connected()
{
_lock.lock();
_cond.notify_all();
connect_finish = true;
_lock.unlock();
}
void on_close(client::close_reason const& reason)
{
std::cout<<"sio closed "<<std::endl;
exit(0);
}
void on_fail()
{
std::cout<<"sio failed "<<std::endl;
exit(0);
}
};
socket::ptr current_socket;
void bind_events()
{
current_socket->on("server", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp)
{
_lock.lock();
cout << name << endl;
cout << data->get_string() << endl;
_lock.unlock();
}));
}
MAIN_FUNC
{
sio::client h;
connection_listener l(h);
h.set_open_listener(std::bind(&connection_listener::on_connected, &l));
h.set_close_listener(std::bind(&connection_listener::on_close, &l,std::placeholders::_1));
h.set_fail_listener(std::bind(&connection_listener::on_fail, &l));
h.connect("http://127.0.0.1:8081");
_lock.lock();
if(!connect_finish)
{
cout << "wait\n";
_cond.wait(_lock);
}
_lock.unlock();
current_socket = h.socket();
string nickname;
while (nickname.length() == 0) {
HIGHLIGHT("Type your nickname:");
getline(cin, nickname);
}
current_socket->emit("message", nickname);
bind_events();
sleep(10);
h.sync_close();
h.clear_con_listeners();
return 0;
}
Sample client app, which emits message to server, and receives message from server,
We've made good progress in getting GRPC running under RHEL 7.
Our application has one rather complicated structure with three levels of nesting with the outer level implementing a "oneof" keyword.
We find that all our other structures run fine, but this one gives us an RPC failure with code=14.
We've simplified this part of the application as much as possible so it can hopefully be recompiled and run easily.
Here's the .proto file, updated to accommodate Uli's question:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.debug";
option java_outer_classname = "DebugProto";
option objc_class_prefix = "DEBUG";
package DEBUGpackage;
service DEBUGservice {
rpc DEBUG_val_container_get (input_int32_request) returns (outer_container) {}
}
message input_int32_request {
int32 ival = 1;
}
message inner_container {
repeated uint32 val_array = 1;
}
message middle_container {
inner_container vac = 1;
}
message other_container {
int32 other_val = 1;
}
message outer_container {
oneof reply {
middle_container r1 = 1;
other_container r2 = 2;
}
}
(Please note that the java lines in this prototype code are just in there because they are in the GRPC website examples. Our code is entirely C++, with no java. Don't know if that means we can do without some of these "option java..." lines).
Here's our client source code:
#include <iostream>
#include <memory>
#include <string>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#include <thread>
#include <unistd.h>
#include "debug.grpc.pb.h"
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using DEBUGpackage::input_int32_request;
using DEBUGpackage::inner_container;
using DEBUGpackage::middle_container;
using DEBUGpackage::outer_container;
using DEBUGpackage::DEBUGservice;
class DEBUGClient {
public:
explicit DEBUGClient(std::shared_ptr<Channel> channel)
: stub_(DEBUGservice::NewStub(channel)) {}
void DEBUG_val_container_get() {
std::cout << "in DEBUG_val_container_get" << std::endl;
// Data we are sending to the server
input_int32_request val;
val.set_ival(0);
AsyncClientCall* call = new AsyncClientCall;
call->response_reader = stub_->AsyncDEBUG_val_container_get(&call->context, val, &cq_);
call->response_reader->Finish(&call->reply_, &call->status, (void*)call);
}
void AsyncCompleteRpc() {
void* got_tag;
bool ok = false;
while (cq_.Next(&got_tag, &ok)) {
AsyncClientCall* call = static_cast<AsyncClientCall*>(got_tag);
GPR_ASSERT(ok);
if (call->status.ok()) {
if (call->reply_.has_r1()) {
std::cout << call << " DEBUG received: "
<< call->reply_.r1().vac().val_array(0) << std::endl;
}
}
else {
std::cout << call << " RPC failed" << std::endl;
std::cout << " RPC failure code = " << call->status.error_code() << std::endl;
std::cout << " RPC failure message = " << call->status.error_message() << std::endl;
}
delete call;
}
}
private:
struct AsyncClientCall {
outer_container reply_;
ClientContext context;
Status status;
std::unique_ptr<ClientAsyncResponseReader<outer_container>> response_reader;
};
std::unique_ptr<DEBUGservice::Stub> stub_;
CompletionQueue cq_;
};
int main(int argc, char** argv) {
DEBUGClient DEBUG0(grpc::CreateChannel("172.16.17.46:50050", grpc::InsecureChannelCredentials()));
std::thread thread0_ = std::thread(&DEBUGClient::AsyncCompleteRpc, &DEBUG0);
DEBUG0.DEBUG_val_container_get();
sleep(1);
std::cout << "Press control-c to quit" << std::endl << std::endl;
thread0_.join(); //blocks forever
return 0;
}
And, here's our server source code:
#include <memory>
#include <iostream>
#include <string>
#include <thread>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#include "debug.grpc.pb.h"
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using DEBUGpackage::inner_container;
using DEBUGpackage::input_int32_request;
using DEBUGpackage::middle_container;
using DEBUGpackage::outer_container;
using DEBUGpackage::DEBUGservice;
std::string save_server_address;
class ServerImpl final {
public:
~ServerImpl() {
server_->Shutdown();
cq_->Shutdown();
}
void Run() {
std::string server_address("0.0.0.0:50050");
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service_);
cq_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
save_server_address = server_address;
HandleRpcs();
}
private:
class CallData {
public:
virtual void Proceed() = 0;
};
class DebugGetCallData final : public CallData{
public:
DebugGetCallData(DEBUGservice::AsyncService* service, ServerCompletionQueue* cq)
: service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
Proceed();
}
void Proceed() {
if (status_ == CREATE) {
status_ = PROCESS;
service_->RequestDEBUG_val_container_get(&ctx_, &request_, &responder_, cq_, cq_, this);
} else if (status_ == PROCESS) {
new DebugGetCallData(service_, cq_);
char *portchar;
portchar = (char *) save_server_address.c_str();
long cq_addr = (long) cq_;
int cq_addr32 = (int) (cq_addr & 0xfffffff);
srand(cq_addr32);
fprintf(stderr, "%s task started\n", portchar); fflush(stderr);
unsigned int return_val = 10;
inner_container ic;
ic.add_val_array(return_val);
middle_container reply_temp;
reply_temp.set_allocated_vac(&ic);
reply_.set_allocated_r1(&reply_temp);
fprintf(stderr, "%s %s task done\n", portchar, "val_container_get"); fflush(stderr);
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
}
}
private:
DEBUGservice::AsyncService* service_;
ServerCompletionQueue* cq_;
ServerContext ctx_;
input_int32_request request_;
outer_container reply_;
ServerAsyncResponseWriter<outer_container> responder_;
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
};
void HandleRpcs() {
new DebugGetCallData(&service_, cq_.get());
void* tag;
bool ok;
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
}
std::unique_ptr<ServerCompletionQueue> cq_;
DEBUGservice::AsyncService service_;
std::unique_ptr<Server> server_;
};
int main() {
ServerImpl server;
server.Run();
return 0;
}
The output when I run it looks like this:
[fossum#netsres46 debug]$ DEBUG_client2
in DEBUG_val_container_get
0xb73ff0 RPC failed
RPC failure code = 14
RPC failure message = Endpoint read failed
Press control-c to quit
We ran the server under gdb, and found a place in the generated
file "debug.pb.cc" where if we just comment out one line, it all starts working.
Here's the pertinent piece of the generated file "debug.pb.cc":
middle_container::~middle_container() {
// ##protoc_insertion_point(destructor:DEBUGpackage.middle_container)
SharedDtor();
}
void middle_container::SharedDtor() {
if (this != internal_default_instance()) {
delete vac_; // comment out this one line, to make the problem go away
}
}
The "delete vac_" line appears to be an attempt to delete storage that either has already been deleted, or is about to be deleted somewhere else. Please, can someone look into this? [The files below are still the files we use to generate this code, and to debug the problem to this point]
I have no idea whether I've uncovered a bug in GRPC, or whether I've coded something wrong.
The issue is that you are allocated middle_container reply_tmp on the stack in your server. As a result it gets destructed as soon as you pass out of the scope. At that time, you have called Finish but not yet waited for its result. Since this is an async server, the data must remain alive until you've received the tag back for it. This is why manually editing your destructor works in your case; you're basically nullifying the destructor (and leaking memory as a result).
i'm writing server with will handle client connection, but i have problem moving class with thread inside to vector, i know if i had only thread i can move it to vector with std::move(), but here i have thread inside a class and i'm getting a lot of errors because thread is non-movable object.
Core.cpp:
#define OS_LINUX
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <csignal>
#include <cstdlib>
#include <TCPServer/TCPServer.h>
#include <TCPServer/TCPServerConnection.h>
#include <ProcessManip/ProcessManip.h>
#include <Log/Log.h>
#include "ConnectionHandler.h"
//Global
bool TERMNINATE = false;//If true loops must end
const int PORT = 8822;
//Proto
void terminate(int sig);
void receive_message(ConnectionHandler *handler,string message);
using namespace std;
int main(int argc,char *argv[])
{
//Add Terminate handler
signal(SIGINT,terminate);
//Load configuration
Log::logDebug("Starting ...");
//Init
vector<ConnectionHandler> connections;
//Init modules
//Main
Log::logDebug("Running");
TCPServer server;
if(!server._bind(PORT))
return 1;
if(!server._listen())
return 2;
ProcessManip::cleanProcess(); /* Clean dead processes */
server.setBlocking(false);/* accept returns invalid socket if no connection */
Log::logDebug("Listening ...");
while(!TERMNINATE)
{
TCPServerConnection conn = server._accept();
if(!conn.isValid())
continue;
Log::logDebug((string)"Got connection from: "+conn.getAddress());/* Print IP address of client */
ConnectionHandler ch(&TERMNINATE,std::move(conn));
ch.setCallback(receive_message);
ch.start();
connections.push_back(std::move(ch)); //here is problem
/*connections.push_back(ConnectionHandler(&TERMNINATE,conn)); /* Add it to vector */
/*connections.back().setCallback(receive_message);
connections.back().start();*/
Log::logDebug("Connection added to vector");
}
server.setBlocking(true);
//Dispose
Log::logDebug("Stopping ...");
/*for(auto it = connections.begin();it!=connections.end();)
{
Log::logDebug((string)"Closed connection with: "+(*it).getConnection().getAddress());/* Print IP address of client */
//(*it).close(); /* Close connetion */
// it = connections.erase(it); /* Delete ConnectionHandler from vector */
// }
server._close();
Log::logDebug("Closed");
return 0;
}
void terminate(int sig)
{
//Change global value to true
TERMNINATE = true;
}
void receive_message(ConnectionHandler *handler,string message)
{
Log::logDebug((string)"Message ("+handler->getConnection().getAddress()+") : "+message);
}
ConnectionHandler.h
#ifndef EXT_CONNECTIONHANDLER
#define EXT_CONNECTIONHANDLER
#include <TCPServer/TCPServerConnection.h>
#include <thread>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
class ConnectionHandler
{
public:
ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection);
ConnectionHandler(TCPServerConnection pConnection);
~ConnectionHandler();
void start(); /* Start listening */
void stop(); /* Stop listening */
void close(); /* Stops listening + close connection */
void setConnection(TCPServerConnection pConnection);
TCPServerConnection getConnection();
void setCallback(function<void(ConnectionHandler*,string)> pFunction);
private:
bool *mainTerminate = NULL;
bool handler_terminate = false;
short status = 0;
TCPServerConnection connection;
bool needTerminate();
void run();
void waitForEnd();
function<void(ConnectionHandler*,string)> callback = NULL;
std::thread m_thread;
};
#endif
ConnectionHandler.cpp
#include "ConnectionHandler.h"
ConnectionHandler::ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection)
{
this->mainTerminate = pMainTerminate;
this->connection = pConnection;
}
ConnectionHandler::ConnectionHandler(TCPServerConnection pConnection)
{
this->mainTerminate = NULL;
this->connection = pConnection;
}
ConnectionHandler::~ConnectionHandler()
{
this->close();
}
void ConnectionHandler::start()
{
m_thread = std::thread(&ConnectionHandler::run, this);
this->status = 1;
}
void ConnectionHandler::waitForEnd()
{
if(this->m_thread.joinable())
this->m_thread.join();
}
bool ConnectionHandler::needTerminate()
{
if(mainTerminate!=NULL)
return this->handler_terminate||*(this->mainTerminate);
else
return this->handler_terminate;
}
void ConnectionHandler::run()
{
string message = "";
string tmp = "";
this->connection.setBlocking(false); // So we can terminate any time
while(!this->needTerminate())
{
message = this->connection._receive();
if(message!="")
{
do
{
tmp = this->connection._receive();
message+=tmp;
}while(tmp!=""); /* If we get longer message than we can grab at one time */
this->connection._send(message); /* TODO Remove */
if(this->callback!=NULL)
this->callback(this,message);
message = "";
}
}
this->connection.setBlocking(true);
}
void ConnectionHandler::stop()
{
this->handler_terminate = true; /* Signals thread to stop */
this->waitForEnd();
this->status = 2;
}
void ConnectionHandler::close()
{
this->stop();
this->connection._close(); /* Close connection */
this->status = 3;
}
TCPServerConnection ConnectionHandler::getConnection()
{
return this->connection;
}
void ConnectionHandler::setConnection(TCPServerConnection pConnection)
{
this->connection = pConnection;
}
void ConnectionHandler::setCallback(function<void(ConnectionHandler*,string)> pFunction)
{
this->callback = pFunction;
}
Because this class violates the Rule Of Three, even if the std::thread issue gets addressed, other problems will likely appear; most likely taking the form of mysterious runtime bugs.
The compilation issue with std::thread is not the problem, it's merely a symptom of the real problem: this class should not be moved or copied. This class should only be new-constructed, then stuffed into a std::shared_ptr (or a reasonable facsimile) and stay there until it gets destroyed. Only the std::shared_ptr should be passed around, stuffed into a vector, etc...
I checked quite a few posts but could not be sure if answers were related to what I want to do. All I could understand was to use threads.
I want to have a Server object which constantly checks a boolean value in a Client object and in case it is true server should do something and turn the flag to false.
I want this checking process to be done parallel to my main. btw server and client have to do other things too which i dont want to put in the same thread as the function that checks the client's flag
EDIT: I need to have multiple objects of Server and Client
so here is what i mean in C++ code:
main.cpp
#include <iostream>
using namespace std;
class Client
{
public:
bool flag;
void setFlag(bool flagStat)
{
flag = flagStat;
}
bool getFlag()
{
return flag;
}
};
class Server
{
public:
void checkClientFlag(Client *client)
{
while (true)
{
if (client->getFlag())
{
alarmServer();
client->setFlag(false);
}
}
}
void alarmServer()
{
cout << "Client Flag UP!!!" << endl;
}
};
int main()
{
Server *newSrv = new Server;
Client *newCnt = new Client;
newSrv->checkClientFlag(newCnt);
return 0;
}
so help is appreciated.
Thank you in advance.
You can use condition variable that will deal with this checking for you.
The idea is that you have a flag that is set to true/flase depending on whether the work has been done (set up as false and will be set to true when the work method is done).
the wait method means that the program will wait until a certain condition is filled.
you use notify to set this condition to true
Server.h
#include <condition_variable>
#include <mutex>
class CServer
{
std::condition_variable & cv;
std::mutex & mut;
bool & flag;
public:
~CServer(void);
CServer::CServer(std::condition_variable & cv,
std::mutex & mut,
bool & flag);
CServer::CServer(CServer &);
int Notify();
int DisplayNotification();
std::condition_variable & getCondVar(){return cv;}
std::mutex & getMutex(){return mut;}
bool & getFlag(){return flag;}
};
Server.cpp
#include "Server.h"
#include <iostream>
using namespace std;
CServer::~CServer(void)
{
}
CServer::CServer(std::condition_variable & cv_,
std::mutex & mut_,
bool & flag_):
cv(cv_),
mut(mut_),
flag(flag_)
{
}
CServer::CServer(CServer &toCopy):
cv(toCopy.getCondVar()),
mut(toCopy.getMutex()),
flag(toCopy.getFlag())
{
flag=false;
cout<<"Copy constructor"<<std::endl;
}
int CServer::Notify()
{
{
std::lock_guard<std::mutex> lk(mut);
flag=true;
std::cout << "ready for notfication"<<endl;
}
cv.notify_one();
return 0;
}
int CServer::DisplayNotification()
{
// wait for the worker
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, [this]{return this->getFlag();});
}
cout<<"Notification displayed"<<endl;
return 0;
}
Client.h
#include <chrono>
#include "Server.h"
class CClient
{
CServer & serv;
std::chrono::seconds sleepTime;
bool finishedWork;
public:
CClient(CServer & serv,
std::chrono::seconds sleepTime);
~CClient(void);
int work();
};
Client.cpp
#include "Client.h"
#include <thread>
using namespace std;
CClient::CClient(CServer & serv_,
std::chrono::seconds sleepTime_):
serv(serv_),
sleepTime(sleepTime_),
finishedWork(false)
{
}
CClient::~CClient(void)
{
}
int CClient::work()
{
this_thread::sleep_for(sleepTime);
finishedWork=true;
serv.Notify();
return 0;
}
And the main program is
#include "Client.h"
#include <thread>
using namespace std;
int main()
{
std::chrono::seconds sleepTime=std::chrono::seconds(10);
//create client and server
condition_variable cv;
mutex mut;
bool flag=false;
CServer serv(cv,mut, flag);
CClient cli(serv,sleepTime);
//thread with the server
thread thServ(&CServer::DisplayNotification,serv);
////thread with the client
thread thCli (&CClient::work,cli);
////join threads
thServ.join();
thCli.join();
return 0;
}
Hope that helps, ask me if you have any questions