Skip to content

Commit 8874f10

Browse files
committed
Add packet reactor
1 parent 449f8b4 commit 8874f10

18 files changed

Lines changed: 3720 additions & 0 deletions

common/bufio/channel_demux.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package bufio
2+
3+
import (
4+
"context"
5+
"reflect"
6+
"sync"
7+
"sync/atomic"
8+
9+
N "github.com/sagernet/sing/common/network"
10+
)
11+
12+
type channelDemuxEntry struct {
13+
channel <-chan *N.PacketBuffer
14+
stream *reactorStream
15+
}
16+
17+
type ChannelDemultiplexer struct {
18+
ctx context.Context
19+
cancel context.CancelFunc
20+
mutex sync.Mutex
21+
entries map[<-chan *N.PacketBuffer]*channelDemuxEntry
22+
updateChan chan struct{}
23+
running bool
24+
closed atomic.Bool
25+
wg sync.WaitGroup
26+
}
27+
28+
func NewChannelDemultiplexer(ctx context.Context) *ChannelDemultiplexer {
29+
ctx, cancel := context.WithCancel(ctx)
30+
demux := &ChannelDemultiplexer{
31+
ctx: ctx,
32+
cancel: cancel,
33+
entries: make(map[<-chan *N.PacketBuffer]*channelDemuxEntry),
34+
updateChan: make(chan struct{}, 1),
35+
}
36+
return demux
37+
}
38+
39+
func (d *ChannelDemultiplexer) Add(stream *reactorStream, channel <-chan *N.PacketBuffer) {
40+
d.mutex.Lock()
41+
42+
if d.closed.Load() {
43+
d.mutex.Unlock()
44+
return
45+
}
46+
47+
entry := &channelDemuxEntry{
48+
channel: channel,
49+
stream: stream,
50+
}
51+
d.entries[channel] = entry
52+
if !d.running {
53+
d.running = true
54+
d.wg.Add(1)
55+
go d.run()
56+
}
57+
d.mutex.Unlock()
58+
d.signalUpdate()
59+
}
60+
61+
func (d *ChannelDemultiplexer) Remove(channel <-chan *N.PacketBuffer) {
62+
d.mutex.Lock()
63+
delete(d.entries, channel)
64+
d.mutex.Unlock()
65+
d.signalUpdate()
66+
}
67+
68+
func (d *ChannelDemultiplexer) signalUpdate() {
69+
select {
70+
case d.updateChan <- struct{}{}:
71+
default:
72+
}
73+
}
74+
75+
func (d *ChannelDemultiplexer) Close() error {
76+
d.mutex.Lock()
77+
d.closed.Store(true)
78+
d.mutex.Unlock()
79+
80+
d.cancel()
81+
d.signalUpdate()
82+
d.wg.Wait()
83+
return nil
84+
}
85+
86+
func (d *ChannelDemultiplexer) run() {
87+
defer d.wg.Done()
88+
89+
for {
90+
d.mutex.Lock()
91+
if len(d.entries) == 0 {
92+
d.running = false
93+
d.mutex.Unlock()
94+
return
95+
}
96+
97+
cases := make([]reflect.SelectCase, 0, len(d.entries)+2)
98+
99+
cases = append(cases, reflect.SelectCase{
100+
Dir: reflect.SelectRecv,
101+
Chan: reflect.ValueOf(d.ctx.Done()),
102+
})
103+
104+
cases = append(cases, reflect.SelectCase{
105+
Dir: reflect.SelectRecv,
106+
Chan: reflect.ValueOf(d.updateChan),
107+
})
108+
109+
entryList := make([]*channelDemuxEntry, 0, len(d.entries))
110+
for _, entry := range d.entries {
111+
cases = append(cases, reflect.SelectCase{
112+
Dir: reflect.SelectRecv,
113+
Chan: reflect.ValueOf(entry.channel),
114+
})
115+
entryList = append(entryList, entry)
116+
}
117+
d.mutex.Unlock()
118+
119+
chosen, recv, recvOK := reflect.Select(cases)
120+
121+
switch chosen {
122+
case 0:
123+
d.mutex.Lock()
124+
d.running = false
125+
d.mutex.Unlock()
126+
return
127+
case 1:
128+
continue
129+
default:
130+
entry := entryList[chosen-2]
131+
d.mutex.Lock()
132+
delete(d.entries, entry.channel)
133+
d.mutex.Unlock()
134+
135+
if recvOK {
136+
packet := recv.Interface().(*N.PacketBuffer)
137+
go entry.stream.runActiveLoop(packet)
138+
} else {
139+
go entry.stream.closeWithError(nil)
140+
}
141+
}
142+
}
143+
}

