-
Notifications
You must be signed in to change notification settings - Fork 12
/
deferralcompletedhandler.go
204 lines (161 loc) · 5.14 KB
/
deferralcompletedhandler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Code generated by winrt-go-gen. DO NOT EDIT.
//go:build windows
//nolint:all
package foundation
import (
"sync"
"time"
"unsafe"
"github.com/go-ole/go-ole"
"github.com/saltosystems/winrt-go/internal/delegate"
"github.com/saltosystems/winrt-go/internal/kernel32"
)
const GUIDDeferralCompletedHandler string = "ed32a372-f3c8-4faa-9cfb-470148da3888"
const SignatureDeferralCompletedHandler string = "delegate({ed32a372-f3c8-4faa-9cfb-470148da3888})"
type DeferralCompletedHandler struct {
ole.IUnknown
sync.Mutex
refs uintptr
IID ole.GUID
}
type DeferralCompletedHandlerVtbl struct {
ole.IUnknownVtbl
Invoke uintptr
}
type DeferralCompletedHandlerCallback func(instance *DeferralCompletedHandler)
var callbacksDeferralCompletedHandler = &deferralCompletedHandlerCallbacks{
mu: &sync.Mutex{},
callbacks: make(map[unsafe.Pointer]DeferralCompletedHandlerCallback),
}
var releaseChannelsDeferralCompletedHandler = &deferralCompletedHandlerReleaseChannels{
mu: &sync.Mutex{},
chans: make(map[unsafe.Pointer]chan struct{}),
}
func NewDeferralCompletedHandler(iid *ole.GUID, callback DeferralCompletedHandlerCallback) *DeferralCompletedHandler {
// create type instance
size := unsafe.Sizeof(*(*DeferralCompletedHandler)(nil))
instPtr := kernel32.Malloc(size)
inst := (*DeferralCompletedHandler)(instPtr)
// get the callbacks for the VTable
callbacks := delegate.RegisterCallbacks(instPtr, inst)
// the VTable should also be allocated in the heap
sizeVTable := unsafe.Sizeof(*(*DeferralCompletedHandlerVtbl)(nil))
vTablePtr := kernel32.Malloc(sizeVTable)
inst.RawVTable = (*interface{})(vTablePtr)
vTable := (*DeferralCompletedHandlerVtbl)(vTablePtr)
vTable.IUnknownVtbl = ole.IUnknownVtbl{
QueryInterface: callbacks.QueryInterface,
AddRef: callbacks.AddRef,
Release: callbacks.Release,
}
vTable.Invoke = callbacks.Invoke
// Initialize all properties: the malloc may contain garbage
inst.IID = *iid // copy contents
inst.Mutex = sync.Mutex{}
inst.refs = 0
callbacksDeferralCompletedHandler.add(unsafe.Pointer(inst), callback)
// See the docs in the releaseChannelsDeferralCompletedHandler struct
releaseChannelsDeferralCompletedHandler.acquire(unsafe.Pointer(inst))
inst.addRef()
return inst
}
func (r *DeferralCompletedHandler) GetIID() *ole.GUID {
return &r.IID
}
// addRef increments the reference counter by one
func (r *DeferralCompletedHandler) addRef() uintptr {
r.Lock()
defer r.Unlock()
r.refs++
return r.refs
}
// removeRef decrements the reference counter by one. If it was already zero, it will just return zero.
func (r *DeferralCompletedHandler) removeRef() uintptr {
r.Lock()
defer r.Unlock()
if r.refs > 0 {
r.refs--
}
return r.refs
}
func (instance *DeferralCompletedHandler) Invoke(instancePtr, rawArgs0, rawArgs1, rawArgs2, rawArgs3, rawArgs4, rawArgs5, rawArgs6, rawArgs7, rawArgs8 unsafe.Pointer) uintptr {
// See the quote above.
if callback, ok := callbacksDeferralCompletedHandler.get(instancePtr); ok {
callback(instance)
}
return ole.S_OK
}
func (instance *DeferralCompletedHandler) AddRef() uintptr {
return instance.addRef()
}
func (instance *DeferralCompletedHandler) Release() uintptr {
rem := instance.removeRef()
if rem == 0 {
// We're done.
instancePtr := unsafe.Pointer(instance)
callbacksDeferralCompletedHandler.delete(instancePtr)
// stop release channels used to avoid
// https://github.com/golang/go/issues/55015
releaseChannelsDeferralCompletedHandler.release(instancePtr)
kernel32.Free(unsafe.Pointer(instance.RawVTable))
kernel32.Free(instancePtr)
}
return rem
}
type deferralCompletedHandlerCallbacks struct {
mu *sync.Mutex
callbacks map[unsafe.Pointer]DeferralCompletedHandlerCallback
}
func (m *deferralCompletedHandlerCallbacks) add(p unsafe.Pointer, v DeferralCompletedHandlerCallback) {
m.mu.Lock()
defer m.mu.Unlock()
m.callbacks[p] = v
}
func (m *deferralCompletedHandlerCallbacks) get(p unsafe.Pointer) (DeferralCompletedHandlerCallback, bool) {
m.mu.Lock()
defer m.mu.Unlock()
v, ok := m.callbacks[p]
return v, ok
}
func (m *deferralCompletedHandlerCallbacks) delete(p unsafe.Pointer) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.callbacks, p)
}
// typedEventHandlerReleaseChannels keeps a map with channels
// used to keep a goroutine alive during the lifecycle of this object.
// This is required to avoid causing a deadlock error.
// See this: https://github.com/golang/go/issues/55015
type deferralCompletedHandlerReleaseChannels struct {
mu *sync.Mutex
chans map[unsafe.Pointer]chan struct{}
}
func (m *deferralCompletedHandlerReleaseChannels) acquire(p unsafe.Pointer) {
m.mu.Lock()
defer m.mu.Unlock()
c := make(chan struct{})
m.chans[p] = c
go func() {
// we need a timer to trick the go runtime into
// thinking there's still something going on here
// but we are only really interested in <-c
t := time.NewTimer(time.Minute)
for {
select {
case <-t.C:
t.Reset(time.Minute)
case <-c:
t.Stop()
return
}
}
}()
}
func (m *deferralCompletedHandlerReleaseChannels) release(p unsafe.Pointer) {
m.mu.Lock()
defer m.mu.Unlock()
if c, ok := m.chans[p]; ok {
close(c)
delete(m.chans, p)
}
}