/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace Kernel { static Atomic s_next_fifo_id = 1; ErrorOr> FIFO::try_create(UserID uid) { auto buffer = TRY(DoubleBuffer::try_create()); return adopt_nonnull_ref_or_enomem(new (nothrow) FIFO(uid, move(buffer))); } ErrorOr> FIFO::open_direction(FIFO::Direction direction) { auto description = TRY(OpenFileDescription::try_create(*this)); attach(direction); description->set_fifo_direction({}, direction); return description; } ErrorOr> FIFO::open_direction_blocking(FIFO::Direction direction) { MutexLocker locker(m_open_lock); auto description = TRY(open_direction(direction)); if (direction == Direction::Reader) { m_read_open_queue.wake_all(); if (m_writers == 0) { locker.unlock(); m_write_open_queue.wait_forever("FIFO"); locker.lock(); } } if (direction == Direction::Writer) { m_write_open_queue.wake_all(); if (m_readers == 0) { locker.unlock(); m_read_open_queue.wait_forever("FIFO"); locker.lock(); } } return description; } FIFO::FIFO(UserID uid, NonnullOwnPtr buffer) : m_buffer(move(buffer)) , m_uid(uid) { m_fifo_id = ++s_next_fifo_id; // Use the same block condition for read and write m_buffer->set_unblock_callback([this]() { evaluate_block_conditions(); }); } FIFO::~FIFO() = default; void FIFO::attach(Direction direction) { if (direction == Direction::Reader) { ++m_readers; } else if (direction == Direction::Writer) { ++m_writers; } evaluate_block_conditions(); } void FIFO::detach(Direction direction) { if (direction == Direction::Reader) { VERIFY(m_readers); --m_readers; } else if (direction == Direction::Writer) { VERIFY(m_writers); --m_writers; } evaluate_block_conditions(); } bool FIFO::can_read(OpenFileDescription const&, u64) const { return !m_buffer->is_empty() || !m_writers; } bool FIFO::can_write(OpenFileDescription const&, u64) const { return m_buffer->space_for_writing() || !m_readers; } ErrorOr FIFO::read(OpenFileDescription& fd, u64, UserOrKernelBuffer& buffer, size_t size) { if (m_buffer->is_empty()) { if (!m_writers) return 0; if (!fd.is_blocking()) return EAGAIN; } return m_buffer->read(buffer, size); } ErrorOr FIFO::write(OpenFileDescription& fd, u64, UserOrKernelBuffer const& buffer, size_t size) { if (!m_readers) { Thread::current()->send_signal(SIGPIPE, &Process::current()); return EPIPE; } if (!fd.is_blocking() && m_buffer->space_for_writing() == 0) return EAGAIN; return m_buffer->write(buffer, size); } ErrorOr> FIFO::pseudo_path(OpenFileDescription const&) const { return KString::formatted("fifo:{}", m_fifo_id); } ErrorOr FIFO::stat() const { struct stat st = {}; st.st_mode = S_IFIFO; return st; } }