mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-20 20:07:03 +01:00
Prevent intermittent NPE in queue tests (#19301)
There appears to be an intermittent NPE in queue tests relating to the deferred shutdown/terminate functions. This PR more formally asserts that shutdown and termination occurs before starting and finishing the tests but leaves the defer in place to ensure that if there is an issue shutdown/termination will occur. Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
parent
cf5d4a7230
commit
7b4c3c7bb1
2 changed files with 97 additions and 28 deletions
|
@ -128,6 +128,8 @@ func TestChannelQueue_Pause(t *testing.T) {
|
||||||
queueShutdown := []func(){}
|
queueShutdown := []func(){}
|
||||||
queueTerminate := []func(){}
|
queueTerminate := []func(){}
|
||||||
|
|
||||||
|
terminated := make(chan struct{})
|
||||||
|
|
||||||
queue, err = NewChannelQueue(handle,
|
queue, err = NewChannelQueue(handle,
|
||||||
ChannelQueueConfiguration{
|
ChannelQueueConfiguration{
|
||||||
WorkerPoolConfiguration: WorkerPoolConfiguration{
|
WorkerPoolConfiguration: WorkerPoolConfiguration{
|
||||||
|
@ -142,15 +144,18 @@ func TestChannelQueue_Pause(t *testing.T) {
|
||||||
}, &testData{})
|
}, &testData{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
go queue.Run(func(shutdown func()) {
|
go func() {
|
||||||
lock.Lock()
|
queue.Run(func(shutdown func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueShutdown = append(queueShutdown, shutdown)
|
defer lock.Unlock()
|
||||||
}, func(terminate func()) {
|
queueShutdown = append(queueShutdown, shutdown)
|
||||||
lock.Lock()
|
}, func(terminate func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueTerminate = append(queueTerminate, terminate)
|
defer lock.Unlock()
|
||||||
})
|
queueTerminate = append(queueTerminate, terminate)
|
||||||
|
})
|
||||||
|
close(terminated)
|
||||||
|
}()
|
||||||
|
|
||||||
// Shutdown and Terminate in defer
|
// Shutdown and Terminate in defer
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -278,4 +283,30 @@ func TestChannelQueue_Pause(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Equal(t, test1.TestString, result1.TestString)
|
assert.Equal(t, test1.TestString, result1.TestString)
|
||||||
assert.Equal(t, test1.TestInt, result1.TestInt)
|
assert.Equal(t, test1.TestInt, result1.TestInt)
|
||||||
|
|
||||||
|
lock.Lock()
|
||||||
|
callbacks := make([]func(), len(queueShutdown))
|
||||||
|
copy(callbacks, queueShutdown)
|
||||||
|
queueShutdown = queueShutdown[:0]
|
||||||
|
lock.Unlock()
|
||||||
|
// Now shutdown the queue
|
||||||
|
for _, callback := range callbacks {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminate the queue
|
||||||
|
lock.Lock()
|
||||||
|
callbacks = make([]func(), len(queueTerminate))
|
||||||
|
copy(callbacks, queueTerminate)
|
||||||
|
queueShutdown = queueTerminate[:0]
|
||||||
|
lock.Unlock()
|
||||||
|
for _, callback := range callbacks {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-terminated:
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
assert.Fail(t, "Queue should have terminated")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
|
|
||||||
queueShutdown := []func(){}
|
queueShutdown := []func(){}
|
||||||
queueTerminate := []func(){}
|
queueTerminate := []func(){}
|
||||||
|
terminated := make(chan struct{})
|
||||||
|
|
||||||
tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-pause-test-data")
|
tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-pause-test-data")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -237,15 +238,18 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
}, &testData{})
|
}, &testData{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
go queue.Run(func(shutdown func()) {
|
go func() {
|
||||||
lock.Lock()
|
queue.Run(func(shutdown func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueShutdown = append(queueShutdown, shutdown)
|
defer lock.Unlock()
|
||||||
}, func(terminate func()) {
|
queueShutdown = append(queueShutdown, shutdown)
|
||||||
lock.Lock()
|
}, func(terminate func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueTerminate = append(queueTerminate, terminate)
|
defer lock.Unlock()
|
||||||
})
|
queueTerminate = append(queueTerminate, terminate)
|
||||||
|
})
|
||||||
|
close(terminated)
|
||||||
|
}()
|
||||||
|
|
||||||
// Shutdown and Terminate in defer
|
// Shutdown and Terminate in defer
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -417,7 +421,10 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
case <-handleChan:
|
case <-handleChan:
|
||||||
assert.Fail(t, "Handler processing should have stopped")
|
assert.Fail(t, "Handler processing should have stopped")
|
||||||
return
|
return
|
||||||
default:
|
case <-terminated:
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
assert.Fail(t, "Queue should have terminated")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
|
@ -425,6 +432,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
|
|
||||||
// Reopen queue
|
// Reopen queue
|
||||||
|
terminated = make(chan struct{})
|
||||||
queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
|
queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
|
||||||
DataDir: tmpDir,
|
DataDir: tmpDir,
|
||||||
BatchLength: 1,
|
BatchLength: 1,
|
||||||
|
@ -442,15 +450,18 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
|
|
||||||
paused, _ = pausable.IsPausedIsResumed()
|
paused, _ = pausable.IsPausedIsResumed()
|
||||||
|
|
||||||
go queue.Run(func(shutdown func()) {
|
go func() {
|
||||||
lock.Lock()
|
queue.Run(func(shutdown func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueShutdown = append(queueShutdown, shutdown)
|
defer lock.Unlock()
|
||||||
}, func(terminate func()) {
|
queueShutdown = append(queueShutdown, shutdown)
|
||||||
lock.Lock()
|
}, func(terminate func()) {
|
||||||
defer lock.Unlock()
|
lock.Lock()
|
||||||
queueTerminate = append(queueTerminate, terminate)
|
defer lock.Unlock()
|
||||||
})
|
queueTerminate = append(queueTerminate, terminate)
|
||||||
|
})
|
||||||
|
close(terminated)
|
||||||
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-handleChan:
|
case <-handleChan:
|
||||||
|
@ -510,4 +521,31 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, test2.TestString, result4.TestString)
|
assert.Equal(t, test2.TestString, result4.TestString)
|
||||||
assert.Equal(t, test2.TestInt, result4.TestInt)
|
assert.Equal(t, test2.TestInt, result4.TestInt)
|
||||||
|
|
||||||
|
lock.Lock()
|
||||||
|
callbacks = make([]func(), len(queueShutdown))
|
||||||
|
copy(callbacks, queueShutdown)
|
||||||
|
queueShutdown = queueShutdown[:0]
|
||||||
|
lock.Unlock()
|
||||||
|
// Now shutdown the queue
|
||||||
|
for _, callback := range callbacks {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminate the queue
|
||||||
|
lock.Lock()
|
||||||
|
callbacks = make([]func(), len(queueTerminate))
|
||||||
|
copy(callbacks, queueTerminate)
|
||||||
|
queueShutdown = queueTerminate[:0]
|
||||||
|
lock.Unlock()
|
||||||
|
for _, callback := range callbacks {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
assert.Fail(t, "Queue should have terminated")
|
||||||
|
return
|
||||||
|
case <-terminated:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue