Home | History | Annotate | Line # | Download | only in libobjc
      1 /* Sparse Arrays for Objective C dispatch tables
      2    Copyright (C) 1993-2022 Free Software Foundation, Inc.
      3 
      4 This file is part of GCC.
      5 
      6 GCC is free software; you can redistribute it and/or modify
      7 it under the terms of the GNU General Public License as published by
      8 the Free Software Foundation; either version 3, or (at your option)
      9 any later version.
     10 
     11 GCC 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
     14 GNU General Public License for more details.
     15 
     16 Under Section 7 of GPL version 3, you are granted additional
     17 permissions described in the GCC Runtime Library Exception, version
     18 3.1, as published by the Free Software Foundation.
     19 
     20 You should have received a copy of the GNU General Public License and
     21 a copy of the GCC Runtime Library Exception along with this program;
     22 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23 <http://www.gnu.org/licenses/>.  */
     24 
     25 #include "objc-private/common.h"
     26 #include "objc-private/sarray.h"
     27 #include "objc/runtime.h" /* For objc_malloc */
     28 #include "objc/thr.h"     /* For objc_mutex_lock */
     29 #include "objc-private/module-abi-8.h"
     30 #include "objc-private/runtime.h"
     31 #include <stdio.h>
     32 #include <string.h> /* For memset */
     33 #include <assert.h> /* For assert */
     34 
     35 int nbuckets = 0;					/* !T:MUTEX */
     36 int nindices = 0;					/* !T:MUTEX */
     37 int narrays = 0;					/* !T:MUTEX */
     38 int idxsize = 0;					/* !T:MUTEX */
     39 
     40 static void *first_free_data = NULL;			/* !T:MUTEX */
     41 
     42 #ifdef OBJC_SPARSE2
     43 const char *__objc_sparse2_id = "2 level sparse indices";
     44 #endif
     45 
     46 #ifdef OBJC_SPARSE3
     47 const char *__objc_sparse3_id = "3 level sparse indices";
     48 #endif
     49 
     50 /* This function removes any structures left over from free operations
     51    that were not safe in a multi-threaded environment. */
     52 void
     53 sarray_remove_garbage (void)
     54 {
     55   void **vp;
     56   void *np;
     57 
     58   objc_mutex_lock (__objc_runtime_mutex);
     59 
     60   vp = first_free_data;
     61   first_free_data = NULL;
     62 
     63   while (vp)
     64     {
     65       np = *vp;
     66       objc_free (vp);
     67       vp = np;
     68     }
     69 
     70   objc_mutex_unlock (__objc_runtime_mutex);
     71 }
     72 
     73 /* Free a block of dynamically allocated memory.  If we are in
     74    multi-threaded mode, it is ok to free it.  If not, we add it to the
     75    garbage heap to be freed later. */
     76 static void
     77 sarray_free_garbage (void *vp)
     78 {
     79   objc_mutex_lock (__objc_runtime_mutex);
     80 
     81   if (__objc_runtime_threads_alive == 1)
     82     {
     83       objc_free (vp);
     84       if (first_free_data)
     85 	sarray_remove_garbage ();
     86     }
     87   else
     88     {
     89       *(void **)vp = first_free_data;
     90       first_free_data = vp;
     91     }
     92 
     93   objc_mutex_unlock (__objc_runtime_mutex);
     94 }
     95 
     96 /* sarray_at_put copies data in such a way as to be thread reader
     97    safe.  */
     98 void
     99 sarray_at_put (struct sarray *array, sidx index, void *element)
    100 {
    101 #ifdef OBJC_SPARSE3
    102   struct sindex **the_index;
    103   struct sindex *new_index;
    104 #endif
    105   struct sbucket **the_bucket;
    106   struct sbucket *new_bucket;
    107 #ifdef OBJC_SPARSE3
    108   size_t ioffset;
    109 #endif
    110   size_t boffset;
    111   size_t eoffset;
    112 #ifdef PRECOMPUTE_SELECTORS
    113   union sofftype xx;
    114   xx.idx = index;
    115 #ifdef OBJC_SPARSE3
    116   ioffset = xx.off.ioffset;
    117 #endif
    118   boffset = xx.off.boffset;
    119   eoffset = xx.off.eoffset;
    120 #else /* not PRECOMPUTE_SELECTORS */
    121 #ifdef OBJC_SPARSE3
    122   ioffset = index/INDEX_CAPACITY;
    123   boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
    124   eoffset = index%BUCKET_SIZE;
    125 #else
    126   boffset = index/BUCKET_SIZE;
    127   eoffset = index%BUCKET_SIZE;
    128 #endif
    129 #endif /* not PRECOMPUTE_SELECTORS */
    130 
    131   assert (soffset_decode (index) < array->capacity); /* Range check */
    132 
    133 #ifdef OBJC_SPARSE3
    134   the_index = &(array->indices[ioffset]);
    135   the_bucket = &((*the_index)->buckets[boffset]);
    136 #else
    137   the_bucket = &(array->buckets[boffset]);
    138 #endif
    139 
    140   if ((*the_bucket)->elems[eoffset] == element)
    141     return;		/* Great! we just avoided a lazy copy.  */
    142 
    143 #ifdef OBJC_SPARSE3
    144 
    145   /* First, perform lazy copy/allocation of index if needed.  */
    146 
    147   if ((*the_index) == array->empty_index)
    148     {
    149       /* The index was previously empty, allocate a new.  */
    150       new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
    151       memcpy (new_index, array->empty_index, sizeof (struct sindex));
    152       new_index->version.version = array->version.version;
    153       *the_index = new_index;                     /* Prepared for install. */
    154       the_bucket = &((*the_index)->buckets[boffset]);
    155 
    156       nindices += 1;
    157     }
    158   else if ((*the_index)->version.version != array->version.version)
    159     {
    160       /* This index must be lazy copied.  */
    161       struct sindex *old_index = *the_index;
    162       new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
    163       memcpy (new_index, old_index, sizeof (struct sindex));
    164       new_index->version.version = array->version.version;
    165       *the_index = new_index;                     /* Prepared for install. */
    166       the_bucket = &((*the_index)->buckets[boffset]);
    167 
    168       nindices += 1;
    169     }
    170 
    171 #endif /* OBJC_SPARSE3 */
    172 
    173   /* Next, perform lazy allocation/copy of the bucket if needed.  */
    174   if ((*the_bucket) == array->empty_bucket)
    175     {
    176       /* The bucket was previously empty (or something like that),
    177 	 allocate a new.  This is the effect of `lazy' allocation.  */
    178       new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
    179       memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
    180 	      sizeof (struct sbucket));
    181       new_bucket->version.version = array->version.version;
    182       *the_bucket = new_bucket;                   /* Prepared for install. */
    183 
    184       nbuckets += 1;
    185 
    186     }
    187   else if ((*the_bucket)->version.version != array->version.version)
    188     {
    189       /* Perform lazy copy.  */
    190       struct sbucket *old_bucket = *the_bucket;
    191       new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
    192       memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
    193       new_bucket->version.version = array->version.version;
    194       *the_bucket = new_bucket;                   /* Prepared for install. */
    195 
    196       nbuckets += 1;
    197     }
    198   (*the_bucket)->elems[eoffset] = element;
    199 }
    200 
    201 void
    202 sarray_at_put_safe (struct sarray *array, sidx index, void *element)
    203 {
    204   if (soffset_decode (index) >= array->capacity)
    205     sarray_realloc (array, soffset_decode (index) + 1);
    206   sarray_at_put (array, index, element);
    207 }
    208 
    209 struct sarray *
    210 sarray_new (int size, void *default_element)
    211 {
    212   struct sarray *arr;
    213 #ifdef OBJC_SPARSE3
    214   size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
    215   struct sindex **new_indices;
    216 #else /* OBJC_SPARSE2 */
    217   size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
    218   struct sbucket **new_buckets;
    219 #endif
    220   size_t counter;
    221 
    222   assert (size > 0);
    223 
    224   /* Allocate core array.  */
    225   arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
    226   arr->version.version = 0;
    227 
    228   /* Initialize members.  */
    229 #ifdef OBJC_SPARSE3
    230   arr->capacity = num_indices*INDEX_CAPACITY;
    231   new_indices = (struct sindex **)
    232     objc_malloc (sizeof (struct sindex *) * num_indices);
    233 
    234   arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
    235   arr->empty_index->version.version = 0;
    236 
    237   narrays  += 1;
    238   idxsize  += num_indices;
    239   nindices += 1;
    240 
    241 #else /* OBJC_SPARSE2 */
    242   arr->capacity = num_indices*BUCKET_SIZE;
    243   new_buckets = (struct sbucket **)
    244     objc_malloc (sizeof (struct sbucket *) * num_indices);
    245 
    246   narrays  += 1;
    247   idxsize  += num_indices;
    248 
    249 #endif
    250 
    251   arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
    252   arr->empty_bucket->version.version = 0;
    253 
    254   nbuckets += 1;
    255 
    256   arr->ref_count = 1;
    257   arr->is_copy_of = (struct sarray *) 0;
    258 
    259   for (counter = 0; counter < BUCKET_SIZE; counter++)
    260     arr->empty_bucket->elems[counter] = default_element;
    261 
    262 #ifdef OBJC_SPARSE3
    263   for (counter = 0; counter < INDEX_SIZE; counter++)
    264     arr->empty_index->buckets[counter] = arr->empty_bucket;
    265 
    266   for (counter = 0; counter < num_indices; counter++)
    267     new_indices[counter] = arr->empty_index;
    268 
    269 #else /* OBJC_SPARSE2 */
    270 
    271   for (counter = 0; counter < num_indices; counter++)
    272     new_buckets[counter] = arr->empty_bucket;
    273 
    274 #endif
    275 
    276 #ifdef OBJC_SPARSE3
    277   arr->indices = new_indices;
    278 #else /* OBJC_SPARSE2 */
    279   arr->buckets = new_buckets;
    280 #endif
    281 
    282   return arr;
    283 }
    284 
    285 
    287 /* Reallocate the sparse array to hold `newsize' entries Note: We
    288    really allocate and then free.  We have to do this to ensure that
    289    any concurrent readers notice the update.  */
    290 void
    291 sarray_realloc (struct sarray *array, int newsize)
    292 {
    293 #ifdef OBJC_SPARSE3
    294   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
    295   size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
    296   size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
    297 
    298   struct sindex **new_indices;
    299   struct sindex **old_indices;
    300 
    301 #else /* OBJC_SPARSE2 */
    302   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
    303   size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
    304   size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
    305 
    306   struct sbucket **new_buckets;
    307   struct sbucket **old_buckets;
    308 
    309 #endif
    310 
    311   size_t counter;
    312 
    313   assert (newsize > 0);
    314 
    315   /* The size is the same, just ignore the request.  */
    316   if (rounded_size <= array->capacity)
    317     return;
    318 
    319   assert (array->ref_count == 1);	/* stop if lazy copied... */
    320 
    321   /* We are asked to extend the array -- allocate new bucket table,
    322      and insert empty_bucket in newly allocated places.  */
    323   if (rounded_size > array->capacity)
    324     {
    325 #ifdef OBJC_SPARSE3
    326       new_max_index += 4;
    327       rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
    328 #else /* OBJC_SPARSE2 */
    329       new_max_index += 4;
    330       rounded_size = (new_max_index + 1) * BUCKET_SIZE;
    331 #endif
    332 
    333       /* Update capacity.  */
    334       array->capacity = rounded_size;
    335 
    336 #ifdef OBJC_SPARSE3
    337       /* Alloc to force re-read by any concurrent readers.  */
    338       old_indices = array->indices;
    339       new_indices = (struct sindex **)
    340 	objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
    341 #else /* OBJC_SPARSE2 */
    342       old_buckets = array->buckets;
    343       new_buckets = (struct sbucket **)
    344 	objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
    345 #endif
    346 
    347       /* Copy buckets below old_max_index (they are still valid).  */
    348       for (counter = 0; counter <= old_max_index; counter++ )
    349 	{
    350 #ifdef OBJC_SPARSE3
    351 	  new_indices[counter] = old_indices[counter];
    352 #else /* OBJC_SPARSE2 */
    353 	  new_buckets[counter] = old_buckets[counter];
    354 #endif
    355 	}
    356 
    357 #ifdef OBJC_SPARSE3
    358       /* Reset entries above old_max_index to empty_bucket.  */
    359       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
    360 	new_indices[counter] = array->empty_index;
    361 #else /* OBJC_SPARSE2 */
    362       /* Reset entries above old_max_index to empty_bucket.  */
    363       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
    364 	new_buckets[counter] = array->empty_bucket;
    365 #endif
    366 
    367 #ifdef OBJC_SPARSE3
    368       /* Install the new indices.  */
    369       array->indices = new_indices;
    370 #else /* OBJC_SPARSE2 */
    371       array->buckets = new_buckets;
    372 #endif
    373 
    374 #ifdef OBJC_SPARSE3
    375       /* Free the old indices.  */
    376       sarray_free_garbage (old_indices);
    377 #else /* OBJC_SPARSE2 */
    378       sarray_free_garbage (old_buckets);
    379 #endif
    380 
    381       idxsize += (new_max_index-old_max_index);
    382       return;
    383     }
    384 }
    385 
    386 
    388 /* Free a sparse array allocated with sarray_new */
    389 void
    390 sarray_free (struct sarray *array) {
    391 #ifdef OBJC_SPARSE3
    392   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
    393   struct sindex **old_indices;
    394 #else
    395   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
    396   struct sbucket **old_buckets;
    397 #endif
    398   size_t counter = 0;
    399 
    400   assert (array->ref_count != 0);	/* Freed multiple times!!! */
    401 
    402   if (--(array->ref_count) != 0)	/* There exists copies of me */
    403     return;
    404 
    405 #ifdef OBJC_SPARSE3
    406   old_indices = array->indices;
    407 #else
    408   old_buckets = array->buckets;
    409 #endif
    410 
    411   /* Free all entries that do not point to empty_bucket.  */
    412   for (counter = 0; counter <= old_max_index; counter++ )
    413     {
    414 #ifdef OBJC_SPARSE3
    415       struct sindex *idx = old_indices[counter];
    416       if ((idx != array->empty_index)
    417 	  && (idx->version.version == array->version.version))
    418 	{
    419 	  int c2;
    420 	  for (c2 = 0; c2 < INDEX_SIZE; c2++)
    421 	    {
    422 	      struct sbucket *bkt = idx->buckets[c2];
    423 	      if ((bkt != array->empty_bucket)
    424 		  && (bkt->version.version == array->version.version))
    425 		{
    426 		  sarray_free_garbage (bkt);
    427 		  nbuckets -= 1;
    428 		}
    429 	    }
    430 	  sarray_free_garbage (idx);
    431 	  nindices -= 1;
    432 	}
    433 #else /* OBJC_SPARSE2 */
    434       struct sbucket *bkt = old_buckets[counter];
    435       if ((bkt != array->empty_bucket)
    436 	  && (bkt->version.version == array->version.version))
    437 	{
    438 	  sarray_free_garbage (bkt);
    439 	  nbuckets -= 1;
    440 	}
    441 #endif
    442     }
    443 
    444 #ifdef OBJC_SPARSE3
    445   /* Free empty_index.  */
    446   if (array->empty_index->version.version == array->version.version)
    447     {
    448       sarray_free_garbage (array->empty_index);
    449       nindices -= 1;
    450     }
    451 #endif
    452 
    453   /* Free empty_bucket.  */
    454   if (array->empty_bucket->version.version == array->version.version)
    455     {
    456       sarray_free_garbage (array->empty_bucket);
    457       nbuckets -= 1;
    458     }
    459   idxsize -= (old_max_index + 1);
    460   narrays -= 1;
    461 
    462 #ifdef OBJC_SPARSE3
    463   /* Free bucket table.  */
    464   sarray_free_garbage (array->indices);
    465 #else
    466   /* Free bucket table.  */
    467   sarray_free_garbage (array->buckets);
    468 #endif
    469 
    470   /* If this is a copy of another array, we free it (which might just
    471      decrement its reference count so it will be freed when no longer
    472      in use).  */
    473   if (array->is_copy_of)
    474     sarray_free (array->is_copy_of);
    475 
    476   /* Free array.  */
    477   sarray_free_garbage (array);
    478 }
    479 
    480 /* This is a lazy copy.  Only the core of the structure is actually
    481    copied.  */
    482 struct sarray *
    483 sarray_lazy_copy (struct sarray *oarr)
    484 {
    485   struct sarray *arr;
    486 
    487 #ifdef OBJC_SPARSE3
    488   size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
    489   struct sindex **new_indices;
    490 #else /* OBJC_SPARSE2 */
    491   size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
    492   struct sbucket **new_buckets;
    493 #endif
    494 
    495   /* Allocate core array.  */
    496   arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
    497   arr->version.version = oarr->version.version + 1;
    498 #ifdef OBJC_SPARSE3
    499   arr->empty_index = oarr->empty_index;
    500 #endif
    501   arr->empty_bucket = oarr->empty_bucket;
    502   arr->ref_count = 1;
    503   oarr->ref_count += 1;
    504   arr->is_copy_of = oarr;
    505   arr->capacity = oarr->capacity;
    506 
    507 #ifdef OBJC_SPARSE3
    508   /* Copy bucket table.  */
    509   new_indices = (struct sindex **)
    510     objc_malloc (sizeof (struct sindex *) * num_indices);
    511   memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
    512   arr->indices = new_indices;
    513 #else
    514   /* Copy bucket table.  */
    515   new_buckets = (struct sbucket **)
    516     objc_malloc (sizeof (struct sbucket *) * num_indices);
    517   memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
    518   arr->buckets = new_buckets;
    519 #endif
    520 
    521   idxsize += num_indices;
    522   narrays += 1;
    523 
    524   return arr;
    525 }
    526