-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add hal transmitter and socket support for remote hal
Add base class hal_transmitter which can be used for simple send/ receive operations of data. This can be used by client and servers for different models of communication. Also added a socket version which will be the default use case.
- Loading branch information
Showing
5 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Copyright (C) Codeplay Software Limited | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License") with LLVM | ||
# Exceptions; you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
set(HAL_SOURCE | ||
${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_transmitter.h | ||
${CMAKE_CURRENT_SOURCE_DIR}/source/hal_socket_transmitter.cpp | ||
) | ||
|
||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||
list (APPEND HAL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_socket_transmitter.h) | ||
endif() | ||
|
||
add_library( | ||
hal_remote STATIC ${HAL_SOURCE}) | ||
target_include_directories( | ||
hal_remote PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) | ||
set_target_properties( | ||
hal_remote PROPERTIES LINKER_LANGUAGE CXX) | ||
|
||
target_link_libraries(hal_remote PUBLIC hal_common) |
117 changes: 117 additions & 0 deletions
117
hal/hal_remote/include/hal_remote/hal_socket_transmitter.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Copyright (C) Codeplay Software Limited | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License") with LLVM | ||
// Exceptions; you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#ifndef _HAL_SOCKET_TRANSMITTER_H | ||
#define _HAL_SOCKET_TRANSMITTER_H | ||
|
||
#include <hal_remote/hal_transmitter.h> | ||
#include <netinet/in.h> | ||
|
||
namespace hal { | ||
|
||
/// @brief A very simple socket based version of a hal_transmitter | ||
/// @note This supports both client and server mode, and the sure should use | ||
/// start_server() or make_connection() as appropriate. This supports the | ||
/// required port being 0 for a server allows it to find a free port. For | ||
/// some operations error_code enum will be used, but for `send` and `receive` | ||
/// these are derived functions, so get_last_error() can be used. | ||
class hal_socket_transmitter : public hal_transmitter { | ||
public: | ||
hal_socket_transmitter(uint16_t port = 0) : port_requested(port) {} | ||
~hal_socket_transmitter(); | ||
|
||
enum error_code { | ||
success, | ||
bind_failed, | ||
connect_failed, | ||
connection_closed, | ||
listen_failed, | ||
accept_failed, | ||
send_error, | ||
recv_error, | ||
getsockname_failed | ||
}; | ||
|
||
/// @brief set port we wish to request on. This must be done before any calls | ||
/// to start_server or make_connection. | ||
/// @param port we wish to make request on | ||
/// @note This duplicates the constructor argument but makes it easier | ||
/// in some cases to default it initially and then set it later. | ||
void set_port(uint16_t port) { port_requested = port; } | ||
|
||
/// @brief Start the server end | ||
/// @param print_port optionally print out that we are listening on a | ||
/// particular port | ||
/// @return success if successful. If unsuccessful last_error also will | ||
/// contain error | ||
error_code start_server(bool print_port); | ||
|
||
/// @brief make a connection to a server | ||
/// @return success if successful. If unsuccessful last_error also will | ||
/// contain error | ||
error_code make_connection(); | ||
|
||
/// @brief Indicates that a connection is live (either as server or as a | ||
/// client) | ||
/// @note it may still be the case that the last send/receive was in error | ||
/// but we keep the connected flag live and report false in that case. | ||
/// If the connection was dropped, recv can receive 0 bytes which is | ||
/// what is used to set this to false. | ||
/// @return true if connected. | ||
bool connected() const { return is_connected; } | ||
|
||
/// @brief returns the last error from socket operations | ||
/// @return last error reported from the socket | ||
int get_last_error() const { return last_error; } | ||
|
||
/// @brief Receive `size` bytes of data into `data` | ||
/// @return true if was able to receive the data. Note that false may | ||
/// indicate that the connection was dropped or there were errors. In this | ||
/// case check last_error for information (connection_closed if dropped) | ||
bool receive(void *data, uint32_t size) override; | ||
|
||
/// @brief Send `size` bytes of `data` with an optional flush | ||
bool send(const void *data, uint32_t size, bool flush); | ||
|
||
private: | ||
/// @brief connect to the remote server | ||
/// @return error returned by connect socket function | ||
error_code connect(); | ||
|
||
/// @brief set up a connection basic ready for connect() or listen/accept | ||
/// optionally adding a bind if being used as server and getting the | ||
/// port number bound to. | ||
/// @param server in server mode, requiring a bind() call | ||
/// @return 0 for success, anything else represents the error from bind or | ||
/// getsockname | ||
error_code setup_connection(bool server); | ||
|
||
int listen(); | ||
int accept(); | ||
|
||
int get_port() { return current_port; } | ||
|
||
uint16_t port_requested; | ||
uint16_t current_port = 0; | ||
sockaddr_in server_address; | ||
int sock = 0; | ||
int fd_to_use = 0; | ||
bool setup_connection_done = false; | ||
error_code last_error = error_code::success; | ||
bool is_connected = false; | ||
}; | ||
} // namespace hal | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright (C) Codeplay Software Limited | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License") with LLVM | ||
// Exceptions; you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#include <cstdint> | ||
|
||
#ifndef _HAL_TRANSMITTER_H | ||
#define _HAL_TRANSMITTER_H | ||
|
||
namespace hal { | ||
|
||
/// @brief A simple class interface that can be used for transmitting and | ||
/// sending data - derive from this to support sockets, file descriptors etc. | ||
class hal_transmitter { | ||
public: | ||
/// @brief Send `size` bytes of `data` with an optional flush | ||
/// @return true if the send succeeds | ||
virtual bool send(const void *data, uint32_t size, bool flush) = 0; | ||
|
||
/// Receive `size` bytes of data into `data` | ||
/// @return true if the receive succeeds | ||
virtual bool receive(void *data, uint32_t size) = 0; | ||
virtual ~hal_transmitter(){}; | ||
|
||
void enable_debug(bool debug_enabled) { debug = debug_enabled; } | ||
bool debug_enabled() { return debug; } | ||
|
||
private: | ||
bool debug = false; | ||
}; | ||
} // namespace hal | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright (C) Codeplay Software Limited | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License") with LLVM | ||
// Exceptions; you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#include <hal_remote/hal_socket_transmitter.h> | ||
#include <hal_remote/hal_transmitter.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
#include <cstdio> | ||
|
||
namespace hal { | ||
hal_socket_transmitter::~hal_socket_transmitter() { | ||
if (sock) { | ||
close(sock); | ||
} | ||
if (fd_to_use && fd_to_use != sock) { | ||
close(fd_to_use); | ||
} | ||
} | ||
|
||
hal_socket_transmitter::error_code hal_socket_transmitter::start_server( | ||
bool print_port) { | ||
if (!setup_connection_done) { | ||
hal_socket_transmitter::error_code res = setup_connection(true); | ||
if (res != 0) { | ||
return res; | ||
} | ||
setup_connection_done = true; | ||
} | ||
if (listen() != 0) { | ||
last_error = hal_socket_transmitter::listen_failed; | ||
return last_error; | ||
} | ||
if (print_port) { | ||
printf("Listening on port %d\n", get_port()); | ||
fflush(stdout); | ||
} | ||
if (accept() == -1) { | ||
last_error = hal_socket_transmitter::accept_failed; | ||
return last_error; | ||
} | ||
last_error = hal_socket_transmitter::success; | ||
return last_error; | ||
} | ||
|
||
/// @brief make a connection to a server | ||
/// @return true if successful, otherwise will set last_error to errno | ||
hal_socket_transmitter::error_code hal_socket_transmitter::make_connection() { | ||
if (!setup_connection_done) { | ||
last_error = setup_connection(false); | ||
if (last_error != hal_socket_transmitter::success) { | ||
return last_error; | ||
} | ||
setup_connection_done = true; | ||
} | ||
last_error = connect(); | ||
return last_error; | ||
} | ||
|
||
bool hal_socket_transmitter::receive(void *data, uint32_t size) { | ||
uint32_t data_to_read = size; | ||
uint32_t offset = 0; | ||
|
||
// repeatedly recv until `size` bytes is read. | ||
do { | ||
int res = recv(fd_to_use, ((char *)data) + offset, data_to_read, 0); | ||
// If recv returns 0, this indicates the connection has been dropped | ||
// It's not an error as such but we are not able to continue | ||
if (res == 0) { | ||
is_connected = false; | ||
fd_to_use = 0; | ||
last_error = hal_socket_transmitter::connection_closed; | ||
return false; | ||
} | ||
if (res == -1) { | ||
last_error = hal_socket_transmitter::recv_error; | ||
return false; | ||
} | ||
data_to_read = data_to_read - res; | ||
offset += res; | ||
} while (data_to_read); | ||
last_error = hal_socket_transmitter::success; | ||
return true; | ||
} | ||
|
||
/// @brief Send `size` bytes of `data` with an optional flush | ||
bool hal_socket_transmitter::send(const void *data, uint32_t size, bool flush) { | ||
const int ret = ::send(fd_to_use, data, size, 0); | ||
if (ret == -1) { | ||
last_error = hal_socket_transmitter::send_error; | ||
return false; | ||
} | ||
last_error = hal_socket_transmitter::success; | ||
return true; | ||
} | ||
|
||
hal_socket_transmitter::error_code hal_socket_transmitter::connect() { | ||
int res = ::connect(sock, (struct sockaddr *)&server_address, | ||
sizeof(server_address)); | ||
if (res != -1) { | ||
fd_to_use = sock; | ||
is_connected = true; | ||
return hal_socket_transmitter::success; | ||
} else { | ||
return hal_socket_transmitter::connect_failed; | ||
} | ||
} | ||
|
||
hal_socket_transmitter::error_code hal_socket_transmitter::setup_connection( | ||
bool server) { | ||
// @return 0 if success, otherwise -1 and errno set | ||
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
server_address.sin_family = AF_INET; | ||
server_address.sin_port = htons(port_requested); | ||
server_address.sin_addr.s_addr = INADDR_ANY; | ||
if (server) { | ||
if (int res = bind(sock, (struct sockaddr *)&server_address, | ||
sizeof(server_address)) != 0) { | ||
return hal_socket_transmitter::bind_failed; | ||
} | ||
|
||
struct sockaddr_in local_addr; | ||
socklen_t len = sizeof(local_addr); | ||
if (int res = | ||
getsockname(sock, (struct sockaddr *)&local_addr, &len) != 0) { | ||
return hal_socket_transmitter::getsockname_failed; | ||
} | ||
current_port = ntohs(local_addr.sin_port); | ||
} else { | ||
current_port = port_requested; | ||
} | ||
setup_connection_done = true; | ||
return hal_socket_transmitter::success; | ||
} | ||
|
||
int hal_socket_transmitter::listen() { | ||
if (int res = ::listen(sock, 1) != 0) { | ||
return res; | ||
} | ||
return 0; | ||
} | ||
int hal_socket_transmitter::accept() { | ||
int res = ::accept(sock, nullptr, nullptr); | ||
if (res != -1) { | ||
fd_to_use = res; | ||
} | ||
return res; | ||
} | ||
|
||
} // namespace hal |