| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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. |
| */ |
| |
| #include <cassert> |
| |
| #include "common/vsoc/lib/circqueue_impl.h" |
| #include "common/vsoc/lib/lock_guard.h" |
| #include "common/vsoc/lib/socket_forward_region_view.h" |
| #include "common/vsoc/shm/lock.h" |
| #include "common/vsoc/shm/socket_forward_layout.h" |
| |
| using vsoc::layout::socket_forward::Queue; |
| using vsoc::layout::socket_forward::QueuePair; |
| // store the read and write direction as variables to keep the ifdefs and macros |
| // in later code to a minimum |
| constexpr auto ReadDirection = &QueuePair:: |
| #ifdef CUTTLEFISH_HOST |
| guest_to_host; |
| #else |
| host_to_guest; |
| #endif |
| |
| constexpr auto WriteDirection = &QueuePair:: |
| #ifdef CUTTLEFISH_HOST |
| host_to_guest; |
| #else |
| guest_to_host; |
| #endif |
| |
| using vsoc::socket_forward::SocketForwardRegionView; |
| |
| vsoc::socket_forward::Packet vsoc::socket_forward::Packet::MakeBegin( |
| std::uint16_t port) { |
| auto packet = MakePacket(Header::BEGIN); |
| std::memcpy(packet.payload(), &port, sizeof port); |
| packet.set_payload_length(sizeof port); |
| return packet; |
| } |
| |
| void SocketForwardRegionView::Recv(int connection_id, Packet* packet) { |
| CHECK(packet != nullptr); |
| do { |
| (data()->queues_[connection_id].*ReadDirection) |
| .queue.Read(this, reinterpret_cast<char*>(packet), sizeof *packet); |
| } while (packet->IsBegin()); |
| CHECK(!packet->empty()) << "zero-size data message received"; |
| CHECK_LE(packet->payload_length(), kMaxPayloadSize) << "invalid size"; |
| } |
| |
| bool SocketForwardRegionView::Send(int connection_id, const Packet& packet) { |
| CHECK(!packet.empty()); |
| CHECK_LE(packet.payload_length(), kMaxPayloadSize); |
| |
| (data()->queues_[connection_id].*WriteDirection) |
| .queue.Write(this, packet.raw_data(), packet.raw_data_length()); |
| return true; |
| } |
| |
| int SocketForwardRegionView::IgnoreUntilBegin(int connection_id) { |
| Packet packet{}; |
| do { |
| (data()->queues_[connection_id].*ReadDirection) |
| .queue.Read(this, reinterpret_cast<char*>(&packet), sizeof packet); |
| } while (!packet.IsBegin()); |
| return packet.port(); |
| } |
| |
| constexpr int kNumQueues = |
| static_cast<int>(vsoc::layout::socket_forward::kNumQueues); |
| |
| void SocketForwardRegionView::CleanUpPreviousConnections() { |
| data()->Recover(); |
| |
| static constexpr auto kRestartPacket = Packet::MakeRestart(); |
| for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) { |
| Send(connection_id, kRestartPacket); |
| } |
| } |
| |
| SocketForwardRegionView::ConnectionViewCollection |
| SocketForwardRegionView::AllConnections() { |
| SocketForwardRegionView::ConnectionViewCollection all_queues; |
| for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) { |
| all_queues.emplace_back(this, connection_id); |
| } |
| return all_queues; |
| } |
| |
| // --- Connection ---- // |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::Recv(Packet* packet) { |
| std::unique_lock<std::mutex> guard(receive_thread_data_lock_); |
| while (received_packet_free_) { |
| receive_thread_data_cv_.wait(guard); |
| } |
| CHECK(received_packet_.IsData()); |
| *packet = received_packet_; |
| received_packet_free_ = true; |
| receive_thread_data_cv_.notify_one(); |
| } |
| |
| bool SocketForwardRegionView::ShmConnectionView::Receiver::GotRecvClosed() const { |
| return received_packet_.IsRecvClosed() || (received_packet_.IsRestart() |
| #ifdef CUTTLEFISH_HOST |
| && saw_data_ |
| #endif |
| ); |
| } |
| |
| bool SocketForwardRegionView::ShmConnectionView::Receiver::ShouldReceiveAnotherPacket() const { |
| return (received_packet_.IsRecvClosed() && !saw_end_) || |
| (saw_end_ && received_packet_.IsEnd()) |
| #ifdef CUTTLEFISH_HOST |
| || (received_packet_.IsRestart() && !saw_data_) || |
| (received_packet_.IsBegin()) |
| #endif |
| ; |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::ReceivePacket() { |
| view_->region_view()->Recv(view_->connection_id(), &received_packet_); |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForRecvClosed() { |
| if (GotRecvClosed()) { |
| saw_recv_closed_ = true; |
| view_->MarkOtherSideRecvClosed(); |
| } |
| #ifdef CUTTLEFISH_HOST |
| if (received_packet_.IsData()) { |
| saw_data_ = true; |
| } |
| #endif |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForEnd() { |
| if (received_packet_.IsEnd() || received_packet_.IsRestart()) { |
| CHECK(!saw_end_ || received_packet_.IsRestart()); |
| saw_end_ = true; |
| } |
| } |
| |
| |
| bool SocketForwardRegionView::ShmConnectionView::Receiver::ExpectMorePackets() const { |
| return !saw_recv_closed_ || !saw_end_; |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::UpdatePacketAndSignalAvailable() { |
| if (!received_packet_.IsData()) { |
| static constexpr auto kEmptyPacket = Packet::MakeData(); |
| received_packet_ = kEmptyPacket; |
| } |
| received_packet_free_ = false; |
| receive_thread_data_cv_.notify_one(); |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Receiver::Start() { |
| while (ExpectMorePackets()) { |
| std::unique_lock<std::mutex> guard(receive_thread_data_lock_); |
| while (!received_packet_free_) { |
| receive_thread_data_cv_.wait(guard); |
| } |
| |
| do { |
| ReceivePacket(); |
| CheckPacketForRecvClosed(); |
| } while (ShouldReceiveAnotherPacket()); |
| |
| if (received_packet_.empty()) { |
| LOG(ERROR) << "Received empty packet."; |
| } |
| |
| CheckPacketForEnd(); |
| |
| UpdatePacketAndSignalAvailable(); |
| } |
| } |
| |
| auto SocketForwardRegionView::ShmConnectionView::ResetAndConnect() |
| -> ShmSenderReceiverPair { |
| if (receiver_) { |
| receiver_->Join(); |
| } |
| |
| { |
| std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_); |
| other_side_receive_closed_ = false; |
| } |
| |
| #ifdef CUTTLEFISH_HOST |
| region_view()->IgnoreUntilBegin(connection_id()); |
| region_view()->Send(connection_id(), Packet::MakeBegin(port_)); |
| #else |
| region_view()->Send(connection_id(), Packet::MakeBegin(port_)); |
| port_ = |
| region_view()->IgnoreUntilBegin(connection_id()); |
| #endif |
| |
| receiver_.reset(new Receiver{this}); |
| return {ShmSender{this}, ShmReceiver{this}}; |
| } |
| |
| #ifdef CUTTLEFISH_HOST |
| auto SocketForwardRegionView::ShmConnectionView::EstablishConnection(int port) |
| -> ShmSenderReceiverPair { |
| port_ = port; |
| return ResetAndConnect(); |
| } |
| #else |
| auto SocketForwardRegionView::ShmConnectionView::WaitForNewConnection() |
| -> ShmSenderReceiverPair { |
| port_ = 0; |
| return ResetAndConnect(); |
| } |
| #endif |
| |
| bool SocketForwardRegionView::ShmConnectionView::Send(const Packet& packet) { |
| if (packet.empty()) { |
| LOG(ERROR) << "Sending empty packet"; |
| } |
| if (packet.IsData() && IsOtherSideRecvClosed()) { |
| return false; |
| } |
| return region_view()->Send(connection_id(), packet); |
| } |
| |
| void SocketForwardRegionView::ShmConnectionView::Recv(Packet* packet) { |
| receiver_->Recv(packet); |
| } |
| |
| void SocketForwardRegionView::ShmReceiver::Recv(Packet* packet) { |
| view_->Recv(packet); |
| } |
| |
| bool SocketForwardRegionView::ShmSender::Send(const Packet& packet) { |
| return view_->Send(packet); |
| } |