go-tg-screenshot-bot

Форк
0
426 строк · 11.4 Кб
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
// Leaks monitor
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
// ispired by https://golang.org/src/runtime/debug/stack.go?s=587:606#L21
39
// stack returns a formatted stack trace of all goroutines.
40
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
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
		//filter out our stack routine
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
	//t.Logf("possible goroutine leakage, waiting %v", leakTimeout)
104
	grs := l.leakingGoroutines()
105
	for _, gr := range grs {
106
		t.Errorf("%s: %s is leaking", l.name, gr.name)
107
		//t.Errorf("%s: %s is leaking\n%v", l.name, gr.name, string(gr.stack))
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
// dummy net.Conn
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
// dummy net.Conn interface. Needs to be constructed via newDummyNetConn([...]) function.
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
// Results running dummy server, satisfying net.Conn interface for test purposes.
164
// 'name' parameter will be returned via (*dNC).Local/RemoteAddr().String()
165
// 'reply' parameter function will be runned only on successful (*dNC).Write(b) with 'b' as parameter to 'reply'. The result will be stored in internal buffer and can be retrieved later via (*dNC).Read([...]) method.
166
// It is users responsibility to stop and clean up resources with (*dNC).Close, if not needed anymore.
167
// By default, the (*dNC).Write([...]) and (*dNC).Read([...]) methods are unlocked and will not result in error.
168
//TODO make (*dNC).SetDeadline, (*dNC).SetReadDeadline, (*dNC).SetWriteDeadline work proprely.
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
// Shuts down dummy net.Conn server. Every blocking or future method calls will do nothing and result in error.
248
// Result will be dNCErrClosed if server was allready closed.
249
// Server can not be unclosed.
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
// Performs a write action to server.
261
// If not locked by (*dNC).WriteLock, it results in error or success. If locked, this method will block until unlocked, or closed.
262
//
263
// This method can be set to result in error or success, via (*dNC).WriteError() or (*dNC).WriteSuccess() methods.
264
//
265
// If setted to result in error, the 'reply' function will NOT be called and internal buffer will NOT increasethe.
266
// Result will be (0, dNCErrWrite).
267
//
268
// If setted to result in success, the 'reply' function will be called and its result will be writen to internal buffer.
269
// If there is something in the internal buffer, the (*dNC).Read([...]) will be unblocked (if not previously locked with (*dNC).ReadLock).
270
// Result will be (len(b), nil)
271
//
272
// If server was closed previously, result will be (0, dNCErrClosed).
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
// Performs a read action from server.
285
// If locked by (*dNC).ReadLock(), this method will block until unlocked with (*dNC).ReadUnlock(), or server closes.
286
//
287
// If not locked, this method can be setted to result imidiatly in error, will block if internal buffer is empty or will perform an read operation from internal buffer.
288
//
289
// If setted to result in error via (*dNC).ReadError(), the result will be (0, dNCErrWrite).
290
//
291
// If not locked and not setted to result in error via (*dNC).ReadSuccess(), this method will block until internall buffer is not empty, than it returns the result of the buffer read operation via (*bytes.Buffer).Read([...]).
292
// If the internal buffer is empty after this method, all follwing (*dNC).Read([...]), requests will block until internall buffer is filled after successful write requests.
293
//
294
// If server was closed previously, result will be (0, io.EOF).
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
// Locks writing. All write requests will be blocked until write is unlocked with (*dNC).WriteUnlock, or server closes.
321
func (s *dNC) WriteLock() error {
322
	return s.Control(dNCCWriteLock{})
323
}
324

325
// Unlocks writing. All blocked write requests until now will be accepted.
326
func (s *dNC) WriteUnlock() error {
327
	return s.Control(dNCCWriteUnlock{})
328
}
329

330
// Unlocks writing and makes (*dNC).Write to result (0, dNCErrWrite).
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
// Unlocks writing and makes (*dNC).Write([...]) not result in error. See (*dNC).Write for details.
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
// Locks reading. All read requests will be blocked until read is unlocked with (*dNC).ReadUnlock, or server closes.
347
// (*dNC).Read([...]) wil block even after successful write.
348
func (s *dNC) ReadLock() error {
349
	return s.Control(dNCCReadLock{})
350
}
351

352
// Unlocks reading. If the internall buffer is not empty, next read will not block.
353
func (s *dNC) ReadUnlock() error {
354
	return s.Control(dNCCReadUnlock{})
355
}
356

357
// Unlocks read and makes every blocked and following (*dNC).Read([...]) imidiatly result in error. See (*dNC).Read for details.
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
// Unlocks read and makes every blocked and following (*dNC).Read([...]) requests be handled, if according to internal buffer. See (*dNC).Read for details.
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
// dummy X server replier for dummy net.Conn
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
	// register xgb error & event replies
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
	// sequence number generator
398
	seqId := uint16(1)
399
	incrementSequenceId := func() {
400
		// this has to be the same algorithm as in (*Conn).generateSeqIds
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   // error
415
			res[1] = 255 // error function
416
		default:
417
			res[0] = 1 // reply
418
		}
419
		Put16(res[2:], seqId) // sequence number
420
		incrementSequenceId()
421
		if string(request) == "noreply" {
422
			return nil
423
		}
424
		return res
425
	}
426
}
427

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.