Skip to content

Commit dfa6e84

Browse files
committedDec 12, 2018
Issue yuin#209 : Cancelling context should unblock any channel blocked on Recv
1 parent a0dfe84 commit dfa6e84

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed
 

‎channellib.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,20 @@ func channelSelect(L *LState) int {
8383
cases[i] = cas
8484
}
8585

86+
if L.ctx != nil {
87+
cases = append(cases, reflect.SelectCase{
88+
Dir: reflect.SelectRecv,
89+
Chan: reflect.ValueOf(L.ctx.Done()),
90+
Send: reflect.ValueOf(nil),
91+
})
92+
}
93+
8694
pos, recv, rok := reflect.Select(cases)
95+
96+
if L.ctx != nil && pos == L.GetTop() {
97+
return 0
98+
}
99+
87100
lv := LNil
88101
if recv.Kind() != 0 {
89102
lv, _ = recv.Interface().(LValue)
@@ -129,7 +142,22 @@ var channelMethods = map[string]LGFunction{
129142

130143
func channelReceive(L *LState) int {
131144
rch := checkChannel(L, 1)
132-
v, ok := rch.Recv()
145+
var v reflect.Value
146+
var ok bool
147+
if L.ctx != nil {
148+
cases := []reflect.SelectCase{{
149+
Dir: reflect.SelectRecv,
150+
Chan: reflect.ValueOf(L.ctx.Done()),
151+
Send: reflect.ValueOf(nil),
152+
}, {
153+
Dir: reflect.SelectRecv,
154+
Chan: rch,
155+
Send: reflect.ValueOf(nil),
156+
}}
157+
_, v, ok = reflect.Select(cases)
158+
} else {
159+
v, ok = rch.Recv()
160+
}
133161
if ok {
134162
L.Push(LTrue)
135163
L.Push(v.Interface().(LValue))

‎channellib_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lua
22

33
import (
4+
"context"
45
"reflect"
56
"sync"
67
"testing"
@@ -259,3 +260,35 @@ func TestChannelSendReceive1(t *testing.T) {
259260
go sender(ch)
260261
wg.Wait()
261262
}
263+
264+
func TestCancelChannelReceive(t *testing.T) {
265+
done := make(chan struct{})
266+
ctx, cancel := context.WithCancel(context.Background())
267+
go func() {
268+
defer close(done)
269+
L := NewState()
270+
L.SetContext(ctx)
271+
defer L.Close()
272+
L.SetGlobal("ch", LChannel(make(chan LValue)))
273+
errorIfScriptNotFail(t, L, `ch:receive()`, context.Canceled.Error())
274+
}()
275+
time.Sleep(time.Second)
276+
cancel()
277+
<-done
278+
}
279+
280+
func TestCancelChannelReceive2(t *testing.T) {
281+
done := make(chan struct{})
282+
ctx, cancel := context.WithCancel(context.Background())
283+
go func() {
284+
defer close(done)
285+
L := NewState()
286+
L.SetContext(ctx)
287+
defer L.Close()
288+
L.SetGlobal("ch", LChannel(make(chan LValue)))
289+
errorIfScriptNotFail(t, L, `channel.select({"|<-", ch})`, context.Canceled.Error())
290+
}()
291+
time.Sleep(time.Second)
292+
cancel()
293+
<-done
294+
}

0 commit comments

Comments
 (0)