media_message_fifo.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
20namespace chromecast {
21namespace media {
22class MediaMemoryChunk;
23class MediaMessage;
24class 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//
84class 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