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

     1  package luagc
     2  
     3  import (
     4  	"sort"
     5  	"sync"
     6  )
     7  
     8  //
     9  // Clone-based Pool implementation
    10  //
    11  
    12  // ClonePool is an implementation of Pool that makes every effort to let values
    13  // be GCed when they are only reachable via WeakRefs.  Unlike UnsafePool, it
    14  // doesn't rely on any undocumented properties of the Go runtime so it is safe
    15  // to use in any compliant Go implementation.  The downside of this
    16  // implementation is that it cannot provide any WeakRefs.
    17  type ClonePool struct {
    18  	cloneRegister map[Key]cloneEntry
    19  	lastMarkOrder int
    20  	mx            sync.Mutex
    21  
    22  	pendingFinalize sortablePendingClones
    23  	pendingRelease  sortablePendingClones
    24  }
    25  
    26  // NewClonePool returns a new *ClonePool ready to be used.
    27  func NewClonePool() *ClonePool {
    28  	return &ClonePool{
    29  		cloneRegister: make(map[Key]cloneEntry),
    30  	}
    31  }
    32  
    33  var _ Pool = (*ClonePool)(nil)
    34  
    35  // Get always returns nil because ClonePool doesn't support weak references.
    36  func (p *ClonePool) Get(v Value) WeakRef {
    37  	return nil
    38  }
    39  
    40  // Mark marks v for finalizing, i.e. when v is garbage collected, its finalizer
    41  // should be run.
    42  func (p *ClonePool) Mark(v Value, flags MarkFlags) {
    43  	k := v.Key()
    44  	p.mx.Lock()
    45  	defer p.mx.Unlock()
    46  	c, ok := p.cloneRegister[k]
    47  	if flags == 0 {
    48  		if ok {
    49  			setFinalizer(v, nil)
    50  			delete(p.cloneRegister, k)
    51  		}
    52  		return
    53  	}
    54  	if !ok {
    55  		setFinalizer(v, p.goFinalizer)
    56  	}
    57  	c.value = v.Clone()
    58  	p.lastMarkOrder++
    59  	c.markOrder = p.lastMarkOrder
    60  	if flags&Finalize == 0 {
    61  		c.setFlag(wrFinalized)
    62  	} else {
    63  		c.clearFlag(wrFinalized)
    64  	}
    65  	if flags&Release == 0 {
    66  		c.setFlag(wrReleased)
    67  	} else {
    68  		c.clearFlag(wrReleased)
    69  	}
    70  	p.cloneRegister[k] = c
    71  }
    72  
    73  // ExtractPendingFinalize returns the set of values which are being garbage
    74  // collected and need their finalizer running, in the order that they should be
    75  // run.  The caller of this function has the responsibility to run all the
    76  // finalizers. The values returned are removed from the pool and their weak refs
    77  // are invalidated.
    78  func (p *ClonePool) ExtractPendingFinalize() []Value {
    79  	p.mx.Lock()
    80  	pending := p.pendingFinalize
    81  	if pending == nil {
    82  		p.mx.Unlock()
    83  		return nil
    84  	}
    85  	p.pendingFinalize = nil
    86  	p.mx.Unlock()
    87  
    88  	for _, c := range pending {
    89  		// The finalizer code might resurrect the value, or there may still be
    90  		// release code to run, so we need the clone to have a finalizer.
    91  		setFinalizer(c.value, p.goFinalizer)
    92  	}
    93  
    94  	// Lua wants to run finalizers in reverse order
    95  	sort.Sort(pending)
    96  	return pending.vals()
    97  }
    98  
    99  func (p *ClonePool) ExtractPendingRelease() []Value {
   100  	p.mx.Lock()
   101  	pending := p.pendingRelease
   102  	if pending == nil {
   103  		p.mx.Unlock()
   104  		return nil
   105  	}
   106  	p.pendingRelease = nil
   107  	p.mx.Unlock()
   108  
   109  	sort.Sort(pending)
   110  	return pending.vals()
   111  }
   112  
   113  // ExtractAllMarkedFinalized returns all the values that have been marked for
   114  // finalizing, even if their go finalizer hasn't run yet.  This is useful e.g.
   115  // when closing a runtime, to run all pending finalizers.
   116  func (p *ClonePool) ExtractAllMarkedFinalize() []Value {
   117  	p.mx.Lock()
   118  
   119  	// Disregard the pendingFinalize list as all values are still present in the
   120  	// weakrefs map.
   121  	p.pendingFinalize = nil
   122  	var marked sortablePendingClones
   123  	for k, c := range p.cloneRegister {
   124  		if !c.hasFlag(wrFinalized) {
   125  			c.setFlag(wrFinalized)
   126  			p.cloneRegister[k] = c
   127  			marked = append(marked, c)
   128  		}
   129  	}
   130  	p.mx.Unlock()
   131  
   132  	// Sort in reverse order
   133  	sort.Sort(marked)
   134  	return marked.vals()
   135  }
   136  
   137  // ExtractAllMarkedRelease returns all the values that have been marked for
   138  // release, even if their go finalizer hasn't run yet.  This is useful e.g. when
   139  // closing a runtime, to release all resources.
   140  func (p *ClonePool) ExtractAllMarkedRelease() []Value {
   141  	p.mx.Lock()
   142  
   143  	// Start from values whose go finalizer has already run and are awaiting
   144  	// release, then add all values in the weakrefs map not yet released.
   145  	marked := p.pendingRelease
   146  	for _, c := range p.cloneRegister {
   147  		if !c.hasFlag(wrReleased) {
   148  			marked = append(marked, c)
   149  		}
   150  	}
   151  	p.pendingRelease = nil
   152  	p.cloneRegister = nil
   153  	p.mx.Unlock()
   154  
   155  	// Sort in reverse order
   156  	sort.Sort(marked)
   157  	return marked.vals()
   158  }
   159  
   160  func (p *ClonePool) goFinalizer(v Value) {
   161  	k := v.Key()
   162  	p.mx.Lock()
   163  	defer p.mx.Unlock()
   164  	c, ok := p.cloneRegister[k]
   165  	if !ok {
   166  		// We are too late - ExtractAllMarkedRelease() has been run
   167  		return
   168  	}
   169  
   170  	if !c.hasFlag(wrFinalized) {
   171  		p.pendingFinalize = append(p.pendingFinalize, c)
   172  		c.setFlag(wrFinalized)
   173  		p.cloneRegister[k] = c
   174  		return
   175  	}
   176  
   177  	if !c.hasFlag(wrReleased) {
   178  		p.pendingRelease = append(p.pendingRelease, c)
   179  	}
   180  
   181  	delete(p.cloneRegister, k)
   182  }
   183  
   184  type cloneEntry struct {
   185  	value     Value
   186  	markOrder int
   187  	flags     wrStatusFlags
   188  }
   189  
   190  func (c *cloneEntry) hasFlag(flag wrStatusFlags) bool {
   191  	return c.flags&flag != 0
   192  }
   193  
   194  func (c *cloneEntry) setFlag(flag wrStatusFlags) {
   195  	c.flags |= flag
   196  }
   197  
   198  func (c *cloneEntry) clearFlag(flag wrStatusFlags) {
   199  	c.flags &= ^flag
   200  }
   201  
   202  type sortablePendingClones []cloneEntry
   203  
   204  var _ sort.Interface = sortableVals(nil)
   205  
   206  func (vs sortablePendingClones) Len() int {
   207  	return len(vs)
   208  }
   209  
   210  func (vs sortablePendingClones) Less(i, j int) bool {
   211  	return vs[i].markOrder > vs[j].markOrder
   212  }
   213  
   214  func (vs sortablePendingClones) Swap(i, j int) {
   215  	vs[i], vs[j] = vs[j], vs[i]
   216  }
   217  
   218  // Extract the values.
   219  func (vs sortablePendingClones) vals() []Value {
   220  	vals := make([]Value, len(vs))
   221  	for i, v := range vs {
   222  		vals[i] = v.value
   223  	}
   224  	return vals
   225  }