1 package xgb
2
3 import (
4 "bytes"
5 "errors"
6 "io"
7 "net"
8 "regexp"
9 "runtime"
10 "strconv"
11 "strings"
12 "testing"
13 "time"
14 )
15
16
17
18 type goroutine struct {
19 id int
20 name string
21 stack []byte
22 }
23
24 type leaks struct {
25 name string
26 goroutines map[int]goroutine
27 report []*leaks
28 }
29
30 func leaksMonitor(name string, monitors ...*leaks) *leaks {
31 return &leaks{
32 name,
33 leaks{}.collectGoroutines(),
34 monitors,
35 }
36 }
37
38
39
40
41 func (_ leaks) stack() []byte {
42 buf := make([]byte, 1024)
43 for {
44 n := runtime.Stack(buf, true)
45 if n < len(buf) {
46 return buf[:n]
47 }
48 buf = make([]byte, 2*len(buf))
49 }
50 }
51
52 func (l leaks) collectGoroutines() map[int]goroutine {
53 res := make(map[int]goroutine)
54 stacks := bytes.Split(l.stack(), []byte{'\n', '\n'})
55
56 regexpId := regexp.MustCompile(`^\s*goroutine\s*(\d+)`)
57 for _, st := range stacks {
58 lines := bytes.Split(st, []byte{'\n'})
59 if len(lines) < 2 {
60 panic("routine stach has less tnan two lines: " + string(st))
61 }
62
63 idMatches := regexpId.FindSubmatch(lines[0])
64 if len(idMatches) < 2 {
65 panic("no id found in goroutine stack's first line: " + string(lines[0]))
66 }
67 id, err := strconv.Atoi(string(idMatches[1]))
68 if err != nil {
69 panic("converting goroutine id to number error: " + err.Error())
70 }
71 if _, ok := res[id]; ok {
72 panic("2 goroutines with same id: " + strconv.Itoa(id))
73 }
74 name := strings.TrimSpace(string(lines[1]))
75
76
77 if strings.Contains(name, "xgb.leaks.stack") {
78 continue
79 }
80
81 res[id] = goroutine{id, name, st}
82 }
83 return res
84 }
85
86 func (l leaks) leakingGoroutines() []goroutine {
87 goroutines := l.collectGoroutines()
88 res := []goroutine{}
89 for id, gr := range goroutines {
90 if _, ok := l.goroutines[id]; ok {
91 continue
92 }
93 res = append(res, gr)
94 }
95 return res
96 }
97 func (l leaks) checkTesting(t *testing.T) {
98 if len(l.leakingGoroutines()) == 0 {
99 return
100 }
101 leakTimeout := 10 * time.Millisecond
102 time.Sleep(leakTimeout)
103
104 grs := l.leakingGoroutines()
105 for _, gr := range grs {
106 t.Errorf("%s: %s is leaking", l.name, gr.name)
107
108 }
109 for _, rl := range l.report {
110 rl.ignoreLeak(grs...)
111 }
112 }
113 func (l *leaks) ignoreLeak(grs ...goroutine) {
114 for _, gr := range grs {
115 l.goroutines[gr.id] = gr
116 }
117 }
118
119
120
121 type dAddr struct {
122 s string
123 }
124
125 func (_ dAddr) Network() string { return "dummy" }
126 func (a dAddr) String() string { return a.s }
127
128 var (
129 dNCErrNotImplemented = errors.New("command not implemented")
130 dNCErrClosed = errors.New("server closed")
131 dNCErrWrite = errors.New("server write failed")
132 dNCErrRead = errors.New("server read failed")
133 dNCErrResponse = errors.New("server response error")
134 )
135
136 type dNCIoResult struct {
137 n int
138 err error
139 }
140 type dNCIo struct {
141 b []byte
142 result chan dNCIoResult
143 }
144
145 type dNCCWriteLock struct{}
146 type dNCCWriteUnlock struct{}
147 type dNCCWriteError struct{}
148 type dNCCWriteSuccess struct{}
149 type dNCCReadLock struct{}
150 type dNCCReadUnlock struct{}
151 type dNCCReadError struct{}
152 type dNCCReadSuccess struct{}
153
154
155 type dNC struct {
156 reply func([]byte) []byte
157 addr dAddr
158 in, out chan dNCIo
159 control chan interface{}
160 done chan struct{}
161 }
162
163
164
165
166
167
168
169 func newDummyNetConn(name string, reply func([]byte) []byte) *dNC {
170
171 s := &dNC{
172 reply,
173 dAddr{name},
174 make(chan dNCIo), make(chan dNCIo),
175 make(chan interface{}),
176 make(chan struct{}),
177 }
178
179 in, out := s.in, chan dNCIo(nil)
180 buf := &bytes.Buffer{}
181 errorRead, errorWrite := false, false
182 lockRead := false
183
184 go func() {
185 defer close(s.done)
186 for {
187 select {
188 case dxsio := <-in:
189 if errorWrite {
190 dxsio.result <- dNCIoResult{0, dNCErrWrite}
191 break
192 }
193
194 response := s.reply(dxsio.b)
195
196 buf.Write(response)
197 dxsio.result <- dNCIoResult{len(dxsio.b), nil}
198
199 if !lockRead && buf.Len() > 0 && out == nil {
200 out = s.out
201 }
202 case dxsio := <-out:
203 if errorRead {
204 dxsio.result <- dNCIoResult{0, dNCErrRead}
205 break
206 }
207
208 n, err := buf.Read(dxsio.b)
209 dxsio.result <- dNCIoResult{n, err}
210
211 if buf.Len() == 0 {
212 out = nil
213 }
214 case ci := <-s.control:
215 if ci == nil {
216 return
217 }
218 switch ci.(type) {
219 case dNCCWriteLock:
220 in = nil
221 case dNCCWriteUnlock:
222 in = s.in
223 case dNCCWriteError:
224 errorWrite = true
225 case dNCCWriteSuccess:
226 errorWrite = false
227 case dNCCReadLock:
228 out = nil
229 lockRead = true
230 case dNCCReadUnlock:
231 lockRead = false
232 if buf.Len() > 0 && out == nil {
233 out = s.out
234 }
235 case dNCCReadError:
236 errorRead = true
237 case dNCCReadSuccess:
238 errorRead = false
239 default:
240 }
241 }
242 }
243 }()
244 return s
245 }
246
247
248
249
250 func (s *dNC) Close() error {
251 select {
252 case s.control <- nil:
253 <-s.done
254 return nil
255 case <-s.done:
256 }
257 return dNCErrClosed
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 func (s *dNC) Write(b []byte) (int, error) {
274 resChan := make(chan dNCIoResult)
275 select {
276 case s.in <- dNCIo{b, resChan}:
277 res := <-resChan
278 return res.n, res.err
279 case <-s.done:
280 }
281 return 0, dNCErrClosed
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295 func (s *dNC) Read(b []byte) (int, error) {
296 resChan := make(chan dNCIoResult)
297 select {
298 case s.out <- dNCIo{b, resChan}:
299 res := <-resChan
300 return res.n, res.err
301 case <-s.done:
302 }
303 return 0, io.EOF
304 }
305 func (s *dNC) LocalAddr() net.Addr { return s.addr }
306 func (s *dNC) RemoteAddr() net.Addr { return s.addr }
307 func (s *dNC) SetDeadline(t time.Time) error { return dNCErrNotImplemented }
308 func (s *dNC) SetReadDeadline(t time.Time) error { return dNCErrNotImplemented }
309 func (s *dNC) SetWriteDeadline(t time.Time) error { return dNCErrNotImplemented }
310
311 func (s *dNC) Control(i interface{}) error {
312 select {
313 case s.control <- i:
314 return nil
315 case <-s.done:
316 }
317 return dNCErrClosed
318 }
319
320
321 func (s *dNC) WriteLock() error {
322 return s.Control(dNCCWriteLock{})
323 }
324
325
326 func (s *dNC) WriteUnlock() error {
327 return s.Control(dNCCWriteUnlock{})
328 }
329
330
331 func (s *dNC) WriteError() error {
332 if err := s.WriteUnlock(); err != nil {
333 return err
334 }
335 return s.Control(dNCCWriteError{})
336 }
337
338
339 func (s *dNC) WriteSuccess() error {
340 if err := s.WriteUnlock(); err != nil {
341 return err
342 }
343 return s.Control(dNCCWriteSuccess{})
344 }
345
346
347
348 func (s *dNC) ReadLock() error {
349 return s.Control(dNCCReadLock{})
350 }
351
352
353 func (s *dNC) ReadUnlock() error {
354 return s.Control(dNCCReadUnlock{})
355 }
356
357
358 func (s *dNC) ReadError() error {
359 if err := s.ReadUnlock(); err != nil {
360 return err
361 }
362 return s.Control(dNCCReadError{})
363 }
364
365
366 func (s *dNC) ReadSuccess() error {
367 if err := s.ReadUnlock(); err != nil {
368 return err
369 }
370 return s.Control(dNCCReadSuccess{})
371 }
372
373
374
375 type dXSEvent struct{}
376
377 func (_ dXSEvent) Bytes() []byte { return nil }
378 func (_ dXSEvent) String() string { return "dummy X server event" }
379
380 type dXSError struct {
381 seqId uint16
382 }
383
384 func (e dXSError) SequenceId() uint16 { return e.seqId }
385 func (_ dXSError) BadId() uint32 { return 0 }
386 func (_ dXSError) Error() string { return "dummy X server error reply" }
387
388 func newDummyXServerReplier() func([]byte) []byte {
389
390 NewErrorFuncs[255] = func(buf []byte) Error {
391 return dXSError{Get16(buf[2:])}
392 }
393 NewEventFuncs[128&127] = func(buf []byte) Event {
394 return dXSEvent{}
395 }
396
397
398 seqId := uint16(1)
399 incrementSequenceId := func() {
400
401 if seqId == uint16((1<<16)-1) {
402 seqId = 0
403 } else {
404 seqId++
405 }
406 }
407 return func(request []byte) []byte {
408 res := make([]byte, 32)
409 switch string(request) {
410 case "event":
411 res[0] = 128
412 return res
413 case "error":
414 res[0] = 0
415 res[1] = 255
416 default:
417 res[0] = 1
418 }
419 Put16(res[2:], seqId)
420 incrementSequenceId()
421 if string(request) == "noreply" {
422 return nil
423 }
424 return res
425 }
426 }
427
View as plain text