Install googletest on OpenBSD

To use googletest on OpenBSD, I need to install CMake firstly:

# pkg_add cmake

Then Download googletest and create build directory:

# git clone https://github.com/google/googletest.git
# cd googletest
# mkdir build
# cd build

If use default gcc compiler, the compilation will generate following errors:

# make
[  2%] Building CXX object googlemock/CMakeFiles/gmock.dir/__/googletest/src/gtest-all.cc.o
cc1plus: warnings being treated as errors
In file included from /usr/include/g++/tr1/tuple:159,
                 from /root/Project/googletest/googletest/include/gtest/internal/gtest-port.h:746,
                 from /root/Project/googletest/googletest/include/gtest/internal/gtest-internal.h:40,
                 from /root/Project/googletest/googletest/include/gtest/gtest.h:58,
                 from /root/Project/googletest/googletest/src/gtest-all.cc:39:
/usr/include/g++/tr1/functional: In constructor 'std::tr1::_Mem_fn<_Res _Class::*>::_Mem_fn(_Res _Class::*)':
/usr/include/g++/tr1/functional:470: warning: declaration of '__pm' shadows a member of 'this'
In file included from /usr/include/g++/tr1/tuple:159,
                 from /root/Project/googletest/googletest/include/gtest/internal/gtest-port.h:746,
                 from /root/Project/googletest/googletest/include/gtest/internal/gtest-internal.h:40,
                 from /root/Project/googletest/googletest/include/gtest/gtest.h:58,
                 from /root/Project/googletest/googletest/src/gtest-all.cc:39:
/usr/include/g++/tr1/functional: In constructor 'std::tr1::_Simple_type_wrapper<_Tp>::_Simple_type_wrapper(_Tp)':
/usr/include/g++/tr1/functional:804: warning: declaration of '__value' shadows a member of 'this'
*** Error 1 in . (googlemock/CMakeFiles/gmock.dir/build.make:63 'googlemock/CMakeFiles/gmock.dir/__/googletest/src/gtest-all.cc.o': cd /root...)
*** Error 1 in . (CMakeFiles/Makefile2:90 'googlemock/CMakeFiles/gmock.dir/all')
*** Error 1 in /root/Project/googletest/build (Makefile:139 'all')

So I switch to clang, and all build is OK:

# CC=clang cmake ..
# make
# make install

Be careful of using grpc::ClientStreamingInterface::Finish() function

No matter server-side streaming RPC or bidirectional streaming RPC, if there is still message for Client to read, but Client callsgrpc::ClientStreamingInterface::Finish(), it will block here forever. The call stack is like this:

#0  0x00007ffff5b24076 in epoll_pwait () from /usr/lib/libc.so.6
#1  0x00007ffff71c6ae5 in ?? () from /usr/lib/libgrpc.so.4
#2  0x00007ffff71e3a26 in ?? () from /usr/lib/libgrpc.so.4
#3  0x000055555558300f in grpc::CompletionQueue::Pluck (this=0x555555fd5340, tag=0x7fffffffd800)
    at /usr/include/grpc++/impl/codegen/completion_queue.h:224
#4  0x0000555555585e46 in grpc::ClientReaderWriter<..., ...>::Finish (
    this=0x555555fd5320) at /usr/include/grpc++/impl/codegen/sync_stream.h:494

So please pay attention to it. Before calling grpc::ClientStreamingInterface::Finish(), make sure the grpc::ReaderInterface::Read() return false:

while (stream->Read(&server_note)) {
  ...
}
Status status = stream->Finish();

First taste of gRPC

In the past month, I made my hand dirty on gRPC, and tried to use it in my current project. The effect seems good until now, I want to share some feeling here:

(1) gRPC lets you concentrate on message definition, and it will generate all necessary code for you. This is really very neat! The APIs are also simple and handy: they help process all kinds of exceptional cases which will save users a lot of energy.

(2) Now gRPC‘s maximum message length is INT_MAX (please refer this post) , so on general 64-bit platform, its value is 2^31 - 1, nearly 2GiB. Personally, I think maybe LONG_MAX, 2^63 - 1, is better, since it is nearly equal to say there is no limitation for message length. BTW, gRPC useProtocol Buffers under the hood. From Protocol Buffers document:

Protocol Buffers are not designed to handle large messages. As a general rule of thumb, if you are dealing in messages larger than a megabyte each, it may be time to consider an alternate strategy.

I haven’t dived into Protocol Buffers code, so not sure what is the negative effect of transmitting large message. But anyway, whether support and whether support well are 2 different things.

 

The basics of Client/Server socket programming

While Client/Server communication model is ubiquitous nowadays, most of them involve socket programming knowledge. In this post, I will introduce some rudimentary aspects of it:

(1) Short/Long-lived TCP connection.
Short-lived TCP connection refers to following pattern: Client creates a connection to server; send message, then close the connection. If Client wants to transmit information again, repeat the above steps. Because establishing and destroying TCP sessions have overhead, if Client needs to have transactions with Server frequently, long-lived connection may be a better choice: connect Server; deliver message, deliver message, …, disconnect Server. A caveat about long-lived connection is Client may need to send heartbeat message to Server to keep the TCP session active.

(2) Synchronous/Asynchronous communication.
After Client sends the request, it can block here to wait for Server’s response, this is called synchronous mode. Certainly the Client should set a timer in case the response never come. The Client can also choose not to block, and continue to do other things. On the contrary, this is called asynchronous mode. If the Client can send multiple requests before receiving responses, it needs to add ID for every message, so it can distinguish the corresponding response for every request.

(3) Error handling.
A big pain point of socket programming is you need to consider so many exceptional cases. For example, the Server suddenly crashes; the network cable is plugged out, or the response message is half-received, etc. So your code should process as many abnormalities as possible. It is no exaggeration to say that error-handling code quality is the cornerstone of program’s robustness.

(4) Portability.
Different *NIX flavors may have small divergences on socket programming, so the program works well on Linux may not guarantee it also run as you expect on FreeBSD. BTW, I summarized a post about tips of Solaris/illumos socket programming before, and you can read it if you happen to work on these platforms.

(5) Leverage sniffer tools.
Tcpdump/Wireshark/snoop are amazing tools for debugging network programming, and they can tell you what really happens under the hood. Try to be sophisticated at these tools, and they will save you at one day, trust me!

 

The header file and library search path of cc on OpenBSD

On my OpenBSD 6.1/amd64 system, the default header file search path of cc compiler is only /usr/include:

# echo | cc -E -Wp,-v -
ignoring duplicate directory "/usr/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include
End of search list.
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"

The default library search path is only /usr/lib:

# cc -Xlinker --verbose  2>/dev/null | grep SEARCH | sed 's/SEARCH_DIR("=\?\([^"]\+\)"); */\1\n/g'  | grep -vE '^$'
SEARCH_DIR("/usr/lib");

So if you want to include other header file or link libraries in other directory, you need to specify it explicitly. For example:

# cc -I/usr/local/include -L/usr/local/lib ...

Reference:
Finding out what the GCC include path is;
How to print the ld(linker) search path.