grpc sync server limit handle thread - c++

I use the grpc cpp example "helloworold" code to test limit handle thread. But I can't find any way to do it.
grpc version: 1.15
linux: ubuntu 16.04
I set the builder like this:
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::MIN_POLLERS, 1);
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::MAX_POLLERS, 1);
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::NUM_CQS, 1);
set the handle like this:
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
std::cout << "start " << std::this_thread::get_id() << std::endl;
reply->set_message(prefix + request->name());
//**** sleep 5s, keep this thread block ****
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "end " << std::this_thread::get_id() << std::endl;
return Status::OK;
}
};
I use the example client and call SayHello in 100 threads, and server log show the thread is created by 100 times.
In this test, is my test way wrong? or somethings miss setup??

You can use SetMaxThread in this way:
grpc::ResourceQuota rq;
rq.SetMaxThreads(n);
builder.SetResourceQuota(rq);
It seems that a thread is needed for every completion queue. So if n=4 when you have 1 completion queue, 3 threads are remained for processing requests.

What you are using is the sync API, which will initiate a thread per call. You can look at the async API to reduce the number of threads.

Related

C++/gRPC - IsCancelled not working properly

I am using gRPC sync api with C++.
Here is how on server side I am checking if the client has stopped the stream.
grpc::Status AuthServer::ConnectServiceImpl::HearthBeat(grpc::ServerContext *context,
grpc::ServerReaderWriter<Pulse, Pulse> *stream) {
Pulse note;
if(ctx_.IsCancelled()){
std::cout << "DISCONNECT" << std::endl;
}
while (stream->Read(&note)) {
Pulse reply;
reply.set_rate(note.rate()+1);
std::cout << "RECEIVED: " << note.rate() << std::endl;
stream->Write(reply);
}
return grpc::Status::OK;
}
This is bidi stream which is stopped forcefully on client side with killing the client app and still the "DISCONNECT" message does not appear.
Why is that, am I using IsCancelled() not correctly?
I think I already answered this in GRPC/C++ - How to detect client disconnected in Async Server.
Your code appears to be checking IsCancelled() on ctx_. I'm not sure what that object is, but the context you want to be checking is the one passed into the request handler method as context.

why GRPC AsyncClient throws Segfault when waiting for the Next result in the completion queue

I am using version 1.23.1 of the GRPC library.
I have an asynchronous RPC c++ Client class, which initiates each RPC with the following method:
void Client::SendTaskAsync(const Task& task) {
unique_lock<mutex> lock(mtx_);
cout << "Sending task with id " << task.id() << endl;
ClientContext context;
Status status;
unique_ptr<ClientAsyncResponseReader<Result>> rpc(
stub_->PrepareAsyncSendTask(&context, task, &queue_));
rpc->StartCall();
// Allocating memory to store result from RPC
Result* result = &results_.emplace_back();
int* tag = new int(results_.size() - 1);
rpc->Finish(result, &status, static_cast<void*>(tag));
}
In the main thread I call SendTaskAsync five times in a loop.
The Client class has a background thread informing when each RPC has returned a Result:
while (true) {
void* tag;
bool ok = false;
{
unique_lock<mutex> lock(mtx_);
cout << "Waiting the for next result" << endl;
const time_point<system_clock> deadline =
system_clock::now() + milliseconds(1000);
// SEGFAULT HERE, WHY?
GPR_ASSERT(queue_.AsyncNext(&tag, &ok, deadline));
}
if (ok) {
int index = *static_cast<int*>(tag);
cout << "Got result with tag " << index << endl;
} else {
cout << "Sleeping" << endl;
sleep_for(milliseconds(1000));
}
}
If I start my client, the following log is observed:
BACKGROUND: Waiting for the next result
MAIN THREAD: Sending task with id 0
BACKGROUND: Sleeping
MAIN THREAD: Sending task with id 1
MAIN THREAD: Sending task with id 2
MAIN THREAD: Sending task with id 3
MAIN THREAD: Sending task with id 4
BACKGROUND: Waiting for the next result
BACKGROUND: Segmentation fault (core dumped)
What happens is that
Background thread checks if a queue_ contains a result, there is none yet, so it goes to sleep;
Main thread makes 5 RPC that at the end should populate the queue_ with results;
Background thread wakes up and checks if a queue_ contains a result, AND CRASHES.
Any ideas why?
The code in the question is written according to this tutorial, which sends only one request and waits for a reply in the same thread.
If you want to use multiple threads, follow the client example here.

