fnlz_mlc.c raw

   1  /*
   2   * Copyright (c) 2011 by Hewlett-Packard Company.  All rights reserved.
   3   *
   4   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   5   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   6   *
   7   * Permission is hereby granted to use or copy this program
   8   * for any purpose, provided the above notices are retained on all copies.
   9   * Permission to modify the code and to distribute modified code is granted,
  10   * provided the above notices are retained, and a notice that the code was
  11   * modified is included with the above copyright notice.
  12   */
  13  
  14  #include "private/gc_priv.h"
  15  
  16  #ifdef ENABLE_DISCLAIM
  17  
  18  #  include "gc/gc_disclaim.h"
  19  #  include "private/dbg_mlc.h" /*< for `oh` type */
  20  
  21  #  if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
  22  /* The first bit is already used for a debug purpose. */
  23  #    define FINALIZER_CLOSURE_FLAG 0x2
  24  #  else
  25  #    define FINALIZER_CLOSURE_FLAG 0x1
  26  #  endif
  27  
  28  STATIC int GC_CALLBACK
  29  GC_finalized_disclaim(void *obj)
  30  {
  31  #  ifdef AO_HAVE_load
  32    ptr_t fc_p = GC_cptr_load((volatile ptr_t *)obj);
  33  #  else
  34    ptr_t fc_p = *(ptr_t *)obj;
  35  #  endif
  36  
  37    if ((ADDR(fc_p) & FINALIZER_CLOSURE_FLAG) != 0) {
  38      /*
  39       * The disclaim function may be passed fragments from the free-list,
  40       * on which it should not run finalization.  To recognize this case,
  41       * we use the fact that the value of the first pointer of such
  42       * fragments is always, at least, multiple of a pointer size (a link
  43       * to the next fragment, or `NULL`).
  44       *
  45       * Note: if it is desirable to have a finalizer which does not use
  46       * the first pointer for storing the finalization information,
  47       * `GC_disclaim_and_reclaim()` must be extended to clear fragments
  48       * so that the assumption holds for the selected pointer location.
  49       */
  50      const struct GC_finalizer_closure *fc
  51          = (struct GC_finalizer_closure *)CPTR_CLEAR_FLAGS(
  52              fc_p, FINALIZER_CLOSURE_FLAG);
  53  
  54      GC_ASSERT(!GC_find_leak_inner);
  55      fc->proc((ptr_t *)obj + 1, fc->cd);
  56    }
  57    return 0;
  58  }
  59  
  60  STATIC void
  61  GC_register_disclaim_proc_inner(unsigned kind, GC_disclaim_proc proc,
  62                                  GC_bool mark_unconditionally)
  63  {
  64    GC_ASSERT(kind < MAXOBJKINDS);
  65    if (UNLIKELY(GC_find_leak_inner))
  66      return;
  67  
  68    GC_obj_kinds[kind].ok_disclaim_proc = proc;
  69    GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally;
  70  }
  71  
  72  GC_API void GC_CALL
  73  GC_init_finalized_malloc(void)
  74  {
  75    /* Initialize the collector just in case it is not done yet. */
  76    GC_init();
  77  
  78    LOCK();
  79    if (GC_finalized_kind != 0) {
  80      UNLOCK();
  81      return;
  82    }
  83  
  84    /*
  85     * The finalizer closure is placed in the first pointer of the
  86     * object in order to use the lower bits to distinguish live
  87     * objects from objects on the free list.  The downside of this is
  88     * that we need one-pointer offset interior pointers, and that
  89     * `GC_base()` does not return the start of the user region.
  90     */
  91    GC_register_displacement_inner(sizeof(ptr_t));
  92  
  93    /*
  94     * And, the pointer to the finalizer closure object itself is displaced
  95     * due to baking in this indicator.
  96     */
  97    GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG);
  98    GC_register_displacement_inner(sizeof(oh) | FINALIZER_CLOSURE_FLAG);
  99  
 100    GC_finalized_kind
 101        = GC_new_kind_inner(GC_new_free_list_inner(), GC_DS_LENGTH, TRUE, TRUE);
 102    GC_ASSERT(GC_finalized_kind != 0);
 103    GC_register_disclaim_proc_inner(GC_finalized_kind, GC_finalized_disclaim,
 104                                    TRUE);
 105    UNLOCK();
 106  }
 107  
 108  GC_API void GC_CALL
 109  GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
 110                            int mark_unconditionally)
 111  {
 112    LOCK();
 113    GC_register_disclaim_proc_inner((unsigned)kind, proc,
 114                                    (GC_bool)mark_unconditionally);
 115    UNLOCK();
 116  }
 117  
 118  GC_API GC_ATTR_MALLOC void *GC_CALL
 119  GC_finalized_malloc(size_t lb, const struct GC_finalizer_closure *fclos)
 120  {
 121    void *op;
 122    ptr_t fc_p;
 123  
 124  #  ifndef LINT2
 125    /* Actually, there is no data race because the variable is set once. */
 126    GC_ASSERT(GC_finalized_kind != 0);
 127  #  endif
 128    GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos));
 129    GC_ASSERT((ADDR(fclos) & FINALIZER_CLOSURE_FLAG) == 0);
 130    op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(ptr_t)),
 131                        (int)GC_finalized_kind);
 132    if (UNLIKELY(NULL == op))
 133      return NULL;
 134  
 135    /*
 136     * Set the flag (w/o conversion to a numeric type) and store
 137     * the finalizer closure.
 138     */
 139    fc_p = CPTR_SET_FLAGS(GC_CAST_AWAY_CONST_PVOID(fclos),
 140                          FINALIZER_CLOSURE_FLAG);
 141  #  ifdef AO_HAVE_store
 142    GC_cptr_store((volatile ptr_t *)op, fc_p);
 143  #  else
 144    *(ptr_t *)op = fc_p;
 145  #  endif
 146    GC_dirty(op);
 147    REACHABLE_AFTER_DIRTY(fc_p);
 148    return (ptr_t *)op + 1;
 149  }
 150  
 151  #endif /* ENABLE_DISCLAIM */
 152