Merge branch 'feat/purego-callback-lifecycle' into main
6 files · +309 -8
M templates/glib +114 -3
| @@ -14,10 +14,14 @@ var callbacks = struct { | |||
| 14 | 14 | refs map[uintptr]uintptr | |
| 15 | 15 | closures map[uintptr]interface{} | |
| 16 | 16 | handlerToCallback map[uint]uintptr | |
| 17 | + | sourceToCallback map[uint]uintptr | |
| 18 | + | callbackRefCount map[uintptr]int | |
| 17 | 19 | }{ | |
| 18 | 20 | refs: make(map[uintptr]uintptr), | |
| 19 | 21 | closures: make(map[uintptr]interface{}), | |
| 20 | 22 | handlerToCallback: make(map[uint]uintptr), | |
| 23 | + | sourceToCallback: make(map[uint]uintptr), | |
| 24 | + | callbackRefCount: make(map[uintptr]int), | |
| 21 | 25 | } | |
| 22 | 26 | ||
| 23 | 27 | // GetCallback retrives a callback reference by value. | |
| @@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||
| 44 | 48 | callbacks.Lock() | |
| 45 | 49 | callbacks.refs[cbPtr] = refPtr | |
| 46 | 50 | callbacks.closures[cbPtr] = closure | |
| 51 | + | if _, ok := callbacks.callbackRefCount[cbPtr]; !ok { | |
| 52 | + | callbacks.callbackRefCount[cbPtr] = 1 | |
| 53 | + | } | |
| 47 | 54 | callbacks.Unlock() | |
| 48 | 55 | } | |
| 49 | 56 | ||
| @@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||
| 52 | 59 | // Users should not need to call this. | |
| 53 | 60 | func RemoveCallback(cbPtr uintptr) { | |
| 54 | 61 | callbacks.Lock() | |
| 62 | + | for handlerID, mappedCbPtr := range callbacks.handlerToCallback { | |
| 63 | + | if mappedCbPtr == cbPtr { | |
| 64 | + | delete(callbacks.handlerToCallback, handlerID) | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | for sourceID, mappedCbPtr := range callbacks.sourceToCallback { | |
| 68 | + | if mappedCbPtr == cbPtr { | |
| 69 | + | delete(callbacks.sourceToCallback, sourceID) | |
| 70 | + | } | |
| 71 | + | } | |
| 55 | 72 | delete(callbacks.refs, cbPtr) | |
| 56 | 73 | delete(callbacks.closures, cbPtr) | |
| 74 | + | delete(callbacks.callbackRefCount, cbPtr) | |
| 57 | 75 | callbacks.Unlock() | |
| 58 | 76 | } | |
| 59 | 77 | ||
| 78 | + | // acquireCallbackRef increments callbackRefCount for cbPtr. | |
| 79 | + | // Caller must hold callbacks.Lock(). | |
| 80 | + | func acquireCallbackRef(cbPtr uintptr) { | |
| 81 | + | callbacks.callbackRefCount[cbPtr]++ | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func hasCallbackMappings(cbPtr uintptr) bool { | |
| 85 | + | for _, mappedCbPtr := range callbacks.handlerToCallback { | |
| 86 | + | if mappedCbPtr == cbPtr { | |
| 87 | + | return true | |
| 88 | + | } | |
| 89 | + | } | |
| 90 | + | for _, mappedCbPtr := range callbacks.sourceToCallback { | |
| 91 | + | if mappedCbPtr == cbPtr { | |
| 92 | + | return true | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | return false | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | // releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback | |
| 99 | + | // data when it reaches zero. | |
| 100 | + | // Caller must hold callbacks.Lock(). | |
| 101 | + | // Handler/source mappings to cbPtr are expected to be removed or replaced by | |
| 102 | + | // the caller (RemoveCallbackByHandler, RemoveCallbackBySource, | |
| 103 | + | // SaveHandlerMapping, SaveSourceMapping). | |
| 104 | + | func releaseCallbackRef(cbPtr uintptr) { | |
| 105 | + | count, ok := callbacks.callbackRefCount[cbPtr] | |
| 106 | + | if !ok { | |
| 107 | + | return | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | count-- | |
| 111 | + | if count > 0 { | |
| 112 | + | callbacks.callbackRefCount[cbPtr] = count | |
| 113 | + | return | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | delete(callbacks.callbackRefCount, cbPtr) | |
| 117 | + | delete(callbacks.refs, cbPtr) | |
| 118 | + | delete(callbacks.closures, cbPtr) | |
| 119 | + | } | |
| 120 | + | ||
| 60 | 121 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | |
| 61 | 122 | // so that DisconnectSignal can clean up the callback registry. | |
| 62 | 123 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | |
| 124 | + | if handlerID == 0 { | |
| 125 | + | return | |
| 126 | + | } | |
| 127 | + | ||
| 63 | 128 | callbacks.Lock() | |
| 129 | + | defer callbacks.Unlock() | |
| 130 | + | if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |
| 131 | + | if prevCbPtr == cbPtr { | |
| 132 | + | return | |
| 133 | + | } | |
| 134 | + | releaseCallbackRef(prevCbPtr) | |
| 135 | + | if !hasCallbackMappings(prevCbPtr) { | |
| 136 | + | releaseCallbackRef(prevCbPtr) | |
| 137 | + | } | |
| 138 | + | } | |
| 64 | 139 | callbacks.handlerToCallback[handlerID] = cbPtr | |
| 65 | - | callbacks.Unlock() | |
| 140 | + | acquireCallbackRef(cbPtr) | |
| 66 | 141 | } | |
| 67 | 142 | ||
| 68 | 143 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | |
| 69 | 144 | func RemoveCallbackByHandler(handlerID uint) { | |
| 70 | 145 | callbacks.Lock() | |
| 71 | 146 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |
| 72 | - | delete(callbacks.refs, cbPtr) | |
| 73 | - | delete(callbacks.closures, cbPtr) | |
| 74 | 147 | delete(callbacks.handlerToCallback, handlerID) | |
| 148 | + | releaseCallbackRef(cbPtr) | |
| 149 | + | if !hasCallbackMappings(cbPtr) { | |
| 150 | + | releaseCallbackRef(cbPtr) | |
| 151 | + | } | |
| 152 | + | } | |
| 153 | + | callbacks.Unlock() | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | // SaveSourceMapping records a source ID -> callback pointer mapping. | |
| 157 | + | func SaveSourceMapping(sourceID uint, cbPtr uintptr) { | |
| 158 | + | if sourceID == 0 { | |
| 159 | + | return | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | callbacks.Lock() | |
| 163 | + | defer callbacks.Unlock() | |
| 164 | + | if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |
| 165 | + | if prevCbPtr == cbPtr { | |
| 166 | + | return | |
| 167 | + | } | |
| 168 | + | releaseCallbackRef(prevCbPtr) | |
| 169 | + | if !hasCallbackMappings(prevCbPtr) { | |
| 170 | + | releaseCallbackRef(prevCbPtr) | |
| 171 | + | } | |
| 172 | + | } | |
| 173 | + | callbacks.sourceToCallback[sourceID] = cbPtr | |
| 174 | + | acquireCallbackRef(cbPtr) | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | // RemoveCallbackBySource removes a callback mapping using a source ID. | |
| 178 | + | func RemoveCallbackBySource(sourceID uint) { | |
| 179 | + | callbacks.Lock() | |
| 180 | + | if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |
| 181 | + | delete(callbacks.sourceToCallback, sourceID) | |
| 182 | + | releaseCallbackRef(cbPtr) | |
| 183 | + | if !hasCallbackMappings(cbPtr) { | |
| 184 | + | releaseCallbackRef(cbPtr) | |
| 185 | + | } | |
| 75 | 186 | } | |
| 76 | 187 | callbacks.Unlock() | |
| 77 | 188 | } | |
| @@ -14,10 +14,14 @@ var callbacks = struct { | |||||
| 14 | refs map[uintptr]uintptr | 14 | refs map[uintptr]uintptr | ||
| 15 | closures map[uintptr]interface{} | 15 | closures map[uintptr]interface{} | ||
| 16 | handlerToCallback map[uint]uintptr | 16 | handlerToCallback map[uint]uintptr | ||
| 17 | + | sourceToCallback map[uint]uintptr | |||
| 18 | + | callbackRefCount map[uintptr]int | |||
| 17 | }{ | 19 | }{ | ||
| 18 | refs: make(map[uintptr]uintptr), | 20 | refs: make(map[uintptr]uintptr), | ||
| 19 | closures: make(map[uintptr]interface{}), | 21 | closures: make(map[uintptr]interface{}), | ||
| 20 | handlerToCallback: make(map[uint]uintptr), | 22 | handlerToCallback: make(map[uint]uintptr), | ||
| 23 | + | sourceToCallback: make(map[uint]uintptr), | |||
| 24 | + | callbackRefCount: make(map[uintptr]int), | |||
| 21 | } | 25 | } | ||
| 22 | 26 | ||||
| 23 | // GetCallback retrives a callback reference by value. | 27 | // GetCallback retrives a callback reference by value. | ||
| @@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||||
| 44 | callbacks.Lock() | 48 | callbacks.Lock() | ||
| 45 | callbacks.refs[cbPtr] = refPtr | 49 | callbacks.refs[cbPtr] = refPtr | ||
| 46 | callbacks.closures[cbPtr] = closure | 50 | callbacks.closures[cbPtr] = closure | ||
| 51 | + | if _, ok := callbacks.callbackRefCount[cbPtr]; !ok { | |||
| 52 | + | callbacks.callbackRefCount[cbPtr] = 1 | |||
| 53 | + | } | |||
| 47 | callbacks.Unlock() | 54 | callbacks.Unlock() | ||
| 48 | } | 55 | } | ||
| 49 | 56 | ||||
| @@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||||
| 52 | // Users should not need to call this. | 59 | // Users should not need to call this. | ||
| 53 | func RemoveCallback(cbPtr uintptr) { | 60 | func RemoveCallback(cbPtr uintptr) { | ||
| 54 | callbacks.Lock() | 61 | callbacks.Lock() | ||
| 62 | + | for handlerID, mappedCbPtr := range callbacks.handlerToCallback { | |||
| 63 | + | if mappedCbPtr == cbPtr { | |||
| 64 | + | delete(callbacks.handlerToCallback, handlerID) | |||
| 65 | + | } | |||
| 66 | + | } | |||
| 67 | + | for sourceID, mappedCbPtr := range callbacks.sourceToCallback { | |||
| 68 | + | if mappedCbPtr == cbPtr { | |||
| 69 | + | delete(callbacks.sourceToCallback, sourceID) | |||
| 70 | + | } | |||
| 71 | + | } | |||
| 55 | delete(callbacks.refs, cbPtr) | 72 | delete(callbacks.refs, cbPtr) | ||
| 56 | delete(callbacks.closures, cbPtr) | 73 | delete(callbacks.closures, cbPtr) | ||
| 74 | + | delete(callbacks.callbackRefCount, cbPtr) | |||
| 57 | callbacks.Unlock() | 75 | callbacks.Unlock() | ||
| 58 | } | 76 | } | ||
| 59 | 77 | ||||
| 78 | + | // acquireCallbackRef increments callbackRefCount for cbPtr. | |||
| 79 | + | // Caller must hold callbacks.Lock(). | |||
| 80 | + | func acquireCallbackRef(cbPtr uintptr) { | |||
| 81 | + | callbacks.callbackRefCount[cbPtr]++ | |||
| 82 | + | } | |||
| 83 | + | ||||
| 84 | + | func hasCallbackMappings(cbPtr uintptr) bool { | |||
| 85 | + | for _, mappedCbPtr := range callbacks.handlerToCallback { | |||
| 86 | + | if mappedCbPtr == cbPtr { | |||
| 87 | + | return true | |||
| 88 | + | } | |||
| 89 | + | } | |||
| 90 | + | for _, mappedCbPtr := range callbacks.sourceToCallback { | |||
| 91 | + | if mappedCbPtr == cbPtr { | |||
| 92 | + | return true | |||
| 93 | + | } | |||
| 94 | + | } | |||
| 95 | + | return false | |||
| 96 | + | } | |||
| 97 | + | ||||
| 98 | + | // releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback | |||
| 99 | + | // data when it reaches zero. | |||
| 100 | + | // Caller must hold callbacks.Lock(). | |||
| 101 | + | // Handler/source mappings to cbPtr are expected to be removed or replaced by | |||
| 102 | + | // the caller (RemoveCallbackByHandler, RemoveCallbackBySource, | |||
| 103 | + | // SaveHandlerMapping, SaveSourceMapping). | |||
| 104 | + | func releaseCallbackRef(cbPtr uintptr) { | |||
| 105 | + | count, ok := callbacks.callbackRefCount[cbPtr] | |||
| 106 | + | if !ok { | |||
| 107 | + | return | |||
| 108 | + | } | |||
| 109 | + | ||||
| 110 | + | count-- | |||
| 111 | + | if count > 0 { | |||
| 112 | + | callbacks.callbackRefCount[cbPtr] = count | |||
| 113 | + | return | |||
| 114 | + | } | |||
| 115 | + | ||||
| 116 | + | delete(callbacks.callbackRefCount, cbPtr) | |||
| 117 | + | delete(callbacks.refs, cbPtr) | |||
| 118 | + | delete(callbacks.closures, cbPtr) | |||
| 119 | + | } | |||
| 120 | + | ||||
| 60 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | 121 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | ||
| 61 | // so that DisconnectSignal can clean up the callback registry. | 122 | // so that DisconnectSignal can clean up the callback registry. | ||
| 62 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | 123 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | ||
| 124 | + | if handlerID == 0 { | |||
| 125 | + | return | |||
| 126 | + | } | |||
| 127 | + | ||||
| 63 | callbacks.Lock() | 128 | callbacks.Lock() | ||
| 129 | + | defer callbacks.Unlock() | |||
| 130 | + | if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |||
| 131 | + | if prevCbPtr == cbPtr { | |||
| 132 | + | return | |||
| 133 | + | } | |||
| 134 | + | releaseCallbackRef(prevCbPtr) | |||
| 135 | + | if !hasCallbackMappings(prevCbPtr) { | |||
| 136 | + | releaseCallbackRef(prevCbPtr) | |||
| 137 | + | } | |||
| 138 | + | } | |||
| 64 | callbacks.handlerToCallback[handlerID] = cbPtr | 139 | callbacks.handlerToCallback[handlerID] = cbPtr | ||
| 65 | - | callbacks.Unlock() | 140 | + | acquireCallbackRef(cbPtr) |
| 66 | } | 141 | } | ||
| 67 | 142 | ||||
| 68 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | 143 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | ||
| 69 | func RemoveCallbackByHandler(handlerID uint) { | 144 | func RemoveCallbackByHandler(handlerID uint) { | ||
| 70 | callbacks.Lock() | 145 | callbacks.Lock() | ||
| 71 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | 146 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | ||
| 72 | - | delete(callbacks.refs, cbPtr) | |||
| 73 | - | delete(callbacks.closures, cbPtr) | |||
| 74 | delete(callbacks.handlerToCallback, handlerID) | 147 | delete(callbacks.handlerToCallback, handlerID) | ||
| 148 | + | releaseCallbackRef(cbPtr) | |||
| 149 | + | if !hasCallbackMappings(cbPtr) { | |||
| 150 | + | releaseCallbackRef(cbPtr) | |||
| 151 | + | } | |||
| 152 | + | } | |||
| 153 | + | callbacks.Unlock() | |||
| 154 | + | } | |||
| 155 | + | ||||
| 156 | + | // SaveSourceMapping records a source ID -> callback pointer mapping. | |||
| 157 | + | func SaveSourceMapping(sourceID uint, cbPtr uintptr) { | |||
| 158 | + | if sourceID == 0 { | |||
| 159 | + | return | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | callbacks.Lock() | |||
| 163 | + | defer callbacks.Unlock() | |||
| 164 | + | if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |||
| 165 | + | if prevCbPtr == cbPtr { | |||
| 166 | + | return | |||
| 167 | + | } | |||
| 168 | + | releaseCallbackRef(prevCbPtr) | |||
| 169 | + | if !hasCallbackMappings(prevCbPtr) { | |||
| 170 | + | releaseCallbackRef(prevCbPtr) | |||
| 171 | + | } | |||
| 172 | + | } | |||
| 173 | + | callbacks.sourceToCallback[sourceID] = cbPtr | |||
| 174 | + | acquireCallbackRef(cbPtr) | |||
| 175 | + | } | |||
| 176 | + | ||||
| 177 | + | // RemoveCallbackBySource removes a callback mapping using a source ID. | |||
| 178 | + | func RemoveCallbackBySource(sourceID uint) { | |||
| 179 | + | callbacks.Lock() | |||
| 180 | + | if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |||
| 181 | + | delete(callbacks.sourceToCallback, sourceID) | |||
| 182 | + | releaseCallbackRef(cbPtr) | |||
| 183 | + | if !hasCallbackMappings(cbPtr) { | |||
| 184 | + | releaseCallbackRef(cbPtr) | |||
| 185 | + | } | |||
| 75 | } | 186 | } | ||
| 76 | callbacks.Unlock() | 187 | callbacks.Unlock() | ||
| 77 | } | 188 | } | ||
M templates/go +19 -0
| @@ -288,6 +288,19 @@ const ( | |||
| 288 | 288 | ) | |
| 289 | 289 | {{end}} | |
| 290 | 290 | ||
| 291 | + | {{- define "glib_source_mapping_post_hook" -}} | |
| 292 | + | {{- if or (eq .Name "ChildWatchAdd") (eq .Name "ChildWatchAddFull") (eq .Name "IdleAdd") (eq .Name "IdleAddFull") (eq .Name "IdleAddOnce") (eq .Name "TimeoutAdd") (eq .Name "TimeoutAddFull") (eq .Name "TimeoutAddOnce") (eq .Name "TimeoutAddSeconds") (eq .Name "TimeoutAddSecondsFull") (eq .Name "TimeoutAddSecondsOnce")}} | |
| 293 | + | if FunctionVar != nil { | |
| 294 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 295 | + | } | |
| 296 | + | {{- end}} | |
| 297 | + | {{- if eq .Name "SourceRemove"}} | |
| 298 | + | if cret { | |
| 299 | + | RemoveCallbackBySource(TagVar) | |
| 300 | + | } | |
| 301 | + | {{- end}} | |
| 302 | + | {{- end}} | |
| 303 | + | ||
| 291 | 304 | {{range .Functions -}} | |
| 292 | 305 | var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}} | |
| 293 | 306 | ||
| @@ -315,6 +328,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} { | |||
| 315 | 328 | defer core.GFreeNullable({{.Name}}Ptr) | |
| 316 | 329 | {{end}} | |
| 317 | 330 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | |
| 331 | + | {{- if eq $.PkgName "glib"}} | |
| 332 | + | {{template "glib_source_mapping_post_hook" .}} | |
| 333 | + | {{- end}} | |
| 318 | 334 | {{.Ret.Fmt $NotGObject}} | |
| 319 | 335 | } | |
| 320 | 336 | {{end}} | |
| @@ -545,6 +561,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} { | |||
| 545 | 561 | defer core.GFreeNullable({{.Name}}Ptr) | |
| 546 | 562 | {{end}} | |
| 547 | 563 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | |
| 564 | + | {{- if eq $.PkgName "glib"}} | |
| 565 | + | {{template "glib_source_mapping_post_hook" .}} | |
| 566 | + | {{- end}} | |
| 548 | 567 | {{.Ret.Fmt $NotGObject}} | |
| 549 | 568 | } | |
| 550 | 569 | {{end}} | |
| @@ -288,6 +288,19 @@ const ( | |||||
| 288 | ) | 288 | ) | ||
| 289 | {{end}} | 289 | {{end}} | ||
| 290 | 290 | ||||
| 291 | + | {{- define "glib_source_mapping_post_hook" -}} | |||
| 292 | + | {{- if or (eq .Name "ChildWatchAdd") (eq .Name "ChildWatchAddFull") (eq .Name "IdleAdd") (eq .Name "IdleAddFull") (eq .Name "IdleAddOnce") (eq .Name "TimeoutAdd") (eq .Name "TimeoutAddFull") (eq .Name "TimeoutAddOnce") (eq .Name "TimeoutAddSeconds") (eq .Name "TimeoutAddSecondsFull") (eq .Name "TimeoutAddSecondsOnce")}} | |||
| 293 | + | if FunctionVar != nil { | |||
| 294 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 295 | + | } | |||
| 296 | + | {{- end}} | |||
| 297 | + | {{- if eq .Name "SourceRemove"}} | |||
| 298 | + | if cret { | |||
| 299 | + | RemoveCallbackBySource(TagVar) | |||
| 300 | + | } | |||
| 301 | + | {{- end}} | |||
| 302 | + | {{- end}} | |||
| 303 | + | ||||
| 291 | {{range .Functions -}} | 304 | {{range .Functions -}} | ||
| 292 | var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}} | 305 | var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}} | ||
| 293 | 306 | ||||
| @@ -315,6 +328,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} { | |||||
| 315 | defer core.GFreeNullable({{.Name}}Ptr) | 328 | defer core.GFreeNullable({{.Name}}Ptr) | ||
| 316 | {{end}} | 329 | {{end}} | ||
| 317 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | 330 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | ||
| 331 | + | {{- if eq $.PkgName "glib"}} | |||
| 332 | + | {{template "glib_source_mapping_post_hook" .}} | |||
| 333 | + | {{- end}} | |||
| 318 | {{.Ret.Fmt $NotGObject}} | 334 | {{.Ret.Fmt $NotGObject}} | ||
| 319 | } | 335 | } | ||
| 320 | {{end}} | 336 | {{end}} | ||
| @@ -545,6 +561,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} { | |||||
| 545 | defer core.GFreeNullable({{.Name}}Ptr) | 561 | defer core.GFreeNullable({{.Name}}Ptr) | ||
| 546 | {{end}} | 562 | {{end}} | ||
| 547 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | 563 | {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}}) | ||
| 564 | + | {{- if eq $.PkgName "glib"}} | |||
| 565 | + | {{template "glib_source_mapping_post_hook" .}} | |||
| 566 | + | {{- end}} | |||
| 548 | {{.Ret.Fmt $NotGObject}} | 567 | {{.Ret.Fmt $NotGObject}} | ||
| 549 | } | 568 | } | ||
| 550 | {{end}} | 569 | {{end}} | ||
M templates/gobject +13 -1
| @@ -2,6 +2,7 @@ package gobject | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "reflect" | |
| 5 | + | "unsafe" | |
| 5 | 6 | ||
| 6 | 7 | "github.com/jwijenbergh/puregotk/v4/glib" | |
| 7 | 8 | ) | |
| @@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) { | |||
| 37 | 38 | } | |
| 38 | 39 | ||
| 39 | 40 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | |
| 40 | - | return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb)) | |
| 41 | + | cbPtr := uintptr(unsafe.Pointer(cb)) | |
| 42 | + | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |
| 43 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |
| 44 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |
| 45 | + | return handlerID | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | cbRefPtr := glib.NewCallback(cb) | |
| 49 | + | glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb) | |
| 50 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |
| 51 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |
| 52 | + | return handlerID | |
| 41 | 53 | } | |
| 42 | 54 | ||
| 43 | 55 | func (o Object) DisconnectSignal(handler uint) { | |
| @@ -2,6 +2,7 @@ package gobject | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "reflect" | 4 | "reflect" | ||
| 5 | + | "unsafe" | |||
| 5 | 6 | ||||
| 6 | "github.com/jwijenbergh/puregotk/v4/glib" | 7 | "github.com/jwijenbergh/puregotk/v4/glib" | ||
| 7 | ) | 8 | ) | ||
| @@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) { | |||||
| 37 | } | 38 | } | ||
| 38 | 39 | ||||
| 39 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | 40 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | ||
| 40 | - | return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb)) | 41 | + | cbPtr := uintptr(unsafe.Pointer(cb)) |
| 42 | + | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |||
| 43 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |||
| 44 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |||
| 45 | + | return handlerID | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | cbRefPtr := glib.NewCallback(cb) | |||
| 49 | + | glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb) | |||
| 50 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |||
| 51 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |||
| 52 | + | return handlerID | |||
| 41 | } | 53 | } | ||
| 42 | 54 | ||||
| 43 | func (o Object) DisconnectSignal(handler uint) { | 55 | func (o Object) DisconnectSignal(handler uint) { | ||
M v4/glib/gmain.go +36 -0
| @@ -1716,6 +1716,9 @@ func ChildWatchAdd(PidVar Pid, FunctionVar *ChildWatchFunc, DataVar uintptr) uin | |||
| 1716 | 1716 | } | |
| 1717 | 1717 | ||
| 1718 | 1718 | cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar) | |
| 1719 | + | if FunctionVar != nil { | |
| 1720 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 1721 | + | } | |
| 1719 | 1722 | return cret | |
| 1720 | 1723 | } | |
| 1721 | 1724 | ||
| @@ -1779,6 +1782,9 @@ func ChildWatchAddFull(PriorityVar int, PidVar Pid, FunctionVar *ChildWatchFunc, | |||
| 1779 | 1782 | } | |
| 1780 | 1783 | ||
| 1781 | 1784 | cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef) | |
| 1785 | + | if FunctionVar != nil { | |
| 1786 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 1787 | + | } | |
| 1782 | 1788 | return cret | |
| 1783 | 1789 | } | |
| 1784 | 1790 | ||
| @@ -1948,6 +1954,9 @@ func IdleAdd(FunctionVar *SourceFunc, DataVar uintptr) uint { | |||
| 1948 | 1954 | } | |
| 1949 | 1955 | ||
| 1950 | 1956 | cret := xIdleAdd(FunctionVarRef, DataVar) | |
| 1957 | + | if FunctionVar != nil { | |
| 1958 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 1959 | + | } | |
| 1951 | 1960 | return cret | |
| 1952 | 1961 | } | |
| 1953 | 1962 | ||
| @@ -2000,6 +2009,9 @@ func IdleAddFull(PriorityVar int, FunctionVar *SourceFunc, DataVar uintptr, Noti | |||
| 2000 | 2009 | } | |
| 2001 | 2010 | ||
| 2002 | 2011 | cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef) | |
| 2012 | + | if FunctionVar != nil { | |
| 2013 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2014 | + | } | |
| 2003 | 2015 | return cret | |
| 2004 | 2016 | } | |
| 2005 | 2017 | ||
| @@ -2033,6 +2045,9 @@ func IdleAddOnce(FunctionVar *SourceOnceFunc, DataVar uintptr) uint { | |||
| 2033 | 2045 | } | |
| 2034 | 2046 | ||
| 2035 | 2047 | cret := xIdleAddOnce(FunctionVarRef, DataVar) | |
| 2048 | + | if FunctionVar != nil { | |
| 2049 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2050 | + | } | |
| 2036 | 2051 | return cret | |
| 2037 | 2052 | } | |
| 2038 | 2053 | ||
| @@ -2266,6 +2281,9 @@ var xSourceRemove func(uint) bool | |||
| 2266 | 2281 | func SourceRemove(TagVar uint) bool { | |
| 2267 | 2282 | ||
| 2268 | 2283 | cret := xSourceRemove(TagVar) | |
| 2284 | + | if cret { | |
| 2285 | + | RemoveCallbackBySource(TagVar) | |
| 2286 | + | } | |
| 2269 | 2287 | return cret | |
| 2270 | 2288 | } | |
| 2271 | 2289 | ||
| @@ -2371,6 +2389,9 @@ func TimeoutAdd(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintptr) uint | |||
| 2371 | 2389 | } | |
| 2372 | 2390 | ||
| 2373 | 2391 | cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar) | |
| 2392 | + | if FunctionVar != nil { | |
| 2393 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2394 | + | } | |
| 2374 | 2395 | return cret | |
| 2375 | 2396 | } | |
| 2376 | 2397 | ||
| @@ -2437,6 +2458,9 @@ func TimeoutAddFull(PriorityVar int, IntervalVar uint, FunctionVar *SourceFunc, | |||
| 2437 | 2458 | } | |
| 2438 | 2459 | ||
| 2439 | 2460 | cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | |
| 2461 | + | if FunctionVar != nil { | |
| 2462 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2463 | + | } | |
| 2440 | 2464 | return cret | |
| 2441 | 2465 | } | |
| 2442 | 2466 | ||
| @@ -2467,6 +2491,9 @@ func TimeoutAddOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVar uintp | |||
| 2467 | 2491 | } | |
| 2468 | 2492 | ||
| 2469 | 2493 | cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar) | |
| 2494 | + | if FunctionVar != nil { | |
| 2495 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2496 | + | } | |
| 2470 | 2497 | return cret | |
| 2471 | 2498 | } | |
| 2472 | 2499 | ||
| @@ -2513,6 +2540,9 @@ func TimeoutAddSeconds(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintpt | |||
| 2513 | 2540 | } | |
| 2514 | 2541 | ||
| 2515 | 2542 | cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar) | |
| 2543 | + | if FunctionVar != nil { | |
| 2544 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2545 | + | } | |
| 2516 | 2546 | return cret | |
| 2517 | 2547 | } | |
| 2518 | 2548 | ||
| @@ -2591,6 +2621,9 @@ func TimeoutAddSecondsFull(PriorityVar int, IntervalVar uint, FunctionVar *Sourc | |||
| 2591 | 2621 | } | |
| 2592 | 2622 | ||
| 2593 | 2623 | cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | |
| 2624 | + | if FunctionVar != nil { | |
| 2625 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2626 | + | } | |
| 2594 | 2627 | return cret | |
| 2595 | 2628 | } | |
| 2596 | 2629 | ||
| @@ -2616,6 +2649,9 @@ func TimeoutAddSecondsOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVa | |||
| 2616 | 2649 | } | |
| 2617 | 2650 | ||
| 2618 | 2651 | cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar) | |
| 2652 | + | if FunctionVar != nil { | |
| 2653 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |
| 2654 | + | } | |
| 2619 | 2655 | return cret | |
| 2620 | 2656 | } | |
| 2621 | 2657 | ||
| @@ -1716,6 +1716,9 @@ func ChildWatchAdd(PidVar Pid, FunctionVar *ChildWatchFunc, DataVar uintptr) uin | |||||
| 1716 | } | 1716 | } | ||
| 1717 | 1717 | ||||
| 1718 | cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar) | 1718 | cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar) | ||
| 1719 | + | if FunctionVar != nil { | |||
| 1720 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 1721 | + | } | |||
| 1719 | return cret | 1722 | return cret | ||
| 1720 | } | 1723 | } | ||
| 1721 | 1724 | ||||
| @@ -1779,6 +1782,9 @@ func ChildWatchAddFull(PriorityVar int, PidVar Pid, FunctionVar *ChildWatchFunc, | |||||
| 1779 | } | 1782 | } | ||
| 1780 | 1783 | ||||
| 1781 | cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef) | 1784 | cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef) | ||
| 1785 | + | if FunctionVar != nil { | |||
| 1786 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 1787 | + | } | |||
| 1782 | return cret | 1788 | return cret | ||
| 1783 | } | 1789 | } | ||
| 1784 | 1790 | ||||
| @@ -1948,6 +1954,9 @@ func IdleAdd(FunctionVar *SourceFunc, DataVar uintptr) uint { | |||||
| 1948 | } | 1954 | } | ||
| 1949 | 1955 | ||||
| 1950 | cret := xIdleAdd(FunctionVarRef, DataVar) | 1956 | cret := xIdleAdd(FunctionVarRef, DataVar) | ||
| 1957 | + | if FunctionVar != nil { | |||
| 1958 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 1959 | + | } | |||
| 1951 | return cret | 1960 | return cret | ||
| 1952 | } | 1961 | } | ||
| 1953 | 1962 | ||||
| @@ -2000,6 +2009,9 @@ func IdleAddFull(PriorityVar int, FunctionVar *SourceFunc, DataVar uintptr, Noti | |||||
| 2000 | } | 2009 | } | ||
| 2001 | 2010 | ||||
| 2002 | cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef) | 2011 | cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef) | ||
| 2012 | + | if FunctionVar != nil { | |||
| 2013 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2014 | + | } | |||
| 2003 | return cret | 2015 | return cret | ||
| 2004 | } | 2016 | } | ||
| 2005 | 2017 | ||||
| @@ -2033,6 +2045,9 @@ func IdleAddOnce(FunctionVar *SourceOnceFunc, DataVar uintptr) uint { | |||||
| 2033 | } | 2045 | } | ||
| 2034 | 2046 | ||||
| 2035 | cret := xIdleAddOnce(FunctionVarRef, DataVar) | 2047 | cret := xIdleAddOnce(FunctionVarRef, DataVar) | ||
| 2048 | + | if FunctionVar != nil { | |||
| 2049 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2050 | + | } | |||
| 2036 | return cret | 2051 | return cret | ||
| 2037 | } | 2052 | } | ||
| 2038 | 2053 | ||||
| @@ -2266,6 +2281,9 @@ var xSourceRemove func(uint) bool | |||||
| 2266 | func SourceRemove(TagVar uint) bool { | 2281 | func SourceRemove(TagVar uint) bool { | ||
| 2267 | 2282 | ||||
| 2268 | cret := xSourceRemove(TagVar) | 2283 | cret := xSourceRemove(TagVar) | ||
| 2284 | + | if cret { | |||
| 2285 | + | RemoveCallbackBySource(TagVar) | |||
| 2286 | + | } | |||
| 2269 | return cret | 2287 | return cret | ||
| 2270 | } | 2288 | } | ||
| 2271 | 2289 | ||||
| @@ -2371,6 +2389,9 @@ func TimeoutAdd(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintptr) uint | |||||
| 2371 | } | 2389 | } | ||
| 2372 | 2390 | ||||
| 2373 | cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar) | 2391 | cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar) | ||
| 2392 | + | if FunctionVar != nil { | |||
| 2393 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2394 | + | } | |||
| 2374 | return cret | 2395 | return cret | ||
| 2375 | } | 2396 | } | ||
| 2376 | 2397 | ||||
| @@ -2437,6 +2458,9 @@ func TimeoutAddFull(PriorityVar int, IntervalVar uint, FunctionVar *SourceFunc, | |||||
| 2437 | } | 2458 | } | ||
| 2438 | 2459 | ||||
| 2439 | cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | 2460 | cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | ||
| 2461 | + | if FunctionVar != nil { | |||
| 2462 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2463 | + | } | |||
| 2440 | return cret | 2464 | return cret | ||
| 2441 | } | 2465 | } | ||
| 2442 | 2466 | ||||
| @@ -2467,6 +2491,9 @@ func TimeoutAddOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVar uintp | |||||
| 2467 | } | 2491 | } | ||
| 2468 | 2492 | ||||
| 2469 | cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar) | 2493 | cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar) | ||
| 2494 | + | if FunctionVar != nil { | |||
| 2495 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2496 | + | } | |||
| 2470 | return cret | 2497 | return cret | ||
| 2471 | } | 2498 | } | ||
| 2472 | 2499 | ||||
| @@ -2513,6 +2540,9 @@ func TimeoutAddSeconds(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintpt | |||||
| 2513 | } | 2540 | } | ||
| 2514 | 2541 | ||||
| 2515 | cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar) | 2542 | cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar) | ||
| 2543 | + | if FunctionVar != nil { | |||
| 2544 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2545 | + | } | |||
| 2516 | return cret | 2546 | return cret | ||
| 2517 | } | 2547 | } | ||
| 2518 | 2548 | ||||
| @@ -2591,6 +2621,9 @@ func TimeoutAddSecondsFull(PriorityVar int, IntervalVar uint, FunctionVar *Sourc | |||||
| 2591 | } | 2621 | } | ||
| 2592 | 2622 | ||||
| 2593 | cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | 2623 | cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef) | ||
| 2624 | + | if FunctionVar != nil { | |||
| 2625 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2626 | + | } | |||
| 2594 | return cret | 2627 | return cret | ||
| 2595 | } | 2628 | } | ||
| 2596 | 2629 | ||||
| @@ -2616,6 +2649,9 @@ func TimeoutAddSecondsOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVa | |||||
| 2616 | } | 2649 | } | ||
| 2617 | 2650 | ||||
| 2618 | cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar) | 2651 | cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar) | ||
| 2652 | + | if FunctionVar != nil { | |||
| 2653 | + | SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar))) | |||
| 2654 | + | } | |||
| 2619 | return cret | 2655 | return cret | ||
| 2620 | } | 2656 | } | ||
| 2621 | 2657 | ||||
M v4/glib/more.go +114 -3
| @@ -14,10 +14,14 @@ var callbacks = struct { | |||
| 14 | 14 | refs map[uintptr]uintptr | |
| 15 | 15 | closures map[uintptr]interface{} | |
| 16 | 16 | handlerToCallback map[uint]uintptr | |
| 17 | + | sourceToCallback map[uint]uintptr | |
| 18 | + | callbackRefCount map[uintptr]int | |
| 17 | 19 | }{ | |
| 18 | 20 | refs: make(map[uintptr]uintptr), | |
| 19 | 21 | closures: make(map[uintptr]interface{}), | |
| 20 | 22 | handlerToCallback: make(map[uint]uintptr), | |
| 23 | + | sourceToCallback: make(map[uint]uintptr), | |
| 24 | + | callbackRefCount: make(map[uintptr]int), | |
| 21 | 25 | } | |
| 22 | 26 | ||
| 23 | 27 | // GetCallback retrives a callback reference by value. | |
| @@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||
| 44 | 48 | callbacks.Lock() | |
| 45 | 49 | callbacks.refs[cbPtr] = refPtr | |
| 46 | 50 | callbacks.closures[cbPtr] = closure | |
| 51 | + | if _, ok := callbacks.callbackRefCount[cbPtr]; !ok { | |
| 52 | + | callbacks.callbackRefCount[cbPtr] = 1 | |
| 53 | + | } | |
| 47 | 54 | callbacks.Unlock() | |
| 48 | 55 | } | |
| 49 | 56 | ||
| @@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||
| 52 | 59 | // Users should not need to call this. | |
| 53 | 60 | func RemoveCallback(cbPtr uintptr) { | |
| 54 | 61 | callbacks.Lock() | |
| 62 | + | for handlerID, mappedCbPtr := range callbacks.handlerToCallback { | |
| 63 | + | if mappedCbPtr == cbPtr { | |
| 64 | + | delete(callbacks.handlerToCallback, handlerID) | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | for sourceID, mappedCbPtr := range callbacks.sourceToCallback { | |
| 68 | + | if mappedCbPtr == cbPtr { | |
| 69 | + | delete(callbacks.sourceToCallback, sourceID) | |
| 70 | + | } | |
| 71 | + | } | |
| 55 | 72 | delete(callbacks.refs, cbPtr) | |
| 56 | 73 | delete(callbacks.closures, cbPtr) | |
| 74 | + | delete(callbacks.callbackRefCount, cbPtr) | |
| 57 | 75 | callbacks.Unlock() | |
| 58 | 76 | } | |
| 59 | 77 | ||
| 78 | + | // acquireCallbackRef increments callbackRefCount for cbPtr. | |
| 79 | + | // Caller must hold callbacks.Lock(). | |
| 80 | + | func acquireCallbackRef(cbPtr uintptr) { | |
| 81 | + | callbacks.callbackRefCount[cbPtr]++ | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func hasCallbackMappings(cbPtr uintptr) bool { | |
| 85 | + | for _, mappedCbPtr := range callbacks.handlerToCallback { | |
| 86 | + | if mappedCbPtr == cbPtr { | |
| 87 | + | return true | |
| 88 | + | } | |
| 89 | + | } | |
| 90 | + | for _, mappedCbPtr := range callbacks.sourceToCallback { | |
| 91 | + | if mappedCbPtr == cbPtr { | |
| 92 | + | return true | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | return false | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | // releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback | |
| 99 | + | // data when it reaches zero. | |
| 100 | + | // Caller must hold callbacks.Lock(). | |
| 101 | + | // Handler/source mappings to cbPtr are expected to be removed or replaced by | |
| 102 | + | // the caller (RemoveCallbackByHandler, RemoveCallbackBySource, | |
| 103 | + | // SaveHandlerMapping, SaveSourceMapping). | |
| 104 | + | func releaseCallbackRef(cbPtr uintptr) { | |
| 105 | + | count, ok := callbacks.callbackRefCount[cbPtr] | |
| 106 | + | if !ok { | |
| 107 | + | return | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | count-- | |
| 111 | + | if count > 0 { | |
| 112 | + | callbacks.callbackRefCount[cbPtr] = count | |
| 113 | + | return | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | delete(callbacks.callbackRefCount, cbPtr) | |
| 117 | + | delete(callbacks.refs, cbPtr) | |
| 118 | + | delete(callbacks.closures, cbPtr) | |
| 119 | + | } | |
| 120 | + | ||
| 60 | 121 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | |
| 61 | 122 | // so that DisconnectSignal can clean up the callback registry. | |
| 62 | 123 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | |
| 124 | + | if handlerID == 0 { | |
| 125 | + | return | |
| 126 | + | } | |
| 127 | + | ||
| 63 | 128 | callbacks.Lock() | |
| 129 | + | defer callbacks.Unlock() | |
| 130 | + | if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |
| 131 | + | if prevCbPtr == cbPtr { | |
| 132 | + | return | |
| 133 | + | } | |
| 134 | + | releaseCallbackRef(prevCbPtr) | |
| 135 | + | if !hasCallbackMappings(prevCbPtr) { | |
| 136 | + | releaseCallbackRef(prevCbPtr) | |
| 137 | + | } | |
| 138 | + | } | |
| 64 | 139 | callbacks.handlerToCallback[handlerID] = cbPtr | |
| 65 | - | callbacks.Unlock() | |
| 140 | + | acquireCallbackRef(cbPtr) | |
| 66 | 141 | } | |
| 67 | 142 | ||
| 68 | 143 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | |
| 69 | 144 | func RemoveCallbackByHandler(handlerID uint) { | |
| 70 | 145 | callbacks.Lock() | |
| 71 | 146 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |
| 72 | - | delete(callbacks.refs, cbPtr) | |
| 73 | - | delete(callbacks.closures, cbPtr) | |
| 74 | 147 | delete(callbacks.handlerToCallback, handlerID) | |
| 148 | + | releaseCallbackRef(cbPtr) | |
| 149 | + | if !hasCallbackMappings(cbPtr) { | |
| 150 | + | releaseCallbackRef(cbPtr) | |
| 151 | + | } | |
| 152 | + | } | |
| 153 | + | callbacks.Unlock() | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | // SaveSourceMapping records a source ID -> callback pointer mapping. | |
| 157 | + | func SaveSourceMapping(sourceID uint, cbPtr uintptr) { | |
| 158 | + | if sourceID == 0 { | |
| 159 | + | return | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | callbacks.Lock() | |
| 163 | + | defer callbacks.Unlock() | |
| 164 | + | if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |
| 165 | + | if prevCbPtr == cbPtr { | |
| 166 | + | return | |
| 167 | + | } | |
| 168 | + | releaseCallbackRef(prevCbPtr) | |
| 169 | + | if !hasCallbackMappings(prevCbPtr) { | |
| 170 | + | releaseCallbackRef(prevCbPtr) | |
| 171 | + | } | |
| 172 | + | } | |
| 173 | + | callbacks.sourceToCallback[sourceID] = cbPtr | |
| 174 | + | acquireCallbackRef(cbPtr) | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | // RemoveCallbackBySource removes a callback mapping using a source ID. | |
| 178 | + | func RemoveCallbackBySource(sourceID uint) { | |
| 179 | + | callbacks.Lock() | |
| 180 | + | if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |
| 181 | + | delete(callbacks.sourceToCallback, sourceID) | |
| 182 | + | releaseCallbackRef(cbPtr) | |
| 183 | + | if !hasCallbackMappings(cbPtr) { | |
| 184 | + | releaseCallbackRef(cbPtr) | |
| 185 | + | } | |
| 75 | 186 | } | |
| 76 | 187 | callbacks.Unlock() | |
| 77 | 188 | } | |
| @@ -14,10 +14,14 @@ var callbacks = struct { | |||||
| 14 | refs map[uintptr]uintptr | 14 | refs map[uintptr]uintptr | ||
| 15 | closures map[uintptr]interface{} | 15 | closures map[uintptr]interface{} | ||
| 16 | handlerToCallback map[uint]uintptr | 16 | handlerToCallback map[uint]uintptr | ||
| 17 | + | sourceToCallback map[uint]uintptr | |||
| 18 | + | callbackRefCount map[uintptr]int | |||
| 17 | }{ | 19 | }{ | ||
| 18 | refs: make(map[uintptr]uintptr), | 20 | refs: make(map[uintptr]uintptr), | ||
| 19 | closures: make(map[uintptr]interface{}), | 21 | closures: make(map[uintptr]interface{}), | ||
| 20 | handlerToCallback: make(map[uint]uintptr), | 22 | handlerToCallback: make(map[uint]uintptr), | ||
| 23 | + | sourceToCallback: make(map[uint]uintptr), | |||
| 24 | + | callbackRefCount: make(map[uintptr]int), | |||
| 21 | } | 25 | } | ||
| 22 | 26 | ||||
| 23 | // GetCallback retrives a callback reference by value. | 27 | // GetCallback retrives a callback reference by value. | ||
| @@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||||
| 44 | callbacks.Lock() | 48 | callbacks.Lock() | ||
| 45 | callbacks.refs[cbPtr] = refPtr | 49 | callbacks.refs[cbPtr] = refPtr | ||
| 46 | callbacks.closures[cbPtr] = closure | 50 | callbacks.closures[cbPtr] = closure | ||
| 51 | + | if _, ok := callbacks.callbackRefCount[cbPtr]; !ok { | |||
| 52 | + | callbacks.callbackRefCount[cbPtr] = 1 | |||
| 53 | + | } | |||
| 47 | callbacks.Unlock() | 54 | callbacks.Unlock() | ||
| 48 | } | 55 | } | ||
| 49 | 56 | ||||
| @@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{}) | |||||
| 52 | // Users should not need to call this. | 59 | // Users should not need to call this. | ||
| 53 | func RemoveCallback(cbPtr uintptr) { | 60 | func RemoveCallback(cbPtr uintptr) { | ||
| 54 | callbacks.Lock() | 61 | callbacks.Lock() | ||
| 62 | + | for handlerID, mappedCbPtr := range callbacks.handlerToCallback { | |||
| 63 | + | if mappedCbPtr == cbPtr { | |||
| 64 | + | delete(callbacks.handlerToCallback, handlerID) | |||
| 65 | + | } | |||
| 66 | + | } | |||
| 67 | + | for sourceID, mappedCbPtr := range callbacks.sourceToCallback { | |||
| 68 | + | if mappedCbPtr == cbPtr { | |||
| 69 | + | delete(callbacks.sourceToCallback, sourceID) | |||
| 70 | + | } | |||
| 71 | + | } | |||
| 55 | delete(callbacks.refs, cbPtr) | 72 | delete(callbacks.refs, cbPtr) | ||
| 56 | delete(callbacks.closures, cbPtr) | 73 | delete(callbacks.closures, cbPtr) | ||
| 74 | + | delete(callbacks.callbackRefCount, cbPtr) | |||
| 57 | callbacks.Unlock() | 75 | callbacks.Unlock() | ||
| 58 | } | 76 | } | ||
| 59 | 77 | ||||
| 78 | + | // acquireCallbackRef increments callbackRefCount for cbPtr. | |||
| 79 | + | // Caller must hold callbacks.Lock(). | |||
| 80 | + | func acquireCallbackRef(cbPtr uintptr) { | |||
| 81 | + | callbacks.callbackRefCount[cbPtr]++ | |||
| 82 | + | } | |||
| 83 | + | ||||
| 84 | + | func hasCallbackMappings(cbPtr uintptr) bool { | |||
| 85 | + | for _, mappedCbPtr := range callbacks.handlerToCallback { | |||
| 86 | + | if mappedCbPtr == cbPtr { | |||
| 87 | + | return true | |||
| 88 | + | } | |||
| 89 | + | } | |||
| 90 | + | for _, mappedCbPtr := range callbacks.sourceToCallback { | |||
| 91 | + | if mappedCbPtr == cbPtr { | |||
| 92 | + | return true | |||
| 93 | + | } | |||
| 94 | + | } | |||
| 95 | + | return false | |||
| 96 | + | } | |||
| 97 | + | ||||
| 98 | + | // releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback | |||
| 99 | + | // data when it reaches zero. | |||
| 100 | + | // Caller must hold callbacks.Lock(). | |||
| 101 | + | // Handler/source mappings to cbPtr are expected to be removed or replaced by | |||
| 102 | + | // the caller (RemoveCallbackByHandler, RemoveCallbackBySource, | |||
| 103 | + | // SaveHandlerMapping, SaveSourceMapping). | |||
| 104 | + | func releaseCallbackRef(cbPtr uintptr) { | |||
| 105 | + | count, ok := callbacks.callbackRefCount[cbPtr] | |||
| 106 | + | if !ok { | |||
| 107 | + | return | |||
| 108 | + | } | |||
| 109 | + | ||||
| 110 | + | count-- | |||
| 111 | + | if count > 0 { | |||
| 112 | + | callbacks.callbackRefCount[cbPtr] = count | |||
| 113 | + | return | |||
| 114 | + | } | |||
| 115 | + | ||||
| 116 | + | delete(callbacks.callbackRefCount, cbPtr) | |||
| 117 | + | delete(callbacks.refs, cbPtr) | |||
| 118 | + | delete(callbacks.closures, cbPtr) | |||
| 119 | + | } | |||
| 120 | + | ||||
| 60 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | 121 | // SaveHandlerMapping records a signal handler ID → callback pointer mapping | ||
| 61 | // so that DisconnectSignal can clean up the callback registry. | 122 | // so that DisconnectSignal can clean up the callback registry. | ||
| 62 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | 123 | func SaveHandlerMapping(handlerID uint, cbPtr uintptr) { | ||
| 124 | + | if handlerID == 0 { | |||
| 125 | + | return | |||
| 126 | + | } | |||
| 127 | + | ||||
| 63 | callbacks.Lock() | 128 | callbacks.Lock() | ||
| 129 | + | defer callbacks.Unlock() | |||
| 130 | + | if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | |||
| 131 | + | if prevCbPtr == cbPtr { | |||
| 132 | + | return | |||
| 133 | + | } | |||
| 134 | + | releaseCallbackRef(prevCbPtr) | |||
| 135 | + | if !hasCallbackMappings(prevCbPtr) { | |||
| 136 | + | releaseCallbackRef(prevCbPtr) | |||
| 137 | + | } | |||
| 138 | + | } | |||
| 64 | callbacks.handlerToCallback[handlerID] = cbPtr | 139 | callbacks.handlerToCallback[handlerID] = cbPtr | ||
| 65 | - | callbacks.Unlock() | 140 | + | acquireCallbackRef(cbPtr) |
| 66 | } | 141 | } | ||
| 67 | 142 | ||||
| 68 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | 143 | // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID. | ||
| 69 | func RemoveCallbackByHandler(handlerID uint) { | 144 | func RemoveCallbackByHandler(handlerID uint) { | ||
| 70 | callbacks.Lock() | 145 | callbacks.Lock() | ||
| 71 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | 146 | if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok { | ||
| 72 | - | delete(callbacks.refs, cbPtr) | |||
| 73 | - | delete(callbacks.closures, cbPtr) | |||
| 74 | delete(callbacks.handlerToCallback, handlerID) | 147 | delete(callbacks.handlerToCallback, handlerID) | ||
| 148 | + | releaseCallbackRef(cbPtr) | |||
| 149 | + | if !hasCallbackMappings(cbPtr) { | |||
| 150 | + | releaseCallbackRef(cbPtr) | |||
| 151 | + | } | |||
| 152 | + | } | |||
| 153 | + | callbacks.Unlock() | |||
| 154 | + | } | |||
| 155 | + | ||||
| 156 | + | // SaveSourceMapping records a source ID -> callback pointer mapping. | |||
| 157 | + | func SaveSourceMapping(sourceID uint, cbPtr uintptr) { | |||
| 158 | + | if sourceID == 0 { | |||
| 159 | + | return | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | callbacks.Lock() | |||
| 163 | + | defer callbacks.Unlock() | |||
| 164 | + | if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |||
| 165 | + | if prevCbPtr == cbPtr { | |||
| 166 | + | return | |||
| 167 | + | } | |||
| 168 | + | releaseCallbackRef(prevCbPtr) | |||
| 169 | + | if !hasCallbackMappings(prevCbPtr) { | |||
| 170 | + | releaseCallbackRef(prevCbPtr) | |||
| 171 | + | } | |||
| 172 | + | } | |||
| 173 | + | callbacks.sourceToCallback[sourceID] = cbPtr | |||
| 174 | + | acquireCallbackRef(cbPtr) | |||
| 175 | + | } | |||
| 176 | + | ||||
| 177 | + | // RemoveCallbackBySource removes a callback mapping using a source ID. | |||
| 178 | + | func RemoveCallbackBySource(sourceID uint) { | |||
| 179 | + | callbacks.Lock() | |||
| 180 | + | if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok { | |||
| 181 | + | delete(callbacks.sourceToCallback, sourceID) | |||
| 182 | + | releaseCallbackRef(cbPtr) | |||
| 183 | + | if !hasCallbackMappings(cbPtr) { | |||
| 184 | + | releaseCallbackRef(cbPtr) | |||
| 185 | + | } | |||
| 75 | } | 186 | } | ||
| 76 | callbacks.Unlock() | 187 | callbacks.Unlock() | ||
| 77 | } | 188 | } | ||
M v4/gobject/more.go +13 -1
| @@ -2,6 +2,7 @@ package gobject | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "reflect" | |
| 5 | + | "unsafe" | |
| 5 | 6 | ||
| 6 | 7 | "github.com/jwijenbergh/puregotk/v4/glib" | |
| 7 | 8 | ) | |
| @@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) { | |||
| 37 | 38 | } | |
| 38 | 39 | ||
| 39 | 40 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | |
| 40 | - | return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb)) | |
| 41 | + | cbPtr := uintptr(unsafe.Pointer(cb)) | |
| 42 | + | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |
| 43 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |
| 44 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |
| 45 | + | return handlerID | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | cbRefPtr := glib.NewCallback(cb) | |
| 49 | + | glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb) | |
| 50 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |
| 51 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |
| 52 | + | return handlerID | |
| 41 | 53 | } | |
| 42 | 54 | ||
| 43 | 55 | func (o Object) DisconnectSignal(handler uint) { | |
| @@ -2,6 +2,7 @@ package gobject | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "reflect" | 4 | "reflect" | ||
| 5 | + | "unsafe" | |||
| 5 | 6 | ||||
| 6 | "github.com/jwijenbergh/puregotk/v4/glib" | 7 | "github.com/jwijenbergh/puregotk/v4/glib" | ||
| 7 | ) | 8 | ) | ||
| @@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) { | |||||
| 37 | } | 38 | } | ||
| 38 | 39 | ||||
| 39 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | 40 | func (o Object) ConnectSignal(signal string, cb *func()) uint { | ||
| 40 | - | return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb)) | 41 | + | cbPtr := uintptr(unsafe.Pointer(cb)) |
| 42 | + | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |||
| 43 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |||
| 44 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |||
| 45 | + | return handlerID | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | cbRefPtr := glib.NewCallback(cb) | |||
| 49 | + | glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb) | |||
| 50 | + | handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr) | |||
| 51 | + | glib.SaveHandlerMapping(handlerID, cbPtr) | |||
| 52 | + | return handlerID | |||
| 41 | } | 53 | } | ||
| 42 | 54 | ||||
| 43 | func (o Object) DisconnectSignal(handler uint) { | 55 | func (o Object) DisconnectSignal(handler uint) { | ||