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