1 /* GStreamer
   2  * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.be>
   3  *
   4  * gstallocator.c: memory block allocator
   5  *
   6  * This library is free software; you can redistribute it and/or
   7  * modify it under the terms of the GNU Library General Public
   8  * License as published by the Free Software Foundation; either
   9  * version 2 of the License, or (at your option) any later version.
  10  *
  11  * This library is distributed in the hope that it will be useful,
  12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14  * Library General Public License for more details.
  15  *
  16  * You should have received a copy of the GNU Library General Public
  17  * License along with this library; if not, write to the
  18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  19  * Boston, MA 02110-1301, USA.
  20  */
  21 
  22 /**
  23  * SECTION:gstallocator
  24  * @short_description: allocate memory blocks
  25  * @see_also: #GstMemory
  26  *
  27  * Memory is usually created by allocators with a gst_allocator_alloc()
  28  * method call. When %NULL is used as the allocator, the default allocator will
  29  * be used.
  30  *
  31  * New allocators can be registered with gst_allocator_register().
  32  * Allocators are identified by name and can be retrieved with
  33  * gst_allocator_find(). gst_allocator_set_default() can be used to change the
  34  * default allocator.
  35  *
  36  * New memory can be created with gst_memory_new_wrapped() that wraps the memory
  37  * allocated elsewhere.
  38  */
  39 
  40 #ifdef HAVE_CONFIG_H
  41 #include "config.h"
  42 #endif
  43 
  44 #include "gst_private.h"
  45 #include "gstmemory.h"
  46 
  47 GST_DEBUG_CATEGORY_STATIC (gst_allocator_debug);
  48 #define GST_CAT_DEFAULT gst_allocator_debug
  49 
  50 #define GST_ALLOCATOR_GET_PRIVATE(obj)  \
  51      (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_ALLOCATOR, GstAllocatorPrivate))
  52 
  53 struct _GstAllocatorPrivate
  54 {
  55   gpointer dummy;
  56 };
  57 
  58 #ifdef GSTREAMER_LITE
  59 // In GStLite we require to have memory aligned to 16 bytes for RAW video.
  60 // For now lets have all memory 16 bytes aligned.
  61 gsize gst_memory_alignment = 15;
  62 #else // GSTREAMER_LITE
  63 #if defined(MEMORY_ALIGNMENT_MALLOC)
  64 gsize gst_memory_alignment = 7;
  65 #elif defined(MEMORY_ALIGNMENT_PAGESIZE)
  66 /* we fill this in in the _init method */
  67 gsize gst_memory_alignment = 0;
  68 #elif defined(MEMORY_ALIGNMENT)
  69 gsize gst_memory_alignment = MEMORY_ALIGNMENT - 1;
  70 #else
  71 #error "No memory alignment configured"
  72 gsize gst_memory_alignment = 0;
  73 #endif
  74 #endif // GSTREAMER_LITE
  75 
  76 /* the default allocator */
  77 static GstAllocator *_default_allocator;
  78 
  79 static GstAllocator *_sysmem_allocator;
  80 
  81 /* registered allocators */
  82 static GRWLock lock;
  83 static GHashTable *allocators;
  84 
  85 G_DEFINE_ABSTRACT_TYPE (GstAllocator, gst_allocator, GST_TYPE_OBJECT);
  86 
  87 static void
  88 gst_allocator_class_init (GstAllocatorClass * klass)
  89 {
  90   g_type_class_add_private (klass, sizeof (GstAllocatorPrivate));
  91 
  92   GST_DEBUG_CATEGORY_INIT (gst_allocator_debug, "allocator", 0,
  93       "allocator debug");
  94 }
  95 
  96 static GstMemory *
  97 _fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
  98 {
  99   GstMemory *copy;
 100   GstMapInfo sinfo, dinfo;
 101   GstAllocationParams params = { 0, mem->align, 0, 0, };
 102   GstAllocator *allocator;
 103 
 104   if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
 105     return NULL;
 106 
 107   if (size == -1)
 108     size = sinfo.size > offset ? sinfo.size - offset : 0;
 109 
 110   /* use the same allocator as the memory we copy  */
 111   allocator = mem->allocator;
 112   if (GST_OBJECT_FLAG_IS_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC))
 113     allocator = NULL;
 114   copy = gst_allocator_alloc (allocator, size, &params);
 115 
 116   if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
 117     GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy);
 118     gst_allocator_free (mem->allocator, copy);
 119     gst_memory_unmap (mem, &sinfo);
 120     return NULL;
 121   }
 122 
 123   GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
 124       "memcpy %" G_GSSIZE_FORMAT " memory %p -> %p", size, mem, copy);
 125   memcpy (dinfo.data, sinfo.data + offset, size);
 126   gst_memory_unmap (copy, &dinfo);
 127   gst_memory_unmap (mem, &sinfo);
 128 
 129   return copy;
 130 }
 131 
 132 static gboolean
 133 _fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
 134 {
 135   return FALSE;
 136 }
 137 
 138 static void
 139 gst_allocator_init (GstAllocator * allocator)
 140 {
 141   allocator->priv = GST_ALLOCATOR_GET_PRIVATE (allocator);
 142 
 143   allocator->mem_copy = _fallback_mem_copy;
 144   allocator->mem_is_span = _fallback_mem_is_span;
 145 }
 146 
 147 G_DEFINE_BOXED_TYPE (GstAllocationParams, gst_allocation_params,
 148     (GBoxedCopyFunc) gst_allocation_params_copy,
 149     (GBoxedFreeFunc) gst_allocation_params_free);
 150 
 151 /**
 152  * gst_allocation_params_init:
 153  * @params: a #GstAllocationParams
 154  *
 155  * Initialize @params to its default values
 156  */
 157 void
 158 gst_allocation_params_init (GstAllocationParams * params)
 159 {
 160   g_return_if_fail (params != NULL);
 161 
 162   memset (params, 0, sizeof (GstAllocationParams));
 163 }
 164 
 165 /**
 166  * gst_allocation_params_copy:
 167  * @params: (transfer none): a #GstAllocationParams
 168  *
 169  * Create a copy of @params.
 170  *
 171  * Free-function: gst_allocation_params_free
 172  *
 173  * Returns: (transfer full): a new ##GstAllocationParams, free with
 174  * gst_allocation_params_free().
 175  */
 176 GstAllocationParams *
 177 gst_allocation_params_copy (const GstAllocationParams * params)
 178 {
 179   GstAllocationParams *result = NULL;
 180 
 181   if (params) {
 182     result =
 183         (GstAllocationParams *) g_slice_copy (sizeof (GstAllocationParams),
 184         params);
 185   }
 186   return result;
 187 }
 188 
 189 /**
 190  * gst_allocation_params_free:
 191  * @params: (in) (transfer full): a #GstAllocationParams
 192  *
 193  * Free @params
 194  */
 195 void
 196 gst_allocation_params_free (GstAllocationParams * params)
 197 {
 198   g_slice_free (GstAllocationParams, params);
 199 }
 200 
 201 /**
 202  * gst_allocator_register:
 203  * @name: the name of the allocator
 204  * @allocator: (transfer full): #GstAllocator
 205  *
 206  * Registers the memory @allocator with @name. This function takes ownership of
 207  * @allocator.
 208  */
 209 void
 210 gst_allocator_register (const gchar * name, GstAllocator * allocator)
 211 {
 212   g_return_if_fail (name != NULL);
 213   g_return_if_fail (allocator != NULL);
 214 
 215   GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"",
 216       allocator, name);
 217 
 218   g_rw_lock_writer_lock (&lock);
 219   g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
 220   g_rw_lock_writer_unlock (&lock);
 221 }
 222 
 223 /**
 224  * gst_allocator_find:
 225  * @name: (allow-none): the name of the allocator
 226  *
 227  * Find a previously registered allocator with @name. When @name is %NULL, the
 228  * default allocator will be returned.
 229  *
 230  * Returns: (transfer full) (nullable): a #GstAllocator or %NULL when
 231  * the allocator with @name was not registered. Use gst_object_unref()
 232  * to release the allocator after usage.
 233  */
 234 GstAllocator *
 235 gst_allocator_find (const gchar * name)
 236 {
 237   GstAllocator *allocator;
 238 
 239   g_rw_lock_reader_lock (&lock);
 240   if (name) {
 241     allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
 242   } else {
 243     allocator = _default_allocator;
 244   }
 245   if (allocator)
 246     gst_object_ref (allocator);
 247   g_rw_lock_reader_unlock (&lock);
 248 
 249   return allocator;
 250 }
 251 
 252 /**
 253  * gst_allocator_set_default:
 254  * @allocator: (transfer full): a #GstAllocator
 255  *
 256  * Set the default allocator. This function takes ownership of @allocator.
 257  */
 258 void
 259 gst_allocator_set_default (GstAllocator * allocator)
 260 {
 261   GstAllocator *old;
 262 
 263   g_return_if_fail (GST_IS_ALLOCATOR (allocator));
 264 
 265   g_rw_lock_writer_lock (&lock);
 266   old = _default_allocator;
 267   _default_allocator = allocator;
 268   g_rw_lock_writer_unlock (&lock);
 269 
 270   if (old)
 271     gst_object_unref (old);
 272 }
 273 
 274 /**
 275  * gst_allocator_alloc:
 276  * @allocator: (transfer none) (allow-none): a #GstAllocator to use
 277  * @size: size of the visible memory area
 278  * @params: (transfer none) (allow-none): optional parameters
 279  *
 280  * Use @allocator to allocate a new memory block with memory that is at least
 281  * @size big.
 282  *
 283  * The optional @params can specify the prefix and padding for the memory. If
 284  * %NULL is passed, no flags, no extra prefix/padding and a default alignment is
 285  * used.
 286  *
 287  * The prefix/padding will be filled with 0 if flags contains
 288  * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
 289  *
 290  * When @allocator is %NULL, the default allocator will be used.
 291  *
 292  * The alignment in @params is given as a bitmask so that @align + 1 equals
 293  * the amount of bytes to align to. For example, to align to 8 bytes,
 294  * use an alignment of 7.
 295  *
 296  * Returns: (transfer full): a new #GstMemory.
 297  */
 298 GstMemory *
 299 gst_allocator_alloc (GstAllocator * allocator, gsize size,
 300     GstAllocationParams * params)
 301 {
 302   GstMemory *mem;
 303   static GstAllocationParams defparams = { 0, 0, 0, 0, };
 304   GstAllocatorClass *aclass;
 305 
 306   if (params) {
 307     g_return_val_if_fail (((params->align + 1) & params->align) == 0, NULL);
 308   } else {
 309     params = &defparams;
 310   }
 311 
 312   if (allocator == NULL)
 313     allocator = _default_allocator;
 314 
 315   aclass = GST_ALLOCATOR_GET_CLASS (allocator);
 316   if (aclass->alloc)
 317     mem = aclass->alloc (allocator, size, params);
 318   else
 319     mem = NULL;
 320 
 321   return mem;
 322 }
 323 
 324 /**
 325  * gst_allocator_free:
 326  * @allocator: (transfer none): a #GstAllocator to use
 327  * @memory: (transfer full): the memory to free
 328  *
 329  * Free @memory that was previously allocated with gst_allocator_alloc().
 330  */
 331 void
 332 gst_allocator_free (GstAllocator * allocator, GstMemory * memory)
 333 {
 334   GstAllocatorClass *aclass;
 335 
 336   g_return_if_fail (GST_IS_ALLOCATOR (allocator));
 337   g_return_if_fail (memory != NULL);
 338   g_return_if_fail (memory->allocator == allocator);
 339 
 340   aclass = GST_ALLOCATOR_GET_CLASS (allocator);
 341   if (aclass->free)
 342     aclass->free (allocator, memory);
 343 }
 344 
 345 /* default memory implementation */
 346 typedef struct
 347 {
 348   GstMemory mem;
 349 
 350   gsize slice_size;
 351   guint8 *data;
 352 
 353   gpointer user_data;
 354   GDestroyNotify notify;
 355 } GstMemorySystem;
 356 
 357 typedef struct
 358 {
 359   GstAllocator parent;
 360 } GstAllocatorSysmem;
 361 
 362 typedef struct
 363 {
 364   GstAllocatorClass parent_class;
 365 } GstAllocatorSysmemClass;
 366 
 367 GType gst_allocator_sysmem_get_type (void);
 368 G_DEFINE_TYPE (GstAllocatorSysmem, gst_allocator_sysmem, GST_TYPE_ALLOCATOR);
 369 
 370 /* initialize the fields */
 371 static inline void
 372 _sysmem_init (GstMemorySystem * mem, GstMemoryFlags flags,
 373     GstMemory * parent, gsize slice_size,
 374     gpointer data, gsize maxsize, gsize align, gsize offset, gsize size,
 375     gpointer user_data, GDestroyNotify notify)
 376 {
 377   gst_memory_init (GST_MEMORY_CAST (mem),
 378       flags, _sysmem_allocator, parent, maxsize, align, offset, size);
 379 
 380   mem->slice_size = slice_size;
 381   mem->data = data;
 382   mem->user_data = user_data;
 383   mem->notify = notify;
 384 }
 385 
 386 /* create a new memory block that manages the given memory */
 387 static inline GstMemorySystem *
 388 _sysmem_new (GstMemoryFlags flags,
 389     GstMemory * parent, gpointer data, gsize maxsize, gsize align, gsize offset,
 390     gsize size, gpointer user_data, GDestroyNotify notify)
 391 {
 392   GstMemorySystem *mem;
 393   gsize slice_size;
 394 
 395   slice_size = sizeof (GstMemorySystem);
 396 
 397   mem = g_slice_alloc (slice_size);
 398   _sysmem_init (mem, flags, parent, slice_size,
 399       data, maxsize, align, offset, size, user_data, notify);
 400 
 401   return mem;
 402 }
 403 
 404 /* allocate the memory and structure in one block */
 405 static GstMemorySystem *
 406 _sysmem_new_block (GstMemoryFlags flags,
 407     gsize maxsize, gsize align, gsize offset, gsize size)
 408 {
 409   GstMemorySystem *mem;
 410   gsize aoffset, slice_size, padding;
 411   guint8 *data;
 412 
 413   /* ensure configured alignment */
 414   align |= gst_memory_alignment;
 415   /* allocate more to compensate for alignment */
 416   maxsize += align;
 417   /* alloc header and data in one block */
 418   slice_size = sizeof (GstMemorySystem) + maxsize;
 419 
 420   mem = g_slice_alloc (slice_size);
 421   if (mem == NULL)
 422     return NULL;
 423 
 424   data = (guint8 *) mem + sizeof (GstMemorySystem);
 425 
 426   /* do alignment */
 427   if ((aoffset = ((guintptr) data & align))) {
 428     aoffset = (align + 1) - aoffset;
 429     data += aoffset;
 430     maxsize -= aoffset;
 431   }
 432 
 433   if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
 434     memset (data, 0, offset);
 435 
 436   padding = maxsize - (offset + size);
 437   if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
 438     memset (data + offset + size, 0, padding);
 439 
 440   _sysmem_init (mem, flags, NULL, slice_size, data, maxsize,
 441       align, offset, size, NULL, NULL);
 442 
 443   return mem;
 444 }
 445 
 446 static gpointer
 447 _sysmem_map (GstMemorySystem * mem, gsize maxsize, GstMapFlags flags)
 448 {
 449   return mem->data;
 450 }
 451 
 452 static gboolean
 453 _sysmem_unmap (GstMemorySystem * mem)
 454 {
 455   return TRUE;
 456 }
 457 
 458 static GstMemorySystem *
 459 _sysmem_copy (GstMemorySystem * mem, gssize offset, gsize size)
 460 {
 461   GstMemorySystem *copy;
 462 
 463   if (size == -1)
 464     size = mem->mem.size > offset ? mem->mem.size - offset : 0;
 465 
 466   copy = _sysmem_new_block (0, size, mem->mem.align, 0, size);
 467   GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
 468       "memcpy %" G_GSIZE_FORMAT " memory %p -> %p", size, mem, copy);
 469   memcpy (copy->data, mem->data + mem->mem.offset + offset, size);
 470 
 471   return copy;
 472 }
 473 
 474 static GstMemorySystem *
 475 _sysmem_share (GstMemorySystem * mem, gssize offset, gsize size)
 476 {
 477   GstMemorySystem *sub;
 478   GstMemory *parent;
 479 
 480   /* find the real parent */
 481   if ((parent = mem->mem.parent) == NULL)
 482     parent = (GstMemory *) mem;
 483 
 484   if (size == -1)
 485     size = mem->mem.size - offset;
 486 
 487   /* the shared memory is always readonly */
 488   sub =
 489       _sysmem_new (GST_MINI_OBJECT_FLAGS (parent) |
 490       GST_MINI_OBJECT_FLAG_LOCK_READONLY, parent, mem->data, mem->mem.maxsize,
 491       mem->mem.align, mem->mem.offset + offset, size, NULL, NULL);
 492 
 493   return sub;
 494 }
 495 
 496 static gboolean
 497 _sysmem_is_span (GstMemorySystem * mem1, GstMemorySystem * mem2, gsize * offset)
 498 {
 499 
 500   if (offset) {
 501     GstMemorySystem *parent;
 502 
 503     parent = (GstMemorySystem *) mem1->mem.parent;
 504 
 505     *offset = mem1->mem.offset - parent->mem.offset;
 506   }
 507 
 508   /* and memory is contiguous */
 509   return mem1->data + mem1->mem.offset + mem1->mem.size ==
 510       mem2->data + mem2->mem.offset;
 511 }
 512 
 513 static GstMemory *
 514 default_alloc (GstAllocator * allocator, gsize size,
 515     GstAllocationParams * params)
 516 {
 517   gsize maxsize = size + params->prefix + params->padding;
 518 
 519   return (GstMemory *) _sysmem_new_block (params->flags,
 520       maxsize, params->align, params->prefix, size);
 521 }
 522 
 523 static void
 524 default_free (GstAllocator * allocator, GstMemory * mem)
 525 {
 526   GstMemorySystem *dmem = (GstMemorySystem *) mem;
 527   gsize slice_size;
 528 
 529   if (dmem->notify)
 530     dmem->notify (dmem->user_data);
 531 
 532   slice_size = dmem->slice_size;
 533 
 534 #ifdef USE_POISONING
 535   /* just poison the structs, not all the data */
 536   memset (mem, 0xff, sizeof (GstMemorySystem));
 537 #endif
 538 
 539   g_slice_free1 (slice_size, mem);
 540 }
 541 
 542 static void
 543 gst_allocator_sysmem_finalize (GObject * obj)
 544 {
 545   g_warning ("The default memory allocator was freed!");
 546 }
 547 
 548 static void
 549 gst_allocator_sysmem_class_init (GstAllocatorSysmemClass * klass)
 550 {
 551   GObjectClass *gobject_class;
 552   GstAllocatorClass *allocator_class;
 553 
 554   gobject_class = (GObjectClass *) klass;
 555   allocator_class = (GstAllocatorClass *) klass;
 556 
 557   gobject_class->finalize = gst_allocator_sysmem_finalize;
 558 
 559   allocator_class->alloc = default_alloc;
 560   allocator_class->free = default_free;
 561 }
 562 
 563 static void
 564 gst_allocator_sysmem_init (GstAllocatorSysmem * allocator)
 565 {
 566   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
 567 
 568   GST_CAT_DEBUG (GST_CAT_MEMORY, "init allocator %p", allocator);
 569 
 570   alloc->mem_type = GST_ALLOCATOR_SYSMEM;
 571   alloc->mem_map = (GstMemoryMapFunction) _sysmem_map;
 572   alloc->mem_unmap = (GstMemoryUnmapFunction) _sysmem_unmap;
 573   alloc->mem_copy = (GstMemoryCopyFunction) _sysmem_copy;
 574   alloc->mem_share = (GstMemoryShareFunction) _sysmem_share;
 575   alloc->mem_is_span = (GstMemoryIsSpanFunction) _sysmem_is_span;
 576 }
 577 
 578 void
 579 _priv_gst_allocator_initialize (void)
 580 {
 581   g_rw_lock_init (&lock);
 582   allocators = g_hash_table_new (g_str_hash, g_str_equal);
 583 
 584 #ifdef HAVE_GETPAGESIZE
 585 #ifdef MEMORY_ALIGNMENT_PAGESIZE
 586   gst_memory_alignment = getpagesize () - 1;
 587 #endif
 588 #endif
 589 
 590   GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT,
 591       gst_memory_alignment);
 592 
 593   _sysmem_allocator = g_object_new (gst_allocator_sysmem_get_type (), NULL);
 594 
 595   gst_allocator_register (GST_ALLOCATOR_SYSMEM,
 596       gst_object_ref (_sysmem_allocator));
 597 
 598   _default_allocator = gst_object_ref (_sysmem_allocator);
 599 }
 600 
 601 /**
 602  * gst_memory_new_wrapped:
 603  * @flags: #GstMemoryFlags
 604  * @data: (array length=size) (element-type guint8) (transfer none): data to
 605  *   wrap
 606  * @maxsize: allocated size of @data
 607  * @offset: offset in @data
 608  * @size: size of valid data
 609  * @user_data: (allow-none): user_data
 610  * @notify: (allow-none) (scope async) (closure user_data): called with @user_data when the memory is freed
 611  *
 612  * Allocate a new memory block that wraps the given @data.
 613  *
 614  * The prefix/padding must be filled with 0 if @flags contains
 615  * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
 616  *
 617  * Returns: (transfer full): a new #GstMemory.
 618  */
 619 GstMemory *
 620 gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
 621     gsize maxsize, gsize offset, gsize size, gpointer user_data,
 622     GDestroyNotify notify)
 623 {
 624   GstMemorySystem *mem;
 625 
 626   g_return_val_if_fail (data != NULL, NULL);
 627   g_return_val_if_fail (offset + size <= maxsize, NULL);
 628 
 629   mem =
 630       _sysmem_new (flags, NULL, data, maxsize, 0, offset, size, user_data,
 631       notify);
 632 
 633   return (GstMemory *) mem;
 634 }