refactor: remove NoCopy struct; move RefCounter struct to goutils and update usage; remove internal/utils entirely

This commit is contained in:
yusing
2026-01-07 17:17:12 +08:00
parent 1ebba20216
commit 9ea9e62ee8
8 changed files with 4 additions and 152 deletions

Submodule goutils updated: 06f20f6b87...4b046d275f

View File

@@ -19,7 +19,6 @@ import (
nettypes "github.com/yusing/godoxy/internal/net/types"
"github.com/yusing/godoxy/internal/route/routes"
"github.com/yusing/godoxy/internal/types"
U "github.com/yusing/godoxy/internal/utils"
"github.com/yusing/godoxy/internal/watcher/events"
"github.com/yusing/godoxy/internal/watcher/health/monitor"
gperr "github.com/yusing/goutils/errs"
@@ -48,7 +47,6 @@ type (
}
Watcher struct {
_ U.NoCopy
routeHelper
l zerolog.Logger

View File

@@ -8,7 +8,7 @@ import (
"sync"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/internal/utils"
"github.com/yusing/goutils/synk"
)
type File struct {
@@ -18,7 +18,7 @@ type File struct {
// Store it for later delete from `openedFiles`.
path string
refCount *utils.RefCount
refCount *synk.RefCount
}
var (
@@ -55,7 +55,7 @@ func NewFileIO(path string) (Writer, error) {
if _, err := f.Seek(0, io.SeekEnd); err != nil {
return nil, fmt.Errorf("access log seek error: %w", err)
}
file = &File{f: f, path: path, refCount: utils.NewRefCounter()}
file = &File{f: f, path: path, refCount: synk.NewRefCounter()}
openedFiles[path] = file
go file.closeOnZero()
return file, nil

View File

@@ -7,12 +7,9 @@ import (
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
nettypes "github.com/yusing/godoxy/internal/net/types"
"github.com/yusing/godoxy/internal/types"
U "github.com/yusing/godoxy/internal/utils"
)
type server struct {
_ U.NoCopy
name string
url *nettypes.URL
weight int

View File

@@ -36,13 +36,10 @@ import (
"github.com/yusing/godoxy/internal/route/rules"
rulepresets "github.com/yusing/godoxy/internal/route/rules/presets"
route "github.com/yusing/godoxy/internal/route/types"
"github.com/yusing/godoxy/internal/utils"
)
type (
Route struct {
_ utils.NoCopy
Alias string `json:"alias"`
Scheme route.Scheme `json:"scheme,omitempty" swaggertype:"string" enums:"http,https,h2c,tcp,udp,fileserver"`
Host string `json:"host,omitempty"`

View File

@@ -1,8 +0,0 @@
package utils
// empty struct that implements Locker interface
// for hinting that no copy should be performed.
type NoCopy struct{}
func (*NoCopy) Lock() {}
func (*NoCopy) Unlock() {}

View File

@@ -1,54 +0,0 @@
package utils
import (
"sync/atomic"
)
type RefCount struct {
_ NoCopy
refCount uint32
zeroCh chan struct{}
}
func NewRefCounter() *RefCount {
rc := &RefCount{
refCount: 1,
zeroCh: make(chan struct{}),
}
return rc
}
func (rc *RefCount) Zero() <-chan struct{} {
return rc.zeroCh
}
func (rc *RefCount) Add() {
// We add before checking to ensure proper ordering
newV := atomic.AddUint32(&rc.refCount, 1)
if newV == 1 {
// If it was 0 before we added, that means we're incrementing after a close
// This is a programming error
panic("RefCount.Add() called after count reached zero")
}
}
func (rc *RefCount) Sub() {
// First read the current value
for {
current := atomic.LoadUint32(&rc.refCount)
if current == 0 {
// Already at zero, channel should be closed
return
}
// Try to decrement, but only if the value hasn't changed
if atomic.CompareAndSwapUint32(&rc.refCount, current, current-1) {
if current == 1 { // Was this the last reference?
close(rc.zeroCh)
}
return
}
// If CAS failed, someone else modified the count, try again
}
}

View File

@@ -1,78 +0,0 @@
package utils
import (
"sync"
"testing"
"time"
expect "github.com/yusing/goutils/testing"
)
func TestRefCounterAddSub(t *testing.T) {
rc := NewRefCounter() // Count starts at 1
var wg sync.WaitGroup
rc.Add()
for range 2 {
wg.Go(rc.Sub)
}
wg.Wait()
expect.Equal(t, int(rc.refCount), 0)
select {
case <-rc.Zero():
// Expected behavior
case <-time.After(1 * time.Second):
t.Fatal("Expected Zero channel to close, but it didn't")
}
}
func TestRefCounterMultipleAddSub(t *testing.T) {
rc := NewRefCounter()
var wg sync.WaitGroup
numAdds := 5
numSubs := 5
wg.Add(numAdds)
for range numAdds {
go func() {
defer wg.Done()
rc.Add()
}()
}
wg.Wait()
expect.Equal(t, int(rc.refCount), numAdds+1)
wg.Add(numSubs)
for range numSubs {
go func() {
defer wg.Done()
rc.Sub()
}()
}
wg.Wait()
expect.Equal(t, int(rc.refCount), numAdds+1-numSubs)
rc.Sub()
select {
case <-rc.Zero():
// Expected behavior
case <-time.After(1 * time.Second):
t.Fatal("Expected Zero channel to close, but it didn't")
}
}
func TestRefCounterOneInitially(t *testing.T) {
rc := NewRefCounter()
rc.Sub() // Bring count to zero
select {
case <-rc.Zero():
// Expected behavior
case <-time.After(1 * time.Second):
t.Fatal("Expected Zero channel to close, but it didn't")
}
}