Home | History | Annotate | Download | only in ipc
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_FIFO_H_
      6 #define CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_FIFO_H_
      7 
      8 #include <list>
      9 
     10 #include "base/atomicops.h"
     11 #include "base/basictypes.h"
     12 #include "base/callback.h"
     13 #include "base/logging.h"
     14 #include "base/macros.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/memory/weak_ptr.h"
     18 #include "base/threading/thread_checker.h"
     19 
     20 namespace chromecast {
     21 namespace media {
     22 class MediaMemoryChunk;
     23 class MediaMessage;
     24 class MediaMessageFlag;
     25 
     26 // MediaMessageFifo is a lock free fifo implementation
     27 // to pass audio/video data from one thread to another or from one process
     28 // to another one (in that case using shared memory).
     29 //
     30 // Assuming the feeder and the consumer have a common block of shared memory
     31 // (representing the serialized structure of the fifo),
     32 // the feeder (which must be running on a single thread) instantiates its own
     33 // instance of MediaMessageFifo, same applies to the consumer.
     34 //
     35 // Example: assuming the block of shared memory is given by |mem|, a typical
     36 // feeder (using MediaMessageFifo instance fifo_feeder) will push messages
     37 // in the following way:
     38 //   // Create a dummy message to calculate the size of the serialized message.
     39 //   scoped_ptr<MediaMessage> dummy_msg(
     40 //     MediaMessage::CreateDummyMessage(msg_type));
     41 //   // ...
     42 //   // Write all the fields to the dummy message.
     43 //   // ...
     44 //
     45 //   // Create the real message, once the size of the serialized message
     46 //   // is known.
     47 //   scoped_ptr<MediaMessage> msg(
     48 //     MediaMessage::CreateMessage(
     49 //       msg_type,
     50 //       base::Bind(&MediaMessageFifo::ReserveMemory,
     51 //                  base::Unretained(fifo_feeder.get())),
     52 //       dummy_msg->content_size()));
     53 //   if (!msg) {
     54 //     // Not enough space for the message:
     55 //     // retry later (e.g. when receiving a read activity event, meaning
     56 //     // some enough space might have been release).
     57 //     return;
     58 //   }
     59 //   // ...
     60 //   // Write all the fields to the real message
     61 //   // in exactly the same way it was done for the dummy message.
     62 //   // ...
     63 //   // Once message |msg| is going out of scope, then MediaMessageFifo
     64 //   // fifo_feeder is informed that the message is not needed anymore
     65 //   // (i.e. it was fully written), and fifo_feeder can then update
     66 //   // the external write pointer of the fifo so that the consumer
     67 //   // can start consuming this message.
     68 //
     69 // A typical consumer (using MediaMessageFifo instance fifo_consumer)
     70 // will retrive messages in the following way:
     71 //   scoped_ptr<MediaMessage> msg(fifo_consumer->Pop());
     72 //   if (!msg) {
     73 //     // The fifo is empty, i.e. no message left.
     74 //     // Try reading again later (e.g. after receiving a write activity event.
     75 //     return;
     76 //   }
     77 //   // Parse the message using Read functions of MediaMessage:
     78 //   // ...
     79 //   // Once the message is going out of scope, MediaMessageFifo will receive
     80 //   // a notification that the underlying memory can be released
     81 //   // (i.e. the external read pointer can be updated).
     82 //
     83 //
     84 class MediaMessageFifo {
     85  public:
     86   // Creates a media message fifo using |mem| as the underlying serialized
     87   // structure.
     88   // If |init| is true, the underlying fifo structure is initialized.
     89   MediaMessageFifo(scoped_ptr<MediaMemoryChunk> mem, bool init);
     90   ~MediaMessageFifo();
     91 
     92   // When the consumer and the feeder are living in two different processes,
     93   // we might want to convey some messages between these two processes to notify
     94   // about some fifo activity.
     95   void ObserveReadActivity(const base::Closure& read_event_cb);
     96   void ObserveWriteActivity(const base::Closure& write_event_cb);
     97 
     98   // Reserves a writeable block of memory at the back of the fifo,
     99   // corresponding to the serialized structure of the message.
    100   // Returns NULL if the required size cannot be allocated.
    101   scoped_ptr<MediaMemoryChunk> ReserveMemory(size_t size);
    102 
    103   // Pop a message from the queue.
    104   // Returns a null pointer if there is no message left.
    105   scoped_ptr<MediaMessage> Pop();
    106 
    107   // Flush the fifo.
    108   void Flush();
    109 
    110  private:
    111   struct Descriptor {
    112     size_t size;
    113     size_t rd_offset;
    114     size_t wr_offset;
    115 
    116     // Ensure the first item has the same alignment as an int64.
    117     int64 first_item;
    118   };
    119 
    120   // Add some accessors to ensure security on the browser process side.
    121   size_t current_rd_offset() const;
    122   size_t current_wr_offset() const;
    123   size_t internal_rd_offset() const {
    124     DCHECK_LT(internal_rd_offset_, size_);
    125     return internal_rd_offset_;
    126   }
    127   size_t internal_wr_offset() const {
    128     DCHECK_LT(internal_wr_offset_, size_);
    129     return internal_wr_offset_;
    130   }
    131 
    132   // Reserve a block of free memory without doing any check on the available
    133   // space. Invoke this function only when all the checks have been done.
    134   scoped_ptr<MediaMemoryChunk> ReserveMemoryNoCheck(size_t size);
    135 
    136   // Invoked each time there is a memory region in the free space of the fifo
    137   // that has possibly been written.
    138   void OnWrMemoryReleased();
    139 
    140   // Invoked each time there is a memory region in the allocated space
    141   // of the fifo that has possibly been released.
    142   void OnRdMemoryReleased();
    143 
    144   // Functions to modify the internal/external read/write pointers.
    145   void CommitRead(size_t new_rd_offset);
    146   void CommitWrite(size_t new_wr_offset);
    147   void CommitInternalRead(size_t new_rd_offset);
    148   void CommitInternalWrite(size_t new_wr_offset);
    149 
    150   // An instance of MediaMessageFifo must be running on a single thread.
    151   // If the fifo feeder and consumer are living on 2 different threads
    152   // or 2 different processes, they must create their own instance
    153   // of MediaMessageFifo using the same underlying block of (shared) memory
    154   // in the constructor.
    155   base::ThreadChecker thread_checker_;
    156 
    157   // Callbacks invoked to notify either of some read or write activity on the
    158   // fifo. This is especially useful when the feeder and consumer are living in
    159   // two different processes.
    160   base::Closure read_event_cb_;
    161   base::Closure write_event_cb_;
    162 
    163   // The serialized structure of the fifo.
    164   scoped_ptr<MediaMemoryChunk> mem_;
    165 
    166   // The size in bytes of the fifo is cached locally for security purpose.
    167   // (the renderer process cannot modify the size and make the browser process
    168   // access out of range addresses).
    169   size_t size_;
    170 
    171   // TODO(damienv): This is a work-around since atomicops.h does not define
    172   // an atomic size_t type.
    173 #if SIZE_MAX == UINT32_MAX
    174   typedef base::subtle::Atomic32 AtomicSize;
    175 #elif SIZE_MAX == UINT64_MAX
    176   typedef base::subtle::Atomic64 AtomicSize;
    177 #elif
    178 #error "Unsupported size_t"
    179 #endif
    180   AtomicSize* rd_offset_;
    181   AtomicSize* wr_offset_;
    182 
    183   // Internal read offset: this is where data is actually read from.
    184   // The external offset |rd_offset_| is only used to protect data from being
    185   // overwritten by the feeder.
    186   // At any time, the internal read pointer must be between the external read
    187   // offset and the write offset (circular fifo definition of "between").
    188   size_t internal_rd_offset_;
    189   size_t internal_wr_offset_;
    190 
    191   // Note: all the memory read/write are followed by a memory fence before
    192   // updating the rd/wr pointer.
    193   void* base_;
    194 
    195   // Protects the messages that are either being read or written.
    196   std::list<scoped_refptr<MediaMessageFlag> > rd_flags_;
    197   std::list<scoped_refptr<MediaMessageFlag> > wr_flags_;
    198 
    199   base::WeakPtrFactory<MediaMessageFifo> weak_factory_;
    200   base::WeakPtr<MediaMessageFifo> weak_this_;
    201 
    202   DISALLOW_COPY_AND_ASSIGN(MediaMessageFifo);
    203 };
    204 
    205 }  // namespace media
    206 }  // namespace chromecast
    207 
    208 #endif  // CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_FIFO_H_
    209