Search code examples
c++arraysrpcgrpcprotocol-buffers

How do I make an in-place modification on an array using grpc and google protocol buffers?


I'm having a problem with a const request with the google protocol buffers using grpc. Here is my problem:

I would like to make an in-place modification of an array's value. For that I wrote this simple example where I try to pass an array and sum all of it's content. Here's my code:

adder.proto:

syntax = "proto3";

option java_package = "io.grpc.examples";

package adder;

// The greeter service definition.
service Adder {
    // Sends a greeting
    rpc Add (AdderRequest) returns (AdderReply) {}
}

// The request message containing the user's name.
message AdderRequest {
    repeated int32 values = 1;
}

// The response message containing the greetings
message AdderReply {
    int32 sum = 1;
}

server.cc:

//
// Created by Eric Reis on 7/6/16.
//

#include <iostream>
#include <grpc++/grpc++.h>

#include "adder.grpc.pb.h"

class AdderImpl final : public adder::Adder::Service
{
public:
    grpc::Status Add(grpc::ServerContext* context, const adder::AdderRequest* request,
                     adder::AdderReply* reply) override
    {
        int sum = 0;
        for(int i = 0, sz = request->values_size(); i < sz; i++)
        {
            request->set_values(i, 10); // -> this gives an error caused by the const declaration of the request variable
                                        //    error: "Non-const function 'set_values' is called on the const object"
            sum += request->values(i);  // -> this works fine
        }
        reply->set_sum(sum);
        return grpc::Status::OK;
    }
};

void RunServer()
{
    std::string server_address("0.0.0.0:50051");
    AdderImpl service;

    grpc::ServerBuilder builder;
    // Listen on the given address without any authentication mechanism.
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    // Register "service" as the instance through which we'll communicate with
    // clients. In this case it corresponds to an *synchronous* service.
    builder.RegisterService(&service);
    // Finally assemble the server.
    std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_address << std::endl;

    // Wait for the server to shutdown. Note that some other thread must be
    // responsible for shutting down the server for this call to ever return.
    server->Wait();
}

int main(int argc, char** argv)
{
    RunServer();
    return 0;
}

client.cc:

//
// Created by Eric Reis on 7/6/16.
//

#include <iostream>
#include <grpc++/grpc++.h>

#include "adder.grpc.pb.h"

class AdderClient
{
public:
    AdderClient(std::shared_ptr<grpc::Channel> channel) : stub_(adder::Adder::NewStub(channel)) {}

    int Add(int* values, int sz) {
        // Data we are sending to the server.
        adder::AdderRequest request;
        for (int i = 0; i < sz; i++)
        {
            request.add_values(values[i]);
        }

        // Container for the data we expect from the server.
        adder::AdderReply reply;

        // Context for the client. It could be used to convey extra information to
        // the server and/or tweak certain RPC behaviors.
        grpc::ClientContext context;

        // The actual RPC.
        grpc::Status status = stub_->Add(&context, request, &reply);

        // Act upon its status.
        if (status.ok())
        {
            return reply.sum();
        }
        else {
            std::cout << "RPC failed" << std::endl;
            return -1;
        }
    }

private:
    std::unique_ptr<adder::Adder::Stub> stub_;
};

int main(int argc, char** argv) {
    // Instantiate the client. It requires a channel, out of which the actual RPCs
    // are created. This channel models a connection to an endpoint (in this case,
    // localhost at port 50051). We indicate that the channel isn't authenticated
    // (use of InsecureChannelCredentials()).
    AdderClient adder(grpc::CreateChannel("localhost:50051",
                                          grpc::InsecureChannelCredentials()));
    int values[] = {1,2};
    int sum = adder.Add(values, 2);
    std::cout << "Adder received: " << sum << std::endl;

    return 0;
}

My error happens when i try to call the method set_values() on the request object that is defined as const. I understand why this error is occurring but I just can't figure out a way to overcome it without making a copy of the array.

I tried to remove the const definition but the RPC calls fails when I do that.

Since I'm new to this RPC world and even more on grpc and the google protocol buffers I'd like to call for your help. What is the best way to solve this problem?


Solution

  • Please see my answer here. The server receives a copy of the AdderRequest sent by the client. If you were to modify it, the client's original AdderRequest would not be modified. If by "in place" you mean the server modifies the client's original memory, no RPC technology can truly accomplish that, because the client and server run in separate address spaces (processes), even on different machines.

    If you truly need the server to modify the client's memory:

    1. Ensure the server and client run on the same machine.
    2. Use OS-specific shared-memory APIs such as shm_open() and mmap() to map the same chunk of physical memory into the address spaces of both the client and the server.
    3. Use RPC to transmit the identifier (name) of the shared memory (not the actual data in the memory) and to invoke the server's processing.
    4. When both client and server have opened and mapped the memory, they both have pointers (likely with different values in the different address spaces) to the same physical memory, so the server will be able to read what the client writes there (with no copying or transmitting) and vice versa.