anbox/src/anbox/graphics/buffered_io_stream.cpp
Simon Fels 7ce2da3be3 Rework client renderer setup
This gets us rid of one unneeded socket based communication point which
we kept around for historic reasons (from Android QEMU) but don't really
need in our case. This allows a simplication of our setup and improves
performance.
2016-12-21 08:40:40 +01:00

132 lines
3.9 KiB
C++

/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/graphics/buffered_io_stream.h"
#include "anbox/logger.h"
namespace anbox {
namespace graphics {
BufferedIOStream::BufferedIOStream(
const std::shared_ptr<anbox::network::SocketMessenger> &messenger,
size_t buffer_size)
: IOStream(buffer_size),
messenger_(messenger),
in_queue_(1024U),
out_queue_(16U),
worker_thread_(&BufferedIOStream::thread_main, this) {
write_buffer_.resize_noinit(buffer_size);
}
BufferedIOStream::~BufferedIOStream() {
forceStop();
if (worker_thread_.joinable()) worker_thread_.join();
}
void *BufferedIOStream::allocBuffer(size_t min_size) {
std::unique_lock<std::mutex> l(out_lock_);
if (write_buffer_.size() < min_size) write_buffer_.resize_noinit(min_size);
return write_buffer_.data();
}
size_t BufferedIOStream::commitBuffer(size_t size) {
std::unique_lock<std::mutex> l(out_lock_);
assert(size <= write_buffer_.size());
if (write_buffer_.isAllocated()) {
write_buffer_.resize(size);
out_queue_.push_locked(std::move(write_buffer_), l);
} else {
out_queue_.push_locked(
Buffer{write_buffer_.data(), write_buffer_.data() + size}, l);
}
return size;
}
const unsigned char *BufferedIOStream::read(void *buf, size_t *inout_len) {
std::unique_lock<std::mutex> l(lock_);
size_t wanted = *inout_len;
size_t count = 0U;
auto dst = static_cast<uint8_t *>(buf);
while (count < wanted) {
if (read_buffer_left_ > 0) {
size_t avail = std::min<size_t>(wanted - count, read_buffer_left_);
memcpy(dst + count,
read_buffer_.data() + (read_buffer_.size() - read_buffer_left_),
avail);
count += avail;
read_buffer_left_ -= avail;
continue;
}
bool blocking = (count == 0);
auto result = BufferQueue::Result::Error;
if (blocking)
result = in_queue_.pop_locked(&read_buffer_, l);
else
result = in_queue_.try_pop_locked(&read_buffer_);
if (result == BufferQueue::Result::Ok) {
read_buffer_left_ = read_buffer_.size();
continue;
}
if (count > 0) break;
// If we end up here something went wrong and we couldn't read
// any valid data.
return nullptr;
}
*inout_len = count;
return static_cast<const unsigned char *>(buf);
}
void BufferedIOStream::forceStop() {
std::lock_guard<std::mutex> l(lock_);
in_queue_.close_locked();
out_queue_.close_locked();
}
void BufferedIOStream::post_data(Buffer &&data) {
std::unique_lock<std::mutex> l(lock_);
in_queue_.push_locked(std::move(data), l);
}
void BufferedIOStream::thread_main() {
while (true) {
std::unique_lock<std::mutex> l(out_lock_);
Buffer buffer;
auto result = out_queue_.pop_locked(&buffer, l);
if (result == BufferQueue::Result::Error) break;
auto bytes_left = buffer.size();
while (bytes_left > 0) {
const auto written = messenger_->send_raw(
buffer.data() + (buffer.size() - bytes_left), bytes_left);
if (written < 0) {
if (errno != EINTR || errno != EAGAIN) {
ERROR("Failed to write data: %s", std::strerror(errno));
break;
}
// Socket is busy, lets try again
} else
bytes_left -= written;
}
}
}
} // namespace graphics
} // namespace anbox