github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/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 runtime 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 }