github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/weakref.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package grumpy
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"runtime"
    21  	"sync"
    22  	"sync/atomic"
    23  	"unsafe"
    24  )
    25  
    26  var (
    27  	// WeakRefType is the object representing the Python 'weakref' type.
    28  	WeakRefType = newBasisType("weakref", reflect.TypeOf(WeakRef{}), toWeakRefUnsafe, ObjectType)
    29  )
    30  
    31  type weakRefState int
    32  
    33  const (
    34  	weakRefStateNew weakRefState = iota
    35  	weakRefStateUsed
    36  	weakRefStateDead
    37  )
    38  
    39  // WeakRef represents Python 'weakref' objects.
    40  type WeakRef struct {
    41  	Object
    42  	ptr       uintptr
    43  	mutex     sync.Mutex
    44  	state     weakRefState
    45  	callbacks []*Object
    46  	hash      *Object
    47  }
    48  
    49  func toWeakRefUnsafe(o *Object) *WeakRef {
    50  	return (*WeakRef)(o.toPointer())
    51  }
    52  
    53  // get returns r's referent, or nil if r is "dead".
    54  func (r *WeakRef) get() *Object {
    55  	if r.state == weakRefStateDead {
    56  		return nil
    57  	}
    58  	r.state = weakRefStateUsed
    59  	return (*Object)(unsafe.Pointer(r.ptr))
    60  }
    61  
    62  // ToObject upcasts r to an Object.
    63  func (r *WeakRef) ToObject() *Object {
    64  	return &r.Object
    65  }
    66  
    67  func weakRefCall(f *Frame, callable *Object, args Args, _ KWArgs) (*Object, *BaseException) {
    68  	if raised := checkFunctionArgs(f, "__call__", args); raised != nil {
    69  		return nil, raised
    70  	}
    71  	r := toWeakRefUnsafe(callable)
    72  	r.mutex.Lock()
    73  	o := r.get()
    74  	r.mutex.Unlock()
    75  	if o == nil {
    76  		o = None
    77  	}
    78  	return o, nil
    79  }
    80  
    81  func weakRefHash(f *Frame, o *Object) (result *Object, raised *BaseException) {
    82  	r := toWeakRefUnsafe(o)
    83  	var referent *Object
    84  	r.mutex.Lock()
    85  	if r.hash != nil {
    86  		result = r.hash
    87  	} else {
    88  		referent = r.get()
    89  	}
    90  	r.mutex.Unlock()
    91  	if referent != nil {
    92  		var hash *Int
    93  		hash, raised = Hash(f, referent)
    94  		if raised == nil {
    95  			result = hash.ToObject()
    96  			r.mutex.Lock()
    97  			r.hash = result
    98  			r.mutex.Unlock()
    99  		}
   100  	} else if result == nil {
   101  		raised = f.RaiseType(TypeErrorType, "weak object has gone away")
   102  	}
   103  	return result, raised
   104  }
   105  
   106  func weakRefNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseException) {
   107  	if raised := checkFunctionVarArgs(f, "__new__", args, ObjectType); raised != nil {
   108  		return nil, raised
   109  	}
   110  	argc := len(args)
   111  	if argc > 2 {
   112  		format := "__new__ expected at most 2 arguments, got %d"
   113  		return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, argc))
   114  	}
   115  	o := args[0]
   116  	nilPtr := unsafe.Pointer(nil)
   117  	addr := (*unsafe.Pointer)(unsafe.Pointer(&o.ref))
   118  	var r *WeakRef
   119  	// Atomically fetch or initialize o.ref.
   120  	for {
   121  		p := atomic.LoadPointer(addr)
   122  		if p != nilPtr {
   123  			r = (*WeakRef)(p)
   124  			break
   125  		} else {
   126  			r = &WeakRef{Object: Object{typ: WeakRefType}, ptr: uintptr(o.toPointer())}
   127  			if atomic.CompareAndSwapPointer(addr, nilPtr, r.toPointer()) {
   128  				runtime.SetFinalizer(o, weakRefFinalizeReferent)
   129  				break
   130  			}
   131  		}
   132  	}
   133  	if argc > 1 {
   134  		r.mutex.Lock()
   135  		r.callbacks = append(r.callbacks, args[1])
   136  		r.mutex.Unlock()
   137  	}
   138  	return r.ToObject(), nil
   139  }
   140  
   141  func weakRefRepr(f *Frame, o *Object) (*Object, *BaseException) {
   142  	r := toWeakRefUnsafe(o)
   143  	r.mutex.Lock()
   144  	p := r.get()
   145  	r.mutex.Unlock()
   146  	s := "dead"
   147  	if p != nil {
   148  		s = fmt.Sprintf("to '%s' at %p", p.Type().Name(), p)
   149  	}
   150  	return NewStr(fmt.Sprintf("<weakref at %p; %s>", r, s)).ToObject(), nil
   151  }
   152  
   153  func initWeakRefType(map[string]*Object) {
   154  	WeakRefType.slots.Call = &callSlot{weakRefCall}
   155  	WeakRefType.slots.Hash = &unaryOpSlot{weakRefHash}
   156  	WeakRefType.slots.New = &newSlot{weakRefNew}
   157  	WeakRefType.slots.Repr = &unaryOpSlot{weakRefRepr}
   158  }
   159  
   160  func weakRefFinalizeReferent(o *Object) {
   161  	// Note that although o should be the last reference to that object
   162  	// (since this is its finalizer), in the time between the runtime
   163  	// scheduling this finalizer and the Lock() call below, r may have
   164  	// handed out another reference to o. So we can't simply mark r "dead".
   165  	addr := (*unsafe.Pointer)(unsafe.Pointer(&o.ref))
   166  	r := (*WeakRef)(atomic.LoadPointer(addr))
   167  	numCallbacks := 0
   168  	var callbacks []*Object
   169  	r.mutex.Lock()
   170  	switch r.state {
   171  	case weakRefStateNew:
   172  		// State "new" means that no references have been handed out by
   173  		// r and therefore o is the only live reference.
   174  		r.state = weakRefStateDead
   175  		numCallbacks = len(r.callbacks)
   176  		callbacks = make([]*Object, numCallbacks)
   177  		copy(callbacks, r.callbacks)
   178  	case weakRefStateUsed:
   179  		// Most likely it's safe to mark r "dead" at this point, but
   180  		// because a reference was handed out at some point, play it
   181  		// safe and reset the finalizer. If no more references are
   182  		// handed out before the next finalize then it will be "dead".
   183  		r.state = weakRefStateNew
   184  		runtime.SetFinalizer(o, weakRefFinalizeReferent)
   185  	}
   186  	r.mutex.Unlock()
   187  	// Don't hold r.mutex while invoking callbacks in case they access r
   188  	// and attempt to acquire the mutex.
   189  	for i := numCallbacks - 1; i >= 0; i-- {
   190  		f := NewRootFrame()
   191  		if _, raised := callbacks[i].Call(f, Args{r.ToObject()}, nil); raised != nil {
   192  			Stderr.writeString(FormatExc(f))
   193  		}
   194  	}
   195  }