github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/runtime/mfinal.c (about)

     1  // Copyright 2010 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  #include "runtime.h"
     6  #include "arch_GOARCH.h"
     7  #include "malloc.h"
     8  
     9  enum { debug = 0 };
    10  
    11  typedef struct Fin Fin;
    12  struct Fin
    13  {
    14  	FuncVal *fn;
    15  	uintptr nret;
    16  };
    17  
    18  // Finalizer hash table.  Direct hash, linear scan, at most 3/4 full.
    19  // Table size is power of 3 so that hash can be key % max.
    20  // Key[i] == (void*)-1 denotes free but formerly occupied entry
    21  // (doesn't stop the linear scan).
    22  // Key and val are separate tables because the garbage collector
    23  // must be instructed to ignore the pointers in key but follow the
    24  // pointers in val.
    25  typedef struct Fintab Fintab;
    26  struct Fintab
    27  {
    28  	Lock;
    29  	void **key;
    30  	Fin *val;
    31  	int32 nkey;	// number of non-nil entries in key
    32  	int32 ndead;	// number of dead (-1) entries in key
    33  	int32 max;	// size of key, val allocations
    34  };
    35  
    36  #define TABSZ 17
    37  #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ])
    38  
    39  static struct {
    40  	Fintab;
    41  	uint8 pad[CacheLineSize - sizeof(Fintab)];	
    42  } fintab[TABSZ];
    43  
    44  static void
    45  addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
    46  {
    47  	int32 i, j;
    48  
    49  	i = (uintptr)k % (uintptr)t->max;
    50  	for(j=0; j<t->max; j++) {
    51  		if(t->key[i] == nil) {
    52  			t->nkey++;
    53  			goto ret;
    54  		}
    55  		if(t->key[i] == (void*)-1) {
    56  			t->ndead--;
    57  			goto ret;
    58  		}
    59  		if(++i == t->max)
    60  			i = 0;
    61  	}
    62  
    63  	// cannot happen - table is known to be non-full
    64  	runtime·throw("finalizer table inconsistent");
    65  
    66  ret:
    67  	t->key[i] = k;
    68  	t->val[i].fn = fn;
    69  	t->val[i].nret = nret;
    70  }
    71  
    72  static bool
    73  lookfintab(Fintab *t, void *k, bool del, Fin *f)
    74  {
    75  	int32 i, j;
    76  
    77  	if(t->max == 0)
    78  		return false;
    79  	i = (uintptr)k % (uintptr)t->max;
    80  	for(j=0; j<t->max; j++) {
    81  		if(t->key[i] == nil)
    82  			return false;
    83  		if(t->key[i] == k) {
    84  			if(f)
    85  				*f = t->val[i];
    86  			if(del) {
    87  				t->key[i] = (void*)-1;
    88  				t->val[i].fn = nil;
    89  				t->val[i].nret = 0;
    90  				t->ndead++;
    91  			}
    92  			return true;
    93  		}
    94  		if(++i == t->max)
    95  			i = 0;
    96  	}
    97  
    98  	// cannot happen - table is known to be non-full
    99  	runtime·throw("finalizer table inconsistent");
   100  	return false;
   101  }
   102  
   103  static void
   104  resizefintab(Fintab *tab)
   105  {
   106  	Fintab newtab;
   107  	void *k;
   108  	int32 i;
   109  
   110  	runtime·memclr((byte*)&newtab, sizeof newtab);
   111  	newtab.max = tab->max;
   112  	if(newtab.max == 0)
   113  		newtab.max = 3*3*3;
   114  	else if(tab->ndead < tab->nkey/2) {
   115  		// grow table if not many dead values.
   116  		// otherwise just rehash into table of same size.
   117  		newtab.max *= 3;
   118  	}
   119  	
   120  	newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], FlagNoPointers, 0, 1);
   121  	newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1);
   122  	
   123  	for(i=0; i<tab->max; i++) {
   124  		k = tab->key[i];
   125  		if(k != nil && k != (void*)-1)
   126  			addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret);
   127  	}
   128  	
   129  	runtime·free(tab->key);
   130  	runtime·free(tab->val);
   131  	
   132  	tab->key = newtab.key;
   133  	tab->val = newtab.val;
   134  	tab->nkey = newtab.nkey;
   135  	tab->ndead = newtab.ndead;
   136  	tab->max = newtab.max;
   137  }
   138  
   139  bool
   140  runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
   141  {
   142  	Fintab *tab;
   143  	byte *base;
   144  	
   145  	if(debug) {
   146  		if(!runtime·mlookup(p, &base, nil, nil) || p != base)
   147  			runtime·throw("addfinalizer on invalid pointer");
   148  	}
   149  	
   150  	tab = TAB(p);
   151  	runtime·lock(tab);
   152  	if(f == nil) {
   153  		lookfintab(tab, p, true, nil);
   154  		runtime·unlock(tab);
   155  		return true;
   156  	}
   157  
   158  	if(lookfintab(tab, p, false, nil)) {
   159  		runtime·unlock(tab);
   160  		return false;
   161  	}
   162  
   163  	if(tab->nkey >= tab->max/2+tab->max/4) {
   164  		// keep table at most 3/4 full:
   165  		// allocate new table and rehash.
   166  		resizefintab(tab);
   167  	}
   168  
   169  	addfintab(tab, p, f, nret);
   170  	runtime·setblockspecial(p, true);
   171  	runtime·unlock(tab);
   172  	return true;
   173  }
   174  
   175  // get finalizer; if del, delete finalizer.
   176  // caller is responsible for updating RefHasFinalizer (special) bit.
   177  bool
   178  runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
   179  {
   180  	Fintab *tab;
   181  	bool res;
   182  	Fin f;
   183  	
   184  	tab = TAB(p);
   185  	runtime·lock(tab);
   186  	res = lookfintab(tab, p, del, &f);
   187  	runtime·unlock(tab);
   188  	if(res==false)
   189  		return false;
   190  	*fn = f.fn;
   191  	*nret = f.nret;
   192  	return true;
   193  }
   194  
   195  void
   196  runtime·walkfintab(void (*fn)(void*))
   197  {
   198  	void **key;
   199  	void **ekey;
   200  	int32 i;
   201  
   202  	for(i=0; i<TABSZ; i++) {
   203  		runtime·lock(&fintab[i]);
   204  		key = fintab[i].key;
   205  		ekey = key + fintab[i].max;
   206  		for(; key < ekey; key++)
   207  			if(*key != nil && *key != ((void*)-1))
   208  				fn(*key);
   209  		runtime·unlock(&fintab[i]);
   210  	}
   211  }