Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions internal/internal_workflow_testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ type (
testResult converter.EncodedValue
testError error
doneChannel chan struct{}
doneChannelOnce sync.Once
workerOptions WorkerOptions
dataConverter converter.DataConverter
failureConverter converter.FailureConverter
Expand Down Expand Up @@ -830,7 +831,11 @@ func (env *testWorkflowEnvironmentImpl) startWorkflowTask() {
func (env *testWorkflowEnvironmentImpl) isChildWorkflow() bool {
return env.parentEnv != nil
}

func (env *testWorkflowEnvironmentImpl) closeDoneChannel() {
env.doneChannelOnce.Do(func() {
close(env.doneChannel)
})
}
func (env *testWorkflowEnvironmentImpl) startMainLoop() {
if env.isChildWorkflow() {
// child workflow rely on parent workflow's main loop to process events
Expand All @@ -839,7 +844,7 @@ func (env *testWorkflowEnvironmentImpl) startMainLoop() {
}

// notify all child workflows to exit their main loop
defer close(env.doneChannel)
defer env.closeDoneChannel()

for !env.shouldStopEventLoop() {
// use non-blocking-select to check if there is anything pending in the main thread.
Expand Down Expand Up @@ -1100,6 +1105,7 @@ func (env *testWorkflowEnvironmentImpl) Complete(result *commonpb.Payloads, err

// properly handle child workflows based on their ParentClosePolicy
env.handleParentClosePolicy()
env.closeDoneChannel()
}

func (env *testWorkflowEnvironmentImpl) handleParentClosePolicy() {
Expand Down
42 changes: 42 additions & 0 deletions test/child_workflow_leak_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package test

import (
"testing"

"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
"go.uber.org/goleak"
)

func ChildWorkflow(ctx workflow.Context) (string, error) {
return "ok", nil
}

func ParentWorkflow(ctx workflow.Context) (string, error) {
// Execute child by *registered name* (the path that triggered #2090).
var out string
if err := workflow.ExecuteChildWorkflow(ctx, "ChildWorkflow").Get(ctx, &out); err != nil {
return "", err
}
return out, nil
}

func TestChildWorkflowByName_NoGoroutineLeak(t *testing.T) {
defer goleak.VerifyNone(t)

var suite testsuite.WorkflowTestSuite
env := suite.NewTestWorkflowEnvironment()

// Register child under the name we call above.
env.RegisterWorkflowWithOptions(ChildWorkflow, workflow.RegisterOptions{Name: "ChildWorkflow"})
env.RegisterWorkflow(ParentWorkflow)

env.ExecuteWorkflow(ParentWorkflow)

if !env.IsWorkflowCompleted() {
t.Fatal("workflow did not complete")
}
if err := env.GetWorkflowError(); err != nil {
t.Fatalf("workflow failed: %v", err)
}
}
Loading