mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
app: [macOS] fix display link callback race
Commit c0c25b777 replaced the synchronizing of the display link callback
from a sync.Map to a cgo.Handle. However, the change didn't take into
account the lifecycle issues: a callback may happen just as the cgo.Handle
is freed, leading to a misuse crash.
This change restores the sync.Map synchronization, which avoids the
lifecycle issue.
Fixes: https://todo.sr.ht/~eliasnaur/gio/526
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+18
-11
@@ -6,7 +6,7 @@ package app
|
|||||||
#include <Foundation/Foundation.h>
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
|
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(uintptr_t handle);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
|
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
|
||||||
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
|
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
|
||||||
__attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl);
|
__attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl);
|
||||||
@@ -42,7 +42,7 @@ static CFTypeRef newNSString(unichar *chars, NSUInteger length) {
|
|||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/cgo"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
@@ -70,6 +70,9 @@ type displayLink struct {
|
|||||||
running uint32
|
running uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// displayLinks maps CFTypeRefs to *displayLinks.
|
||||||
|
var displayLinks sync.Map
|
||||||
|
|
||||||
var mainFuncs = make(chan func(), 1)
|
var mainFuncs = make(chan func(), 1)
|
||||||
|
|
||||||
// runOnMain runs the function on the main thread.
|
// runOnMain runs the function on the main thread.
|
||||||
@@ -128,18 +131,18 @@ func NewDisplayLink(callback func()) (*displayLink, error) {
|
|||||||
states: make(chan bool),
|
states: make(chan bool),
|
||||||
dids: make(chan uint64),
|
dids: make(chan uint64),
|
||||||
}
|
}
|
||||||
h := cgo.NewHandle(d)
|
dl := C.gio_createDisplayLink()
|
||||||
dl := C.gio_createDisplayLink(C.uintptr_t(h))
|
|
||||||
if dl == 0 {
|
if dl == 0 {
|
||||||
return nil, errors.New("app: failed to create display link")
|
return nil, errors.New("app: failed to create display link")
|
||||||
}
|
}
|
||||||
go d.run(dl, h)
|
go d.run(dl)
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *displayLink) run(dl C.CFTypeRef, h cgo.Handle) {
|
func (d *displayLink) run(dl C.CFTypeRef) {
|
||||||
defer C.gio_releaseDisplayLink(dl)
|
defer C.gio_releaseDisplayLink(dl)
|
||||||
defer h.Delete()
|
displayLinks.Store(dl, d)
|
||||||
|
defer displayLinks.Delete(dl)
|
||||||
var stopTimer *time.Timer
|
var stopTimer *time.Timer
|
||||||
var tchan <-chan time.Time
|
var tchan <-chan time.Time
|
||||||
started := false
|
started := false
|
||||||
@@ -200,10 +203,14 @@ func (d *displayLink) SetDisplayID(did uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFrameCallback
|
//export gio_onFrameCallback
|
||||||
func gio_onFrameCallback(dl C.CFTypeRef, handle C.uintptr_t) {
|
func gio_onFrameCallback(ref C.CFTypeRef) {
|
||||||
d := cgo.Handle(handle).Value().(*displayLink)
|
d, exists := displayLinks.Load(ref)
|
||||||
if atomic.LoadUint32(&d.running) != 0 {
|
if !exists {
|
||||||
d.callback()
|
return
|
||||||
|
}
|
||||||
|
dl := d.(*displayLink)
|
||||||
|
if atomic.LoadUint32(&dl.running) != 0 {
|
||||||
|
dl.callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-17
@@ -123,6 +123,9 @@ static void handleTouches(int last, UIView *view, NSSet<UITouch *> *touches, UIE
|
|||||||
|
|
||||||
@implementation GioView
|
@implementation GioView
|
||||||
NSArray<UIKeyCommand *> *_keyCommands;
|
NSArray<UIKeyCommand *> *_keyCommands;
|
||||||
|
+ (void)onFrameCallback:(CADisplayLink *)link {
|
||||||
|
gio_onFrameCallback((__bridge CFTypeRef)link);
|
||||||
|
}
|
||||||
+ (Class)layerClass {
|
+ (Class)layerClass {
|
||||||
return gio_layerClass();
|
return gio_layerClass();
|
||||||
}
|
}
|
||||||
@@ -227,23 +230,8 @@ NSArray<UIKeyCommand *> *_keyCommands;
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface DisplayLinkHandle : NSObject {
|
CFTypeRef gio_createDisplayLink(void) {
|
||||||
}
|
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)];
|
||||||
@property uintptr_t handle;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DisplayLinkHandle {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)onFrameCallback:(CADisplayLink *)link {
|
|
||||||
gio_onFrameCallback((__bridge CFTypeRef)link, _handle);
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
CFTypeRef gio_createDisplayLink(uintptr_t handle) {
|
|
||||||
DisplayLinkHandle *h = [DisplayLinkHandle alloc];
|
|
||||||
h.handle = handle;
|
|
||||||
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:h selector:@selector(onFrameCallback:)];
|
|
||||||
dl.paused = YES;
|
dl.paused = YES;
|
||||||
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
|
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
|
||||||
[dl addToRunLoop:runLoop forMode:[runLoop currentMode]];
|
[dl addToRunLoop:runLoop forMode:[runLoop currentMode]];
|
||||||
|
|||||||
+3
-3
@@ -193,14 +193,14 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo
|
|||||||
static GioWindowDelegate *globalWindowDel;
|
static GioWindowDelegate *globalWindowDel;
|
||||||
|
|
||||||
static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *handle) {
|
static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *handle) {
|
||||||
gio_onFrameCallback(dl, (uintptr_t)handle);
|
gio_onFrameCallback(dl);
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFTypeRef gio_createDisplayLink(uintptr_t handle) {
|
CFTypeRef gio_createDisplayLink(void) {
|
||||||
CVDisplayLinkRef dl;
|
CVDisplayLinkRef dl;
|
||||||
CVDisplayLinkCreateWithActiveCGDisplays(&dl);
|
CVDisplayLinkCreateWithActiveCGDisplays(&dl);
|
||||||
CVDisplayLinkSetOutputCallback(dl, displayLinkCallback, (void *)(handle));
|
CVDisplayLinkSetOutputCallback(dl, displayLinkCallback, nil);
|
||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user