diff --git a/README.md b/README.md new file mode 100644 index 000000000..182c9f9b1 --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ +# LOT - Low Overhead Transport + +## General description + +This repository contains the C++ implementation of the shared memory mechanism described in the paper _Smart Pointers and Shared Memory Synchronisation for Efficient Inter-process Communication in ROS on an Autonomous Vehicle_ (IROS 2021) along with detailed build and deployment instructions. + +The implementation consists of a modified version of the `ros_comm` package from ROS Kinetic (v1.12.14) along with a [suite of benchmarks](./clients/benchmark). + +The code can be built either directly on the host machine _or_ within a Docker container via [the provided Docker script](./clients/benchmark/docker/Dockerfile). The former assumes the installation of ROS 1.12.14, Boost 1.58.0 and python2 on the host machine. The latter only requires Docker installation on the host machine. See [the Docker website](https://docs.docker.com/get-docker/) for details of setting up Docker on the host machine. + +The benchmarks - either the entire suite or individual tests - can be executed following the procedure described below. Currently, four protocols are supported: TCP, UDP, SHM and TZC. + +When executed from within the Docker containers, the benchmarks results are generated under `${HOME}/lot` folder. Otherwise, environment variable `lot_HOME` dictates the results folder. + +### Notes on provided code + +We apply the following bug fixes on top of ROS 1.12.14 in addition to our own code: + - _roslaunch_ [bug fix](https://github.com/ros/ros_comm/pull/1115). + - replace `boost::condition_variable` variable with `std::condition_variable` in [callback_queue.cpp](./clients/roscpp/src/libros/callback_queue.cpp). + +### Third party code + +[Boost.Process 1_65_0](https://www.boost.org/doc/libs/1_65_0/doc/html/process.html) has been imported into the source tree to support [unit tests](./test/test_roscpp/test/test_shm.cpp). We note that [Boost 1.65.0](https://github.com/boostorg/boost/tree/boost-1.65.0) is released under the [Boost Software License](http://www.boost.org/LICENSE_1_0.txt). + +For convenience of running the benchmarks, we have also included [TZC](https://github.com/qboticslabs/tzc_transport) in this repository. TZC was developed by a group researchers afilliated to Tsinghua University, China and University of Maryland, USA and is described more fully in the _TZC: Efficient Inter-Process Communication for Robotics Middleware with Partial Serialization_. Its authors have released it under the [BSD](https://github.com/qboticslabs/tzc_transport/blob/master/package.xml) license. + +## Cloning the repository and switching to the target branch + +Note that we clone the repository into `$HOME/ros_comm/src/ros_comm` in this step. We assume this directory in subsequent steps. If you prefer to use a different working directory, the subsequent steps will need to be appropriately modified. + +``` +git clone git@github.com:fiveai/ros_comm.git $HOME/ros_comm/src/ros_comm +cd $HOME/ros_comm/src/ros_comm +git checkout lot +``` + +## Building and running the benchmarks natively + +The following steps build the code directly on the host machine. + +### Compiling the entire workspace + +``` +cd $HOME/ros_comm +source /opt/ros/kinetic/setup.sh # this assumes ROS kinetic 1.12.14 is already installed +catkin_make_isolated + --source ./src/ \ + --build $HOME/ros_comm/build-release \ + --devel $HOME/ros_comm/devel-release \ + --install-space $HOME/ros_comm/install-release \ + --install \ + --cmake-args -DCMAKE_BUILD_TYPE=Release +``` + +### Executing the benchmarks specified in launch.xml overriding some of the parameters + +``` +cd $HOME/ros_comm +source ./devel-release/setup.sh +roslaunch --screen -v benchmark launch.xml \ + use_case:=5p1s_same_host sub_stats_file_path:=$HOME \ + transport:=shm pub_queue_size:=200 pub_image_count:=200 +``` + +## Building and running benchmarks within Docker containers + +### Building the Docker image + +``` +cd ~/ros_comm/src/ros_comm/clients/benchmark/docker +export DOCKER_BUILDKIT=1 +docker build --ssh default --tag lot . +``` + +### Executing the benchmark suite, deploying the ROS nodes in separate docker images + +Assuming the Docker container built as above, with a tag of `lot`: + +``` +cd ~/ros_comm/src/ros_comm/clients/benchmark/docker +docker-compose up + +# on a different terminal attach to node1; note it must be node1! +docker attach node1 +``` + +And then, from within the container, our benchmarks can be executed using commands of the form: + +``` +python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py --tcp=no --shm=yes --use_case=5p1s_separate_docker +``` + +The next sections provides examples of such commands. + +### Benchmark execution command examples + +1. Execute the benchmarks suite for 1p5s using TZC protocol, in separate Docker containers, enforcing the subscribers start up order, allocating 16GB of shared memory to be used by the publishers, and telling the subscriber to wait 15 seconds before starting publishing messages, `TCP_NODELAY` enabled. + +``` +python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py \ + --tcp=no --shm=no --tzc=yes --udp=no \ + --use_case=1p5s_separate_docker --no_pool=yes --pool=no \ + --extra_params sub_enable_synch_startup:=true \ + pub_extra_delay_ms:=15000 \ + shm_size_mega_bytes:=16000 +``` + +2. Execute the benchmarks suite for 5p1s using TZC protocol, in separate Docker containers, with each publisher waiting for the subscriber to establish connection and with the subscriber start up delayed by 15secs, `TCP_NODELAY` enabled. + +``` +python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py \ + --tcp=no --shm=no --tzc=yes --udp=no \ + --use_case=5p1s_separate_docker --no_pool=yes --pool=no \ + --extra_params pub_wait_for_subscribers:=true \ + sub_extra_delay_ms:=15000 +``` + +3. Execute the benchmarks suite for 5p1s using TZC protocol, in separate Docker containers, with each publisher waiting for the subscriber to establish connection and with the publishers start up order enforced, `TCP_NODELAY` enabled, overriding the default results path. + +``` +python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py \ + --tcp=no --shm=no --tzc=yes --udp=no \ + --use_case=5p1s_separate_docker --no_pool=yes --pool=no \ + --extra_params pub_wait_for_subscribers:=true \ + pub_enable_synch_startup:=true \ + sub_stats_file_path:=/path/to/results +``` + +4. Execute the benchmarks suite for 5p1s using SHM protocol, in separate Docker containers, with each publisher waiting for the subscriber to establish connection and with the publishers start up order enforced, and image pools disabled. + +``` +python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py \ + --tcp=no --shm=yes --tzc=no --udp=no \ + --use_case=5p1s_separate_docker --no_pool=yes --pool=no \ + --extra_params pub_wait_for_subscribers:=true \ + pub_enable_synch_startup:=true +``` + +5. Execute _one single test_ using TCP protocol, in same docker container, with image pool disabled. + +``` +roslaunch --screen -v benchmark launch.xml \ + pub_image_count:=200 \ + use_case:=1p5s_same_docker \ + transport:=tcp \ + image_width_pixels:=512 \ + image_height_pixels:=512 \ + pub_pool_size:=0 +``` diff --git a/clients/benchmark/CMakeLists.txt b/clients/benchmark/CMakeLists.txt new file mode 100644 index 000000000..28f4461da --- /dev/null +++ b/clients/benchmark/CMakeLists.txt @@ -0,0 +1,82 @@ +# *************************************************************************************************** +# * Copyright Five AI 2021. +# * All rights reserved. +# *************************************************************************************************** + +cmake_minimum_required(VERSION 2.8.3) +project(benchmark) + +add_compile_options(-std=c++11) + +find_package(catkin REQUIRED COMPONENTS + roscpp + rospy + std_msgs +) + +find_package(Boost REQUIRED COMPONENTS system) + +## The catkin_package macro generates cmake config files for your package +## Declare things to be passed to dependent projects +## INCLUDE_DIRS: uncomment this if your package contains header files +## LIBRARIES: libraries you create in this project that dependent projects also need +## CATKIN_DEPENDS: catkin_packages dependent projects also need +## DEPENDS: system dependencies of this project that dependent projects also need +catkin_package( +# INCLUDE_DIRS include +# LIBRARIES benchmark +# CATKIN_DEPENDS roscpp rospy std_msgs +# DEPENDS system_lib +) + +include_directories( + include + ${catkin_INCLUDE_DIRS} +) + +add_library(${PROJECT_NAME}_util + STATIC + src/Util.cpp +) + + +add_executable(${PROJECT_NAME}_publisher src/Publisher.cpp) +add_executable(${PROJECT_NAME}_subscriber src/Subscriber.cpp) +add_executable(${PROJECT_NAME}_monitor src/Monitor.cpp) + +add_custom_target(${PROJECT_NAME}_all) +add_dependencies(${PROJECT_NAME}_all + ${PROJECT_NAME}_publisher + ${PROJECT_NAME}_subscriber + ${PROJECT_NAME}_monitor +) + +target_link_libraries(${PROJECT_NAME}_publisher + ${catkin_LIBRARIES} + ${PROJECT_NAME}_util + rt +) + +target_link_libraries(${PROJECT_NAME}_subscriber + ${PROJECT_NAME}_util + ${catkin_LIBRARIES} + rt +) + +target_link_libraries(${PROJECT_NAME}_monitor + ${PROJECT_NAME}_util + ${catkin_LIBRARIES} +) + +install( + TARGETS + ${PROJECT_NAME}_publisher + ${PROJECT_NAME}_subscriber + ${PROJECT_NAME}_monitor + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) + + install(FILES + launch/launch.xml + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) diff --git a/clients/benchmark/docker/Dockerfile b/clients/benchmark/docker/Dockerfile new file mode 100644 index 000000000..685df22e0 --- /dev/null +++ b/clients/benchmark/docker/Dockerfile @@ -0,0 +1,71 @@ +# syntax=docker/dockerfile:experimental + +# WARNING: The top line must go first in the file and is required to +# allow cloning the git repository through ssh. More details +# can be found at: https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09-ae8161d066 +# To build this docker image run: +# 1. export DOCKER_BUILDKIT=1 +# 2. docker build --ssh default --tag XXX . + +# *************************************************************************************************** +# * Copyright Five AI 2021. +# * All rights reserved. +# *************************************************************************************************** + +FROM osrf/ros:kinetic-desktop-full-xenial + +RUN apt-get update && apt-get install -y \ + openssh-client \ + nano \ + htop \ + openssh-server \ + iputils-ping \ + net-tools + +RUN mkdir -p -m 0600 ~/.ssh && \ssh-keyscan github.com >> ~/.ssh/known_hosts +RUN mkdir /var/run/sshd +RUN echo 'root:abcde' | chpasswd +RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd +ENV NOTVISIBLE "in users profile" + +RUN echo "export VISIBLE=now" >> /etc/profile +RUN echo "/usr/sbin/sshd" >> ~/.bashrc +RUN echo "export ROSLAUNCH_SSH_UNKNOWN=1" >> ~/.bashrc +RUN echo "source /ros_comm/devel-release/setup.sh" >> ~/.bashrc +RUN echo "mkdir -p \${LOT_HOME}" >> ~/.bashrc +# This line is need to workaround a roslaunch bug where multiple instances of roscore are launched +RUN echo "export ROS_MASTER_URI=http://\$(uname -n):11311" >> ~/.bashrc +RUN echo "nano /ros_comm/src/ros_comm/clients/benchmark/launch/launch.xml" >> ~/.bash_history +RUN echo "catkin_make_isolated \ +--source ./src/ \ +--build /ros_comm/build-release \ +--devel /projects/ros_comm/devel-release \ +--install-space /ros_comm/install-release \ +--pkg=benchmark --install --cmake-args -DCMAKE_BUILD_TYPE=Release && \ +roslaunch --screen -v benchmark launch.xml pub_image_count:=2000 \ +sub_stats_file_path:=/root/tmp use_case:=1p1s_same_host" >> ~/.bash_history + +RUN echo "python2 /ros_comm/src/ros_comm/clients/benchmark/execute.py --use_case=1p1s_same_host --extra_params=''" >> ~/.bash_history + + +RUN --mount=type=ssh git clone git@github.com:fiveai/ros_comm.git /ros_comm/src/ros_comm && \ + cd /ros_comm/src/ros_comm && \ + git pull && \ + git checkout lot && \ + cd /ros_comm + +RUN /bin/bash -c "source /opt/ros/kinetic/setup.sh && \ + catkin_make_isolated \ + --source /ros_comm/src/ \ + --build /ros_comm/build-release \ + --devel /ros_comm/devel-release \ + --install-space /ros_comm/install-release \ + --install \ + --cmake-args -DCMAKE_BUILD_TYPE=Release" + +WORKDIR /ros_comm + +EXPOSE 22 diff --git a/clients/benchmark/docker/docker-compose.yaml b/clients/benchmark/docker/docker-compose.yaml new file mode 100644 index 000000000..a447d7d6d --- /dev/null +++ b/clients/benchmark/docker/docker-compose.yaml @@ -0,0 +1,48 @@ +# *************************************************************************************************** +# * Copyright Five AI 2021. +# * All rights reserved. +# *************************************************************************************************** + +version: "3.7" + +x-base_node: &base_node + image: lot + ipc: host + volumes: + - "${HOME}:${HOME}" + stdin_open: true # docker run -i + tty: true # docker run -t + environment: + - LOT_HOME=${HOME}/lot + +services: + # define the nodes as copies of the base node + node_1: + << : *base_node + container_name: node1 + hostname: node1 + + node_2: + << : *base_node + container_name: node2 + hostname: node2 + + node_3: + << : *base_node + container_name: node3 + hostname: node3 + + node_4: + << : *base_node + container_name: node4 + hostname: node4 + + node_5: + << : *base_node + container_name: node5 + hostname: node5 + + node_6: + << : *base_node + container_name: node6 + hostname: node6 diff --git a/clients/benchmark/execute.py b/clients/benchmark/execute.py new file mode 100755 index 000000000..eea864d36 --- /dev/null +++ b/clients/benchmark/execute.py @@ -0,0 +1,129 @@ +# *************************************************************************************************** +# * Copyright Five AI 2021. +# * All rights reserved. +# *************************************************************************************************** + +# This script runs on top of roslaunch. Its purpose is to execute suites +# of benchmarks and it does so by generating sets of parameters +# that are then passed to roslaunch that in turn takes care of launching +# the ROS nodes specified in the launch.xml file. The launch.xml file takes +# in several parameters that can be overriden by this script via --extra_params +# argument. + +import copy +import roslaunch +import rospy +import argparse + +def profile(params): + uuid = roslaunch.rlutil.get_or_generate_uuid(None, False) + roslaunch.configure_logging(uuid) + + args = ['benchmark', 'launch.xml'] + args += params + + file = roslaunch.rlutil.resolve_launch_arguments(args)[0] + args = args[2:] + + launch_files = [(file, args)] + + parent = roslaunch.parent.ROSLaunchParent(uuid, launch_files, verbose=True, force_screen=True) + parent.start() + parent.spin(); + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description='Help') + parser.add_argument('--pub_image_count', type=int, default=2000, help='The number of images broadcast by the publisher') + parser.add_argument('--pub_pool_size', type=int, default=100, help='The number of images each publisher buffers before starting broadcasting') + parser.add_argument('--extra_params', nargs='*', type=str, default="", help='Extra parameters in the form param:=value that are to be passed to roslaunch. Example: --extra_params transport:=tcp pub_hz:=10') + parser.add_argument('--use_case', type=str, help="1p1s_same_host 1p5s_same_host 5p1s_same_host " + + "1p1s_same_docker 1p5s_same_docker 5p1s_same_docker " + + "1p1s_separate_docker 1p5s_separate_docker 5p1s_separate_docker") + parser.add_argument('--tcp', default='yes', choices=['yes', 'no'], help="Executes TCP tests") + parser.add_argument('--udp', default='yes', choices=['yes', 'no'], help="Executes UDP tests") + parser.add_argument('--shm', default='yes', choices=['yes', 'no'], help="Executes SHM tests") + parser.add_argument('--tzc', default='yes', choices=['yes', 'no'], help="Executes SHM tests") + parser.add_argument('--pool', default='yes', choices=['yes', 'no'], help="Executes tests with the pool memory enabled") + parser.add_argument('--no_pool', default='yes', choices=['yes', 'no'], help="Executes tests with the pool memory disabled") + + args = parser.parse_args() + + tcp_pool_params = [ + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=4096", "image_height_pixels:=4096", "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=4096", "image_height_pixels:=2048", "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=4096", "image_height_pixels:=1024", "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=2048", "image_height_pixels:=1024", "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=1024", "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=512" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=128" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=32" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=8" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=4" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=2" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=1024", "image_height_pixels:=1" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=512", "image_height_pixels:=1" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ["use_case:=" + args.use_case, "transport:=tcp", "image_width_pixels:=256", "image_height_pixels:=1" , "pub_image_count:=" + str(args.pub_image_count), "pub_hz:=30", "pub_pool_size:=" + str(args.pub_pool_size)], + ] + + shm_pool_params = copy.deepcopy(tcp_pool_params) + for L in shm_pool_params: + L[1] = "transport:=shm" + + udp_pool_params = copy.deepcopy(tcp_pool_params) + for L in udp_pool_params: + L[1] = "transport:=udp" + + tzc_pool_params = copy.deepcopy(tcp_pool_params) + for L in tzc_pool_params: + L[1] = "transport:=tzc" + + tcp_no_pool_params = copy.deepcopy(tcp_pool_params) + for L in tcp_no_pool_params: + L[-1] = "pub_pool_size:=0" + + udp_no_pool_params = copy.deepcopy(udp_pool_params) + for L in udp_no_pool_params: + L[-1] = "pub_pool_size:=0" + + tzc_no_pool_params = copy.deepcopy(tzc_pool_params) + for L in tzc_no_pool_params: + L[-1] = "pub_pool_size:=0" + + shm_no_pool_params = copy.deepcopy(shm_pool_params) + for L in shm_no_pool_params: + L[-1] = "pub_pool_size:=0" + + # collect all params generated by this script + + all_params = list(list()) + if args.tcp == 'yes': + if args.pool == 'yes': + all_params += tcp_pool_params + if args.no_pool == 'yes': + all_params += tcp_no_pool_params + if args.udp == 'yes': + if args.pool == 'yes': + all_params += udp_pool_params + if args.no_pool == 'yes': + all_params += udp_no_pool_params + if args.tzc == 'yes': + if args.pool == 'yes': + all_params += tzc_pool_params + if args.no_pool == 'yes': + all_params += tzc_no_pool_params + if args.shm == 'yes': + if args.pool == 'yes': + all_params += shm_pool_params + if args.no_pool == 'yes': + all_params += shm_no_pool_params + + # for each parameter set, append the command line extra parameters + for L in all_params: + L += args.extra_params + + for L in all_params: + print(' '.join(L)) + + for param_set in all_params: + profile(param_set) diff --git a/clients/benchmark/include/benchmark/Util.h b/clients/benchmark/include/benchmark/Util.h new file mode 100644 index 000000000..a3d03a4a3 --- /dev/null +++ b/clients/benchmark/include/benchmark/Util.h @@ -0,0 +1,28 @@ +/*************************************************************************************************** + * Copyright Five AI 2021. + * All rights reserved. + ***************************************************************************************************/ + +#pragma once + +#include + +#include +#include + +bool isTransportSupported(const std::string& transport); +void throwIfTransportNotSupported(const std::string& transport); + +// Returns the same as NodeHandle::getParam but rather than returning false +// it throws std::logic_error should the parameter not be found. +template +inline +T getParam(ros::NodeHandle& nh, const std::string& key) +{ + T val; + if (! nh.getParam(key, val)) + { + throw std::logic_error{"Param identified by key " + key + " not found"}; + } + return val; +} diff --git a/clients/benchmark/launch/launch.xml b/clients/benchmark/launch/launch.xml new file mode 100644 index 000000000..5680710a1 --- /dev/null +++ b/clients/benchmark/launch/launch.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clients/benchmark/package.xml b/clients/benchmark/package.xml new file mode 100644 index 000000000..7a51178e9 --- /dev/null +++ b/clients/benchmark/package.xml @@ -0,0 +1,28 @@ + + + benchmark + 1.0.0 + + The benchmark package aims at providing a framework with the intent + of assessing the performance of roscpp and perhsps rospy. + + + Costin Iordache + + TODO + + catkin + + roscpp + rospy + std_msgs + + roscpp + rospy + std_msgs + + roscpp + rospy + std_msgs + + diff --git a/clients/benchmark/src/Monitor.cpp b/clients/benchmark/src/Monitor.cpp new file mode 100644 index 000000000..cb000911a --- /dev/null +++ b/clients/benchmark/src/Monitor.cpp @@ -0,0 +1,91 @@ +/*************************************************************************************************** + * Copyright Five AI 2021. + * All rights reserved. + ***************************************************************************************************/ + +#include "benchmark/Util.h" + +#include +#include +#include +#include +#include +#include + +/** + * ROS node that acts as a barrier waiting for subscribers to complete. + * Its aggregated to the ROS graph as a required ROS node - see launch.xml. + * Its purpose is to force killing the ROS graph should the subscribers or + * publishers refuse to shutdown politely. +*/ + +struct Params +{ + Params() : + subCount{-1} + {} + + int subCount; +}; + +namespace +{ + Params params; + + int s_subFinishCount = 0; + + std::mutex s_mutex; + std::condition_variable s_cv; + bool s_allSubsFinished = false; +} + +void callback(const std_msgs::String& subName) +{ + ROS_INFO_STREAM("Node [" << subName << "] finished"); + + ++s_subFinishCount; + if (s_subFinishCount == params.subCount) + { + { + std::lock_guard lk(s_mutex); + s_allSubsFinished = true; + } + ROS_INFO_STREAM("All " << params.subCount << " subscribers finished"); + s_cv.notify_one(); + } +} + +ros::Subscriber makeSubscriber(ros::NodeHandle& nh) +{ + ros::Subscriber sub = nh.subscribe("/monitor_signal", 5, callback); + return sub; +} + +void loadParams(ros::NodeHandle& nh) +{ + params.subCount = getParam(nh, "/sub_count"); +} + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "monitor"); + ros::NodeHandle nh{"~"}; + + loadParams(nh); + + ros::Subscriber sub = makeSubscriber(nh); + + ros::AsyncSpinner spinner{1}; + spinner.start(); + + ROS_INFO_STREAM("Waiting for " << params.subCount << " subscribers to finish..."); + { + std::unique_lock lk(s_mutex); + s_cv.wait(lk, []{return s_allSubsFinished;}); + } + + ROS_INFO_STREAM("Stopping the monitor spinner..."); + spinner.stop(); + + return 0; +} diff --git a/clients/benchmark/src/Publisher.cpp b/clients/benchmark/src/Publisher.cpp new file mode 100644 index 000000000..bdf80429a --- /dev/null +++ b/clients/benchmark/src/Publisher.cpp @@ -0,0 +1,348 @@ +/*************************************************************************************************** + * Copyright Five AI 2021. + * All rights reserved. + ***************************************************************************************************/ + + +#include "benchmark/Util.h" + +#include "msgs/ShmImage.h" +#include "msgs/ShmSharedPtr.h" +#include "util/Types.h" +#include "threading/Types.h" +#include "msgs/tzc_Image.h" +#include "tzc/tzc_publisher.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * The ROS node @em publisher is in charge of broadcasting images on + * designated topics whilst making use of one of the following transport + * protocols: SHM, TCP, UDP or TZC. + * + * When more than one @em publisher exists in the ROS graph, the initialisation + * order of sibling publishers can be enforced. Likewise, additional delays are + * suppored in key points if more complex synchronisation strategies with + * the subscriber(s) are required. + */ + +using SharedPtrConstShmImage = ros::shm_msgs::SharedPtrConstShmImage; +using SharedPtrShmImage = ros::shm_msgs::SharedPtrShmImage; +using SharedPtrImage = sensor_msgs::ImagePtr; +using SharedPtrConstImage = sensor_msgs::ImageConstPtr; + +using SizePixels = ros::util::SizePixels; + +struct Params +{ + Params() : + transport{"UNKNOWN"}, + queueSize{-1}, + imageCount{-1}, + poolSize{-1}, + imageSize{0,0}, + hz{-1}, + subCount{0}, + pubCount{0}, + id{-1}, + extraDelay{0}, + enableSynchStartup{false}, + waitForSubscribers{false}, + shmSizeMegaBytes{0} + {} + + std::string transport; + int queueSize; + int imageCount; + int poolSize; + SizePixels imageSize; + double hz; + int subCount; + int pubCount; + int id; + std::chrono::milliseconds extraDelay; + bool enableSynchStartup; + bool waitForSubscribers; + int shmSizeMegaBytes; +}; + +namespace +{ + Params params; +} + +inline bool isPoolEnabled() +{ + return params.poolSize > 0; +} + +// Generic factory function. Not implemented, +// see its specialisations for concrete implementations. +template T makeImage(); + +template <> +SharedPtrShmImage makeImage() +{ + auto& eng = ros::ShmEngine::instance(); + auto img = eng.allocateSharedImage(params.imageSize); + return img; +} + +template <> +SharedPtrImage makeImage() +{ + SharedPtrImage img = boost::make_shared(); + + const auto& imageSize = params.imageSize; + + img->width = imageSize.getWidth(); + img->height = imageSize.getHeight(); + img->step = imageSize.getWidth(); + img->data.resize(imageSize.getArea()); + + return img; +} + +/** + * Images can be allocated from pools of images which + * are preallocated upon start up. +*/ +template +std::vector maybeMakePool() +{ + std::vector pool; + + if (isPoolEnabled()) + { + pool.reserve(params.poolSize); + ROS_INFO_STREAM("Pool enabled. Preallocating " << params.poolSize << " images"); + for (int i = 0; i < params.poolSize; ++i) + { + T img = makeImage(); + pool.push_back(img); + } + } + else + { + ROS_INFO_STREAM("Pool disabled. Images will be allocated on the fly"); + } + + return pool; +} + +template +void publish(ros::NodeHandle& nh) +{ + // ad-hoc connection order mechanism + if (params.enableSynchStartup) + { + if (params.id > 0) + { + const std::chrono::seconds delay(params.id *2); + ROS_INFO_STREAM(ros::this_node::getName() << " is waiting " << delay.count() << + "secs for peer publisher with id " << params.id -1 << " to come online..."); + std::this_thread::sleep_for(delay); + } + } + + ros::Publisher pub = nh.advertise("/image", params.queueSize); + ROS_INFO_STREAM(ros::this_node::getName() << " advertised " << pub.getTopic()); + + auto pool = maybeMakePool(); + + if (params.extraDelay > std::chrono::milliseconds::zero()) + { + ROS_INFO_STREAM(ros::this_node::getName() << " is waiting for extra delay " << + params.extraDelay.count() << " ms..."); + std::this_thread::sleep_for(params.extraDelay); + } + + if (params.waitForSubscribers) + { + while (pub.getNumSubscribers() < params.subCount) + { + const int pendingSubCount = params.subCount - pub.getNumSubscribers(); + ROS_INFO_STREAM_THROTTLE(2.0, ros::this_node::getName() << " is waiting for " << + pendingSubCount << " subscribers to come online..."); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + + ROS_INFO_STREAM("Start publishing at " << params.hz << "Hz"); + ros::Rate rate{params.hz}; + for (int i = 0; i < params.imageCount; ++i) + { + T img; + if (isPoolEnabled()) + { + img = pool[i % params.poolSize]; + } + else + { + img = makeImage(); + } + + img->header.seq = i; + img->header.stamp = ros::Time::now(); + // embed the id of this publisher in the first 4 bytes of the image + *reinterpret_cast(&img->data[0]) = params.id; + + pub.publish(img); + + rate.sleep(); + } + + //wait for the messages to be sent to all subscribers before destroying the publisher + std::this_thread::sleep_for(std::chrono::seconds(5)); +} + +void publishThroughShm(ros::NodeHandle& nh) +{ + publish(nh); +} + +void publishThroughTcpOrUdp(ros::NodeHandle& nh) +{ + publish(nh); +} + +// NB: Does not support pools and occasionally crashes in TZC. +void publishThroughTzc(ros::NodeHandle& nh) +{ + using Img = tzc_transport::sensor_msgs::Image; + + auto megaBytesToBytes = [](std::uint64_t megaBytes){return megaBytes * 1024*1024;}; + + // ad-hoc connection order mechanism + if (params.enableSynchStartup) + { + if (params.id > 0) + { + const std::chrono::seconds delay(params.id *2); + ROS_INFO_STREAM(ros::this_node::getName() << " is waiting " << delay.count() << + "secs for peer publisher with id " << params.id -1 << " to come online..."); + std::this_thread::sleep_for(delay); + } + } + + tzc_transport::Topic t(nh); + tzc_transport::Publisher pub = t.advertise("/image", params.queueSize, megaBytesToBytes(params.shmSizeMegaBytes)); + ROS_INFO_STREAM(ros::this_node::getName() << " advertised " << pub.getTopic()); + + if (params.extraDelay > std::chrono::milliseconds::zero()) + { + ROS_INFO_STREAM(ros::this_node::getName() << " is waiting for extra delay " << + params.extraDelay.count() << " ms..."); + std::this_thread::sleep_for(params.extraDelay); + } + + if (params.waitForSubscribers) + { + while (pub.getNumSubscribers() < params.subCount) + { + const int pendingSubCount = params.subCount - pub.getNumSubscribers(); + ROS_INFO_STREAM_THROTTLE(2.0, ros::this_node::getName() << " is waiting for " << + pendingSubCount << " subscribers to come online..."); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + + ROS_INFO_STREAM("Start publishing at " << params.hz << "Hz"); + ros::Rate rate{params.hz}; + const auto& imageSize = params.imageSize; + for (int i = 0; i < params.imageCount; ++i) + { + tzc_transport::sensor_msgs::Image img; + + img.width = imageSize.getWidth(); + img.height = imageSize.getHeight(); + img.step = imageSize.getWidth(); //??? + img.data.resize(imageSize.getArea()); + + if (pub.allocate(img)) + { + img.header.seq = i; + img.header.stamp = ros::Time::now(); + // embed the id of this publisher in the first 4 bytes of the image + *reinterpret_cast(&img.data[0]) = params.id; + + pub.publish(img); + } + else + { + ROS_ERROR_STREAM(ros::this_node::getName() << " failed to allocate image"); + } + + rate.sleep(); + } + + //wait for the messages to be sent to all subscribers before destroying the publisher + std::this_thread::sleep_for(std::chrono::seconds(5)); +} + +void loadParams(ros::NodeHandle& nh) +{ + const auto transport = getParam(nh, "/transport"); + throwIfTransportNotSupported(transport); + + params.transport = transport; + params.queueSize = getParam(nh, "/pub/queue_size"); + params.imageCount = getParam(nh, "/pub/image_count"); + params.poolSize = getParam(nh, "/pub/pool_size"); + params.imageSize = {static_cast(getParam(nh, "/image_width_pixels")), + static_cast(getParam(nh, "/image_height_pixels"))}; + params.hz = getParam(nh, "/pub/hz"); + params.subCount = getParam(nh, "/sub_count"); + params.pubCount = getParam(nh, "/pub_count"); + params.id = getParam(nh, "id"); + params.extraDelay = std::chrono::milliseconds{getParam(nh, "/pub/extra_delay_ms")}; + + params.enableSynchStartup = getParam(nh, "/pub/enable_synch_startup"); + params.waitForSubscribers = getParam(nh, "/pub/wait_for_subscribers"); + params.shmSizeMegaBytes = getParam(nh, "/shm_size_mega_bytes"); +} + +void validateParams() +{ + if (params.transport == "tzc") + { + if (isPoolEnabled()) + { + const std::string msg{"TZC does not support memory pools"}; + ROS_ERROR_STREAM(ros::this_node::getName() << msg); + throw std::logic_error{msg}; + } + } +} + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "publisher"); + ros::NodeHandle nh{"~"}; + + loadParams(nh); + validateParams(); + + if (params.transport == "shm") + { + publishThroughShm(nh); + } + else if (params.transport == "tcp" || params.transport == "udp") + { + publishThroughTcpOrUdp(nh); + } + else if (params.transport == "tzc") + { + publishThroughTzc(nh); + } + + return 0; +} diff --git a/clients/benchmark/src/Subscriber.cpp b/clients/benchmark/src/Subscriber.cpp new file mode 100644 index 000000000..43e48c001 --- /dev/null +++ b/clients/benchmark/src/Subscriber.cpp @@ -0,0 +1,279 @@ +/*************************************************************************************************** + * Copyright Five AI 2021. + * All rights reserved. + ***************************************************************************************************/ + +#include "msgs/ShmImage.h" +#include "msgs/ShmSharedPtr.h" +#include "util/Types.h" + +#include "benchmark/Util.h" +#include "msgs/tzc_Image.h" +#include "tzc/tzc_subscriber.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/** + * The ROS node @em subscriber is in charge of receiving images on + * designated topics and making use of one of the following transport + * protocols: SHM, TCP, UDP or TZC. + * + * When more than one @em subscriber exists in the ROS graph, the initialisation + * order of sibling subscribers can be enforced. Likewise, additional delays are + * suppored in key points if more complex synchronisation strategies with + * the publisher(s) are required. + */ + +using SharedPtrConstShmImage = ros::shm_msgs::SharedPtrConstShmImage; +using SharedPtrConstImage = sensor_msgs::ImageConstPtr; +using SizePixels = ros::util::SizePixels; + +struct Data +{ + Data() : + latency{0} + {} + + ros::Duration latency; +}; + +struct Params +{ + Params() : + transport{"UNKNOWN"}, + imageCount{-1}, + imageSize{0,0}, + queueSize{-1}, + outputStats{true}, + statsFullFilePath{"UNKNOWN"}, + tcpNoDelay{false}, + pubCount{0}, + subCount{0}, + id{-1}, + extraDelay{0}, + enableSynchStartup{false} + {} + + std::string transport; + int imageCount; + SizePixels imageSize; + int queueSize; + bool outputStats; + std::string statsFullFilePath; + bool tcpNoDelay; + int pubCount; + int subCount; + int id; + std::chrono::milliseconds extraDelay; + bool enableSynchStartup; +}; + +namespace +{ + Params params; + + // each publisher gets its own stats + std::vector> s_stats; + int s_receivedImageCount = 0; + + std::mutex s_mutex; + std::condition_variable s_cv; + bool s_allImagesReceived = false; +} + +// Registers stats about the received images in an in-memory table +template +void callback(T img) +{ + const auto& hdr = img->header; + + const auto latency = ros::Time::now() - hdr.stamp; + // retrieve the id of the publisher who broadcast this image + const auto pubId = *reinterpret_cast(&img->data[0]); + s_stats.at(pubId).at(hdr.seq).latency = latency; + + ROS_ERROR_STREAM_COND(params.outputStats, "Latency (" << ros::this_node::getName() << "): " + << latency + << " index " << hdr.seq + << " data size: " << img->data.size() + << " img_width: " << img->width + << " img_height: " << img->height + ); + + ROS_ERROR_STREAM_COND(img->data.size() != params.imageSize.getArea(), + "Received image size does not match the expected size"); + + ROS_INFO_STREAM_DELAYED_THROTTLE(2, "[" << ros::this_node::getName() << "] " + << s_receivedImageCount << " images received so far..."); + ++s_receivedImageCount; + + if (s_receivedImageCount == params.pubCount * params.imageCount) + { + ROS_INFO_STREAM("[" << ros::this_node::getName() << "] " + << "All " << s_receivedImageCount << " images have been received!"); + { + std::lock_guard lk(s_mutex); + s_allImagesReceived = true; + } + s_cv.notify_one(); + } +} + +// Factory function - creates a subscriber specified by transport parameter +boost::variant> +makeSubscriber(ros::NodeHandle& nh) +{ + if (params.transport == "shm") + { + auto cb = callback; + return nh.subscribe("/image", params.queueSize, cb); + } + else if (params.transport == "tcp") + { + auto cb = callback; + return nh.subscribe("/image", params.queueSize, cb, + ros::TransportHints{}.tcp() + .tcpNoDelay(params.tcpNoDelay)); + } + else if (params.transport == "udp") + { + auto cb = callback; + return nh.subscribe("/image", params.queueSize, cb, ros::TransportHints{}.udp()); + } + else if (params.transport == "tzc") + { + auto cb = callback; + using Img = tzc_transport::sensor_msgs::Image; + tzc_transport::Topic t(nh); + return t.subscribe("/image", params.queueSize, cb, + ros::TransportHints{}.tcp() + .tcpNoDelay(params.tcpNoDelay)); + } + + ROS_ERROR_STREAM(ros::this_node::getName() << " failed to create subscriber for " << params.transport << " protocol"); + return {}; +} + +void dumpStats(const std::string& statsFullFilePath) +{ + for (int i = 0; i < s_stats.size(); ++i) + { + auto& subStats = s_stats[i]; + + // do not add the publisher id unless multiple publisher are active + auto fullFilePath = statsFullFilePath; + if (s_stats.size() > 1) + { + boost::algorithm::replace_last(fullFilePath, ".txt", + "_pub" + std::to_string(i+1) + ".txt"); + } + + std::ofstream of(fullFilePath); + for (const auto& st : subStats) + { + of << st.latency << std::endl; + } + } +} + +void loadParams(ros::NodeHandle& nh) +{ + const auto transport = getParam(nh, "/transport"); + throwIfTransportNotSupported(transport); + + params.transport = transport; + params.imageCount = getParam(nh, "/sub/expected_image_count"); + + params.queueSize = getParam(nh, "/sub/queue_size"); + params.outputStats = getParam(nh, "/sub/output_stats"); + params.statsFullFilePath = getParam(nh, "stats_full_file_path"); + + params.imageSize = SizePixels{static_cast(getParam(nh, "/image_width_pixels")), + static_cast(getParam(nh, "/image_height_pixels"))}; + + params.tcpNoDelay = getParam(nh, "/sub/tcp_no_delay"); + params.pubCount = getParam(nh, "/pub_count"); + params.subCount = getParam(nh, "/sub_count"); + params.id = getParam(nh, "id"); + params.extraDelay = std::chrono::milliseconds{getParam(nh, "/sub/extra_delay_ms")}; + + params.enableSynchStartup = getParam(nh, "/sub/enable_synch_startup"); +} + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "subscriber"); + ros::NodeHandle nh{"~"}; + + loadParams(nh); + + // pre-allocate the memory for the s_stats + s_stats.resize(params.pubCount); + for (auto& subStats : s_stats) + { + subStats.resize(params.imageCount, Data{}); + } + + // ad-hoc connection order mechanism + const auto& myName = ros::this_node::getName(); + if (params.enableSynchStartup) + { + if (params.id > 0) + { + const std::chrono::seconds delay(params.id *2); + ROS_INFO_STREAM(myName << " is waiting for extra delay " << delay.count() << + "secs for peer subscriber with id " << params.id -1 << " to come online..."); + std::this_thread::sleep_for(delay); + } + } + + if (params.extraDelay > std::chrono::milliseconds::zero()) + { + ROS_INFO_STREAM(ros::this_node::getName() << " is waiting for extra delay " << + params.extraDelay.count() << " ms..."); + std::this_thread::sleep_for(params.extraDelay); + } + + auto sub = makeSubscriber(nh); + ros::Publisher pub = nh.advertise("/monitor_signal", 5); + + ros::AsyncSpinner spinner{1}; + spinner.start(); + + ROS_INFO_STREAM(myName << " is now ready to receive " << params.pubCount * params.imageCount << " images ..."); + { + std::unique_lock lk(s_mutex); + s_cv.wait(lk, []{return s_allImagesReceived;}); + } + + // Stats are saved once all the images have been received to avoid + // latencies being biased by writing to disk. + ROS_INFO_STREAM("Dumping stats to " << params.statsFullFilePath); + dumpStats(params.statsFullFilePath); + ROS_INFO_STREAM("Stats successfuly saved."); + + ROS_INFO_STREAM(ros::this_node::getName() << " is notifying the monitor that's finished"); + std_msgs::String tmp; + tmp.data = myName; + pub.publish(tmp); + + // wait for the monitor notification to be published + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ROS_INFO_STREAM("Stopping the spinner..."); + spinner.stop(); + + return 0; +} diff --git a/clients/benchmark/src/Util.cpp b/clients/benchmark/src/Util.cpp new file mode 100644 index 000000000..098f2e6c2 --- /dev/null +++ b/clients/benchmark/src/Util.cpp @@ -0,0 +1,28 @@ +/*************************************************************************************************** + * Copyright Five AI 2021. + * All rights reserved. + ***************************************************************************************************/ + +#include "benchmark/Util.h" + +#include + +#include + +bool isTransportSupported(const std::string& transport) +{ + return transport == "shm" || + transport == "tcp" || + transport == "udp" || + transport == "tzc"; +} + +void throwIfTransportNotSupported(const std::string& transport) +{ + if (! isTransportSupported(transport)) + { + const auto msg = std::string("Unknown transport protocol ") + transport; + ROS_ERROR_STREAM(msg); + throw std::logic_error{msg}; + } +} diff --git a/clients/roscpp/CMakeLists.txt b/clients/roscpp/CMakeLists.txt index af68f8e13..6e34cbf59 100644 --- a/clients/roscpp/CMakeLists.txt +++ b/clients/roscpp/CMakeLists.txt @@ -5,6 +5,10 @@ if(NOT WIN32) set_directory_properties(PROPERTIES COMPILE_OPTIONS "-std=c++11;-Wall;-Wextra") endif() +## Compile as C++11, supported in ROS Kinetic and newer +add_compile_options(-std=c++11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + find_package(catkin REQUIRED COMPONENTS cpp_common message_generation rosconsole roscpp_serialization roscpp_traits rosgraph_msgs rostime std_msgs xmlrpcpp ) @@ -55,7 +59,7 @@ catkin_package( INCLUDE_DIRS include ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION}/ros LIBRARIES roscpp ${PTHREAD_LIB} CATKIN_DEPENDS cpp_common message_runtime rosconsole roscpp_serialization roscpp_traits rosgraph_msgs rostime std_msgs xmlrpcpp - DEPENDS Boost + DEPENDS Boost rt ) include(CheckIncludeFiles) @@ -122,6 +126,15 @@ add_library(roscpp src/libros/service.cpp src/libros/this_node.cpp src/libros/steady_timer.cpp + src/libros/shm_engine.cpp + src/libros/shm_puller.cpp + src/libros/shm_pusher.cpp + src/libros/util/Exception.cpp + src/libros/util/String.cpp + src/libros/threading/Attributes.cpp + src/libros/threading/AttributesIO.cpp + src/libros/threading/Thread.cpp + src/libros/threading/Utils.cpp ) add_dependencies(roscpp ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) @@ -129,6 +142,7 @@ add_dependencies(roscpp ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TA target_link_libraries(roscpp ${catkin_LIBRARIES} ${Boost_LIBRARIES} + -lrt ) #explicitly install library and includes @@ -138,7 +152,7 @@ install(TARGETS roscpp RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) install(DIRECTORY include/ DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION} - FILES_MATCHING PATTERN "*.h") + FILES_MATCHING PATTERN "*") install(FILES ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION}/ros/common.h DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}/ros) # install legacy infrastructure needed by rosbuild diff --git a/clients/roscpp/include/boost_1.65.0/boost/process.hpp b/clients/roscpp/include/boost_1.65.0/boost/process.hpp new file mode 100644 index 000000000..e0ad69435 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011 Jeff Flinn, Boris Schaeling +// Copyright (c) 2012 Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_HPP +#define BOOST_PROCESS_HPP + +/** + * \file boost/process.hpp + * + * Convenience header which includes all public and platform-independent + * boost.process header files. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/args.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/args.hpp new file mode 100644 index 000000000..af677cfd8 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/args.hpp @@ -0,0 +1,279 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_ARGS_HPP +#define BOOST_PROCESS_ARGS_HPP + +/** \file boost/process/args.hpp + * + * This header provides the \xmlonly args\endxmlonly property. It also provides the + * alternative name \xmlonly argv\endxmlonly . + * + * +\xmlonly + +namespace boost { + namespace process { + unspecified args; + unspecified argv; + } +} + +\endxmlonly + */ + + +#include +#include + +namespace boost { namespace process { namespace detail { + +struct args_ +{ + template + using remove_reference_t = typename std::remove_reference::type; + template + using value_type = typename remove_reference_t::value_type; + + template + using vvalue_type = value_type>; + + template + arg_setter_, true> operator()(Range &&range) const + { + return arg_setter_, true>(std::forward(range)); + } + template + arg_setter_, true> operator+=(Range &&range) const + { + return arg_setter_, true>(std::forward(range)); + } + template + arg_setter_, false> operator= (Range &&range) const + { + return arg_setter_, false>(std::forward(range)); + } + template + arg_setter_ operator()(std::basic_string && str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator+=(std::basic_string && str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator= (std::basic_string && str) const + { + return arg_setter_(str); + } + template + arg_setter_ operator()(const std::basic_string & str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator+=(const std::basic_string & str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator= (const std::basic_string & str) const + { + return arg_setter_(str); + } + template + arg_setter_ operator()(std::basic_string & str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator+=(std::basic_string & str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator= (std::basic_string & str) const + { + return arg_setter_(str); + } + template + arg_setter_ operator()(const Char* str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator+=(const Char* str) const + { + return arg_setter_ (str); + } + template + arg_setter_ operator= (const Char* str) const + { + return arg_setter_(str); + } +// template +// arg_setter_ operator()(const Char (&str) [Size]) const +// { +// return arg_setter_ (str); +// } +// template +// arg_setter_ operator+=(const Char (&str) [Size]) const +// { +// return arg_setter_ (str); +// } +// template +// arg_setter_ operator= (const Char (&str) [Size]) const +// { +// return arg_setter_(str); +// } + + arg_setter_ operator()(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator+=(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator= (std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator()(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator+=(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator= (std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + + arg_setter_ operator()(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator+=(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator= (std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator()(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator+=(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator= (std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } +}; + + +} +/** + +The `args` property allows to explicitly set arguments for the execution. The +name of the executable will always be the first element in the arg-vector. + +\section args_details Details + +\subsection args_operations Operations + +\subsubsection args_set_var Setting values + +To set a the argument vector the following syntax can be used. + +\code{.cpp} +args = value; +args(value); +\endcode + +`std::initializer_list` is among the allowed types, so the following syntax is also possible. + +\code{.cpp} +args = {value1, value2}; +args({value1, value2}); +\endcode + +Below the possible types for `value` are listed, with `char_type` being either `char` or `wchar_t`. + +\paragraph args_set_var_value value + + - `std::basic_string` + - `const char_type * ` + - `std::initializer_list` + - `std::vector>` + +Additionally any range of `std::basic_string` can be passed. + +\subsubsection args_append_var Appending values + +To append a the argument vector the following syntax can be used. + +\code{.cpp} +args += value; +\endcode + +`std::initializer_list` is among the allowed types, so the following syntax is also possible. + +\code{.cpp} +args += {value1, value2}; +\endcode + +Below the possible types for `value` are listed, with `char_type` being either `char` or `wchar_t`. + +\paragraph args_append_var_value value + + - `std::basic_string` + - `const char_type * ` + - `std::initializer_list` + - `std::vector>` + +Additionally any range of `std::basic_string` can be passed. + + +\subsection args_example Example + +The overload form is used when more than one string is passed, from the second one forward. +I.e. the following expressions have the same results: + +\code{.cpp} +spawn("gcc", "--version"); +spawn("gcc", args ="--version"); +spawn("gcc", args+="--version"); +spawn("gcc", args ={"--version"}); +spawn("gcc", args+={"--version"}); +\endcode + +\note A string will be parsed and set in quotes if it has none and contains spaces. + + + */ +constexpr boost::process::detail::args_ args{}; + +///Alias for \xmlonly args \endxmlonly . +constexpr boost::process::detail::args_ argv{}; + + +}} + +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/async.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/async.hpp new file mode 100644 index 000000000..9698aab93 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/async.hpp @@ -0,0 +1,130 @@ +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** \file boost/process/async.hpp + +The header which provides the basic asynchrounous features. +It provides the on_exit property, which allows callbacks when the process exits. +It also implements the necessary traits for passing an boost::asio::io_service, +which is needed for asynchronous communication. + +It also pulls the [boost::asio::buffer](http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/buffer.html) +into the boost::process namespace for convenience. + +\xmlonly + +namespace boost { + namespace process { + unspecified buffer; + unspecified on_exit; + } +} + + +\endxmlonly + */ + +#ifndef BOOST_PROCESS_ASYNC_HPP_ +#define BOOST_PROCESS_ASYNC_HPP_ + +#include +#include + +#include +#include +#include +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#include +#include +#include + +#elif defined(BOOST_WINDOWS_API) +#include +#include +#include +#include +#endif + +namespace boost { namespace process { namespace detail { + +struct async_tag; + +template +struct is_io_service : std::false_type {}; +template<> +struct is_io_service : std::true_type {}; + +template +inline asio::io_service& get_io_service(const Tuple & tup) +{ + auto& ref = *boost::fusion::find_if>(tup); + return ref.get(); +} + +struct async_builder +{ + boost::asio::io_service * ios; + + void operator()(boost::asio::io_service & ios_) {this->ios = &ios_;}; + + typedef api::io_service_ref result_type; + api::io_service_ref get_initializer() {return api::io_service_ref (*ios);}; +}; + + +template<> +struct initializer_builder +{ + typedef async_builder type; +}; + +} + +using ::boost::asio::buffer; + + +#if defined(BOOST_PROCESS_DOXYGEN) +/** When an io_service is passed, the on_exit property can be used, to be notified + when the child process exits. + + +The following syntax is valid + +\code{.cpp} +on_exit=function; +on_exit(function); +\endcode + +with `function` being a callable object with the signature `(int, const std::error_code&)` or an +`std::future`. + +\par Example + +\code{.cpp} +io_service ios; + +child c("ls", on_exit=[](int exit, const std::error_code& ec_in){}); + +std::future exit_code; +chlid c2("ls", on_exit=exit_code); + +\endcode + +\note The handler is not invoked when the launch fails. +\warning When used \ref ignore_error it might gte invoked on error. + + */ +constexpr static ::boost::process::detail::on_exit_ on_exit{}; +#endif + +}} + + + +#endif /* INCLUDE_BOOST_PROCESS_DETAIL_ASYNC_HPP_ */ diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/async_pipe.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/async_pipe.hpp new file mode 100644 index 000000000..97af16585 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/async_pipe.hpp @@ -0,0 +1,215 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_PROCESS_ASYNC_PIPE_HPP +#define BOOST_PROCESS_ASYNC_PIPE_HPP + +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#elif defined(BOOST_WINDOWS_API) +#include +#endif + +namespace boost { namespace process { + + +#if defined(BOOST_PROCESS_DOXYGEN) + + +/** Class implementing and asnychronous I/O-Object for use with boost.asio. + * It is based on the corresponding I/O Object, that is either boost::asio::windows::stream_handle or + * boost::asio::posix::stream_descriptor. + * + * It can be used directly with boost::asio::async_read or async_write. + * + * \note The object is copyable, but that does invoke a handle duplicate. + */ +class async_pipe +{ +public: + /** Typedef for the native handle representation. + * \note This is the handle on the system, not the boost.asio class. + * + */ + typedef platform_specific native_handle_type; + /** Typedef for the handle representation of boost.asio. + * + */ + typedef platform_specific handle_type; + + /** Construct a new async_pipe, does automatically open the pipe. + * Initializes source and sink with the same io_service. + * @note Windows creates a named pipe here, where the name is automatically generated. + */ + inline async_pipe(boost::asio::io_service & ios); + + /** Construct a new async_pipe, does automatically open the pipe. + * @note Windows creates a named pipe here, where the name is automatically generated. + */ + inline async_pipe(boost::asio::io_service & ios_source, + boost::asio::io_service & ios_sink); + + /** Construct a new async_pipe, does automatically open. + * Initializes source and sink with the same io_service. + * + * @note Windows restricts possible names. + */ + inline async_pipe(boost::asio::io_service & ios, const std::string & name); + + + /** Construct a new async_pipe, does automatically open. + * + * @note Windows restricts possible names. + */ + inline async_pipe(boost::asio::io_service & ios_source, + boost::asio::io_service & ios_sink, const std::string & name); + + /** Copy-Constructor of the async pipe. + * @note Windows requires a named pipe for this, if a the wrong type is used an exception is thrown. + * + */ + async_pipe(const async_pipe& lhs); + + /** Move-Constructor of the async pipe. + */ + async_pipe(async_pipe&& lhs); + + /** Construct the async-pipe from a pipe. + * @note Windows requires a named pipe for this, if a the wrong type is used an exception is thrown. + * + */ + template> + explicit async_pipe(boost::asio::io_service & ios, const basic_pipe & p); + + /** Construct the async-pipe from a pipe, with two different io_service objects. + * @note Windows requires a named pipe for this, if a the wrong type is used an exception is thrown. + * + */ + template> + explicit async_pipe(boost::asio::io_service & ios_source, + boost::asio::io_service & ios_sink, + const basic_pipe & p); + + + /** Assign a basic_pipe. + * @note Windows requires a named pipe for this, if a the wrong type is used an exception is thrown. + * + */ + template> + inline async_pipe& operator=(const basic_pipe& p); + + /** Copy Assign a pipe. + * @note Duplicates the handles. + */ + async_pipe& operator=(const async_pipe& lhs); + /** Move assign a pipe */ + async_pipe& operator=(async_pipe&& lhs); + + /** Destructor. Closes the pipe handles. */ + ~async_pipe(); + + /** Explicit cast to basic_pipe. */ + template> + inline explicit operator basic_pipe() const; + + /** Cancel the current asynchronous operations. */ + void cancel(); + /** Close the pipe handles. */ + void close(); + /** Close the pipe handles. While passing an error_code + * + */ + void close(std::error_code & ec); + + /** Check if the pipes are open. */ + bool is_open() const; + + /** Async close, i.e. close after current operation is completed. + * + * \note There is no guarantee that this will indeed read the entire pipe-buffer + */ + void async_close(); + + /** Read some data from the handle. + + * See the boost.asio documentation for more details. + */ + template + std::size_t read_some(const MutableBufferSequence & buffers); + + /** Write some data to the handle. + + * See the boost.asio documentation for more details. + */ + template + std::size_t write_some(const MutableBufferSequence & buffers); + + /** Get the native handle of the source. */ + native_handle native_source() const {return const_cast(_source).native();} + /** Get the native handle of the sink. */ + native_handle native_sink () const {return const_cast(_sink ).native();} + + /** Start an asynchronous read. + * + * See the [boost.asio documentation](http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html) for more details. + */ + template + detail::dummy async_read_some( + const MutableBufferSequence & buffers, + ReadHandler &&handler); + + /** Start an asynchronous write. + + * See the [boost.asio documentation](http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html) for more details. + */ + template + detail::dummy async_write_some( + const ConstBufferSequence & buffers, + WriteHandler && handler); + + ///Get the asio handle of the pipe sink. + const handle_type & sink () const &; + ///Get the asio handle of the pipe source. + const handle_type & source() const &; + + ///Get the asio handle of the pipe sink. Qualified as rvalue + handle_type && sink () &&; + ///Get the asio handle of the pipe source. Qualified as rvalue + handle_type && source() &&; + + /// Move the source out of this class and change the io_service. Qualified as rvalue. \attention Will always move. + handle_type source(::boost::asio::io_service& ios) &&; + /// Move the sink out of this class and change the io_service. Qualified as rvalue. \attention Will always move + handle_type sink (::boost::asio::io_service& ios) &&; + + /// Copy the source out of this class and change the io_service. \attention Will always copy. + handle_type source(::boost::asio::io_service& ios) const &; + /// Copy the sink out of this class and change the io_service. \attention Will always copy + handle_type sink (::boost::asio::io_service& ios) const &; + + + +}; + +#else +using ::boost::process::detail::api::async_pipe; +#endif + + +}} + + + +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/async_system.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/async_system.hpp new file mode 100644 index 000000000..f92b417d2 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/async_system.hpp @@ -0,0 +1,142 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** + * \file boost/process/async_system.hpp + * + * Defines the asynchrounous version of the system function. + */ + +#ifndef BOOST_PROCESS_ASYNC_SYSTEM_HPP +#define BOOST_PROCESS_ASYNC_SYSTEM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#endif + +namespace boost { +namespace process { +namespace detail +{ + +template +struct async_system_handler : ::boost::process::detail::api::async_handler +{ + boost::asio::io_service & ios; + boost::asio::detail::async_result_init< + ExitHandler, void(boost::system::error_code, int)> init; + +#if defined(BOOST_POSIX_API) + bool errored = false; +#endif + + template + async_system_handler( + boost::asio::io_service & ios, + ExitHandler_ && exit_handler) : ios(ios), init(std::forward(exit_handler)) + { + + } + + + template + void on_error(Exec&, const std::error_code & ec) + { +#if defined(BOOST_POSIX_API) + errored = true; +#endif + auto & h = init.handler; + ios.post( + [h, ec]() mutable + { + h(boost::system::error_code(ec.value(), boost::system::system_category()), -1); + }); + } + + BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int)) + get_result() + { + return init.result.get(); + } + + template + std::function on_exit_handler(Executor&) + { +#if defined(BOOST_POSIX_API) + if (errored) + return [](int exit_code, const std::error_code & ec){}; +#endif + auto & h = init.handler; + return [h](int exit_code, const std::error_code & ec) mutable + { + h(boost::system::error_code(ec.value(), boost::system::system_category()), exit_code); + }; + } +}; + + +template +struct is_error_handler> : std::true_type {}; + +} + +/** This function provides an asynchronous interface to process launching. + +It uses the same properties and parameters as the other launching function, +but is similar to the asynchronous functions in [boost.asio](http://www.boost.org/doc/libs/release/doc/html/boost_asio.html) + +It uses [asio::async_result](http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/async_result.html) to determine +the return value (from the second parameter, `exit_handler`). + +\param ios A reference to an [io_service](http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference.html) +\param exit_handler The exit-handler for the signature `void(boost::system::error_code, int)` + +\note This function does not allow custom error handling, since those are done through the `exit_handler`. + +*/ +#if defined(BOOST_PROCESS_DOXYGEN) +template +inline boost::process::detail::dummy + async_system(boost::asio::io_service & ios, ExitHandler && exit_handler, Args && ...args); +#endif + +template +inline BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int)) + async_system(boost::asio::io_service & ios, ExitHandler && exit_handler, Args && ...args) +{ + detail::async_system_handler async_h{ios, std::forward(exit_handler)}; + + typedef typename ::boost::process::detail::has_error_handler>::type + has_err_handling; + + static_assert(!has_err_handling::value, "async_system cannot have custom error handling"); + + + child(ios, std::forward(args)..., async_h ).detach(); + + return async_h.get_result(); +} + + + +}} +#endif + diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/child.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/child.hpp new file mode 100644 index 000000000..980cecdae --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/child.hpp @@ -0,0 +1,147 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** + * \file boost/process/child.hpp + * + * Defines a child process class. + */ + +#ifndef BOOST_PROCESS_CHILD_HPP +#define BOOST_PROCESS_CHILD_HPP + +#include +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#endif + +namespace boost { + +///The main namespace of boost.process. +namespace process { + +template +child::child(Args&&...args) + : child(::boost::process::detail::execute_impl(std::forward(args)...)) {} + + +#if defined(BOOST_PROCESS_DOXYGEN) +/** The main class to hold a child process. It is simliar to [std::thread](http://en.cppreference.com/w/cpp/thread/thread), + * in that it has a join and detach function. + * + * @attention The destructor will call terminate on the process if not joined or detached without any warning. + * + */ + +class child +{ + /** Type definition for the native process handle. */ + typedef platform_specific native_handle_t; + + /** Construct the child from a pid. + * + * @attention There is no guarantee that this will work. The process need the right access rights, which are very platform specific. + */ + explicit child(pid_t & pid) : _child_handle(pid) {}; + + /** Move-Constructor.*/ + child(child && lhs); + + /** Construct a child from a property list and launch it + * The standard version is to create a subprocess, which will spawn the process. + */ + template + explicit child(Args&&...args); + + /** Construct an empty child. */ + child() = default; + + /** Move assign. */ + child& operator=(child && lhs); + + /** Detach the child, i.e. let it run after this handle dies. */ + void detach(); + /** Join the child. This just calls wait, but that way the naming is similar to std::thread */ + void join(); + /** Check if the child is joinable. */ + bool joinable(); + + /** Destructor. + * @attention Will call terminate (without warning) when the child was neither joined nor detached. + */ + ~child(); + + /** Get the native handle for the child process. */ + native_handle_t native_handle() const; + + /** Get the exit_code. The return value is without any meaning if the child wasn't waited for or if it was terminated. */ + int exit_code() const; + /** Get the Process Identifier. */ + pid_t id() const; + + /** Check if the child process is running. */ + bool running(); + /** \overload void running() */ + bool running(std::error_code & ec) noexcept; + + /** Wait for the child process to exit. */ + void wait(); + /** \overload void wait() */ + void wait(std::error_code & ec) noexcept; + + /** Wait for the child process to exit for a period of time. + * \return True if child exited while waiting. + */ + template< class Rep, class Period > + bool wait_for (const std::chrono::duration& rel_time); + /** \overload bool wait_for(const std::chrono::duration& rel_time) */ + bool wait_for (const std::chrono::duration& rel_time, std::error_code & ec) noexcept; + + /** Wait for the child process to exit until a point in time. + * \return True if child exited while waiting.*/ + template< class Clock, class Duration > + bool wait_until(const std::chrono::time_point& timeout_time ); + /** \overload bool wait_until(const std::chrono::time_point& timeout_time )*/ + bool wait_until(const std::chrono::time_point& timeout_time, std::error_code & ec) noexcept; + + /** Check if this handle holds a child process. + * @note That does not mean, that the process is still running. It only means, that the handle does or did exist. + */ + bool valid() const; + /** Same as valid, for convenience. */ + explicit operator bool() const; + + /** Check if the the chlid process is in any process group. */ + bool in_group() const; + + /** \overload bool in_group() const */ + bool in_group(std::error_code & ec) const noexcept; + + /** Terminate the child process. + * + * This function will cause the child process to unconditionally and immediately exit. + * It is implement with [SIGKILL](http://pubs.opengroup.org/onlinepubs/009695399/functions/kill.html) on posix + * and [TerminateProcess](https://technet.microsoft.com/en-us/library/ms686714.aspx) on windows. + * + */ + void terminate(); + + /** \overload void terminate() */ + void terminate(std::error_code & ec) noexcept; +}; + +#endif + +}} +#endif + diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/cmd.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/cmd.hpp new file mode 100644 index 000000000..5985c8143 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/cmd.hpp @@ -0,0 +1,122 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_DETAIL_CMD_LINE_HPP +#define BOOST_PROCESS_DETAIL_CMD_LINE_HPP + +#include +#include +#include +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#elif defined(BOOST_WINDOWS_API) +#include +#endif + +/** \file boost/process/cmd.hpp + * + * This header provides the \xmlonly cmd\endxmlonly property. + * +\xmlonly + +namespace boost { + namespace process { + unspecified cmd; + } +} + +\endxmlonly +*/ + +namespace boost { namespace process { namespace detail { + + +struct cmd_ +{ + constexpr cmd_() {} + + template + inline api::cmd_setter_ operator()(const Char *s) const + { + return api::cmd_setter_(s); + } + template + inline api::cmd_setter_ operator= (const Char *s) const + { + return api::cmd_setter_(s); + } + + template + inline api::cmd_setter_ operator()(const std::basic_string &s) const + { + return api::cmd_setter_(s); + } + template + inline api::cmd_setter_ operator= (const std::basic_string &s) const + { + return api::cmd_setter_(s); + } +}; + +template<> struct is_wchar_t> : std::true_type {}; + + + +template<> +struct char_converter> +{ + static api::cmd_setter_ conv(const api::cmd_setter_ & in) + { + return { ::boost::process::detail::convert(in.str()) }; + } +}; + +template<> +struct char_converter> +{ + static api::cmd_setter_ conv(const api::cmd_setter_ & in) + { + return { ::boost::process::detail::convert(in.str()) }; + } +}; + + + + + + +} + + +/** The cmd property allows to explicitly set commands for the execution. + +The overload form applies when only one string is passed to a launching function. +The string will be internally parsed and split at spaces. + +The following expressions are valid, with `value` being either a C-String or +a `std::basic_string` with `char` or `wchar_t`. + +\code{.cpp} +cmd="value"; +cmd(value); +\endcode + +The property can only be used for assignments. + + + */ +constexpr static ::boost::process::detail::cmd_ cmd; + +}} + +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/async_handler.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/async_handler.hpp new file mode 100644 index 000000000..832a42014 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/async_handler.hpp @@ -0,0 +1,117 @@ +/* + * async_handler.hpp + * + * Created on: 12.06.2016 + * Author: Klemens + */ + +#ifndef BOOST_PROCESS_DETAIL_ASYNC_HANDLER_HPP_ +#define BOOST_PROCESS_DETAIL_ASYNC_HANDLER_HPP_ + +#include + +#if defined(BOOST_POSIX_API) +#include +#include +#include +#else +#include +#include +#endif + +namespace boost { + +namespace process { + +namespace detail { + +#if defined(BOOST_POSIX_API) +using ::boost::process::detail::posix::is_async_handler; +using ::boost::process::detail::posix::does_require_io_service; +#else +using ::boost::process::detail::windows::is_async_handler; +using ::boost::process::detail::windows::does_require_io_service; +#endif + +template +struct has_io_service; + +template +struct has_io_service +{ + typedef typename has_io_service::type next; + typedef typename std::is_same< + typename std::remove_reference::type, + boost::asio::io_service>::type is_ios; + typedef typename std::conditional::type type; +}; + +template +struct has_io_service +{ + typedef typename std::is_same< + typename std::remove_reference::type, + boost::asio::io_service>::type type; +}; + +template +using has_io_service_t = typename has_io_service::type; + +template +struct has_async_handler; + +template +struct has_async_handler +{ + typedef typename has_async_handler::type next; + typedef typename is_async_handler::type is_ios; + typedef typename std::conditional::type type; +}; + +template +struct has_async_handler +{ + typedef typename is_async_handler::type type; +}; + +template +struct needs_io_service; + +template +struct needs_io_service +{ + typedef typename needs_io_service::type next; + typedef typename does_require_io_service::type is_ios; + typedef typename std::conditional::type type; +}; + +template +struct needs_io_service +{ + typedef typename does_require_io_service::type type; +}; + +template +boost::asio::io_service &get_io_service_var(boost::asio::io_service & f, Args&...) +{ + return f; +} + +template +boost::asio::io_service &get_io_service_var(First&, Args&...args) +{ + return get_io_service_var(args...); +} + +} +} +} + + +#endif /* BOOST_PROCESS_DETAIL_ASYNC_HANDLER_HPP_ */ diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/basic_cmd.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/basic_cmd.hpp new file mode 100644 index 000000000..e387d9f0c --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/basic_cmd.hpp @@ -0,0 +1,292 @@ +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_PROCESS_DETAIL_BASIC_CMD_HPP_ +#define BOOST_PROCESS_DETAIL_BASIC_CMD_HPP_ + +#include + +#include +#include +#include + +#if defined( BOOST_WINDOWS_API ) +#include +#include +#elif defined( BOOST_POSIX_API ) +#include +#include +#endif + +#include + +#include + + +namespace boost { namespace process { namespace detail { + +template +struct exe_setter_ +{ + typedef Char value_type; + typedef std::basic_string string_type; + + string_type exe_; + exe_setter_(string_type && str) : exe_(std::move(str)) {} + exe_setter_(const string_type & str) : exe_(str) {} +}; + +template<> struct is_wchar_t> : std::true_type {}; + + +template<> +struct char_converter> +{ + static exe_setter_ conv(const exe_setter_ & in) + { + return {::boost::process::detail::convert(in.exe_)}; + } +}; + +template<> +struct char_converter> +{ + static exe_setter_ conv(const exe_setter_ & in) + { + return {::boost::process::detail::convert(in.exe_)}; + } +}; + + + +template +struct arg_setter_ +{ + using value_type = Char; + using string_type = std::basic_string; + std::vector _args; + + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + + template + arg_setter_(Iterator && begin, Iterator && end) : _args(begin, end) {} + + template + arg_setter_(Range && str) : + _args(std::begin(str), + std::end(str)) {} + + iterator begin() {return _args.begin();} + iterator end() {return _args.end();} + const_iterator begin() const {return _args.begin();} + const_iterator end() const {return _args.end();} + arg_setter_(string_type & str) : _args{{str}} {} + arg_setter_(string_type && s) : _args({std::move(s)}) {} + arg_setter_(const string_type & s) : _args({s}) {} + arg_setter_(const value_type* s) : _args({std::move(s)}) {} + + template + arg_setter_(const value_type (&s) [Size]) : _args({s}) {} +}; + +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::wstring & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; + } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::string & ws) + { + return ::boost::process::detail::convert(ws); + }); + + return {vec}; + } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::wstring & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::string & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; + } +}; + +using api::exe_cmd_init; + +template +struct exe_builder +{ + //set by path, because that will not be interpreted as a cmd + bool not_cmd = false; + bool shell = false; + using string_type = std::basic_string; + string_type exe; + std::vector args; + + void operator()(const boost::filesystem::path & data) + { + not_cmd = true; + if (exe.empty()) + exe = data.native(); + else + args.push_back(data.native()); + } + + void operator()(const string_type & data) + { + if (exe.empty()) + exe = data; + else + args.push_back(data); + } + void operator()(const Char* data) + { + if (exe.empty()) + exe = data; + else + args.push_back(data); + } + void operator()(shell_) {shell = true;} + void operator()(std::vector && data) + { + if (data.empty()) + return; + + auto itr = std::make_move_iterator(data.begin()); + auto end = std::make_move_iterator(data.end()); + + if (exe.empty()) + { + exe = *itr; + itr++; + } + args.insert(args.end(), itr, end); + } + + void operator()(const std::vector & data) + { + if (data.empty()) + return; + + auto itr = data.begin(); + auto end = data.end(); + + if (exe.empty()) + { + exe = *itr; + itr++; + } + args.insert(args.end(), itr, end); + } + void operator()(exe_setter_ && data) + { + not_cmd = true; + exe = std::move(data.exe_); + } + void operator()(const exe_setter_ & data) + { + not_cmd = true; + exe = data.exe_; + } + void operator()(arg_setter_ && data) + { + args.assign( + std::make_move_iterator(data._args.begin()), + std::make_move_iterator(data._args.end())); + } + void operator()(arg_setter_ && data) + { + args.insert(args.end(), + std::make_move_iterator(data._args.begin()), + std::make_move_iterator(data._args.end())); + } + void operator()(const arg_setter_ & data) + { + args.assign(data._args.begin(), data._args.end()); + } + void operator()(const arg_setter_ & data) + { + args.insert(args.end(), data._args.begin(), data._args.end()); + } + + api::exe_cmd_init get_initializer() + { + if (not_cmd || !args.empty()) + { + if (shell) + return api::exe_cmd_init::exe_args_shell(std::move(exe), std::move(args)); + else + return api::exe_cmd_init::exe_args(std::move(exe), std::move(args)); + } + else + if (shell) + return api::exe_cmd_init::cmd_shell(std::move(exe)); + else + return api::exe_cmd_init::cmd(std::move(exe)); + + } + typedef api::exe_cmd_init result_type; +}; + +template<> +struct initializer_builder> +{ + typedef exe_builder type; +}; + +template<> +struct initializer_builder> +{ + typedef exe_builder type; +}; + +}}} + + + +#endif /* BOOST_PROCESS_DETAIL_EXE_BUILDER_HPP_ */ diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/child_decl.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/child_decl.hpp new file mode 100644 index 000000000..3483f7c4b --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/child_decl.hpp @@ -0,0 +1,243 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** + * \file boost/process/child.hpp + * + * Defines a child process class. + */ + +#ifndef BOOST_PROCESS_CHILD_DECL_HPP +#define BOOST_PROCESS_CHILD_DECL_HPP + +#include +#include +#include + +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#include +#include +#include +#elif defined(BOOST_WINDOWS_API) +#include +#include +#include +#include + +#endif +namespace boost { + +namespace process { + +using ::boost::process::detail::api::pid_t; + +class child +{ + ::boost::process::detail::api::child_handle _child_handle; + std::shared_ptr> _exit_status = std::make_shared>(::boost::process::detail::api::still_active); + bool _attached = true; + bool _terminated = false; + + bool _exited() + { + return _terminated || !::boost::process::detail::api::is_running(_exit_status->load()); + }; +public: + typedef ::boost::process::detail::api::child_handle child_handle; + typedef child_handle::process_handle_t native_handle_t; + explicit child(child_handle &&ch, std::shared_ptr> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} + explicit child(child_handle &&ch, const std::shared_ptr> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} + explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {} + + explicit child(pid_t & pid) : _child_handle(pid), _attached(false) {}; + child(const child&) = delete; + child(child && lhs) noexcept + : _child_handle(std::move(lhs._child_handle)), + _exit_status(std::move(lhs._exit_status)), + _attached (lhs._attached) + { + lhs._attached = false; + } + + template + explicit child(Args&&...args); + child() {} + child& operator=(const child&) = delete; + child& operator=(child && lhs) + { + _child_handle= std::move(lhs._child_handle); + _exit_status = std::move(lhs._exit_status); + _attached = lhs._attached; + lhs._attached = false; + return *this; + }; + + void detach() {_attached = false; } + void join() {wait();} + bool joinable() { return _attached;} + + ~child() + { + std::error_code ec; + if (_attached && !_exited() && running(ec)) + terminate(ec); + } + native_handle_t native_handle() const { return _child_handle.process_handle(); } + + + int exit_code() const {return ::boost::process::detail::api::eval_exit_status(_exit_status->load());} + pid_t id() const {return _child_handle.id(); } + + bool running() + { + if (valid() && !_exited()) + { + int code; + auto res = boost::process::detail::api::is_running(_child_handle, code); + if (!res && !_exited()) + _exit_status->store(code); + + return res; + } + return false; + } + + void terminate() + { + if (valid() && running()) + boost::process::detail::api::terminate(_child_handle); + + _terminated = true; + } + + void wait() + { + if (!_exited() && valid()) + { + int exit_code = 0; + boost::process::detail::api::wait(_child_handle, exit_code); + _exit_status->store(exit_code); + } + } + + template< class Rep, class Period > + bool wait_for (const std::chrono::duration& rel_time) + { + if (!_exited()) + { + int exit_code = 0; + auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time); + if (!b) + return false; + _exit_status->store(exit_code); + } + return true; + } + + template< class Clock, class Duration > + bool wait_until(const std::chrono::time_point& timeout_time ) + { + if (!_exited()) + { + int exit_code = 0; + auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time); + if (!b) + return false; + _exit_status->store(exit_code); + } + return true; + } + + bool running(std::error_code & ec) noexcept + { + if (valid() && !_exited()) + { + int code; + auto res = boost::process::detail::api::is_running(_child_handle, code, ec); + if (!res && !_exited()) + _exit_status->store(code); + + return res; + } + return false; + } + + void terminate(std::error_code & ec) noexcept + { + if (valid() && running(ec)) + boost::process::detail::api::terminate(_child_handle, ec); + + _terminated = true; + } + + void wait(std::error_code & ec) noexcept + { + if (!_exited() && valid()) + { + int exit_code = 0; + boost::process::detail::api::wait(_child_handle, exit_code, ec); + _exit_status->store(exit_code); + } + } + + template< class Rep, class Period > + bool wait_for (const std::chrono::duration& rel_time, std::error_code & ec) noexcept + { + if (!_exited()) + { + int exit_code = 0; + auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time, ec); + if (!b) + return false; + _exit_status->store(exit_code); + } + return true; + } + + template< class Clock, class Duration > + bool wait_until(const std::chrono::time_point& timeout_time, std::error_code & ec) noexcept + { + if (!_exited()) + { + int exit_code = 0; + auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time, ec); + if (!b) + return false; + _exit_status->store(exit_code); + } + return true; + } + + + bool valid() const + { + return _child_handle.valid(); + } + operator bool() const {return valid();} + + bool in_group() const + { + return _child_handle.in_group(); + } + bool in_group(std::error_code &ec) const noexcept + { + return _child_handle.in_group(ec); + } +}; + + + +}} +#endif + diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/config.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/config.hpp new file mode 100644 index 000000000..2e280c1d5 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/config.hpp @@ -0,0 +1,99 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** + * \file boost/process/config.hpp + * + * Defines various macros. + */ + +#ifndef BOOST_PROCESS_DETAIL_CONFIG_HPP +#define BOOST_PROCESS_DETAIL_CONFIG_HPP + +#include +#include +#include + +#include + +#if defined(BOOST_POSIX_API) +#include +#if defined(__GLIBC__) +#include +#else +extern char **environ; +#endif +#elif defined(BOOST_WINDOWS_API) +#include +#else +#error "System API not supported by boost.process" +#endif + +namespace boost { namespace process { namespace detail +{ + +#if !defined(BOOST_PROCESS_PIPE_SIZE) +#define BOOST_PROCESS_PIPE_SIZE 1024 +#endif + +#if defined(BOOST_POSIX_API) +namespace posix {namespace extensions {}} +namespace api = posix; + +inline std::error_code get_last_error() noexcept +{ + return std::error_code(errno, std::system_category()); +} + +//copied from linux spec. +#if defined (__USE_XOPEN_EXTENDED) && !defined (__USE_XOPEN2K8) || defined( __USE_BSD) +#define BOOST_POSIX_HAS_VFORK 1 +#endif + +#elif defined(BOOST_WINDOWS_API) +namespace windows {namespace extensions {}} +namespace api = windows; + +inline std::error_code get_last_error() noexcept +{ + return std::error_code(::boost::detail::winapi::GetLastError(), std::system_category()); +} +#endif + +inline void throw_last_error(const std::string & msg) +{ + throw process_error(get_last_error(), msg); +} + +inline void throw_last_error() +{ + throw process_error(get_last_error()); +} + + +template constexpr Char null_char(); +template<> constexpr char null_char (){return '\0';} +template<> constexpr wchar_t null_char (){return L'\0';} + +template constexpr Char equal_sign(); +template<> constexpr char equal_sign () {return '='; } +template<> constexpr wchar_t equal_sign () {return L'='; } + +template constexpr Char quote_sign(); +template<> constexpr char quote_sign () {return '"'; } +template<> constexpr wchar_t quote_sign () {return L'"'; } + +template constexpr Char space_sign(); +template<> constexpr char space_sign () {return ' '; } +template<> constexpr wchar_t space_sign () {return L' '; } + + +}}} +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/execute_impl.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/execute_impl.hpp new file mode 100644 index 000000000..77722f78c --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/execute_impl.hpp @@ -0,0 +1,284 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** + * \file boost/process/execute.hpp + * + * Defines a function to execute a program. + */ + +#ifndef BOOST_PROCESS_EXECUTE_HPP +#define BOOST_PROCESS_EXECUTE_HPP + +#include +#include + +#if defined(BOOST_POSIX_API) +#include +#elif defined(BOOST_WINDOWS_API) +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { namespace process { + +class child; + +namespace detail { + + +template +struct has_wchar; + +template +struct has_wchar +{ + typedef has_wchar next; + typedef typename std::remove_cv< + typename std::remove_reference::type>::type res_type; + + constexpr static bool my_value = is_wchar_t::value; + constexpr static bool value = my_value || next::value; + + typedef std::integral_constant type; +}; + +template +struct has_wchar +{ + typedef typename std::remove_cv< + typename std::remove_reference::type>::type res_type; + + constexpr static bool value = is_wchar_t::value; + + typedef std::integral_constant type; +}; + + +#if defined(BOOST_WINDOWS_API) +//everything needs to be wchar_t +#if defined(BOOST_NO_ANSI_APIS) +template +struct required_char_type +{ + typedef wchar_t type; +}; +#else +template struct required_char_type; +template<> struct required_char_type +{ + typedef wchar_t type; +}; +template<> struct required_char_type +{ + typedef char type; +}; +#endif + +#elif defined(BOOST_POSIX_API) +template +struct required_char_type +{ + typedef char type; +}; +#endif + +template +using required_char_type_t = typename required_char_type< + has_wchar::value>::type; + + +template +struct make_builders_from_view +{ + typedef boost::fusion::set set; + typedef typename boost::fusion::result_of::deref::type ref_type; + typedef typename std::remove_reference::type res_type; + typedef typename initializer_tag::type tag; + typedef typename initializer_builder::type builder_type; + typedef typename boost::fusion::result_of::has_key has_key; + + typedef typename boost::fusion::result_of::next::type next_itr; + typedef typename make_builders_from_view::type next; + + typedef typename + std::conditional::type, + typename make_builders_from_view::type + >::type type; + +}; + +template +struct make_builders_from_view +{ + typedef boost::fusion::set type; +}; + +template +struct builder_ref +{ + Builders &builders; + builder_ref(Builders & builders) : builders(builders) {}; + + template + void operator()(T && value) const + { + typedef typename initializer_tag::type>::type tag; + typedef typename initializer_builder::type builder_type; + boost::fusion::at_key(builders)(std::forward(value)); + } +}; + +template +struct get_initializers_result +{ + typedef typename T::result_type type; +}; + +template<> +struct get_initializers_result +{ + typedef boost::fusion::void_ type; +}; + +template +struct helper_vector +{ + +}; + +template +struct invoke_get_initializer_collect_keys; + +template +struct invoke_get_initializer_collect_keys, Stack...> +{ + typedef helper_vector type; +}; + + +template +struct invoke_get_initializer_collect_keys, Stack...> +{ + typedef typename invoke_get_initializer_collect_keys, Stack..., First>::type next; + typedef helper_vector stack_t; + + typedef typename std::conditional::value, + stack_t, next>::type type; + + +}; + + +template +struct invoke_get_initializer; + +template +struct invoke_get_initializer> + +{ + typedef boost::fusion::tuple::type...> result_type; + + template + static result_type call(Sequence & seq) + { + return result_type(boost::fusion::at_key(seq).get_initializer()...);; + } +}; + + + + + +template +inline boost::fusion::tuple::type...> + get_initializers(boost::fusion::set & builders) +{ + //typedef boost::fusion::tuple::type...> return_type; + typedef typename invoke_get_initializer_collect_keys>::type keys; + return invoke_get_initializer::call(builders); +} + + +template +inline child basic_execute_impl(Args && ... args) +{ + //create a tuple from the argument list + boost::fusion::tuple::type&...> tup(args...); + + auto inits = boost::fusion::filter_if< + boost::process::detail::is_initializer< + typename std::remove_reference< + boost::mpl::_ + >::type + > + >(tup); + + auto others = boost::fusion::filter_if< + boost::mpl::not_< + boost::process::detail::is_initializer< + typename std::remove_reference< + boost::mpl::_ + >::type + > + > + >(tup); + + // typename detail::make_builders_from_view::type builders; + + //typedef typename boost::fusion::result_of::as_vector::type inits_t; + typedef typename boost::fusion::result_of::as_vector::type others_t; + // typedef decltype(others) others_t; + typedef typename ::boost::process::detail::make_builders_from_view< + typename boost::fusion::result_of::begin::type, + typename boost::fusion::result_of::end ::type>::type builder_t; + + builder_t builders; + ::boost::process::detail::builder_ref builder_ref(builders); + + boost::fusion::for_each(others, builder_ref); + auto other_inits = ::boost::process::detail::get_initializers(builders); + + + boost::fusion::joint_view complete_inits(other_inits, inits); + + auto exec = boost::process::detail::api::make_executor(complete_inits); + return exec(); +} + +template +inline child execute_impl(Args&& ... args) +{ + typedef required_char_type_t req_char_type; + + return basic_execute_impl( + boost::process::detail::char_converter_t::conv( + std::forward(args))... + ); +} + +}}} + + +#endif diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler.hpp new file mode 100644 index 000000000..360c59f81 --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler.hpp @@ -0,0 +1,75 @@ +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_PROCESS_DETAIL_HANDLER_HPP_ +#define BOOST_PROCESS_DETAIL_HANDLER_HPP_ + +#include + +#if defined(BOOST_POSIX_API) +#include +#elif defined(BOOST_WINDOWS_API) +#include +#endif + + +namespace boost { namespace process { namespace detail { + +//extended handler base. +typedef api::handler_base_ext handler; + + +template +struct on_setup_ : handler +{ + explicit on_setup_(Handler handler) : handler_(handler) {} + + template + void on_setup(Executor &e) + { + handler_(e); + } +private: + Handler handler_; +}; + +template +struct on_error_ : handler +{ + explicit on_error_(Handler handler) : handler_(handler) {} + + template + void on_error(Executor &e, const std::error_code &ec) + { + handler_(e, ec); + } +private: + Handler handler_; +}; + +template +struct on_success_ : handler +{ + explicit on_success_(Handler handler) : handler_(handler) {} + + template + void on_success(Executor &e) + { + handler_(e); + } +private: + Handler handler_; +}; + +} + + + +}} + + + +#endif /* BOOST_PROCESS_DETAIL_HANDLER_HPP_ */ diff --git a/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler_base.hpp b/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler_base.hpp new file mode 100644 index 000000000..93a80520e --- /dev/null +++ b/clients/roscpp/include/boost_1.65.0/boost/process/detail/handler_base.hpp @@ -0,0 +1,49 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// Copyright (c) 2016 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_DETAIL_HANDLER_BASE_HPP +#define BOOST_PROCESS_DETAIL_HANDLER_BASE_HPP + +#include + +namespace boost { namespace process { namespace detail { + +template