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