@@ -13,6 +13,7 @@ import (
13
13
"sort"
14
14
"strings"
15
15
"sync"
16
+ "time"
16
17
17
18
"golang.org/x/tools/go/loader"
18
19
@@ -23,7 +24,17 @@ import (
23
24
"github.com/sourcegraph/jsonrpc2"
24
25
)
25
26
27
+ // workspaceReferencesTimeout is the timeout used for workspace/xreferences
28
+ // calls.
29
+ const workspaceReferencesTimeout = 15 * time .Second
30
+
26
31
func (h * LangHandler ) handleWorkspaceReferences (ctx context.Context , conn JSONRPC2Conn , req * jsonrpc2.Request , params lspext.WorkspaceReferencesParams ) ([]referenceInformation , error ) {
32
+ // TODO: Add support for the cancelRequest LSP method instead of using
33
+ // hard-coded timeouts like this here.
34
+ //
35
+ // See: https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#cancelRequest
36
+ ctx , cancel := context .WithTimeout (ctx , workspaceReferencesTimeout )
37
+ defer cancel ()
27
38
rootPath := h .FilePath (h .init .RootPath )
28
39
bctx := h .BuildContext (ctx )
29
40
@@ -101,13 +112,37 @@ func (h *LangHandler) handleWorkspaceReferences(ctx context.Context, conn JSONRP
101
112
}()
102
113
}
103
114
104
- _ , err := h .workspaceRefsTypecheck (ctx , bctx , conn , fset , pkgs , afterTypeCheck )
105
- if err != nil {
106
- return nil , err
107
- }
115
+ // workspaceRefsTypecheck is ran inside it's own goroutine because it can
116
+ // block for longer than our context deadline.
117
+ var err error
118
+ done := make (chan struct {})
119
+ go func () {
120
+ // Prevent any uncaught panics from taking the entire server down.
121
+ defer func () {
122
+ if r := recover (); r != nil {
123
+ // Same as net/http
124
+ const size = 64 << 10
125
+ buf := make ([]byte , size )
126
+ buf = buf [:runtime .Stack (buf , false )]
127
+ log .Printf ("ignoring panic serving %v for pkgs %v: %v\n %s" , req .Method , pkgs , r , buf )
128
+ return
129
+ }
130
+ }()
108
131
109
- // Wait for all worker goroutines to complete.
110
- wg .Wait ()
132
+ _ , err = h .workspaceRefsTypecheck (ctx , bctx , conn , fset , pkgs , afterTypeCheck )
133
+
134
+ // Wait for all worker goroutines to complete.
135
+ wg .Wait ()
136
+ close (done )
137
+ }()
138
+ select {
139
+ case <- done :
140
+ if err != nil {
141
+ return nil , err
142
+ }
143
+ case <- ctx .Done ():
144
+ return nil , ctx .Err ()
145
+ }
111
146
112
147
sort .Sort (& results ) // sort to provide consistent results
113
148
return results .results , nil
@@ -149,7 +184,12 @@ func (h *LangHandler) workspaceRefsTypecheck(ctx context.Context, bctx *build.Co
149
184
}
150
185
return bpkg , nil
151
186
},
152
- AfterTypeCheck : afterTypeCheck ,
187
+ AfterTypeCheck : func (pkg * loader.PackageInfo , files []* ast.File ) {
188
+ if err := ctx .Err (); err != nil {
189
+ return
190
+ }
191
+ afterTypeCheck (pkg , files )
192
+ },
153
193
}
154
194
for _ , path := range pkgs {
155
195
conf .Import (path )
@@ -161,6 +201,10 @@ func (h *LangHandler) workspaceRefsTypecheck(ctx context.Context, bctx *build.Co
161
201
return nil , err
162
202
}
163
203
204
+ if err := ctx .Err (); err != nil {
205
+ return nil , err
206
+ }
207
+
164
208
// Publish typechecking error diagnostics.
165
209
diags , err := errsToDiagnostics (typeErrs , prog )
166
210
if err != nil {
@@ -179,6 +223,9 @@ func (h *LangHandler) workspaceRefsTypecheck(ctx context.Context, bctx *build.Co
179
223
// workspaceRefsFromPkg collects all the references made to dependencies from
180
224
// the specified package and returns the results.
181
225
func (h * LangHandler ) workspaceRefsFromPkg (ctx context.Context , bctx * build.Context , conn JSONRPC2Conn , params lspext.WorkspaceReferencesParams , fs * token.FileSet , pkg * loader.PackageInfo , rootPath string , results * refResultSorter ) (err error ) {
226
+ if err := ctx .Err (); err != nil {
227
+ return err
228
+ }
182
229
span , ctx := opentracing .StartSpanFromContext (ctx , "workspaceRefsFromPkg" )
183
230
defer func () {
184
231
if err != nil {
0 commit comments