DoubleBuffer.cpp 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringView.h>
  7. #include <Kernel/DoubleBuffer.h>
  8. namespace Kernel {
  9. inline void DoubleBuffer::compute_lockfree_metadata()
  10. {
  11. InterruptDisabler disabler;
  12. m_empty = m_read_buffer_index >= m_read_buffer->size && m_write_buffer->size == 0;
  13. m_space_for_writing = m_capacity - m_write_buffer->size;
  14. }
  15. DoubleBuffer::DoubleBuffer(size_t capacity)
  16. : m_write_buffer(&m_buffer1)
  17. , m_read_buffer(&m_buffer2)
  18. , m_storage(KBuffer::create_with_size(capacity * 2, Region::Access::Read | Region::Access::Write, "DoubleBuffer"))
  19. , m_capacity(capacity)
  20. {
  21. m_buffer1.data = m_storage.data();
  22. m_buffer1.size = 0;
  23. m_buffer2.data = m_storage.data() + capacity;
  24. m_buffer2.size = 0;
  25. m_space_for_writing = capacity;
  26. }
  27. void DoubleBuffer::flip()
  28. {
  29. if (m_storage.is_null())
  30. return;
  31. VERIFY(m_read_buffer_index == m_read_buffer->size);
  32. swap(m_read_buffer, m_write_buffer);
  33. m_write_buffer->size = 0;
  34. m_read_buffer_index = 0;
  35. compute_lockfree_metadata();
  36. }
  37. ssize_t DoubleBuffer::write(const UserOrKernelBuffer& data, size_t size)
  38. {
  39. if (!size || m_storage.is_null())
  40. return 0;
  41. Locker locker(m_lock);
  42. size_t bytes_to_write = min(size, m_space_for_writing);
  43. u8* write_ptr = m_write_buffer->data + m_write_buffer->size;
  44. m_write_buffer->size += bytes_to_write;
  45. compute_lockfree_metadata();
  46. if (!data.read(write_ptr, bytes_to_write))
  47. return -EFAULT;
  48. if (m_unblock_callback && !m_empty)
  49. m_unblock_callback();
  50. return (ssize_t)bytes_to_write;
  51. }
  52. ssize_t DoubleBuffer::read(UserOrKernelBuffer& data, size_t size)
  53. {
  54. if (!size || m_storage.is_null())
  55. return 0;
  56. Locker locker(m_lock);
  57. if (m_read_buffer_index >= m_read_buffer->size && m_write_buffer->size != 0)
  58. flip();
  59. if (m_read_buffer_index >= m_read_buffer->size)
  60. return 0;
  61. size_t nread = min(m_read_buffer->size - m_read_buffer_index, size);
  62. if (!data.write(m_read_buffer->data + m_read_buffer_index, nread))
  63. return -EFAULT;
  64. m_read_buffer_index += nread;
  65. compute_lockfree_metadata();
  66. if (m_unblock_callback && m_space_for_writing > 0)
  67. m_unblock_callback();
  68. return (ssize_t)nread;
  69. }
  70. ssize_t DoubleBuffer::peek(UserOrKernelBuffer& data, size_t size)
  71. {
  72. if (!size || m_storage.is_null())
  73. return 0;
  74. Locker locker(m_lock);
  75. if (m_read_buffer_index >= m_read_buffer->size && m_write_buffer->size != 0) {
  76. flip();
  77. }
  78. if (m_read_buffer_index >= m_read_buffer->size)
  79. return 0;
  80. size_t nread = min(m_read_buffer->size - m_read_buffer_index, size);
  81. if (!data.write(m_read_buffer->data + m_read_buffer_index, nread))
  82. return -EFAULT;
  83. compute_lockfree_metadata();
  84. if (m_unblock_callback && m_space_for_writing > 0)
  85. m_unblock_callback();
  86. return (ssize_t)nread;
  87. }
  88. }