mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
160 lines
4.3 KiB
Go
160 lines
4.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package routine
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestManager(t *testing.T) {
|
|
t.Parallel()
|
|
var runs uint32
|
|
var running uint32
|
|
mgr := NewManager(testutil.Logger(t))
|
|
|
|
run := func(ctx context.Context) error {
|
|
atomic.StoreUint32(&running, 1)
|
|
defer atomic.StoreUint32(&running, 0)
|
|
atomic.AddUint32(&runs, 1)
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
|
|
// IsRunning on unregistered service should be false
|
|
require.False(t, mgr.IsRunning("not-found"))
|
|
|
|
// start
|
|
require.NoError(t, mgr.Start(context.Background(), "run", run))
|
|
require.True(t, mgr.IsRunning("run"))
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&runs))
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
|
})
|
|
doneCh := mgr.Stop("run")
|
|
require.NotNil(t, doneCh)
|
|
<-doneCh
|
|
|
|
// ensure the background go routine was actually cancelled
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&runs))
|
|
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
|
})
|
|
|
|
// restart and stop
|
|
require.NoError(t, mgr.Start(context.Background(), "run", run))
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(2), atomic.LoadUint32(&runs))
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
|
})
|
|
|
|
doneCh = mgr.Stop("run")
|
|
require.NotNil(t, doneCh)
|
|
<-doneCh
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
|
})
|
|
|
|
// start with a context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
require.NoError(t, mgr.Start(ctx, "run", run))
|
|
cancel()
|
|
|
|
// The function should exit of its own accord due to the parent
|
|
// context being canceled
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(3), atomic.LoadUint32(&runs))
|
|
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
|
// the task should automatically set itself to not running if
|
|
// it exits early
|
|
require.False(r, mgr.IsRunning("run"))
|
|
})
|
|
}
|
|
|
|
func TestManager_StopAll(t *testing.T) {
|
|
t.Parallel()
|
|
var runs uint32
|
|
var running uint32
|
|
mgr := NewManager(testutil.Logger(t))
|
|
|
|
run := func(ctx context.Context) error {
|
|
atomic.StoreUint32(&running, 1)
|
|
defer atomic.StoreUint32(&running, 0)
|
|
atomic.AddUint32(&runs, 1)
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
|
|
require.NoError(t, mgr.Start(context.Background(), "run1", run))
|
|
require.NoError(t, mgr.Start(context.Background(), "run2", run))
|
|
|
|
mgr.StopAll()
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.False(r, mgr.IsRunning("run1"))
|
|
require.False(r, mgr.IsRunning("run2"))
|
|
})
|
|
}
|
|
|
|
// Test IsRunning when routine is a blocking call that does not
|
|
// immediately return when cancelled
|
|
func TestManager_StopBlocking(t *testing.T) {
|
|
t.Parallel()
|
|
var runs uint32
|
|
var running uint32
|
|
unblock := make(chan struct{}) // To simulate a blocking call
|
|
mgr := NewManager(testutil.Logger(t))
|
|
|
|
// A routine that will be still running for a while after cancelled
|
|
run := func(ctx context.Context) error {
|
|
atomic.StoreUint32(&running, 1)
|
|
defer atomic.StoreUint32(&running, 0)
|
|
atomic.AddUint32(&runs, 1)
|
|
<-ctx.Done()
|
|
<-unblock
|
|
return nil
|
|
}
|
|
|
|
require.NoError(t, mgr.Start(context.Background(), "blocking", run))
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.True(r, mgr.IsRunning("blocking"))
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&runs))
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
|
})
|
|
|
|
doneCh := mgr.Stop("blocking")
|
|
|
|
// IsRunning should return false, however &running is still 1
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.False(r, mgr.IsRunning("blocking"))
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
|
})
|
|
|
|
// New routine should be able to replace old "cancelled but running" routine.
|
|
require.NoError(t, mgr.Start(context.Background(), "blocking", func(ctx context.Context) error {
|
|
<-ctx.Done()
|
|
return nil
|
|
}))
|
|
defer mgr.Stop("blocking")
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.True(r, mgr.IsRunning("blocking")) // New routine
|
|
require.Equal(r, uint32(1), atomic.LoadUint32(&running)) // Old routine
|
|
})
|
|
|
|
// Complete the blocking routine
|
|
close(unblock)
|
|
<-doneCh
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
|
})
|
|
}
|