common/bufio/fd_demux_darwin.go

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
//go:build darwin
2+
3+
package bufio
4+
5+
import (
6+
"context"
7+
"sync"
8+
"sync/atomic"
9+
10+
"golang.org/x/sys/unix"
11+
)
12+
13+
type fdDemuxEntry struct {
14+
fd int
15+
stream *reactorStream
16+
}
17+
18+
type FDDemultiplexer struct {
19+
ctx context.Context
20+
cancel context.CancelFunc
21+
kqueueFD int
22+
mutex sync.Mutex
23+
entries map[int]*fdDemuxEntry
24+
running bool
25+
closed atomic.Bool
26+
wg sync.WaitGroup
27+
pipeFDs [2]int
28+
}
29+
30+
func NewFDDemultiplexer(ctx context.Context) (*FDDemultiplexer, error) {
31+
kqueueFD, err := unix.Kqueue()
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
var pipeFDs [2]int
37+
err = unix.Pipe(pipeFDs[:])
38+
if err != nil {
39+
unix.Close(kqueueFD)
40+
return nil, err
41+
}
42+
43+
err = unix.SetNonblock(pipeFDs[0], true)
44+
if err != nil {
45+
unix.Close(pipeFDs[0])
46+
unix.Close(pipeFDs[1])
47+
unix.Close(kqueueFD)
48+
return nil, err
49+
}
50+
err = unix.SetNonblock(pipeFDs[1], true)
51+
if err != nil {
52+
unix.Close(pipeFDs[0])
53+
unix.Close(pipeFDs[1])
54+
unix.Close(kqueueFD)
55+
return nil, err
56+
}
57+
58+
_, err = unix.Kevent(kqueueFD, []unix.Kevent_t{{
59+
Ident: uint64(pipeFDs[0]),
60+
Filter: unix.EVFILT_READ,
61+
Flags: unix.EV_ADD,
62+
}}, nil, nil)
63+
if err != nil {
64+
unix.Close(pipeFDs[0])
65+
unix.Close(pipeFDs[1])
66+
unix.Close(kqueueFD)
67+
return nil, err
68+
}
69+
70+
ctx, cancel := context.WithCancel(ctx)
71+
demux := &FDDemultiplexer{
72+
ctx: ctx,
73+
cancel: cancel,
74+
kqueueFD: kqueueFD,
75+
entries: make(map[int]*fdDemuxEntry),
76+
pipeFDs: pipeFDs,
77+
}
78+
return demux, nil
79+
}
80+
81+
func (d *FDDemultiplexer) Add(stream *reactorStream, fd int) error {
82+
d.mutex.Lock()
83+
defer d.mutex.Unlock()
84+
85+
if d.closed.Load() {
86+
return unix.EINVAL
87+
}
88+
89+
_, err := unix.Kevent(d.kqueueFD, []unix.Kevent_t{{
90+
Ident: uint64(fd),
91+
Filter: unix.EVFILT_READ,
92+
Flags: unix.EV_ADD,
93+
}}, nil, nil)
94+
if err != nil {
95+
return err
96+
}
97+
98+
entry := &fdDemuxEntry{
99+
fd: fd,
100+
stream: stream,
101+
}
102+
d.entries[fd] = entry
103+
104+
if !d.running {
105+
d.running = true
106+
d.wg.Add(1)
107+
go d.run()
108+
}
109+
110+
return nil
111+
}
112+
113+
func (d *FDDemultiplexer) Remove(fd int) {
114+
d.mutex.Lock()
115+
defer d.mutex.Unlock()
116+
117+
_, ok := d.entries[fd]
118+
if !ok {
119+
return
120+
}
121+
122+
unix.Kevent(d.kqueueFD, []unix.Kevent_t{{
123+
Ident: uint64(fd),
124+
Filter: unix.EVFILT_READ,
125+
Flags: unix.EV_DELETE,
126+
}}, nil, nil)
127+
delete(d.entries, fd)
128+
}
129+
130+
func (d *FDDemultiplexer) wakeup() {
131+
unix.Write(d.pipeFDs[1], []byte{0})
132+
}
133+
134+
func (d *FDDemultiplexer) Close() error {
135+
d.mutex.Lock()
136+
d.closed.Store(true)
137+
d.mutex.Unlock()
138+
139+
d.cancel()
140+
d.wakeup()
141+
d.wg.Wait()
142+
143+
d.mutex.Lock()
144+
defer d.mutex.Unlock()
145+
146+
if d.kqueueFD != -1 {
147+
unix.Close(d.kqueueFD)
148+
d.kqueueFD = -1
149+
}
150+
if d.pipeFDs[0] != -1 {
151+
unix.Close(d.pipeFDs[0])
152+
unix.Close(d.pipeFDs[1])
153+
d.pipeFDs[0] = -1
154+
d.pipeFDs[1] = -1
155+
}
156+
return nil
157+
}
158+
159+
func (d *FDDemultiplexer) run() {
160+
defer d.wg.Done()
161+
162+
events := make([]unix.Kevent_t, 64)
163+
var buffer [1]byte
164+
165+
for {
166+
select {
167+
case <-d.ctx.Done():
168+
d.mutex.Lock()
169+
d.running = false
170+
d.mutex.Unlock()
171+
return
172+
default:
173+
}
174+
175+
n, err := unix.Kevent(d.kqueueFD, nil, events, nil)
176+
if err != nil {
177+
if err == unix.EINTR {
178+
continue
179+
}
180+
d.mutex.Lock()
181+
d.running = false
182+
d.mutex.Unlock()
183+
return
184+
}
185+
186+
for i := 0; i < n; i++ {
187+
event := events[i]
188+
fd := int(event.Ident)
189+
190+
if fd == d.pipeFDs[0] {
191+
unix.Read(d.pipeFDs[0], buffer[:])
192+
continue
193+
}
194+
195+
if event.Flags&unix.EV_ERROR != 0 {
196+
continue
197+
}
198+
199+
d.mutex.Lock()
200+
entry, ok := d.entries[fd]
201+
if !ok {
202+
d.mutex.Unlock()
203+
continue
204+
}
205+
206+
unix.Kevent(d.kqueueFD, []unix.Kevent_t{{
207+
Ident: uint64(fd),
208+
Filter: unix.EVFILT_READ,
209+
Flags: unix.EV_DELETE,
210+
}}, nil, nil)
211+
delete(d.entries, fd)
212+
d.mutex.Unlock()
213+
214+
go entry.stream.runActiveLoop(nil)
215+
}
216+
217+
d.mutex.Lock()
218+
if len(d.entries) == 0 {
219+
d.running = false
220+
d.mutex.Unlock()
221+
return
222+
}
223+
d.mutex.Unlock()
224+
}
225+
}

0 commit comments

Comments
 (0)