Commit a1d7bd02 authored by Wim Taymans's avatar Wim Taymans

memory improvements

Make the memory object simply manage the data pointer and the maxsize and move
the offset and size handling to common functionality.
Use the READONLY flag to set a readonly lock.
Remove the data and size fields from the unmap method. We need an explicit
resize operation instead of using the unmap function.
Make internal helper lock and unlock functions.
Update unit test and users of the old API.
parent e349e571
......@@ -1237,13 +1237,10 @@ gst_iterator_result_get_type
GstMemory
GstMemoryInfo
GstAllocator
GST_MEMORY_IS_WRITABLE
GstMemoryFlags
GstMapFlags
GST_MAP_READWRITE
GstMemoryAllocFunction
GstMemoryGetSizesFunction
GstMemoryResizeFunction
GstMemoryMapFunction
GstMemoryUnmapFunction
GstMemoryFreeFunction
......@@ -1262,6 +1259,8 @@ gst_memory_unref
gst_memory_get_sizes
gst_memory_resize
gst_memory_is_writable
gst_memory_map
gst_memory_unmap
......
......@@ -538,7 +538,7 @@ gst_buffer_new_allocate (const GstAllocator * allocator, gsize size,
* that a finalize won't free the buffer */
data = gst_memory_map (mem, &asize, NULL, GST_MAP_WRITE);
gst_buffer_init ((GstBufferImpl *) data, 0);
gst_memory_unmap (mem, data, asize);
gst_memory_unmap (mem);
/* strip off the buffer */
gst_memory_resize (mem, sizeof (GstBufferImpl), size);
......@@ -1004,7 +1004,6 @@ not_writable:
gboolean
gst_buffer_unmap (GstBuffer * buffer, gpointer data, gssize size)
{
gboolean result;
guint len;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
......@@ -1015,15 +1014,14 @@ gst_buffer_unmap (GstBuffer * buffer, gpointer data, gssize size)
if (G_LIKELY (len == 1)) {
GstMemory *mem = GST_BUFFER_MEM_PTR (buffer, 0);
result = gst_memory_unmap (mem, data, size);
gst_memory_unmap (mem);
} else {
/* this must have been from read-only access. After _map, the buffer either
* only contains 1 memory block or it allocated memory to join memory
* blocks. It's not allowed to add buffers between _map and _unmap. */
g_free (data);
result = TRUE;
}
return result;
return TRUE;
}
/**
......@@ -1071,7 +1069,7 @@ gst_buffer_fill (GstBuffer * buffer, gsize offset, gconstpointer src,
/* offset past buffer, skip */
offset -= ssize;
}
gst_memory_unmap (mem, data, ssize);
gst_memory_unmap (mem);
}
return size - left;
}
......@@ -1119,7 +1117,7 @@ gst_buffer_extract (GstBuffer * buffer, gsize offset, gpointer dest, gsize size)
/* offset past buffer, skip */
offset -= ssize;
}
gst_memory_unmap (mem, data, ssize);
gst_memory_unmap (mem);
}
return size - left;
}
......@@ -1167,7 +1165,7 @@ gst_buffer_memcmp (GstBuffer * buffer, gsize offset, gconstpointer mem,
/* offset past buffer, skip */
offset -= ssize;
}
gst_memory_unmap (mem, data, ssize);
gst_memory_unmap (mem);
}
return res;
}
......@@ -1213,7 +1211,7 @@ gst_buffer_memset (GstBuffer * buffer, gsize offset, guint8 val, gsize size)
/* offset past buffer, skip */
offset -= ssize;
}
gst_memory_unmap (mem, data, ssize);
gst_memory_unmap (mem);
}
return size - left;
}
......@@ -1341,10 +1339,10 @@ _gst_buffer_arr_span (GstMemory ** mem[], gsize len[], guint n, gsize offset,
} else {
offset -= tocopy;
}
gst_memory_unmap (cmem[i], src, ssize);
gst_memory_unmap (cmem[i]);
}
}
gst_memory_unmap (span, dest, size);
gst_memory_unmap (span);
}
return span;
}
......
......@@ -105,9 +105,6 @@ typedef struct
gsize slice_size;
guint8 *data;
GFreeFunc free_func;
gsize maxsize;
gsize offset;
gsize size;
} GstMemoryDefault;
/* the default allocator */
......@@ -126,13 +123,13 @@ _default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
mem->mem.flags = flags;
mem->mem.refcount = 1;
mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
mem->mem.state = 0;
mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x5 : 0);
mem->mem.maxsize = maxsize;
mem->mem.offset = offset;
mem->mem.size = size;
mem->slice_size = slice_size;
mem->data = data;
mem->free_func = free_func;
mem->maxsize = maxsize;
mem->offset = offset;
mem->size = size;
}
/* create a new memory block that manages the given memory */
......@@ -188,62 +185,15 @@ _default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}
static gsize
_default_mem_get_sizes (GstMemoryDefault * mem, gsize * offset, gsize * maxsize)
{
if (offset)
*offset = mem->offset;
if (maxsize)
*maxsize = mem->maxsize;
return mem->size;
}
static void
_default_mem_resize (GstMemoryDefault * mem, gssize offset, gsize size)
{
g_return_if_fail (offset >= 0 || mem->offset >= -offset);
g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
mem->offset += offset;
mem->size = size;
}
static gpointer
_default_mem_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize,
GstMapFlags flags)
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
{
if (size)
*size = mem->size;
if (maxsize)
*maxsize = mem->maxsize - mem->offset;
return mem->data + mem->offset;
return mem->data;
}
static gboolean
_default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
_default_mem_unmap (GstMemoryDefault * mem)
{
GST_DEBUG ("mem: %p, data %p, size %" G_GSIZE_FORMAT, mem, data, size);
GST_DEBUG ("mem: %p, data %p, offset %" G_GSIZE_FORMAT ", size %"
G_GSIZE_FORMAT ", maxsize %" G_GSIZE_FORMAT, mem, mem->data, mem->offset,
mem->size, mem->maxsize);
g_return_val_if_fail ((guint8 *) data >= mem->data
&& (guint8 *) data < mem->data + mem->maxsize, FALSE);
if (mem->data + mem->offset != data) {
gsize newoffset = (guint8 *) data - mem->data;
if (size == -1)
size = mem->offset + mem->size - newoffset;
mem->offset = newoffset;
}
if (size != -1) {
g_return_val_if_fail (mem->offset + size <= mem->maxsize, FALSE);
mem->size = size;
}
return TRUE;
}
......@@ -265,10 +215,12 @@ _default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
GstMemoryDefault *copy;
if (size == -1)
size = mem->size > offset ? mem->size - offset : 0;
size = mem->mem.size > offset ? mem->mem.size - offset : 0;
copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
memcpy (copy->data, mem->data, mem->maxsize);
copy =
_default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
size);
memcpy (copy->data, mem->data, mem->mem.maxsize);
return copy;
}
......@@ -284,10 +236,11 @@ _default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
parent = (GstMemory *) mem;
if (size == -1)
size = mem->size - offset;
size = mem->mem.size - offset;
sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize,
mem->offset + offset, size);
sub =
_default_mem_new (parent->flags, parent, mem->data, NULL,
mem->mem.maxsize, mem->mem.offset + offset, size);
return sub;
}
......@@ -302,11 +255,12 @@ _default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
parent = (GstMemoryDefault *) mem1->mem.parent;
*offset = mem1->offset - parent->offset;
*offset = mem1->mem.offset - parent->mem.offset;
}
/* and memory is contiguous */
return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
return mem1->data + mem1->mem.offset + mem1->mem.size ==
mem2->data + mem2->mem.offset;
}
static GstMemory *
......@@ -317,15 +271,16 @@ _fallback_copy (GstMemory * mem, gssize offset, gssize size)
gsize msize;
data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ);
if (data == NULL)
return NULL;
if (size == -1)
size = msize > offset ? msize - offset : 0;
/* use the same allocator as the memory we copy, FIXME, alignment? */
copy = gst_allocator_alloc (mem->allocator, size, 0);
/* use the same allocator as the memory we copy */
copy = gst_allocator_alloc (mem->allocator, size, mem->align);
dest = gst_memory_map (copy, NULL, NULL, GST_MAP_WRITE);
memcpy (dest, data + offset, size);
gst_memory_unmap (copy, dest, size);
gst_memory_unmap (mem, data, msize);
gst_memory_unmap (copy);
gst_memory_unmap (mem);
return (GstMemory *) copy;
}
......@@ -344,8 +299,6 @@ _priv_gst_memory_initialize (void)
{
static const GstMemoryInfo _mem_info = {
(GstMemoryAllocFunction) _default_mem_alloc,
(GstMemoryGetSizesFunction) _default_mem_get_sizes,
(GstMemoryResizeFunction) _default_mem_resize,
(GstMemoryMapFunction) _default_mem_map,
(GstMemoryUnmapFunction) _default_mem_unmap,
(GstMemoryFreeFunction) _default_mem_free,
......@@ -448,7 +401,12 @@ gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
{
g_return_val_if_fail (mem != NULL, 0);
return mem->allocator->info.get_sizes (mem, offset, maxsize);
if (offset)
*offset = mem->offset;
if (maxsize)
*maxsize = mem->maxsize;
return mem->size;
}
/**
......@@ -465,8 +423,11 @@ gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
{
g_return_if_fail (mem != NULL);
g_return_if_fail (gst_memory_is_writable (mem));
g_return_if_fail (offset >= 0 || mem->offset >= -offset);
g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
mem->allocator->info.resize (mem, offset, size);
mem->offset += offset;
mem->size = size;
}
/**
......@@ -487,6 +448,52 @@ gst_memory_is_writable (GstMemory * mem)
((mem->flags & GST_MEMORY_FLAG_READONLY) == 0);
}
static gboolean
gst_memory_lock (GstMemory * mem, GstMapFlags flags)
{
gint access_mode, state, newstate;
access_mode = flags & 3;
do {
state = g_atomic_int_get (&mem->state);
if (state == 0) {
/* nothing mapped, set access_mode and refcount */
newstate = 4 | access_mode;
} else {
/* access_mode must match */
if ((state & access_mode) != access_mode)
goto lock_failed;
/* increase refcount */
newstate = state + 4;
}
} while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
return TRUE;
lock_failed:
{
GST_DEBUG ("lock failed %p: state %d, access_mode %d", mem, state,
access_mode);
return FALSE;
}
}
static void
gst_memory_unlock (GstMemory * mem)
{
gint state, newstate;
do {
state = g_atomic_int_get (&mem->state);
/* decrease the refcount */
newstate = state - 4;
/* last refcount, unset access_mode */
if (newstate < 4)
newstate = 0;
} while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
}
/**
* gst_memory_map:
* @mem: a #GstMemory
......@@ -499,94 +506,71 @@ gst_memory_is_writable (GstMemory * mem)
* @size and @maxsize will contain the size of the memory and the maximum
* allocated memory of @mem respectively. They can be set to NULL.
*
* This function can return NULL for various reasons:
* - the memory backed by @mem is not accessible with the given @flags.
* - the memory was already mapped with a different mapping.
*
* @pointer remains valid for as long as @mem is alive and until
* gst_memory_unmap() is called.
*
* For each gst_memory_map() call, a corresponding gst_memory_unmap() call
* should be done.
*
* Returns: (transfer none): a pointer to the memory of @mem.
*/
gpointer
gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
GstMapFlags flags)
{
gpointer res;
gint access_mode, state, newstate;
guint8 *res;
g_return_val_if_fail (mem != NULL, NULL);
access_mode = flags & 3;
g_return_val_if_fail (!(access_mode & GST_MAP_WRITE)
|| gst_memory_is_writable (mem), NULL);
do {
state = g_atomic_int_get (&mem->state);
if (state == 0) {
/* nothing mapped, set access_mode and refcount */
newstate = 4 | access_mode;
} else {
/* access_mode must match */
g_return_val_if_fail ((state & access_mode) == access_mode, NULL);
/* increase refcount */
newstate = state + 4;
}
} while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
if (!gst_memory_lock (mem, flags))
goto lock_failed;
res = mem->allocator->info.map (mem, mem->maxsize, flags);
res = mem->allocator->info.map (mem, size, maxsize, flags);
if (G_UNLIKELY (res == NULL))
goto error;
if (G_UNLIKELY (res == NULL)) {
if (size)
*size = mem->size;
if (maxsize)
*maxsize = mem->maxsize - mem->offset;
return res + mem->offset;
/* ERRORS */
lock_failed:
{
g_critical ("memory %p: failed to lock memory", mem);
return NULL;
}
error:
{
/* something went wrong, restore the orginal state again */
do {
state = g_atomic_int_get (&mem->state);
/* there must be a ref */
g_return_val_if_fail (state >= 4, NULL);
/* decrease the refcount */
newstate = state - 4;
/* last refcount, unset access_mode */
if (newstate < 4)
newstate = 0;
} while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
GST_ERROR ("mem %p: map failed", mem);
gst_memory_unlock (mem);
return NULL;
}
return res;
}
/**
* gst_memory_unmap:
* @mem: a #GstMemory
* @data: data to unmap
* @size: new size of @mem, or -1
*
* Release the memory pointer obtained with gst_memory_map() and set the size of
* the memory to @size. @size can be set to -1 when the size should not be
* updated.
*
* It is possible to pass a different @data than that obtained from
* gst_memory_map() in which case the offset of @mem will be updated.
*
* Returns: TRUE when the memory was release successfully.
* Release the memory obtained with gst_memory_map()
*/
gboolean
gst_memory_unmap (GstMemory * mem, gpointer data, gssize size)
void
gst_memory_unmap (GstMemory * mem)
{
gboolean need_unmap = TRUE;
gint state, newstate;
g_return_val_if_fail (mem != NULL, FALSE);
do {
state = g_atomic_int_get (&mem->state);
/* there must be a ref */
g_return_val_if_fail (state >= 4, FALSE);
if (need_unmap) {
/* try to unmap, only do this once */
if (!mem->allocator->info.unmap (mem, data, size))
return FALSE;
need_unmap = FALSE;
}
/* success, try to decrease the refcount */
newstate = state - 4;
/* last refcount, unset access_mode */
if (newstate < 4)
newstate = 0;
} while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
g_return_if_fail (mem != NULL);
/* there must be a ref */
g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
return TRUE;
mem->allocator->info.unmap (mem);
gst_memory_unlock (mem);
}
/**
......@@ -604,9 +588,16 @@ gst_memory_unmap (GstMemory * mem, gpointer data, gssize size)
GstMemory *
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
{
GstMemory *copy;
g_return_val_if_fail (mem != NULL, NULL);
g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);
copy = mem->allocator->info.copy (mem, offset, size);
return mem->allocator->info.copy (mem, offset, size);
gst_memory_unlock (mem);
return copy;
}
/**
......@@ -693,8 +684,6 @@ gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (info->alloc != NULL, NULL);
g_return_val_if_fail (info->get_sizes != NULL, NULL);
g_return_val_if_fail (info->resize != NULL, NULL);
g_return_val_if_fail (info->map != NULL, NULL);
g_return_val_if_fail (info->unmap != NULL, NULL);
g_return_val_if_fail (info->free != NULL, NULL);
......
......@@ -62,6 +62,10 @@ typedef enum {
* @refcount: refcount
* @parent: parent memory block
* @state: private state
* @maxsize: the maximum size allocated
* @align: the alignment of the memory
* @offset: the offset where valid data starts
* @size: the size of valid data
*
* Base structure for memory implementations. Custom memory will put this structure
* as the first member of their structure.
......@@ -73,6 +77,10 @@ struct _GstMemory {
gint refcount;
GstMemory *parent;
volatile gint state;
gsize maxsize;
gsize align;
gsize offset;
gsize size;
};
/**
......@@ -122,63 +130,30 @@ typedef GstMemory * (*GstMemoryAllocFunction) (const GstAllocator *allocator,
gsize maxsize, gsize align,
gpointer user_data);
/**
* GstMemoryGetSizesFunction:
* @mem: a #GstMemory
* @offset: result pointer for offset
* @maxsize: result pointer for maxsize
*
* Retrieve the size, offset and maxsize of @mem.
*
* Returns: the size of @mem, the offset and the maximum allocated size in @maxsize.
*/
typedef gsize (*GstMemoryGetSizesFunction) (GstMemory *mem, gsize *offset, gsize *maxsize);
/**
* GstMemoryResizeFunction:
* @mem: a #GstMemory
* @offset: the offset adjustement
* @size: the new size or -1 to just adjust the offset
*
* Adjust the size and offset of @mem. @offset bytes will be adjusted from the
* current first byte in @mem as retrieved with gst_memory_map() and the new
* size will be set to @size.
*
* @size can be set to -1, which will only adjust the offset.
*/
typedef void (*GstMemoryResizeFunction) (GstMemory *mem, gssize offset, gssize size);
/**
* GstMemoryMapFunction:
* @mem: a #GstMemory
* @size: pointer for the size
* @maxsize: pointer for the maxsize
* @maxsize: size to map
* @flags: access mode for the memory
*
* Get the memory of @mem that can be accessed according to the mode specified
* in @flags. @size and @maxsize will respectively contain the current amount of
* valid bytes in the returned memory and the maximum allocated memory.
* @size and @maxsize can optionally be set to NULL.
* in @flags. The function should return a pointer that contains at least
* @maxsize bytes.
*
* Returns: a pointer to memory. @size bytes are currently used from the
* returned pointer and @maxsize bytes can potentially be used.
* Returns: a pointer to memory of which at least @maxsize bytes can be
* accessed according to the access pattern in @flags.
*/
typedef gpointer (*GstMemoryMapFunction) (GstMemory *mem, gsize *size, gsize *maxsize,
GstMapFlags flags);
typedef gpointer (*GstMemoryMapFunction) (GstMemory *mem, gsize maxsize, GstMapFlags flags);
/**
* GstMemoryUnmapFunction:
* @mem: a #GstMemory
* @data: the data pointer
* @size: the new size, or -1 to not modify the size
*
* Return the pointer previously retrieved with gst_memory_map() and adjust the
* size of the memory with @size. @size can optionally be set to -1 to not
* modify the size.
* Return the pointer previously retrieved with gst_memory_map().
*
* Returns: %TRUE on success.
*/
typedef gboolean (*GstMemoryUnmapFunction) (GstMemory *mem, gpointer data, gssize size);
typedef void (*GstMemoryUnmapFunction) (GstMemory *mem);
/**
* GstMemoryFreeFunction:
......@@ -234,8 +209,6 @@ typedef gboolean (*GstMemoryIsSpanFunction) (GstMemory *mem1, GstMemory *m
/**
* GstMemoryInfo:
* @alloc: the implementation of the GstMemoryAllocFunction
* @get_sizes: the implementation of the GstMemoryGetSizesFunction
* @resize: the implementation of the GstMemoryResizeFunction
* @map: the implementation of the GstMemoryMapFunction
* @unmap: the implementation of the GstMemoryUnmapFunction
* @free: the implementation of the GstMemoryFreeFunction
......@@ -249,8 +222,6 @@ typedef gboolean (*GstMemoryIsSpanFunction) (GstMemory *mem1, GstMemory *m
*/
struct _GstMemoryInfo {
GstMemoryAllocFunction alloc;
GstMemoryGetSizesFunction get_sizes;
GstMemoryResizeFunction resize;
GstMemoryMapFunction map;
GstMemoryUnmapFunction unmap;
GstMemoryFreeFunction free;
......@@ -291,7 +262,7 @@ gboolean gst_memory_is_writable (GstMemory *mem);
gpointer gst_memory_map (GstMemory *mem, gsize *size, gsize *maxsize,
GstMapFlags flags);
gboolean gst_memory_unmap (GstMemory *mem, gpointer data, gssize size);
void gst_memory_unmap (GstMemory *mem);
/* copy and subregions */
GstMemory * gst_memory_copy (GstMemory *mem, gssize offset, gssize size);
......
......@@ -45,7 +45,7 @@ GST_START_TEST (test_submemory)
fail_unless (size == 4, "memory has wrong size");
fail_unless (maxsize >= 4, "memory has wrong size");
memset (data, 0, 4);
gst_memory_unmap (memory, data, 4);
gst_memory_unmap (memory);
data = gst_memory_map (memory, &size, NULL, GST_MAP_READ);
......@@ -57,7 +57,7 @@ GST_START_TEST (test_submemory)
fail_unless (memcmp (data + 1, sdata, 2) == 0,
"submemory contains the wrong data");
ASSERT_MEMORY_REFCOUNT (sub, "submemory", 1);
gst_memory_unmap (sub, sdata, ssize);
gst_memory_unmap (sub);
gst_memory_unref (sub);
/* create a submemory of size 0 */
......@@ -68,7 +68,7 @@ GST_START_TEST (test_submemory)
fail_unless (memcmp (data + 1, sdata, 0) == 0,
"submemory contains the wrong data");
ASSERT_MEMORY_REFCOUNT (sub, "submemory", 1);
gst_memory_unmap (sub, sdata, ssize);
gst_memory_unmap (sub);