@@ -18,32 +18,38 @@ package commands
18
18
import (
19
19
"context"
20
20
"errors"
21
+ "fmt"
22
+ "io"
21
23
"os"
22
24
"path/filepath"
23
25
"runtime"
26
+ "sync"
24
27
"sync/atomic"
25
-
26
- "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
27
- "github.com/arduino/arduino-cli/internal/i18n"
28
- rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
29
- "google.golang.org/grpc/metadata"
30
-
31
- "fmt"
32
- "io"
33
28
"time"
34
29
35
30
"github.com/arduino/arduino-cli/commands/cmderrors"
36
31
"github.com/arduino/arduino-cli/commands/internal/instances"
32
+ "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
33
+ "github.com/arduino/arduino-cli/internal/i18n"
34
+ rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
37
35
paths "github.com/arduino/go-paths-helper"
36
+ "github.com/djherbis/buffer"
37
+ "github.com/djherbis/nio/v3"
38
38
"github.com/sirupsen/logrus"
39
+ "google.golang.org/grpc/metadata"
39
40
)
40
41
41
42
type debugServer struct {
42
43
ctx context.Context
43
44
req atomic.Pointer [rpc.GetDebugConfigRequest ]
44
45
in io.Reader
46
+ inSignal bool
47
+ inData bool
48
+ inEvent * sync.Cond
49
+ inLock sync.Mutex
45
50
out io.Writer
46
51
resultCB func (* rpc.DebugResponse_Result )
52
+ done chan bool
47
53
}
48
54
49
55
func (s * debugServer ) Send (resp * rpc.DebugResponse ) error {
@@ -54,6 +60,7 @@ func (s *debugServer) Send(resp *rpc.DebugResponse) error {
54
60
}
55
61
if res := resp .GetResult (); res != nil {
56
62
s .resultCB (res )
63
+ s .close ()
57
64
}
58
65
return nil
59
66
}
@@ -62,12 +69,33 @@ func (s *debugServer) Recv() (r *rpc.DebugRequest, e error) {
62
69
if conf := s .req .Swap (nil ); conf != nil {
63
70
return & rpc.DebugRequest {DebugRequest : conf }, nil
64
71
}
65
- buff := make ([]byte , 4096 )
66
- n , err := s .in .Read (buff )
67
- if err != nil {
68
- return nil , err
72
+
73
+ s .inEvent .L .Lock ()
74
+ for ! s .inSignal && ! s .inData {
75
+ s .inEvent .Wait ()
76
+ }
77
+ defer s .inEvent .L .Unlock ()
78
+
79
+ if s .inSignal {
80
+ s .inSignal = false
81
+ return & rpc.DebugRequest {SendInterrupt : true }, nil
82
+ }
83
+
84
+ if s .inData {
85
+ s .inData = false
86
+ buff := make ([]byte , 4096 )
87
+ n , err := s .in .Read (buff )
88
+ if err != nil {
89
+ return nil , err
90
+ }
91
+ return & rpc.DebugRequest {Data : buff [:n ]}, nil
69
92
}
70
- return & rpc.DebugRequest {Data : buff [:n ]}, nil
93
+
94
+ panic ("invalid state in debug" )
95
+ }
96
+
97
+ func (s * debugServer ) close () {
98
+ close (s .done )
71
99
}
72
100
73
101
func (s * debugServer ) Context () context.Context { return s .ctx }
@@ -80,7 +108,7 @@ func (s *debugServer) SetTrailer(metadata.MD) {}
80
108
// DebugServerToStreams creates a debug server that proxies the data to the given io streams.
81
109
// The GetDebugConfigRequest is used to configure the debbuger. sig is a channel that can be
82
110
// used to send os.Interrupt to the debug process. resultCB is a callback function that will
83
- // receive the Debug result.
111
+ // receive the Debug result and closes the debug server .
84
112
func DebugServerToStreams (
85
113
ctx context.Context ,
86
114
req * rpc.GetDebugConfigRequest ,
@@ -93,8 +121,45 @@ func DebugServerToStreams(
93
121
in : in ,
94
122
out : out ,
95
123
resultCB : resultCB ,
124
+ done : make (chan bool ),
96
125
}
126
+ serverIn , clientOut := nio .Pipe (buffer .New (32 * 1024 ))
127
+ server .in = serverIn
128
+ server .inEvent = sync .NewCond (& server .inLock )
97
129
server .req .Store (req )
130
+ go func () {
131
+ for {
132
+ select {
133
+ case <- sig :
134
+ server .inEvent .L .Lock ()
135
+ server .inSignal = true
136
+ server .inEvent .Broadcast ()
137
+ server .inEvent .L .Unlock ()
138
+ case <- server .done :
139
+ return
140
+ }
141
+ }
142
+ }()
143
+ go func () {
144
+ defer clientOut .Close ()
145
+ buff := make ([]byte , 4096 )
146
+ for {
147
+ n , readErr := in .Read (buff )
148
+
149
+ server .inEvent .L .Lock ()
150
+ var writeErr error
151
+ if readErr == nil {
152
+ _ , writeErr = clientOut .Write (buff [:n ])
153
+ }
154
+ server .inData = true
155
+ server .inEvent .Broadcast ()
156
+ server .inEvent .L .Unlock ()
157
+ if readErr != nil || writeErr != nil {
158
+ // exit on error
159
+ return
160
+ }
161
+ }
162
+ }()
98
163
return server
99
164
}
100
165
@@ -128,11 +193,18 @@ func (s *arduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer)
128
193
outStream := feedStreamTo (sendData )
129
194
defer outStream .Close ()
130
195
inStream := consumeStreamFrom (func () ([]byte , error ) {
131
- command , err := stream .Recv ()
132
- if command .GetSendInterrupt () {
133
- signalChan <- os .Interrupt
196
+ for {
197
+ req , err := stream .Recv ()
198
+ if err != nil {
199
+ return nil , err
200
+ }
201
+ if req .GetSendInterrupt () {
202
+ signalChan <- os .Interrupt
203
+ }
204
+ if data := req .GetData (); len (data ) > 0 {
205
+ return data , nil
206
+ }
134
207
}
135
- return command .GetData (), err
136
208
})
137
209
138
210
pme , release , err := instances .GetPackageManagerExplorer (debugConfReq .GetInstance ())
0 commit comments