github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/internal/luagc/unsafepool.go (about)

     1  package luagc
     2  
     3  import (
     4  	"runtime"
     5  	"sort"
     6  	"sync"
     7  	"unsafe"
     8  )
     9  
    10  //
    11  // Unsafe Pool implementation
    12  //
    13  
    14  // So that runtime.SetFinalizer can be mocked for testing.
    15  var setFinalizer = runtime.SetFinalizer
    16  
    17  // UnsafePool is an implementation of Pool that makes every effort to let
    18  // values be GCed when they are only reachable via WeakRefs.  It relies on
    19  // casting interface{} to unsafe pointers and back again, which would break if
    20  // Go were to have a moving GC.
    21  type UnsafePool struct {
    22  	mx              sync.Mutex           // Used to synchronize access to weakrefs, pendingVals, pendingOrders.
    23  	weakrefs        map[uintptr]*weakRef //
    24  	pendingFinalize sortableVals         // Values pending Lua finalization
    25  	pendingRelease  sortableVals
    26  	lastMarkOrder   int // this is to sort values by reverse order of mark for finalize
    27  }
    28  
    29  var _ Pool = &UnsafePool{}
    30  
    31  // NewUnsafePool returns a new *UnsafeWeakRefPool ready to be used.
    32  func NewUnsafePool() *UnsafePool {
    33  	return &UnsafePool{weakrefs: make(map[uintptr]*weakRef)}
    34  }
    35  
    36  // Get returns a WeakRef for v if possible.
    37  func (p *UnsafePool) Get(v Value) WeakRef {
    38  	p.mx.Lock()
    39  	defer p.mx.Unlock()
    40  	return p.get(v)
    41  }
    42  
    43  // Returns a *WeakRef for iface, not thread safe, only call when you have the
    44  // pool lock.
    45  func (p *UnsafePool) get(v Value) *weakRef {
    46  	w := getwiface(v)
    47  	id := w.id()
    48  	r := p.weakrefs[id]
    49  	if r == nil {
    50  		setFinalizer(v, p.goFinalizer)
    51  		r = &weakRef{
    52  			w:    w,
    53  			pool: p,
    54  		}
    55  		p.weakrefs[id] = r
    56  	}
    57  	return r
    58  }
    59  
    60  // Mark marks v for finalizing, i.e. when v is garbage collected, its finalizer
    61  // should be run.  It only takes effect if v can have a weak ref.
    62  func (p *UnsafePool) Mark(v Value, flags MarkFlags) {
    63  	if flags == 0 {
    64  		return
    65  	}
    66  	p.mx.Lock()
    67  	defer p.mx.Unlock()
    68  	p.lastMarkOrder++
    69  	r := p.get(v)
    70  	r.markOrder = p.lastMarkOrder
    71  	if flags&Finalize == 0 {
    72  		r.setFlag(wrFinalized)
    73  	} else {
    74  		r.clearFlag(wrFinalized)
    75  	}
    76  	if flags&Release == 0 {
    77  		r.setFlag(wrReleased)
    78  	} else {
    79  		r.clearFlag(wrReleased)
    80  	}
    81  }
    82  
    83  // ExtractPendingFinalize returns the set of values which are being garbage
    84  // collected and need their finalizer running, in the order that they should be
    85  // run.  The caller of this function has the responsibility to run all the
    86  // finalizers. The values returned are removed from the pool and their weak refs
    87  // are invalidated.
    88  func (p *UnsafePool) ExtractPendingFinalize() []Value {
    89  
    90  	// It may be that since a value pending finalizing has been added to the
    91  	// list, it was resurrected by a weak ref, so we need to go through the list
    92  	// and filter these out first.
    93  	p.mx.Lock()
    94  	if p.pendingFinalize == nil {
    95  		p.mx.Unlock()
    96  		return nil
    97  	}
    98  	var pending sortableVals
    99  	for _, rval := range p.pendingFinalize {
   100  		if rval.r.hasFlag(wrResurrected) {
   101  			rval.r.clearFlag(wrResurrected)
   102  		} else {
   103  			rval.r.setFlag(wrFinalized)
   104  			pending = append(pending, rval)
   105  		}
   106  	}
   107  	p.pendingFinalize = nil
   108  	p.mx.Unlock()
   109  
   110  	// Lua wants to run finalizers in reverse order
   111  	sort.Sort(pending)
   112  	return pending.vals()
   113  }
   114  
   115  func (p *UnsafePool) ExtractPendingRelease() []Value {
   116  	p.mx.Lock()
   117  	pending := p.pendingRelease
   118  	if pending == nil {
   119  		p.mx.Unlock()
   120  		return nil
   121  	}
   122  	p.pendingRelease = nil
   123  
   124  	for _, rval := range pending {
   125  		rval.r.setFlag(wrReleased)
   126  	}
   127  	p.mx.Unlock()
   128  
   129  	sort.Sort(pending)
   130  	return pending.vals()
   131  }
   132  
   133  // ExtractAllMarkedFinalized returns all the values that have been marked for
   134  // finalizing, even if their go finalizer hasn't run yet.  This is useful e.g.
   135  // when closing a runtime, to run all pending finalizers.
   136  func (p *UnsafePool) ExtractAllMarkedFinalize() []Value {
   137  	p.mx.Lock()
   138  
   139  	// Disregard the pendingFinalize list as all values are still present in the
   140  	// weakrefs map.
   141  	p.pendingFinalize = nil
   142  	var marked sortableVals
   143  	for _, r := range p.weakrefs {
   144  		if !r.hasFlag(wrFinalized) {
   145  			iface := r.w.iface()
   146  			marked = append(marked, refVal{
   147  				v: iface,
   148  				r: r,
   149  			})
   150  			r.setFlag(wrFinalized)
   151  			// We don't want the finalizer to be triggered anymore, but more
   152  			// important the finalizer is holding a reference to the pool
   153  			// (although that may not affect its reachability?)
   154  			setFinalizer(iface, nil)
   155  		}
   156  	}
   157  	p.mx.Unlock()
   158  
   159  	// Sort in reverse order
   160  	sort.Sort(marked)
   161  	return marked.vals()
   162  }
   163  
   164  // ExtractAllMarkedRelease returns all the values that have been marked for
   165  // release, even if their go finalizer hasn't run yet.  This is useful e.g. when
   166  // closing a runtime, to release all resources.
   167  func (p *UnsafePool) ExtractAllMarkedRelease() []Value {
   168  	p.mx.Lock()
   169  
   170  	// Start from values whose go finalizer has already run and are awaiting
   171  	// release, then add all values in the weakrefs map not yet released.
   172  	marked := p.pendingRelease
   173  	for _, r := range p.weakrefs {
   174  		if !r.hasFlag(wrReleased) {
   175  			iface := r.w.iface()
   176  			marked = append(marked, refVal{
   177  				v: iface,
   178  				r: r,
   179  			})
   180  			r.flags |= wrReleased
   181  			// We don't want the finalizer to be triggered anymore, but more
   182  			// important the finalizer is holding a reference to the pool
   183  			// (although that may not affect its reachability?)
   184  			setFinalizer(iface, nil)
   185  		}
   186  	}
   187  	p.pendingRelease = nil
   188  	p.weakrefs = nil
   189  	p.mx.Unlock()
   190  
   191  	// Sort in reverse order
   192  	sort.Sort(marked)
   193  	return marked.vals()
   194  }
   195  
   196  // This is the finalizer that Go runs on values added to the pool when they
   197  // become unreachable.
   198  func (p *UnsafePool) goFinalizer(v Value) {
   199  	p.mx.Lock()
   200  	defer p.mx.Unlock()
   201  	id := getwiface(v).id()
   202  	r := p.weakrefs[id]
   203  	if r == nil {
   204  		return
   205  	}
   206  
   207  	// A resurrected value has its go finalizer reinstated.
   208  	if r.hasFlag(wrResurrected) {
   209  		r.clearFlag(wrResurrected)
   210  		setFinalizer(v, p.goFinalizer)
   211  		return
   212  	}
   213  
   214  	rval := refVal{
   215  		v: v,
   216  		r: r,
   217  	}
   218  
   219  	// A not yet finalized value is added to the pendingFinalize list.  As it
   220  	// may get resurrected in the finalizer, we reinstate its go finalizer.
   221  	// When it is extracted to be processed, its finalized flag will be set.
   222  	if !r.hasFlag(wrFinalized) {
   223  		p.pendingFinalize = append(p.pendingFinalize, rval)
   224  		setFinalizer(v, p.goFinalizer)
   225  		return
   226  	}
   227  
   228  	// This is a point of no return, this value is now dead to the Lua runtime.
   229  	r.setFlag(wrDead)
   230  
   231  	// A not yet released value is added to the pendingRelease list.
   232  	if !r.hasFlag(wrReleased) {
   233  		p.pendingRelease = append(p.pendingRelease, rval)
   234  	}
   235  
   236  	// It is now safe to remove this value from the weakref pool.
   237  	delete(p.weakrefs, id)
   238  }
   239  
   240  //
   241  // WeakRef implementation for UnsafePool
   242  //
   243  
   244  type weakRef struct {
   245  	w         wiface // encodes the value the weak ref refers to
   246  	markOrder int    // positive if the value was marked with UnsafePool.Mark()
   247  	flags     wrStatusFlags
   248  
   249  	// Needed to sync with the Go finalizers which run in their own goroutine.
   250  	pool *UnsafePool
   251  }
   252  
   253  var _ WeakRef = &weakRef{}
   254  
   255  // Value returns the value this weak ref refers to if it is still alive, else
   256  // returns NilValue.
   257  func (r *weakRef) Value() Value {
   258  	r.pool.mx.Lock()
   259  	defer r.pool.mx.Unlock()
   260  	if r.hasFlag(wrDead) {
   261  		return nil
   262  	}
   263  	r.setFlag(wrResurrected)
   264  	return r.w.iface()
   265  }
   266  
   267  func (r *weakRef) hasFlag(flag wrStatusFlags) bool {
   268  	return r.flags&flag != 0
   269  }
   270  
   271  func (r *weakRef) setFlag(flag wrStatusFlags) {
   272  	r.flags |= flag
   273  }
   274  
   275  func (r *weakRef) clearFlag(flag wrStatusFlags) {
   276  	r.flags &= ^flag
   277  }
   278  
   279  //
   280  // Statuses of a weak ref
   281  //
   282  
   283  type wrStatusFlags uint8
   284  
   285  // A weakRef has 4 status flags: "dead" , "resurrected", "finalized",
   286  // "released".
   287  const (
   288  	wrDead        wrStatusFlags = 1 << iota // The value is dead to the Lua runtime
   289  	wrResurrected                           // The weakRef's value has been obtained
   290  	wrFinalized                             // The Lua finalizer no longer needs to run
   291  	wrReleased                              // The value's resources no longer need to be released (in this case it should be dead)
   292  )
   293  
   294  //
   295  // Non-retaining reference to an interface value
   296  //
   297  
   298  // wiface is an unsafe copy of an interface.  It remembers the type and data of
   299  // a Go interface value, but does not keep it alive.
   300  type wiface [2]uintptr
   301  
   302  func getwiface(r Value) wiface {
   303  	return *(*[2]uintptr)(unsafe.Pointer(&r))
   304  }
   305  
   306  func (w wiface) id() uintptr {
   307  	// This is the address containing the interface data.
   308  	return w[1]
   309  }
   310  
   311  func (w wiface) iface() Value {
   312  	return *(*Value)(unsafe.Pointer(&w))
   313  }
   314  
   315  //
   316  // Values need to be sorted by reverse mark order.  The data structures below help with that.
   317  //
   318  
   319  type refVal struct {
   320  	v Value
   321  	r *weakRef
   322  }
   323  
   324  type sortableVals []refVal
   325  
   326  var _ sort.Interface = sortableVals(nil)
   327  
   328  func (vs sortableVals) Len() int {
   329  	return len(vs)
   330  }
   331  
   332  func (vs sortableVals) Less(i, j int) bool {
   333  	return vs[i].r.markOrder > vs[j].r.markOrder
   334  }
   335  
   336  func (vs sortableVals) Swap(i, j int) {
   337  	vs[i], vs[j] = vs[j], vs[i]
   338  }
   339  
   340  // Extract the values.
   341  func (vs sortableVals) vals() []Value {
   342  	vals := make([]Value, len(vs))
   343  	for i, v := range vs {
   344  		vals[i] = v.v
   345  	}
   346  	return vals
   347  }