Understanding how multithreading works with Boost io_service

I'm learning multithreading and Boost libraries (Asio in particular) and I'm having a hard time understanding how the following code works (slightly modified from Boost.org tutorials)
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
class printer
{
public:
printer(boost::asio::io_service& io)
: timer1_(io, boost::posix_time::seconds(1)),
timer2_(io, boost::posix_time::seconds(1)),
count_(0)
{
timer1_.async_wait(boost::bind(&printer::print1, this));
timer2_.async_wait(boost::bind(&printer::print2, this));
}
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
void print1()
{
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << std::endl;
++count_;
timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(2));
timer1_.async_wait(boost::bind(&printer::print1, this));
}
}
void print2()
{
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << std::endl;
++count_;
timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(2));
timer2_.async_wait(boost::bind(&printer::print2, this));
}
}
private:
boost::asio::deadline_timer timer1_;
boost::asio::deadline_timer timer2_;
int count_;
};
void saysomething()
{
std::string whatyasay;
std::cin >> whatyasay;
std::cout << "You said " << whatyasay << std::endl;
}
int main()
{
boost::asio::io_service io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
io.run();
std::cout << "Hey there\n";
t.join();
return 0;
}
Which results in the following output
Timer 1: 0
Timer 2: 1
Timer 1: 2
Timer 2: 3
Timer 1: 4
Timer 2: 5
Timer 1: 6
Timer 2: 7
Timer 1: 8
Timer 2: 9
Hey there
Final count is 10
What I would've expected from this code was that thread t would be in charge of running the io_service, meaning that other operations could take place in the meantime.
Instead, the code behaves as usual, aka, io.run "blocks" the code flow until the timers inside the printer object stop launching async_waits, so "hey there" is only printed after the timers are not working anymore.
But that's not all: from my understanding, io_services don't stop running after the run() method is called as long as there's work associated to them (be it a work object or, in this case, timers). With that said, since the thread is associated to the io_service, I wonder why the io_service would stop running in the first place: after all, the thread is "linked" to the io_service and keeps on running on its own; this is obviously linked to the fact that I clearly didn't understand what this thread is doing in the first place.
Things got even more complicated when I added the "saysomething" method into the pot: I wanted to be able to write something and having that string printed WHILE the 2 timers kept working. The code I used was the following:
int main()
{
boost::asio::io_service io;
printer p(io);
boost::thread t(&saysomething);
io.run();
std::cout << "Hey there\n";
t.join();
return 0;
}
With the following result:
Timer 1: 0
Timer 2: 1
Timer 1: 2
Timer 2: 3
Timer 1: 4
Timer 2: 5
Timer 1: 6
Timer 2: 7
ghg //<--- my input
You said ghg
Timer 1: 8
Timer 2: 9
Hey there
Final count is 10
It works fine, but now that there is no thread associated to the io_service, what was its purpose in the first place?
To sum up my 3 questions are:
Why isn't the "Hey there" string immediately printed rather than waiting for the io_service to stop running?
How exactly does the io_service stop running if a thread is linked to it, which should be equivalent to the io_service having work to do?
Since the thread wasn't allowing the "code flow" to move forward, and linking said thread to my method instead of the io_service didn't cause any error, what was the purpose of that thread in the first place?
Why isn't the "Hey there" string immediately printed rather than waiting for the io_service to stop running?
main's thread also blocks on the io_service before printing, so "Hey there" doesn't print until the service stops.
How exactly does the io_service stop running if a thread is linked to it, which should be equivalent to the io_service having work to do?
The thread is not what's keeping the io_service alive, the timer tasks are. The io_service is actually the one keeping the thread alive here. The work the service has is waiting on the timers, so until the timers expire, the service has work to do.
Since the thread wasn't allowing the "code flow" to move forward, and linking said thread to my method instead of the io_service didn't cause any error, what was the purpose of that thread in the first place?
The purpose of calling run from a thread is to donate that calling thread to the io_service. Until run exits, the service owns that thread, and that thread is part of the service's thread pool. Any task you post to the service may be handed to that thread while it is in the service's pool. When you added the second thread, that second thread wasn't interacting with the service at all because it didn't call run. Thus, it's not part of the service's thread pool.

