Repository: pingcap/tidb
File: util/memory/tracker_test.go (Go)

Code before:
	GCAwareMemoryTrack.Store(false)
	tracker.Consume(100)
	require.Equal(t, int64(100), tracker.BytesConsumed())
	require.Equal(t, int64(100), parentTracker.BytesConsumed())
	tracker.Release(100)
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(0), parentTracker.BytesReleased())

	GCAwareMemoryTrack.Store(true)
	tracker.Consume(100)
	require.Equal(t, int64(100), tracker.BytesConsumed())
	require.Equal(t, int64(100), parentTracker.BytesConsumed())
	tracker.Release(100)
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(100), parentTracker.BytesReleased())
	runtime.GC()
	runtime.GC() // TODO one call cannot work
	require.Equal(t, int64(0), parentTracker.BytesReleased())

	waitGroup := sync.WaitGroup{}
	waitGroup.Add(10)
	for i := 0; i < 10; i++ {
		go func() {
			defer waitGroup.Done()
			tracker.Consume(10)
		}()
	}
	waitGroup.Add(10)
	for i := 0; i < 10; i++ {
		go func() {
			defer waitGroup.Done()
			tracker.Release(10)
		}()
	}
	waitGroup.Wait()
	runtime.GC()
	runtime.GC() // TODO one call cannot work
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(0), parentTracker.BytesReleased())
}

func TestBufferedConsumeAndRelease(t *testing.T) {
	debug.SetGCPercent(-1)
	defer debug.SetGCPercent(100)
	parentTracker := NewGlobalTracker(LabelForGlobalAnalyzeMemory, -1)

Review comment (suggestion):
use ```util.WaitGroupWrapper``` to avoid using ```Add```/```Done``` by manual.

Code after:
	EnableGCAwareMemoryTrack.Store(false)
	tracker.Consume(100)
	require.Equal(t, int64(100), tracker.BytesConsumed())
	require.Equal(t, int64(100), parentTracker.BytesConsumed())
	tracker.Release(100)
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(0), parentTracker.BytesReleased())

	EnableGCAwareMemoryTrack.Store(true)
	tracker.Consume(100)
	require.Equal(t, int64(100), tracker.BytesConsumed())
	require.Equal(t, int64(100), parentTracker.BytesConsumed())
	tracker.Release(100)
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(100), parentTracker.BytesReleased())
	// call GC() twice to workaround as the same way GO does due to GC() returns without finishing sweep
	// https://github.com/golang/go/issues/45315
	runtime.GC()
	runtime.GC()
	require.Equal(t, int64(0), parentTracker.BytesReleased())

	waitGroup := sync.WaitGroup{}
	waitGroup.Add(10)
	for i := 0; i < 10; i++ {
		go func() {
			defer waitGroup.Done()
			tracker.Consume(10)
		}()
	}
	waitGroup.Add(10)
	for i := 0; i < 10; i++ {
		go func() {
			defer waitGroup.Done()
			tracker.Release(10)
		}()
	}
	waitGroup.Wait()
	// call GC() twice to workaround as the same way GO does due to GC() returns without finishing sweep
	// https://github.com/golang/go/issues/45315
	runtime.GC()
	runtime.GC()
	require.Equal(t, int64(0), tracker.BytesConsumed())
	require.Equal(t, int64(0), parentTracker.BytesConsumed())
	require.Equal(t, int64(0), tracker.BytesReleased())
	require.Equal(t, int64(0), parentTracker.BytesReleased())
}