-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsonPipe.go
More file actions
102 lines (94 loc) · 2.27 KB
/
Copy pathjsonPipe.go
File metadata and controls
102 lines (94 loc) · 2.27 KB
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
package patchright
import (
"encoding/json"
"errors"
"fmt"
"sync"
)
type jsonPipe struct {
channelOwner
// mu guards queue and closed; cond (built on mu) lets Poll wait for either a
// new message or close without busy-waiting.
mu sync.Mutex
cond *sync.Cond
queue []*message
closed bool
}
func (j *jsonPipe) Send(message map[string]any) error {
_, err := j.channel.Send("send", map[string]any{
"message": message,
})
return err
}
func (j *jsonPipe) Close() error {
_, err := j.channel.Send("close")
return err
}
func (j *jsonPipe) Poll() (*message, error) {
j.mu.Lock()
defer j.mu.Unlock()
for len(j.queue) == 0 && !j.closed {
j.cond.Wait()
}
if len(j.queue) > 0 {
msg := j.queue[0]
j.queue = j.queue[1:]
return msg, nil
}
return nil, errors.New("jsonPipe closed")
}
// enqueue appends a message and wakes any waiting Poll(). It never blocks (the
// queue is unbounded), so it is safe to call from the outer connection's
// dispatch goroutine: that goroutine must never block delivering a message
// here, otherwise it cannot deliver the reply an in-flight Send() is waiting
// for, which would deadlock the whole connection (see the streaming upload
// path). Ordering is preserved since the single dispatch goroutine is the only
// appender.
func (j *jsonPipe) enqueue(msg *message) {
j.mu.Lock()
defer j.mu.Unlock()
if j.closed {
return
}
j.queue = append(j.queue, msg)
j.cond.Broadcast()
}
func (j *jsonPipe) markClosed() {
j.mu.Lock()
defer j.mu.Unlock()
if j.closed {
return
}
j.closed = true
j.cond.Broadcast()
}
func newJsonPipe(parent *channelOwner, objectType string, guid string, initializer map[string]any) *jsonPipe {
j := &jsonPipe{}
j.cond = sync.NewCond(&j.mu)
j.createChannelOwner(j, parent, objectType, guid, initializer)
j.channel.On("message", func(ev map[string]any) {
var msg message
m, err := json.Marshal(ev["message"])
if err == nil {
err = json.Unmarshal(m, &msg)
}
if err != nil {
msg = message{
Error: &struct {
Error Error "json:\"error\""
}{
Error: Error{
Name: "Error",
Message: fmt.Sprintf("jsonPipe: could not decode message: %s", err.Error()),
},
},
}
}
j.enqueue(&msg)
})
j.channel.Once("closed", func() {
j.Emit("closed")
j.markClosed()
})
return j
}