AMQP-CPP RabbitMQ async event based consumer not consuming anything

I'm using the AMQ-CPP library (https://github.com/CopernicaMarketingSoftware/AMQP-CPP) to connect to an existing queue I've created but I'm unable to read anything. I've tested that the queue works using another library (https://github.com/alanxz/SimpleAmqpClient, it works and I consume messages), but it uses a polling approach and I need an event based one.
My code looks like (based on the provided example):
int main()
{
auto *poll = EV_DEFAULT;
// handler for libev (so we don't have to implement AMQP::TcpHandler!)
AMQP::LibEvHandler handler(poll);
// make a connection
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
// we need a channel too
AMQP::TcpChannel channel(&connection);
// Define callbacks and start
auto messageCb = [&channel](
const AMQP::Message &message, uint64_t deliveryTag,
bool redelivered)
{
std::cout << "message received" << std::endl;
// acknowledge the message
channel.ack(deliveryTag);
processMessage(message.routingKey(), message.body());
};
// callback function that is called when the consume operation starts
auto startCb = [](const std::string &consumertag) {
std::cout << "consume operation started: " << consumertag << std::endl;
};
// callback function that is called when the consume operation failed
auto errorCb = [](const char *message) {
std::cout << "consume operation failed" << std::endl;
};
channel.consume("domoqueue")
.onReceived(messageCb)
.onSuccess(startCb)
.onError(errorCb);
// run the poll
ev_run(poll, 0);
// done
return 0;
}
I'm running the code in a Raspberry Pi having :
Linux raspberrypi 4.4.26-v7+ #915 SMP Thu Oct 20 17:08:44 BST 2016 armv7l GNU/Linux
What can be the problem? Probably I'm missing some configuration parameters for the queue... I've placed some debug traces and the channel creation does not take place. It blocks in the connection statement:
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
cout << "I never show up" << endl;
// we need a channel too
AMQP::TcpChannel channel(&connection)
I've found my problem: I wasn't using the declareQueue() method! In fact, I had to use it but specifying the following parameters (the same as I did when I created the queue manually):
AMQP::Table arguments;
arguments["x-message-ttl"] = 120 * 1000;
// declare the queue
channel.declareQueue("domoqueue", AMQP::durable + AMQP::passive, arguments).onSuccess(callback);

pplx::task with daemon not executed

I have a problem with pplx::task from cpprest (casablanca). After forking my process to create a daemon, the tasks are not executed and wait forever.
auto task = pplx::create_task([] {
std::cout << "Hi I'm a task " << std::endl;
});
task.wait();
// Create daemon process (not included for simplicity)
auto notWorkingTask = pplx::create_task([] {
std::cout << "Hi I'm a task in daemon" << std::endl;
});
notWorkingTask.wait();
Any idea how to archieve that ? I suppose there is a boost::asio::io_service on background and need to notify it with :
boost::asio::io_service::notify_fork(boost::asio::prepare_fork);
Sysout (this is simulated one, we use syslog because daemon has no access to sysout)
Hi I'm a task
Edit : There are a feature request here
Somebody know a workaround ?
Finally we have patched casablanca, there is a working example for Android :
/include/pplx/threadpool.h