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