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 25static GType _mix_bufferpool_type = 0; 26static MixParamsClass *parent_class = NULL; 27 28#define _do_init { _mix_bufferpool_type = g_define_type_id; } 29 30gboolean mix_bufferpool_copy(MixParams * target, const MixParams * src); 31MixParams *mix_bufferpool_dup(const MixParams * obj); 32gboolean mix_bufferpool_equal(MixParams * first, MixParams * second); 33static void mix_bufferpool_finalize(MixParams * obj); 34 35G_DEFINE_TYPE_WITH_CODE (MixBufferPool, mix_bufferpool, MIX_TYPE_PARAMS, 36 _do_init); 37 38static 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 56static 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 68MixBufferPool * 69mix_bufferpool_new(void) { 70 MixBufferPool *ret = (MixBufferPool *) g_type_create_instance( 71 MIX_TYPE_BUFFERPOOL); 72 return ret; 73} 74 75void 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 91MixBufferPool * 92mix_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 */ 103MixParams * 104mix_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 */ 132gboolean 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 */ 174gboolean 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 */ 219MIX_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 286return 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 */ 295MIX_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 */ 341MIX_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 */ 402MIX_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 456MIX_RESULT 457mix_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 464MIX_RESULT 465mix_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