-
Notifications
You must be signed in to change notification settings - Fork 210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix lost signal from Selector when Default path blocks #1682
base: master
Are you sure you want to change the base?
Changes from 1 commit
9683052
8485964
d0e9246
d196da2
ca909b6
5d2778e
ceadefd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -551,6 +551,191 @@ func TestBlockingSelect(t *testing.T) { | |
require.EqualValues(t, expected, history) | ||
} | ||
|
||
func TestSelectBlockingDefault(t *testing.T) { | ||
// manually create a dispatcher to ensure sdkFlags are not set | ||
var history []string | ||
env := &workflowEnvironmentImpl{ | ||
sdkFlags: &sdkFlags{}, | ||
commandsHelper: newCommandsHelper(), | ||
dataConverter: converter.GetDefaultDataConverter(), | ||
workflowInfo: &WorkflowInfo{ | ||
Namespace: "namespace:" + t.Name(), | ||
TaskQueueName: "taskqueue:" + t.Name(), | ||
}, | ||
} | ||
interceptor, ctx, err := newWorkflowContext(env, nil) | ||
require.NoError(t, err, "newWorkflowContext failed") | ||
d, _ := newDispatcher(ctx, interceptor, func(ctx Context) { | ||
c1 := NewChannel(ctx) | ||
c2 := NewChannel(ctx) | ||
|
||
Go(ctx, func(ctx Context) { | ||
history = append(history, "add-one") | ||
c1.Send(ctx, "one") | ||
history = append(history, "add-one-done") | ||
|
||
}) | ||
|
||
Go(ctx, func(ctx Context) { | ||
history = append(history, "add-two") | ||
c2.Send(ctx, "two") | ||
history = append(history, "add-two-done") | ||
}) | ||
|
||
selector := NewSelector(ctx) | ||
var v string | ||
selector. | ||
AddReceive(c1, func(c ReceiveChannel, more bool) { | ||
c.Receive(ctx, &v) | ||
history = append(history, fmt.Sprintf("c1-%v", v)) | ||
}). | ||
AddDefault(func() { | ||
c2.Receive(ctx, &v) | ||
history = append(history, fmt.Sprintf("c2-%v", v)) | ||
}) | ||
history = append(history, "select1") | ||
require.False(t, selector.HasPending()) | ||
selector.Select(ctx) | ||
|
||
// Default behavior this signal is lost | ||
require.True(t, c1.Len() == 0 && v == "two") | ||
|
||
history = append(history, "select2") | ||
require.False(t, selector.HasPending()) | ||
history = append(history, "done") | ||
}, func() bool { return false }) | ||
defer d.Close() | ||
requireNoExecuteErr(t, d.ExecuteUntilAllBlocked(defaultDeadlockDetectionTimeout)) | ||
require.True(t, d.IsDone()) | ||
|
||
expected := []string{ | ||
"select1", | ||
"add-one", | ||
"add-one-done", | ||
"add-two", | ||
"add-two-done", | ||
"c2-two", | ||
"select2", | ||
"done", | ||
} | ||
require.EqualValues(t, expected, history) | ||
} | ||
|
||
func TestSelectBlockingDefaultWithFlag(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reported bug was that blocking in the default case of a selector could cause signals to be lost, when I last looked at these tests we didn't seem to have any coverage for blocking in one selector case while a signal is received. Can we add tests to verify their is no bugs if a signal is received while blocking in another case of a selector, not just default? |
||
// sdkFlags are set by default for tests | ||
var history []string | ||
d := createNewDispatcher(func(ctx Context) { | ||
c1 := NewChannel(ctx) | ||
c2 := NewChannel(ctx) | ||
|
||
Go(ctx, func(ctx Context) { | ||
history = append(history, "add-one") | ||
c1.Send(ctx, "one") | ||
history = append(history, "add-one-done") | ||
|
||
}) | ||
|
||
Go(ctx, func(ctx Context) { | ||
history = append(history, "add-two") | ||
c2.Send(ctx, "two") | ||
history = append(history, "add-two-done") | ||
}) | ||
|
||
selector := NewSelector(ctx) | ||
var v string | ||
selector. | ||
AddReceive(c1, func(c ReceiveChannel, more bool) { | ||
c.Receive(ctx, &v) | ||
history = append(history, fmt.Sprintf("c1-%v", v)) | ||
}). | ||
AddDefault(func() { | ||
c2.Receive(ctx, &v) | ||
history = append(history, fmt.Sprintf("c2-%v", v)) | ||
}) | ||
history = append(history, "select1") | ||
require.False(t, selector.HasPending()) | ||
selector.Select(ctx) | ||
|
||
// Signal should not be lost | ||
require.False(t, c1.Len() == 0 && v == "two") | ||
|
||
history = append(history, "select2") | ||
require.True(t, selector.HasPending()) | ||
selector.Select(ctx) | ||
require.False(t, selector.HasPending()) | ||
history = append(history, "done") | ||
}) | ||
defer d.Close() | ||
requireNoExecuteErr(t, d.ExecuteUntilAllBlocked(defaultDeadlockDetectionTimeout)) | ||
require.True(t, d.IsDone()) | ||
|
||
expected := []string{ | ||
"select1", | ||
"add-one", | ||
"add-one-done", | ||
"add-two", | ||
"add-two-done", | ||
"c2-two", | ||
"select2", | ||
"c1-one", | ||
"done", | ||
} | ||
require.EqualValues(t, expected, history) | ||
} | ||
|
||
func TestSelectBlockingFuture(t *testing.T) { | ||
var history []string | ||
d := createNewDispatcher(func(ctx Context) { | ||
// TODO | ||
c1 := NewChannel(ctx) | ||
selector := NewSelector(ctx) | ||
var v string | ||
selector.AddReceive(c1, func(c ReceiveChannel, more bool) { | ||
c.Receive(ctx, &v) | ||
history = append(history, fmt.Sprintf("c1-%v", v)) | ||
}).AddFuture(NewTimer(ctx, time.Second), func(f Future) { | ||
f.Get(ctx, nil) | ||
history = append(history, "future") | ||
}) | ||
|
||
history = append(history, "select1") | ||
require.False(t, selector.HasPending()) | ||
selector.Select(ctx) | ||
|
||
// Signal should not be lost | ||
require.False(t, c1.Len() == 0 && v == "two") | ||
|
||
history = append(history, "select2") | ||
require.True(t, selector.HasPending()) | ||
selector.Select(ctx) | ||
require.False(t, selector.HasPending()) | ||
history = append(history, "done") | ||
}) | ||
defer d.Close() | ||
requireNoExecuteErr(t, d.ExecuteUntilAllBlocked(defaultDeadlockDetectionTimeout)) | ||
require.True(t, d.IsDone()) | ||
|
||
expected := []string{ | ||
// TODO | ||
} | ||
require.EqualValues(t, expected, history) | ||
} | ||
|
||
func TestSelectBlockingSend(t *testing.T) { | ||
var history []string | ||
d := createNewDispatcher(func(ctx Context) { | ||
// TODO | ||
}) | ||
defer d.Close() | ||
requireNoExecuteErr(t, d.ExecuteUntilAllBlocked(defaultDeadlockDetectionTimeout)) | ||
require.True(t, d.IsDone()) | ||
|
||
expected := []string{ | ||
// TODO | ||
} | ||
require.EqualValues(t, expected, history) | ||
} | ||
|
||
func TestBlockingSelectAsyncSend(t *testing.T) { | ||
var history []string | ||
d := createNewDispatcher(func(ctx Context) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably don't want the same test both here and in
internal_workflow_testsuite_test.go
right? Which file should hold this test?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually maybe this is fine, one set tests w/ and w/o flags, and the other does default behavior