1 /*
2 INTEL CONFIDENTIAL
3 Copyright 2009 Intel Corporation All Rights Reserved.
4 The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material contains trade secrets and proprietary and confidential information of Intel or its suppliers and licensors. The Material is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel’s prior express written permission.
5
6 No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing.
7 */
8
9 /**
10 * SECTION:mixbufferpool
11 * @short_description: MI-X Input Buffer Pool
12 *
13 * A data object which stores and manipulates a pool of compressed video buffers.
14 */
15
16 #include "mixvideolog.h"
17 #include "mixbufferpool.h"
18 #include "mixbuffer_private.h"
19
20 #define MIX_LOCK(lock) g_mutex_lock(lock);
21 #define MIX_UNLOCK(lock) g_mutex_unlock(lock);
22
23 #define SAFE_FREE(p) if(p) { g_free(p); p = NULL; }
24
25 static GType _mix_bufferpool_type = 0;
26 static MixParamsClass *parent_class = NULL;
27
28 #define _do_init { _mix_bufferpool_type = g_define_type_id; }
29
30 gboolean mix_bufferpool_copy(MixParams * target, const MixParams * src);
31 MixParams *mix_bufferpool_dup(const MixParams * obj);
32 gboolean mix_bufferpool_equal(MixParams * first, MixParams * second);
33 static void mix_bufferpool_finalize(MixParams * obj);
34
35 G_DEFINE_TYPE_WITH_CODE (MixBufferPool, mix_bufferpool, MIX_TYPE_PARAMS,
36 _do_init);
37
mix_bufferpool_init(MixBufferPool * self)38 static void mix_bufferpool_init(MixBufferPool * self) {
39 /* initialize properties here */
40 self->free_list = NULL;
41 self->in_use_list = NULL;
42 self->free_list_max_size = 0;
43 self->high_water_mark = 0;
44
45 self->reserved1 = NULL;
46 self->reserved2 = NULL;
47 self->reserved3 = NULL;
48 self->reserved4 = NULL;
49
50 // TODO: relocate this mutex allocation -we can't communicate failure in ctor.
51 // Note that g_thread_init() has already been called by mix_video_init()
52 self->objectlock = g_mutex_new();
53
54 }
55
mix_bufferpool_class_init(MixBufferPoolClass * klass)56 static void mix_bufferpool_class_init(MixBufferPoolClass * klass) {
57 MixParamsClass *mixparams_class = MIX_PARAMS_CLASS(klass);
58
59 /* setup static parent class */
60 parent_class = (MixParamsClass *) g_type_class_peek_parent(klass);
61
62 mixparams_class->finalize = mix_bufferpool_finalize;
63 mixparams_class->copy = (MixParamsCopyFunction) mix_bufferpool_copy;
64 mixparams_class->dup = (MixParamsDupFunction) mix_bufferpool_dup;
65 mixparams_class->equal = (MixParamsEqualFunction) mix_bufferpool_equal;
66 }
67
68 MixBufferPool *
mix_bufferpool_new(void)69 mix_bufferpool_new(void) {
70 MixBufferPool *ret = (MixBufferPool *) g_type_create_instance(
71 MIX_TYPE_BUFFERPOOL);
72 return ret;
73 }
74
mix_bufferpool_finalize(MixParams * obj)75 void mix_bufferpool_finalize(MixParams * obj) {
76 /* clean up here. */
77
78 MixBufferPool *self = MIX_BUFFERPOOL(obj);
79
80 if (self->objectlock) {
81 g_mutex_free(self->objectlock);
82 self->objectlock = NULL;
83 }
84
85 /* Chain up parent */
86 if (parent_class->finalize) {
87 parent_class->finalize(obj);
88 }
89 }
90
91 MixBufferPool *
mix_bufferpool_ref(MixBufferPool * mix)92 mix_bufferpool_ref(MixBufferPool * mix) {
93 return (MixBufferPool *) mix_params_ref(MIX_PARAMS(mix));
94 }
95
96 /**
97 * mix_bufferpool_dup:
98 * @obj: a #MixBufferPool object
99 * @returns: a newly allocated duplicate of the object.
100 *
101 * Copy duplicate of the object.
102 */
103 MixParams *
mix_bufferpool_dup(const MixParams * obj)104 mix_bufferpool_dup(const MixParams * obj) {
105 MixParams *ret = NULL;
106
107 if (MIX_IS_BUFFERPOOL(obj)) {
108
109 MIX_LOCK(MIX_BUFFERPOOL(obj)->objectlock);
110
111 MixBufferPool *duplicate = mix_bufferpool_new();
112 if (mix_bufferpool_copy(MIX_PARAMS(duplicate), MIX_PARAMS(obj))) {
113 ret = MIX_PARAMS(duplicate);
114 } else {
115 mix_bufferpool_unref(duplicate);
116 }
117
118 MIX_UNLOCK(MIX_BUFFERPOOL(obj)->objectlock);
119
120 }
121 return ret;
122 }
123
124 /**
125 * mix_bufferpool_copy:
126 * @target: copy to target
127 * @src: copy from src
128 * @returns: boolean indicates if copy is successful.
129 *
130 * Copy instance data from @src to @target.
131 */
mix_bufferpool_copy(MixParams * target,const MixParams * src)132 gboolean mix_bufferpool_copy(MixParams * target, const MixParams * src) {
133 MixBufferPool *this_target, *this_src;
134
135 if (MIX_IS_BUFFERPOOL(target) && MIX_IS_BUFFERPOOL(src)) {
136
137 MIX_LOCK(MIX_BUFFERPOOL(src)->objectlock);
138 MIX_LOCK(MIX_BUFFERPOOL(target)->objectlock);
139
140 // Cast the base object to this child object
141 this_target = MIX_BUFFERPOOL(target);
142 this_src = MIX_BUFFERPOOL(src);
143
144 // Free the existing properties
145
146 // Duplicate string
147 this_target->free_list = this_src->free_list;
148 this_target->in_use_list = this_src->in_use_list;
149 this_target->free_list_max_size = this_src->free_list_max_size;
150 this_target->high_water_mark = this_src->high_water_mark;
151
152 MIX_UNLOCK(MIX_BUFFERPOOL(src)->objectlock);
153 MIX_UNLOCK(MIX_BUFFERPOOL(target)->objectlock);
154
155 // Now chainup base class
156 if (parent_class->copy) {
157 return parent_class->copy(MIX_PARAMS_CAST(target), MIX_PARAMS_CAST(
158 src));
159 } else {
160 return TRUE;
161 }
162 }
163 return FALSE;
164 }
165
166 /**
167 * mix_bufferpool_equal:
168 * @first: first object to compare
169 * @second: seond object to compare
170 * @returns: boolean indicates if instance are equal.
171 *
172 * Copy instance data from @src to @target.
173 */
mix_bufferpool_equal(MixParams * first,MixParams * second)174 gboolean mix_bufferpool_equal(MixParams * first, MixParams * second) {
175 gboolean ret = FALSE;
176 MixBufferPool *this_first, *this_second;
177
178 if (MIX_IS_BUFFERPOOL(first) && MIX_IS_BUFFERPOOL(second)) {
179 // Deep compare
180 // Cast the base object to this child object
181
182 MIX_LOCK(MIX_BUFFERPOOL(first)->objectlock);
183 MIX_LOCK(MIX_BUFFERPOOL(second)->objectlock);
184
185 this_first = MIX_BUFFERPOOL(first);
186 this_second = MIX_BUFFERPOOL(second);
187
188 /* TODO: add comparison for other properties */
189 if (this_first->free_list == this_second->free_list
190 && this_first->in_use_list == this_second->in_use_list
191 && this_first->free_list_max_size
192 == this_second->free_list_max_size
193 && this_first->high_water_mark == this_second->high_water_mark) {
194 // members within this scope equal. chaining up.
195 MixParamsClass *klass = MIX_PARAMS_CLASS(parent_class);
196 if (klass->equal)
197 ret = klass->equal(first, second);
198 else
199 ret = TRUE;
200 }
201
202 MIX_LOCK(MIX_BUFFERPOOL(first)->objectlock);
203 MIX_LOCK(MIX_BUFFERPOOL(second)->objectlock);
204
205 }
206
207 return ret;
208 }
209
210 /* Class Methods */
211
212 /**
213 * mix_bufferpool_initialize:
214 * @returns: MIX_RESULT_SUCCESS if successful in creating the buffer pool
215 *
216 * Use this method to create a new buffer pool, consisting of a GSList of
217 * buffer objects that represents a pool of buffers.
218 */
mix_bufferpool_initialize(MixBufferPool * obj,guint num_buffers)219 MIX_RESULT mix_bufferpool_initialize(MixBufferPool * obj, guint num_buffers) {
220
221 LOG_V( "Begin\n");
222
223 if (obj == NULL)
224 return MIX_RESULT_NULL_PTR;
225
226 MIX_LOCK(obj->objectlock);
227
228 if ((obj->free_list != NULL) || (obj->in_use_list != NULL)) {
229 //buffer pool is in use; return error; need proper cleanup
230 //TODO need cleanup here?
231
232 MIX_UNLOCK(obj->objectlock);
233
234 return MIX_RESULT_ALREADY_INIT;
235 }
236
237 if (num_buffers == 0) {
238 obj->free_list = NULL;
239
240 obj->in_use_list = NULL;
241
242 obj->free_list_max_size = num_buffers;
243
244 obj->high_water_mark = 0;
245
246 MIX_UNLOCK(obj->objectlock);
247
248 return MIX_RESULT_SUCCESS;
249 }
250
251 // Initialize the free pool with MixBuffer objects
252
253 gint i = 0;
254 MixBuffer *buffer = NULL;
255
256 for (; i < num_buffers; i++) {
257
258 buffer = mix_buffer_new();
259
260 if (buffer == NULL) {
261 //TODO need to log an error here and do cleanup
262
263 MIX_UNLOCK(obj->objectlock);
264
265 return MIX_RESULT_NO_MEMORY;
266 }
267
268 // Set the pool reference in the private data of the MixBuffer object
269 mix_buffer_set_pool(buffer, obj);
270
271 //Add each MixBuffer object to the pool list
272 obj->free_list = g_slist_append(obj->free_list, buffer);
273
274 }
275
276 obj->in_use_list = NULL;
277
278 obj->free_list_max_size = num_buffers;
279
280 obj->high_water_mark = 0;
281
282 MIX_UNLOCK(obj->objectlock);
283
284 LOG_V( "End\n");
285
286 return MIX_RESULT_SUCCESS;
287 }
288
289 /**
290 * mix_bufferpool_put:
291 * @returns: SUCCESS or FAILURE
292 *
293 * Use this method to return a buffer to the free pool
294 */
mix_bufferpool_put(MixBufferPool * obj,MixBuffer * buffer)295 MIX_RESULT mix_bufferpool_put(MixBufferPool * obj, MixBuffer * buffer) {
296
297 if (obj == NULL || buffer == NULL)
298 return MIX_RESULT_NULL_PTR;
299
300 MIX_LOCK(obj->objectlock);
301
302 if (obj->in_use_list == NULL) {
303 //in use list cannot be empty if a buffer is in use
304 //TODO need better error code for this
305
306 MIX_UNLOCK(obj->objectlock);
307
308 return MIX_RESULT_FAIL;
309 }
310
311 GSList *element = g_slist_find(obj->in_use_list, buffer);
312 if (element == NULL) {
313 //Integrity error; buffer not found in in use list
314 //TODO need better error code and handling for this
315
316 MIX_UNLOCK(obj->objectlock);
317
318 return MIX_RESULT_FAIL;
319 } else {
320 //Remove this element from the in_use_list
321 obj->in_use_list = g_slist_remove_link(obj->in_use_list, element);
322
323 //Concat the element to the free_list
324 obj->free_list = g_slist_concat(obj->free_list, element);
325 }
326
327 //Note that we do nothing with the ref count for this. We want it to
328 //stay at 1, which is what triggered it to be added back to the free list.
329
330 MIX_UNLOCK(obj->objectlock);
331
332 return MIX_RESULT_SUCCESS;
333 }
334
335 /**
336 * mix_bufferpool_get:
337 * @returns: SUCCESS or FAILURE
338 *
339 * Use this method to get a buffer from the free pool
340 */
mix_bufferpool_get(MixBufferPool * obj,MixBuffer ** buffer)341 MIX_RESULT mix_bufferpool_get(MixBufferPool * obj, MixBuffer ** buffer) {
342
343 if (obj == NULL || buffer == NULL)
344 return MIX_RESULT_NULL_PTR;
345
346 MIX_LOCK(obj->objectlock);
347
348 if (obj->free_list == NULL) {
349 //We are out of buffers
350 //TODO need to log this as well
351
352 MIX_UNLOCK(obj->objectlock);
353
354 return MIX_RESULT_POOLEMPTY;
355 }
356
357 //Remove a buffer from the free pool
358
359 //We just remove the one at the head, since it's convenient
360 GSList *element = obj->free_list;
361 obj->free_list = g_slist_remove_link(obj->free_list, element);
362 if (element == NULL) {
363 //Unexpected behavior
364 //TODO need better error code and handling for this
365
366 MIX_UNLOCK(obj->objectlock);
367
368 return MIX_RESULT_FAIL;
369 } else {
370 //Concat the element to the in_use_list
371 obj->in_use_list = g_slist_concat(obj->in_use_list, element);
372
373 //TODO replace with proper logging
374
375 LOG_I( "buffer refcount%d\n",
376 MIX_PARAMS(element->data)->refcount);
377
378 //Set the out buffer pointer
379 *buffer = (MixBuffer *) element->data;
380
381 //Check the high water mark for buffer use
382 guint size = g_slist_length(obj->in_use_list);
383 if (size > obj->high_water_mark)
384 obj->high_water_mark = size;
385 //TODO Log this high water mark
386 }
387
388 //Increment the reference count for the buffer
389 mix_buffer_ref(*buffer);
390
391 MIX_UNLOCK(obj->objectlock);
392
393 return MIX_RESULT_SUCCESS;
394 }
395
396 /**
397 * mix_bufferpool_deinitialize:
398 * @returns: SUCCESS or FAILURE
399 *
400 * Use this method to teardown a buffer pool
401 */
mix_bufferpool_deinitialize(MixBufferPool * obj)402 MIX_RESULT mix_bufferpool_deinitialize(MixBufferPool * obj) {
403 if (obj == NULL)
404 return MIX_RESULT_NULL_PTR;
405
406 MIX_LOCK(obj->objectlock);
407
408 if ((obj->in_use_list != NULL) || (g_slist_length(obj->free_list)
409 != obj->free_list_max_size)) {
410 //TODO better error code
411 //We have outstanding buffer objects in use and they need to be
412 //freed before we can deinitialize.
413
414 MIX_UNLOCK(obj->objectlock);
415
416 return MIX_RESULT_FAIL;
417 }
418
419 //Now remove buffer objects from the list
420
421 MixBuffer *buffer = NULL;
422
423 while (obj->free_list != NULL) {
424 //Get the buffer object from the head of the list
425 buffer = obj->free_list->data;
426 //buffer = g_slist_nth_data(obj->free_list, 0);
427
428 //Release it
429 mix_buffer_unref(buffer);
430
431 //Delete the head node of the list and store the new head
432 obj->free_list = g_slist_delete_link(obj->free_list, obj->free_list);
433
434 //Repeat until empty
435 }
436
437 obj->free_list_max_size = 0;
438
439 //May want to log this information for tuning
440 obj->high_water_mark = 0;
441
442 MIX_UNLOCK(obj->objectlock);
443
444 return MIX_RESULT_SUCCESS;
445 }
446
447 #define MIX_BUFFERPOOL_SETTER_CHECK_INPUT(obj) \
448 if(!obj) return MIX_RESULT_NULL_PTR; \
449 if(!MIX_IS_BUFFERPOOL(obj)) return MIX_RESULT_FAIL; \
450
451 #define MIX_BUFFERPOOL_GETTER_CHECK_INPUT(obj, prop) \
452 if(!obj || !prop) return MIX_RESULT_NULL_PTR; \
453 if(!MIX_IS_BUFFERPOOL(obj)) return MIX_RESULT_FAIL; \
454
455
456 MIX_RESULT
mix_bufferpool_dumpbuffer(MixBuffer * buffer)457 mix_bufferpool_dumpbuffer(MixBuffer *buffer)
458 {
459 LOG_I( "\tBuffer %x, ptr %x, refcount %d\n", (guint)buffer,
460 (guint)buffer->data, MIX_PARAMS(buffer)->refcount);
461 return MIX_RESULT_SUCCESS;
462 }
463
464 MIX_RESULT
mix_bufferpool_dumpprint(MixBufferPool * obj)465 mix_bufferpool_dumpprint (MixBufferPool * obj)
466 {
467 //TODO replace this with proper logging later
468
469 LOG_I( "BUFFER POOL DUMP:\n");
470 LOG_I( "Free list size is %d\n", g_slist_length(obj->free_list));
471 LOG_I( "In use list size is %d\n", g_slist_length(obj->in_use_list));
472 LOG_I( "High water mark is %lu\n", obj->high_water_mark);
473
474 //Walk the free list and report the contents
475 LOG_I( "Free list contents:\n");
476 g_slist_foreach(obj->free_list, (GFunc) mix_bufferpool_dumpbuffer, NULL);
477
478 //Walk the in_use list and report the contents
479 LOG_I( "In Use list contents:\n");
480 g_slist_foreach(obj->in_use_list, (GFunc) mix_bufferpool_dumpbuffer, NULL);
481
482 return MIX_RESULT_SUCCESS;
483 }
484
485