Home | History | Annotate | Download | only in shm
      1 #pragma once
      2 
      3 /*
      4  * Copyright (C) 2017 The Android Open Source Project
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 
     19 // Memory layout for byte-oriented circular queues
     20 
     21 #include <atomic>
     22 #include <cstdint>
     23 #include "common/vsoc/shm/base.h"
     24 #include "common/vsoc/shm/lock.h"
     25 
     26 struct iovec;
     27 
     28 namespace vsoc {
     29 class RegionSignalingInterface;
     30 namespace layout {
     31 
     32 /**
     33  * Base classes for all spinlock protected circular queues.
     34  * This class should be embedded in the per-region data structure that is used
     35  * as the parameter to TypedRegion.
     36  */
     37 template <uint32_t SizeLog2>
     38 class CircularQueueBase {
     39  public:
     40   static constexpr size_t layout_size = (1 << SizeLog2) + 12;
     41 
     42  private:
     43   CircularQueueBase() = delete;
     44   CircularQueueBase(const CircularQueueBase&) = delete;
     45   CircularQueueBase& operator=(const CircularQueueBase&) = delete;
     46 
     47  protected:
     48   /**
     49    * Specifies a part of the queue. Note, the given indexes must be masked
     50    * before they can be used against buffer_
     51    */
     52   struct Range {
     53     // Points to the first bytes that is part of the range
     54     uint32_t start_idx;
     55     // Points to the first byte that is not in the range. This is similar to
     56     // the STL end iterator.
     57     uint32_t end_idx;
     58   };
     59   static const uintptr_t BufferSize = (1 << SizeLog2);
     60 
     61   /**
     62    * Copy bytes from buffer_in into the part of the queue specified by Range.
     63    */
     64   void CopyInRange(const char* buffer_in, const Range& t);
     65 
     66   /**
     67    * Copy the bytes specified by range to the given buffer. They caller must
     68    * ensure that the buffer is large enough to hold the content of the range.
     69    */
     70   void CopyOutRange(const Range& t, char* buffer_out);
     71 
     72   /**
     73    * Wait until data becomes available in the queue. The caller must have
     74    * called Lock() before invoking this. The caller must call Unlock()
     75    * after this returns.
     76    */
     77   void WaitForDataLocked(RegionSignalingInterface* r);
     78 
     79   /**
     80    * Reserve space in the queue for writing. The caller must have called Lock()
     81    * before invoking this. The caller must call Unlock() after this returns.
     82    * Indexes pointing to the reserved space will be placed in range.
     83    * On success this returns bytes.
     84    * On failure a negative errno indicates the problem. -ENOSPC indicates that
     85    * bytes > the queue size, -EWOULDBLOCK indicates that the call would block
     86    * waiting for space but was requested non bloking.
     87    */
     88   intptr_t WriteReserveLocked(RegionSignalingInterface* r, size_t bytes,
     89                               Range* t, bool non_blocking);
     90 
     91   bool RecoverBase() {
     92     return lock_.Recover();
     93   }
     94 
     95   // Note: Both of these fields may hold values larger than the buffer size,
     96   // they should be interpreted modulo the buffer size. This fact along with the
     97   // buffer size being a power of two greatly simplyfies the index calculations.
     98   // Advances when a reader has finished with buffer space
     99   std::atomic<uint32_t> r_released_;
    100   // Advances when buffer space is filled and ready for a reader
    101   std::atomic<uint32_t> w_pub_;
    102   // Spinlock that protects the region. 0 means unlocked
    103   SpinLock lock_;
    104   // The actual memory in the buffer
    105   char buffer_[BufferSize];
    106 };
    107 using CircularQueueBase64k = CircularQueueBase<16>;
    108 ASSERT_SHM_COMPATIBLE(CircularQueueBase64k);
    109 
    110 /**
    111  * Byte oriented circular queue. Reads will always return some data, but
    112  * may return less data than requested. Writes will always write all of the
    113  * data or return an error.
    114  */
    115 template <uint32_t SizeLog2>
    116 class CircularByteQueue : public CircularQueueBase<SizeLog2> {
    117  public:
    118   static constexpr size_t layout_size =
    119       CircularQueueBase<SizeLog2>::layout_size;
    120   /**
    121    * Read at most max_size bytes from the qeueue, placing them in buffer_out
    122    */
    123   intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
    124                 std::size_t max_size);
    125   /**
    126    * Write all of the given bytes into the queue. If non_blocking isn't set the
    127    * call may block until there is enough available space in the queue. On
    128    * success the return value will match bytes. On failure a negative errno is
    129    * returned. -ENOSPC: If the queue size is smaller than the number of bytes to
    130    * write. -EWOULDBLOCK: If non_blocking is true and there is not enough free
    131    * space.
    132    */
    133   intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
    134                  std::size_t bytes, bool non_blocking = false);
    135 
    136   bool Recover() {
    137     return this->RecoverBase();
    138   }
    139 
    140  protected:
    141   using Range = typename CircularQueueBase<SizeLog2>::Range;
    142 };
    143 using CircularByteQueue64k = CircularByteQueue<16>;
    144 ASSERT_SHM_COMPATIBLE(CircularByteQueue64k);
    145 
    146 /**
    147  * Packet oriented circular queue. Reads will either return data or an error.
    148  * Each return from read corresponds to a call to write and returns all of the
    149  * data from that corresponding Write().
    150  */
    151 template <uint32_t SizeLog2, uint32_t MaxPacketSize>
    152 class CircularPacketQueue : public CircularQueueBase<SizeLog2> {
    153  public:
    154   static constexpr size_t layout_size =
    155       CircularQueueBase<SizeLog2>::layout_size;
    156 
    157   /**
    158    * Read a single packet from the queue, placing its data into buffer_out.
    159    * If max_size indicates that buffer_out cannot hold the entire packet
    160    * this function will return -ENOSPC.
    161    */
    162   intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
    163                 std::size_t max_size);
    164 
    165   /**
    166    * Writes [buffer_in, buffer_in + bytes) to the queue.
    167    * If the number of bytes to be written exceeds the size of the queue
    168    * -ENOSPC will be returned.
    169    * If non_blocking is true and there is not enough free space on the queue to
    170    * write all the data -EWOULDBLOCK will be returned.
    171    */
    172   intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
    173                  uint32_t bytes, bool non_blocking = false);
    174 
    175   /**
    176    * Writes the data referenced by the given iov scatter/gather array to the
    177    * queue.
    178    * If the number of bytes to be written exceeds the size of the queue
    179    * -ENOSPC will be returned.
    180    * If non_blocking is true and there is not enough free space on the queue to
    181    * write all the data -EWOULDBLOCK will be returned.
    182    */
    183   intptr_t Writev(
    184           RegionSignalingInterface *r,
    185           const iovec *iov,
    186           size_t iov_count,
    187           bool non_blocking = false);
    188 
    189   bool Recover() {
    190     return this->RecoverBase();
    191   }
    192 
    193  protected:
    194   static_assert(CircularQueueBase<SizeLog2>::BufferSize >= MaxPacketSize,
    195                 "Buffer is too small to hold the maximum sized packet");
    196   using Range = typename CircularQueueBase<SizeLog2>::Range;
    197   intptr_t CalculateBufferedSize(size_t payload);
    198 };
    199 using CircularPacketQueue64k = CircularPacketQueue<16, 1024>;
    200 ASSERT_SHM_COMPATIBLE(CircularPacketQueue64k);
    201 
    202 }  // namespace layout
    203 }  // namespace vsoc
    204