From 3b16efa1d13e58e832604a443a12acaa24524a85 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 4 Jul 2012 16:38:15 +0200 Subject: [PATCH] miniobject: add lock functionality to GstMiniObject Move the locking methods from GstMemory to GstMiniObject. Add a miniobject flag to enable LOCKABLE objects. LOCKABLE objects can use the lock/unlock API to control the access to the object. Add a minobject flag that allows you to lock an object in readonly mode. Modify the _is_writable() method to check the shared counter for LOCKABLE objects. This allows us to control writability separately from the refcount for LOCKABLE objects. --- docs/gst/gstreamer-sections.txt | 2 +- gst/gstbuffer.c | 13 +-- gst/gstbuffer.h | 12 ++- gst/gstbufferlist.c | 2 +- gst/gstcaps.c | 2 +- gst/gstcaps.h | 2 +- gst/gstevent.c | 2 +- gst/gstmemory.c | 124 +-------------------------- gst/gstmemory.h | 45 ++-------- gst/gstmessage.c | 2 +- gst/gstminiobject.c | 143 ++++++++++++++++++++++++++++++-- gst/gstminiobject.h | 61 ++++++++++++-- gst/gstquery.c | 2 +- gst/gstsample.c | 2 +- gst/gsttaglist.c | 2 +- gst/gsttoc.c | 4 +- tests/check/gst/gstmemory.c | 1 - win32/common/libgstreamer.def | 6 +- 18 files changed, 229 insertions(+), 198 deletions(-) diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 25b715960a..c540ea2a5d 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -1339,7 +1339,7 @@ gst_memory_new_wrapped gst_memory_ref gst_memory_unref -gst_memory_is_exclusive +gst_memory_is_writable gst_memory_get_sizes gst_memory_resize diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 5efe1d5764..c9e29a7e99 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -265,8 +265,10 @@ _replace_memory (GstBuffer * buffer, guint len, guint idx, guint length, /* unref old memory */ for (i = idx; i < end; i++) { - gst_memory_unlock (GST_BUFFER_MEM_PTR (buffer, i), GST_LOCK_FLAG_EXCLUSIVE); - gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i)); + GstMemory *old = GST_BUFFER_MEM_PTR (buffer, i); + + gst_memory_unlock (old, GST_LOCK_FLAG_EXCLUSIVE); + gst_memory_unref (old); } if (mem != NULL) { @@ -536,7 +538,7 @@ _gst_buffer_free (GstBuffer * buffer) static void gst_buffer_init (GstBufferImpl * buffer, gsize size) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (buffer), _gst_buffer_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (buffer), 0, _gst_buffer_type, (GstMiniObjectCopyFunction) _gst_buffer_copy, (GstMiniObjectDisposeFunction) _gst_buffer_dispose, (GstMiniObjectFreeFunction) _gst_buffer_free); @@ -818,7 +820,7 @@ _get_mapped (GstBuffer * buffer, guint idx, GstMapInfo * info, * any call that modifies the memory in @buffer. * * Since this call does not influence the refcount of the memory, - * gst_memory_is_exclusive() can be used to check if @buffer is the sole owner + * gst_memory_is_writable() can be used to check if @buffer is the sole owner * of the returned memory. * * Returns: (transfer none): the #GstMemory at @idx. @@ -924,6 +926,7 @@ gst_buffer_replace_memory_range (GstBuffer * buffer, guint idx, gint length, g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (gst_buffer_is_writable (buffer)); + len = GST_BUFFER_MEM_LEN (buffer); g_return_if_fail ((len == 0 && idx == 0 && length == -1) || (length == -1 && idx < len) || (length > 0 && length + idx <= len)); @@ -1230,7 +1233,7 @@ gst_buffer_resize_range (GstBuffer * buffer, guint idx, gint length, left = MIN (bsize - offset, size); if (offset != 0 || left != bsize) { - if (gst_memory_is_exclusive (mem)) { + if (gst_memory_is_writable (mem)) { gst_memory_resize (mem, offset, left); } else { GstMemory *newmem; diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index ef5b1da6d7..a890e3aa9c 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -319,9 +319,8 @@ void gst_buffer_unmap (GstBuffer *buffer, GstMapInfo *info) * Increases the refcount of the given buffer by one. * * Note that the refcount affects the writeability - * of @buf and its metadata, see gst_buffer_is_writable() and - * gst_buffer_is_metadata_writable(). It is - * important to note that keeping additional references to + * of @buf and its metadata, see gst_buffer_is_writable(). + * It is important to note that keeping additional references to * GstBuffer instances can potentially increase the number * of memcpy operations in a pipeline. * @@ -426,10 +425,9 @@ void gst_buffer_copy_into (GstBuffer *dest, GstBuffer *src * gst_buffer_is_writable: * @buf: a #GstBuffer * - * Tests if you can safely write data into a buffer's data array or validly - * modify the caps and timestamp metadata. Metadata in a GstBuffer is always - * writable, but it is only safe to change it when there is only one owner - * of the buffer - ie, the refcount is 1. + * Tests if you can safely write to a buffer's metadata or its memory array. + * It is only safe to change buffer metadata when the current reference is + * writable, i.e. nobody can see the modifications you will make. */ #define gst_buffer_is_writable(buf) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (buf)) /** diff --git a/gst/gstbufferlist.c b/gst/gstbufferlist.c index f0b885d8bf..cec034a9a8 100644 --- a/gst/gstbufferlist.c +++ b/gst/gstbufferlist.c @@ -103,7 +103,7 @@ _gst_buffer_list_free (GstBufferList * list) static void gst_buffer_list_init (GstBufferList * list, guint asize) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (list), _gst_buffer_list_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (list), 0, _gst_buffer_list_type, (GstMiniObjectCopyFunction) _gst_buffer_list_copy, NULL, (GstMiniObjectFreeFunction) _gst_buffer_list_free); diff --git a/gst/gstcaps.c b/gst/gstcaps.c index f49409812a..b5d322d849 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -185,7 +185,7 @@ _gst_caps_free (GstCaps * caps) static void gst_caps_init (GstCaps * caps) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (caps), _gst_caps_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (caps), 0, _gst_caps_type, (GstMiniObjectCopyFunction) _gst_caps_copy, NULL, (GstMiniObjectFreeFunction) _gst_caps_free); diff --git a/gst/gstcaps.h b/gst/gstcaps.h index f229742fa2..f5d428732b 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -262,7 +262,7 @@ gst_caps_copy (const GstCaps * caps) * @caps: a #GstCaps * * Tests if you can safely modify @caps. It is only safe to modify caps when - * there is only one owner of the caps - ie, the refcount is 1. + * there is only one owner of the caps - ie, the object is writable. */ #define gst_caps_is_writable(caps) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (caps)) diff --git a/gst/gstevent.c b/gst/gstevent.c index a7d27ec0fc..96e7530723 100644 --- a/gst/gstevent.c +++ b/gst/gstevent.c @@ -261,7 +261,7 @@ _gst_event_copy (GstEvent * event) static void gst_event_init (GstEventImpl * event, GstEventType type) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (event), _gst_event_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (event), 0, _gst_event_type, (GstMiniObjectCopyFunction) _gst_event_copy, NULL, (GstMiniObjectFreeFunction) _gst_event_free); diff --git a/gst/gstmemory.c b/gst/gstmemory.c index 4092d9457d..b645343f1d 100644 --- a/gst/gstmemory.c +++ b/gst/gstmemory.c @@ -133,8 +133,6 @@ _gst_memory_copy (GstMemory * mem) static void _gst_memory_free (GstMemory * mem) { - /* there should be no outstanding mappings */ - g_return_if_fail ((g_atomic_int_get (&mem->state) & LOCK_MASK) < 4); mem->allocator->info.mem_free (mem); } @@ -145,15 +143,13 @@ _default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags, gsize maxsize, gsize offset, gsize size, gsize align, gpointer user_data, GDestroyNotify notify) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (mem), GST_TYPE_MEMORY, + gst_mini_object_init (GST_MINI_OBJECT_CAST (mem), + flags | GST_MINI_OBJECT_FLAG_LOCKABLE, GST_TYPE_MEMORY, (GstMiniObjectCopyFunction) _gst_memory_copy, NULL, (GstMiniObjectFreeFunction) _gst_memory_free); - mem->mem.mini_object.flags = flags; - mem->mem.allocator = _default_mem_impl; mem->mem.parent = parent ? gst_memory_ref (parent) : NULL; - mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? GST_LOCK_FLAG_READ : 0); mem->mem.maxsize = maxsize; mem->mem.align = align; mem->mem.offset = offset; @@ -434,25 +430,6 @@ gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data, return (GstMemory *) mem; } -/** - * gst_memory_is_exclusive: - * @mem: a #GstMemory - * - * Check if the current EXCLUSIVE lock on @mem is the only one, this means that - * changes to the object will not be visible to any other object. - */ -gboolean -gst_memory_is_exclusive (GstMemory * mem) -{ - gint state; - - g_return_val_if_fail (mem != NULL, FALSE); - - state = g_atomic_int_get (&mem->state); - - return (state & SHARE_MASK) < 2; -} - /** * gst_memory_get_sizes: * @mem: a #GstMemory @@ -507,99 +484,6 @@ gst_memory_resize (GstMemory * mem, gssize offset, gsize size) mem->size = size; } -/** - * gst_memory_lock: - * @mem: a #GstMemory - * @flags: #GstLockFlags - * - * Lock the memory with the specified access mode in @flags. - * - * Returns: %TRUE if the memory could be locked. - */ -gboolean -gst_memory_lock (GstMemory * mem, GstLockFlags flags) -{ - gint access_mode, state, newstate; - - access_mode = flags & FLAG_MASK; - - do { - newstate = state = g_atomic_int_get (&mem->state); - - GST_CAT_TRACE (GST_CAT_MEMORY, "lock %p: state %08x, access_mode %d", - mem, state, access_mode); - - if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) { - /* shared ref */ - newstate += SHARE_ONE; - access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE; - } - - if (access_mode) { - if ((state & LOCK_FLAG_MASK) == 0) { - /* shared counter > 1 and write access */ - if (state > SHARE_ONE && access_mode & GST_LOCK_FLAG_WRITE) - goto lock_failed; - /* nothing mapped, set access_mode */ - newstate |= access_mode; - } else { - /* access_mode must match */ - if ((state & access_mode) != access_mode) - goto lock_failed; - } - /* increase refcount */ - newstate += LOCK_ONE; - } - } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate)); - - return TRUE; - -lock_failed: - { - GST_CAT_DEBUG (GST_CAT_MEMORY, "lock failed %p: state %08x, access_mode %d", - mem, state, access_mode); - return FALSE; - } -} - -/** - * gst_memory_unlock: - * @mem: a #GstMemory - * @flags: #GstLockFlags - * - * Unlock the memory with the specified access mode in @flags. - */ -void -gst_memory_unlock (GstMemory * mem, GstLockFlags flags) -{ - gint access_mode, state, newstate; - - access_mode = flags & FLAG_MASK; - - do { - newstate = state = g_atomic_int_get (&mem->state); - - GST_CAT_TRACE (GST_CAT_MEMORY, "unlock %p: state %08x, access_mode %d", - mem, state, access_mode); - - if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) { - /* shared counter */ - g_return_if_fail (state >= SHARE_ONE); - newstate -= SHARE_ONE; - access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE; - } - - if (access_mode) { - g_return_if_fail ((state & access_mode) == access_mode); - /* decrease the refcount */ - newstate -= LOCK_ONE; - /* last refcount, unset access_mode */ - if ((newstate & LOCK_FLAG_MASK) == access_mode) - newstate &= ~LOCK_FLAG_MASK; - } - } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate)); -} - /** * gst_memory_make_mapped: * @mem: (transfer full): a #GstMemory @@ -721,8 +605,6 @@ gst_memory_unmap (GstMemory * mem, GstMapInfo * info) g_return_if_fail (mem != NULL); g_return_if_fail (info != NULL); g_return_if_fail (info->memory == mem); - /* there must be a ref */ - g_return_if_fail (g_atomic_int_get (&mem->state) >= 4); mem->allocator->info.mem_unmap (mem); gst_memory_unlock (mem, info->flags); @@ -861,7 +743,7 @@ gst_allocator_new (const GstMemoryInfo * info, gpointer user_data, allocator = g_slice_new0 (GstAllocator); - gst_mini_object_init (GST_MINI_OBJECT_CAST (allocator), GST_TYPE_ALLOCATOR, + gst_mini_object_init (GST_MINI_OBJECT_CAST (allocator), 0, GST_TYPE_ALLOCATOR, (GstMiniObjectCopyFunction) _gst_allocator_copy, NULL, (GstMiniObjectFreeFunction) _gst_allocator_free); diff --git a/gst/gstmemory.h b/gst/gstmemory.h index bd4106b80d..d2e6b0ee2f 100644 --- a/gst/gstmemory.h +++ b/gst/gstmemory.h @@ -66,10 +66,10 @@ GST_EXPORT gsize gst_memory_alignment; * Flags for wrapped memory. */ typedef enum { - GST_MEMORY_FLAG_READONLY = (GST_MINI_OBJECT_FLAG_LAST << 0), - GST_MEMORY_FLAG_NO_SHARE = (GST_MINI_OBJECT_FLAG_LAST << 1), - GST_MEMORY_FLAG_ZERO_PREFIXED = (GST_MINI_OBJECT_FLAG_LAST << 2), - GST_MEMORY_FLAG_ZERO_PADDED = (GST_MINI_OBJECT_FLAG_LAST << 3), + GST_MEMORY_FLAG_READONLY = GST_MINI_OBJECT_FLAG_LOCK_READONLY, + GST_MEMORY_FLAG_NO_SHARE = (GST_MINI_OBJECT_FLAG_LAST << 0), + GST_MEMORY_FLAG_ZERO_PREFIXED = (GST_MINI_OBJECT_FLAG_LAST << 1), + GST_MEMORY_FLAG_ZERO_PADDED = (GST_MINI_OBJECT_FLAG_LAST << 2), GST_MEMORY_FLAG_LAST = (GST_MINI_OBJECT_FLAG_LAST << 16) } GstMemoryFlags; @@ -133,7 +133,6 @@ typedef enum { * @mini_object: parent structure * @allocator: pointer to the #GstAllocator * @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 @@ -148,37 +147,12 @@ struct _GstMemory { GstAllocator *allocator; GstMemory *parent; - volatile gint state; gsize maxsize; gsize align; gsize offset; gsize size; }; -/** - * GstLockFlags: - * @GST_LOCK_FLAG_READ: lock for read access - * @GST_LOCK_FLAG_WRITE: lock for write access - * @GST_LOCK_FLAG_EXCLUSIVE: lock for exclusive access - * @GST_LOCK_FLAG_LAST: first flag that can be used for custom purposes - * - * Flags used when locking memory - */ -typedef enum { - GST_LOCK_FLAG_READ = (1 << 0), - GST_LOCK_FLAG_WRITE = (1 << 1), - GST_LOCK_FLAG_EXCLUSIVE = (1 << 2), - - GST_LOCK_FLAG_LAST = (1 << 4) -} GstLockFlags; - -/** - * GST_LOCK_FLAG_READWRITE: - * - * GstLockFlags value alias for GST_LOCK_FLAG_READ | GST_LOCK_FLAG_WRITE - */ -#define GST_LOCK_FLAG_READWRITE (GST_LOCK_FLAG_READ | GST_LOCK_FLAG_WRITE) - /** * GstMapFlags: * @GST_MAP_READ: map for read access @@ -487,17 +461,14 @@ gst_memory_unref (GstMemory * memory) gst_mini_object_unref (GST_MINI_OBJECT_CAST (memory)); } -/* locking */ - -gboolean gst_memory_is_exclusive (GstMemory *mem); - -gboolean gst_memory_lock (GstMemory *mem, GstLockFlags flags); -void gst_memory_unlock (GstMemory *mem, GstLockFlags flags); - /* getting/setting memory properties */ gsize gst_memory_get_sizes (GstMemory *mem, gsize *offset, gsize *maxsize); void gst_memory_resize (GstMemory *mem, gssize offset, gsize size); +#define gst_memory_lock(m,f) gst_mini_object_lock (GST_MINI_OBJECT_CAST (m), (f)) +#define gst_memory_unlock(m,f) gst_mini_object_unlock (GST_MINI_OBJECT_CAST (m), (f)) +#define gst_memory_is_writable(m) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (m)) + /* retrieving data */ GstMemory * gst_memory_make_mapped (GstMemory *mem, GstMapInfo *info, GstMapFlags flags); gboolean gst_memory_map (GstMemory *mem, GstMapInfo *info, GstMapFlags flags); diff --git a/gst/gstmessage.c b/gst/gstmessage.c index 200a1cd6b1..108a023a9d 100644 --- a/gst/gstmessage.c +++ b/gst/gstmessage.c @@ -239,7 +239,7 @@ static void gst_message_init (GstMessageImpl * message, GstMessageType type, GstObject * src) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (message), _gst_message_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (message), 0, _gst_message_type, (GstMiniObjectCopyFunction) _gst_message_copy, NULL, (GstMiniObjectFreeFunction) _gst_message_free); diff --git a/gst/gstminiobject.c b/gst/gstminiobject.c index 923bd8e957..a526b26927 100644 --- a/gst/gstminiobject.c +++ b/gst/gstminiobject.c @@ -68,6 +68,13 @@ static GstAllocTrace *_gst_mini_object_trace; G_LOCK_DEFINE_STATIC (qdata_mutex); static GQuark weak_ref_quark; +#define SHARE_ONE (1 << 16) +#define SHARE_MASK (~(SHARE_ONE - 1)) +#define LOCK_ONE (GST_LOCK_FLAG_LAST) +#define FLAG_MASK (GST_LOCK_FLAG_LAST - 1) +#define LOCK_MASK ((SHARE_ONE - 1) - FLAG_MASK) +#define LOCK_FLAG_MASK (SHARE_ONE - 1) + typedef struct { GQuark quark; @@ -94,7 +101,7 @@ _priv_gst_mini_object_initialize (void) /** * gst_mini_object_init: (skip) - * @mini_object: a #GstMiniObject + * @mini_object: a #GstMiniObject * @type: the #GType of the mini-object to create * @copy_func: the copy function, or NULL * @dispose_func: the dispose function, or NULL @@ -108,14 +115,16 @@ _priv_gst_mini_object_initialize (void) * Returns: (transfer full): the new mini-object. */ void -gst_mini_object_init (GstMiniObject * mini_object, GType type, +gst_mini_object_init (GstMiniObject * mini_object, guint flags, GType type, GstMiniObjectCopyFunction copy_func, GstMiniObjectDisposeFunction dispose_func, GstMiniObjectFreeFunction free_func) { mini_object->type = type; mini_object->refcount = 1; - mini_object->flags = 0; + mini_object->lockstate = + (flags & GST_MINI_OBJECT_FLAG_LOCK_READONLY ? GST_LOCK_FLAG_READ : 0); + mini_object->flags = flags; mini_object->copy = copy_func; mini_object->dispose = dispose_func; @@ -154,24 +163,138 @@ gst_mini_object_copy (const GstMiniObject * mini_object) return copy; } +/** + * gst_mini_object_lock: + * @object: the mini-object to lock + * @flags: #GstLockFlags + * + * Lock the mini-object with the specified access mode in @flags. + * + * Returns: %TRUE if @object could be locked. + */ +gboolean +gst_mini_object_lock (GstMiniObject * object, GstLockFlags flags) +{ + gint access_mode, state, newstate; + + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (GST_MINI_OBJECT_IS_LOCKABLE (object), FALSE); + + access_mode = flags & FLAG_MASK; + + do { + newstate = state = g_atomic_int_get (&object->lockstate); + + GST_CAT_TRACE (GST_CAT_LOCKING, "lock %p: state %08x, access_mode %d", + object, state, access_mode); + + if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) { + /* shared ref */ + newstate += SHARE_ONE; + access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE; + } + + if (access_mode) { + if ((state & LOCK_FLAG_MASK) == 0) { + /* shared counter > 1 and write access */ + if (state > SHARE_ONE && access_mode & GST_LOCK_FLAG_WRITE) + goto lock_failed; + /* nothing mapped, set access_mode */ + newstate |= access_mode; + } else { + /* access_mode must match */ + if ((state & access_mode) != access_mode) + goto lock_failed; + } + /* increase refcount */ + newstate += LOCK_ONE; + } + } while (!g_atomic_int_compare_and_exchange (&object->lockstate, state, + newstate)); + + return TRUE; + +lock_failed: + { + GST_CAT_DEBUG (GST_CAT_LOCKING, + "lock failed %p: state %08x, access_mode %d", object, state, + access_mode); + return FALSE; + } +} + +/** + * gst_mini_object_unlock: + * @object: the mini-object to unlock + * @flags: #GstLockFlags + * + * Unlock the mini-object with the specified access mode in @flags. + */ +void +gst_mini_object_unlock (GstMiniObject * object, GstLockFlags flags) +{ + gint access_mode, state, newstate; + + g_return_if_fail (object != NULL); + g_return_if_fail (GST_MINI_OBJECT_IS_LOCKABLE (object)); + + access_mode = flags & FLAG_MASK; + + do { + newstate = state = g_atomic_int_get (&object->lockstate); + + GST_CAT_TRACE (GST_CAT_LOCKING, "unlock %p: state %08x, access_mode %d", + object, state, access_mode); + + if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) { + /* shared counter */ + g_return_if_fail (state >= SHARE_ONE); + newstate -= SHARE_ONE; + access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE; + } + + if (access_mode) { + g_return_if_fail ((state & access_mode) == access_mode); + /* decrease the refcount */ + newstate -= LOCK_ONE; + /* last refcount, unset access_mode */ + if ((newstate & LOCK_FLAG_MASK) == access_mode) + newstate &= ~LOCK_FLAG_MASK; + } + } while (!g_atomic_int_compare_and_exchange (&object->lockstate, state, + newstate)); +} + /** * gst_mini_object_is_writable: * @mini_object: the mini-object to check * - * Checks if a mini-object is writable. A mini-object is writable - * if the reference count is one. Modification of a mini-object should - * only be done after verifying that it is writable. + * If @mini_object has the LOCKABLE flag set, check if the current EXCLUSIVE + * lock on @object is the only one, this means that changes to the object will + * not be visible to any other object. * - * MT safe + * If the LOCKABLE flag is not set, check if the refcount of @mini_object is + * exactly 1, meaning that no other reference exists to the object and that the + * object is therefore writable. + * + * Modification of a mini-object should only be done after verifying that it + * is writable. * * Returns: TRUE if the object is writable. */ gboolean gst_mini_object_is_writable (const GstMiniObject * mini_object) { + gboolean result; + g_return_val_if_fail (mini_object != NULL, FALSE); - return (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1); + if (GST_MINI_OBJECT_IS_LOCKABLE (mini_object)) { + result = (g_atomic_int_get (&mini_object->lockstate) & SHARE_MASK) < 2; + } else { + result = (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1); + } + return result; } /** @@ -328,6 +451,10 @@ gst_mini_object_unref (GstMiniObject * mini_object) /* if the subclass recycled the object (and returned FALSE) we don't * want to free the instance anymore */ if (G_LIKELY (do_free)) { + /* there should be no outstanding locks */ + g_return_if_fail ((g_atomic_int_get (&mini_object->lockstate) & LOCK_MASK) + < 4); + if (mini_object->n_qdata) { call_finalize_notify (mini_object); g_free (mini_object->qdata); diff --git a/gst/gstminiobject.h b/gst/gstminiobject.h index 679a362d7e..08f1071030 100644 --- a/gst/gstminiobject.h +++ b/gst/gstminiobject.h @@ -84,6 +84,7 @@ typedef void (*GstMiniObjectNotify) (gpointer user_data, GstMiniObject * obj); * This macro returns the type of the mini-object. */ #define GST_MINI_OBJECT_TYPE(obj) (GST_MINI_OBJECT_CAST(obj)->type) + /** * GST_MINI_OBJECT_FLAGS: * @obj: MiniObject to return flags for. @@ -118,16 +119,55 @@ typedef void (*GstMiniObjectNotify) (gpointer user_data, GstMiniObject * obj); /** * GstMiniObjectFlags: + * @GST_MINI_OBJECT_FLAG_LOCKABLE: the object can be locked and unlocked with + * gst_mini_object_lock() and gst_mini_object_unlock(). + * @GST_MINI_OBJECT_FLAG_LOCK_READONLY: the object is permanently locked in + * READONLY mode. Only read locks can be performed on the object. * @GST_MINI_OBJECT_FLAG_LAST: first flag that can be used by subclasses. * * Flags for the mini object */ typedef enum { + GST_MINI_OBJECT_FLAG_LOCKABLE = (1 << 0), + GST_MINI_OBJECT_FLAG_LOCK_READONLY = (1 << 1), /* padding */ - GST_MINI_OBJECT_FLAG_LAST = (1<<4) + GST_MINI_OBJECT_FLAG_LAST = (1 << 4) } GstMiniObjectFlags; +/** + * GST_MINI_OBJECT_IS_LOCKABLE: + * @obj: a #GstMiniObject + * + * Check if @obj is lockable. A lockable object can be locked and unlocked with + * gst_mini_object_lock() and gst_mini_object_unlock(). + */ +#define GST_MINI_OBJECT_IS_LOCKABLE(obj) GST_MINI_OBJECT_FLAG_IS_SET(obj, GST_MINI_OBJECT_FLAG_LOCKABLE) + +/** + * GstLockFlags: + * @GST_LOCK_FLAG_READ: lock for read access + * @GST_LOCK_FLAG_WRITE: lock for write access + * @GST_LOCK_FLAG_EXCLUSIVE: lock for exclusive access + * @GST_LOCK_FLAG_LAST: first flag that can be used for custom purposes + * + * Flags used when locking miniobjects + */ +typedef enum { + GST_LOCK_FLAG_READ = (1 << 0), + GST_LOCK_FLAG_WRITE = (1 << 1), + GST_LOCK_FLAG_EXCLUSIVE = (1 << 2), + + GST_LOCK_FLAG_LAST = (1 << 4) +} GstLockFlags; + +/** + * GST_LOCK_FLAG_READWRITE: + * + * GstLockFlags value alias for GST_LOCK_FLAG_READ | GST_LOCK_FLAG_WRITE + */ +#define GST_LOCK_FLAG_READWRITE (GST_LOCK_FLAG_READ | GST_LOCK_FLAG_WRITE) + /** * GST_MINI_OBJECT_REFCOUNT: * @obj: a #GstMiniObject @@ -147,6 +187,7 @@ typedef enum * GstMiniObject: * @type: the GType of the object * @refcount: atomic refcount + * @lockstate: atomic state of the locks * @flags: extra flags. * @copy: a copy function * @dispose: a dispose function @@ -163,6 +204,7 @@ struct _GstMiniObject { /*< public >*/ /* with COW */ gint refcount; + gint lockstate; guint flags; GstMiniObjectCopyFunction copy; @@ -175,14 +217,12 @@ struct _GstMiniObject { gpointer qdata; }; -void gst_mini_object_init (GstMiniObject *mini_object, GType type, +void gst_mini_object_init (GstMiniObject *mini_object, + guint flags, GType type, GstMiniObjectCopyFunction copy_func, GstMiniObjectDisposeFunction dispose_func, GstMiniObjectFreeFunction free_func); -GstMiniObject * gst_mini_object_copy (const GstMiniObject *mini_object) G_GNUC_MALLOC; -gboolean gst_mini_object_is_writable (const GstMiniObject *mini_object); -GstMiniObject * gst_mini_object_make_writable (GstMiniObject *mini_object); /* refcounting */ GstMiniObject * gst_mini_object_ref (GstMiniObject *mini_object); @@ -195,6 +235,17 @@ void gst_mini_object_weak_unref (GstMiniObject *object, GstMiniObjectNotify notify, gpointer data); +/* locking */ +gboolean gst_mini_object_lock (GstMiniObject *object, GstLockFlags flags); +void gst_mini_object_unlock (GstMiniObject *object, GstLockFlags flags); + +gboolean gst_mini_object_is_writable (const GstMiniObject *mini_object); +GstMiniObject * gst_mini_object_make_writable (GstMiniObject *mini_object); + +/* copy */ +GstMiniObject * gst_mini_object_copy (const GstMiniObject *mini_object) G_GNUC_MALLOC; + + void gst_mini_object_set_qdata (GstMiniObject *object, GQuark quark, gpointer data, GDestroyNotify destroy); gpointer gst_mini_object_get_qdata (GstMiniObject *object, GQuark quark); diff --git a/gst/gstquery.c b/gst/gstquery.c index 6387c593ec..c596547969 100644 --- a/gst/gstquery.c +++ b/gst/gstquery.c @@ -692,7 +692,7 @@ gst_query_new_custom (GstQueryType type, GstStructure * structure) goto had_parent; } - gst_mini_object_init (GST_MINI_OBJECT_CAST (query), _gst_query_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (query), 0, _gst_query_type, (GstMiniObjectCopyFunction) _gst_query_copy, NULL, (GstMiniObjectFreeFunction) _gst_query_free); diff --git a/gst/gstsample.c b/gst/gstsample.c index 4e4a36be5a..1d9a62c00b 100644 --- a/gst/gstsample.c +++ b/gst/gstsample.c @@ -103,7 +103,7 @@ gst_sample_new (GstBuffer * buffer, GstCaps * caps, const GstSegment * segment, GST_LOG ("new %p", sample); - gst_mini_object_init (GST_MINI_OBJECT_CAST (sample), _gst_sample_type, + gst_mini_object_init (GST_MINI_OBJECT_CAST (sample), 0, _gst_sample_type, (GstMiniObjectCopyFunction) _gst_sample_copy, NULL, (GstMiniObjectFreeFunction) _gst_sample_free); diff --git a/gst/gsttaglist.c b/gst/gsttaglist.c index eecc8a11e7..490061a664 100644 --- a/gst/gsttaglist.c +++ b/gst/gsttaglist.c @@ -665,7 +665,7 @@ gst_tag_list_new_internal (GstStructure * s) tag_list = (GstTagList *) g_slice_new (GstTagListImpl); - gst_mini_object_init (GST_MINI_OBJECT_CAST (tag_list), GST_TYPE_TAG_LIST, + gst_mini_object_init (GST_MINI_OBJECT_CAST (tag_list), 0, GST_TYPE_TAG_LIST, (GstMiniObjectCopyFunction) __gst_tag_list_copy, NULL, (GstMiniObjectFreeFunction) __gst_tag_list_free); diff --git a/gst/gsttoc.c b/gst/gsttoc.c index 16cb7952b7..2d58251e04 100644 --- a/gst/gsttoc.c +++ b/gst/gsttoc.c @@ -99,7 +99,7 @@ gst_toc_new (void) toc = g_slice_new0 (GstToc); - gst_mini_object_init (GST_MINI_OBJECT_CAST (toc), GST_TYPE_TOC, + gst_mini_object_init (GST_MINI_OBJECT_CAST (toc), 0, GST_TYPE_TOC, (GstMiniObjectCopyFunction) gst_toc_copy, NULL, (GstMiniObjectFreeFunction) gst_toc_free); @@ -117,7 +117,7 @@ gst_toc_entry_new_internal (GstTocEntryType type, const gchar * uid, entry = g_slice_new0 (GstTocEntry); - gst_mini_object_init (GST_MINI_OBJECT_CAST (entry), GST_TYPE_TOC_ENTRY, + gst_mini_object_init (GST_MINI_OBJECT_CAST (entry), 0, GST_TYPE_TOC_ENTRY, (GstMiniObjectCopyFunction) gst_toc_entry_copy, NULL, (GstMiniObjectFreeFunction) gst_toc_entry_free); diff --git a/tests/check/gst/gstmemory.c b/tests/check/gst/gstmemory.c index 5d594dc1b2..b64f786db2 100644 --- a/tests/check/gst/gstmemory.c +++ b/tests/check/gst/gstmemory.c @@ -428,7 +428,6 @@ GST_START_TEST (test_map_nested) fail_unless (gst_memory_map (mem, &info2, GST_MAP_READ)); gst_memory_unmap (mem, &info2); gst_memory_unmap (mem, &info1); - fail_unless (mem->state == 0); fail_unless (gst_memory_map (mem, &info1, GST_MAP_WRITE)); /* not allowed */ diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index 4133f39b81..4c03f13231 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -10,6 +10,7 @@ EXPORTS GST_CAT_ERROR_SYSTEM DATA GST_CAT_EVENT DATA GST_CAT_GST_INIT DATA + GST_CAT_LOCKING DATA GST_CAT_MEMORY DATA GST_CAT_MESSAGE DATA GST_CAT_META DATA @@ -518,15 +519,12 @@ EXPORTS gst_memory_flags_get_type gst_memory_get_sizes gst_memory_get_type - gst_memory_is_exclusive gst_memory_is_span - gst_memory_lock gst_memory_make_mapped gst_memory_map gst_memory_new_wrapped gst_memory_resize gst_memory_share - gst_memory_unlock gst_memory_unmap gst_message_get_seqnum gst_message_get_stream_status_object @@ -605,6 +603,7 @@ EXPORTS gst_mini_object_get_qdata gst_mini_object_init gst_mini_object_is_writable + gst_mini_object_lock gst_mini_object_make_writable gst_mini_object_ref gst_mini_object_replace @@ -612,6 +611,7 @@ EXPORTS gst_mini_object_steal gst_mini_object_steal_qdata gst_mini_object_take + gst_mini_object_unlock gst_mini_object_unref gst_mini_object_weak_ref gst_mini_object_weak_unref