diff --git a/drivers/115_open/types.go b/drivers/115_open/types.go index 0bd95bfd1..493772a58 100644 --- a/drivers/115_open/types.go +++ b/drivers/115_open/types.go @@ -3,9 +3,9 @@ package _115_open import ( "time" + sdk "github.com/OpenListTeam/115-sdk-go" "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/pkg/utils" - sdk "github.com/OpenListTeam/115-sdk-go" ) type Obj sdk.GetFilesResp_File diff --git a/drivers/189pc/torrent.go b/drivers/189pc/torrent.go index 3068a0f7b..8d591cf4f 100644 --- a/drivers/189pc/torrent.go +++ b/drivers/189pc/torrent.go @@ -89,7 +89,6 @@ func (y *Cloud189PC) RapidUploadFromTorrent(ctx context.Context, dstDir model.Ob sliceMd5Hex = strings.ToUpper(utils.GetMD5EncodeStr(strings.Join(upperSliceMD5s, "\n"))) } - // 使用与 Web 端一致的三步秒传流程 fullUrl := "https://upload.cloud.189.cn" if isFamily { @@ -110,7 +109,6 @@ func (y *Cloud189PC) RapidUploadFromTorrent(ctx context.Context, dstDir model.Ob initParams.Set("familyId", y.FamilyID) } - var uploadInfo InitMultiUploadResp _, err = y.request(fullUrl+"/initMultiUpload", "GET", func(req *resty.Request) { req.SetContext(ctx) @@ -119,7 +117,6 @@ func (y *Cloud189PC) RapidUploadFromTorrent(ctx context.Context, dstDir model.Ob return nil, fmt.Errorf("initMultiUpload 失败: %w", err) } - uploadFileId := uploadInfo.Data.UploadFileID // Step 2: checkTransSecond(用 fileMd5 + sliceMd5 + uploadFileId 检查秒传) @@ -129,7 +126,6 @@ func (y *Cloud189PC) RapidUploadFromTorrent(ctx context.Context, dstDir model.Ob "uploadFileId": uploadFileId, } - var checkResp struct { Data struct { FileDataExists int `json:"fileDataExists"` @@ -143,7 +139,6 @@ func (y *Cloud189PC) RapidUploadFromTorrent(ctx context.Context, dstDir model.Ob return nil, fmt.Errorf("秒传检查失败: %w", err) } - if checkResp.Data.FileDataExists != 1 { return nil, fmt.Errorf("秒传失败:云端不存在该文件(fileMD5=%s, sliceMD5=%s, size=%d)", fileMD5Upper, sliceMd5Hex, fileSize) } diff --git a/drivers/alias/driver.go b/drivers/alias/driver.go index e1ba41eb6..7008495ce 100644 --- a/drivers/alias/driver.go +++ b/drivers/alias/driver.go @@ -249,6 +249,7 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( } linkClosers := make([]io.Closer, 0, len(files)) rrf := make([]model.RangeReaderIF, 0, len(files)) + requireReference := false for _, f := range files { link, fi, err := d.link(ctx, f.GetPath(), args) if err != nil { @@ -258,9 +259,13 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( _ = link.Close() continue } - l := *link // 复制一份,避免修改到原始link - if l.ContentLength == 0 { - l.ContentLength = fi.GetSize() + l := &model.Link{ + URL: link.URL, + Header: link.Header, + RangeReader: link.RangeReader, + Concurrency: link.Concurrency, + PartSize: link.PartSize, + ContentLength: link.ContentLength, } if d.DownloadConcurrency > 0 { l.Concurrency = d.DownloadConcurrency @@ -268,20 +273,27 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( if d.DownloadPartSize > 0 { l.PartSize = d.DownloadPartSize * utils.KB } - rr, err := stream.GetRangeReaderFromLink(l.ContentLength, &l) + if l.ContentLength == 0 { + l.ContentLength = fi.GetSize() + } + rr, err := stream.GetRangeReaderFromLink(l.ContentLength, l) if err != nil { _ = link.Close() continue } linkClosers = append(linkClosers, link) + if link.RequireReference { + requireReference = true + } rrf = append(rrf, rr) } rr := func(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) { return rrf[rand.Intn(len(rrf))].RangeRead(ctx, httpRange) } return &model.Link{ - RangeReader: stream.RangeReaderFunc(rr), - SyncClosers: utils.NewSyncClosers(linkClosers...), + RangeReader: stream.RangeReaderFunc(rr), + SyncClosers: utils.NewSyncClosers(linkClosers...), + RequireReference: requireReference, }, nil } @@ -315,14 +327,10 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( if err != nil { return nil, err } - resultLink := *link // 复制一份,避免修改到原始link + resultLink := link.Clone() // 复制一份,避免修改到原始link resultLink.Expiration = nil - resultLink.SyncClosers = utils.NewSyncClosers(link) if args.Redirect { - return &resultLink, nil - } - if resultLink.ContentLength == 0 { - resultLink.ContentLength = fi.GetSize() + return resultLink, nil } if d.DownloadConcurrency > 0 { resultLink.Concurrency = d.DownloadConcurrency @@ -330,7 +338,10 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( if d.DownloadPartSize > 0 { resultLink.PartSize = d.DownloadPartSize * utils.KB } - return &resultLink, nil + if resultLink.ContentLength == 0 { + resultLink.ContentLength = fi.GetSize() + } + return resultLink, nil } func (d *Alias) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { @@ -501,9 +512,7 @@ func (d *Alias) Extract(ctx context.Context, obj model.Obj, args model.ArchiveIn sign.SignArchive(reqPath)), }, nil } - resultLink := *link - resultLink.SyncClosers = utils.NewSyncClosers(link) - return &resultLink, nil + return link.Clone(), nil } func (d *Alias) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) error { diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index b544391dd..1fceb36e0 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -263,9 +263,7 @@ func (d *Chunk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( if err != nil { return nil, err } - resultLink := *l - resultLink.SyncClosers = utils.NewSyncClosers(l) - return &resultLink, nil + return l.Clone(), nil } // 检查0号块不等于-1 以支持空文件 // 如果块数量大于1 最后一块不可能为0 diff --git a/drivers/lanzou/driver.go b/drivers/lanzou/driver.go index e143cde20..0a7213e10 100644 --- a/drivers/lanzou/driver.go +++ b/drivers/lanzou/driver.go @@ -3,6 +3,7 @@ package lanzou import ( "context" "net/http" + "sync/atomic" "github.com/OpenListTeam/OpenList/v4/drivers/base" "github.com/OpenListTeam/OpenList/v4/internal/driver" @@ -18,7 +19,7 @@ type LanZou struct { uid string vei string - flag int32 + flag atomic.Int32 } func (d *LanZou) Config() driver.Config { diff --git a/drivers/lanzou/util.go b/drivers/lanzou/util.go index 9a15d428e..e70154402 100644 --- a/drivers/lanzou/util.go +++ b/drivers/lanzou/util.go @@ -10,7 +10,6 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "time" "github.com/OpenListTeam/OpenList/v4/drivers/base" @@ -43,9 +42,9 @@ func (d *LanZou) get(url string, callback base.ReqCallback) ([]byte, error) { func (d *LanZou) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) { data, err := d._post(url, callback, resp, false) if err == ErrCookieExpiration && d.IsAccount() { - if atomic.CompareAndSwapInt32(&d.flag, 0, 1) { + if d.flag.CompareAndSwap(0, 1) { _, err2 := d.Login() - atomic.SwapInt32(&d.flag, 0) + d.flag.Swap(0) if err2 != nil { err = errors.Join(err, err2) d.Status = err.Error() @@ -53,7 +52,7 @@ func (d *LanZou) post(url string, callback base.ReqCallback, resp interface{}) ( return data, err } } - for atomic.LoadInt32(&d.flag) != 0 { + for d.flag.Load() != 0 { runtime.Gosched() } return d._post(url, callback, resp, false) diff --git a/drivers/mediafire/meta.go b/drivers/mediafire/meta.go index 1a3d37c8f..f5b420a25 100644 --- a/drivers/mediafire/meta.go +++ b/drivers/mediafire/meta.go @@ -58,4 +58,3 @@ func init() { } }) } - diff --git a/drivers/mediafire/types.go b/drivers/mediafire/types.go index eebc85460..4a59ae568 100644 --- a/drivers/mediafire/types.go +++ b/drivers/mediafire/types.go @@ -244,4 +244,3 @@ type MediafireUserInfoResponse struct { CurrentAPIVersion string `json:"current_api_version"` } `json:"response"` } - diff --git a/drivers/mediafire/util.go b/drivers/mediafire/util.go index 24259d0bc..49711fc5e 100644 --- a/drivers/mediafire/util.go +++ b/drivers/mediafire/util.go @@ -732,4 +732,3 @@ func (d *Mediafire) getFileByHash(ctx context.Context, hash string) (*model.ObjT file := resp.Response.FileInfo[0] return d.fileToObj(file), nil } - diff --git a/drivers/smb/driver.go b/drivers/smb/driver.go index 88325e5cf..35e220747 100644 --- a/drivers/smb/driver.go +++ b/drivers/smb/driver.go @@ -6,6 +6,7 @@ import ( "path" "path/filepath" "strings" + "sync/atomic" "github.com/OpenListTeam/OpenList/v4/internal/driver" "github.com/OpenListTeam/OpenList/v4/internal/model" @@ -16,7 +17,7 @@ import ( ) type SMB struct { - lastConnTime int64 + lastConnTime atomic.Int64 model.Storage Addition fs *smb2.Share diff --git a/drivers/smb/util.go b/drivers/smb/util.go index 6ae365f8f..60db36b27 100644 --- a/drivers/smb/util.go +++ b/drivers/smb/util.go @@ -6,7 +6,6 @@ import ( "io/fs" "os" "path/filepath" - "sync/atomic" "time" "github.com/OpenListTeam/OpenList/v4/pkg/singleflight" @@ -16,15 +15,15 @@ import ( ) func (d *SMB) updateLastConnTime() { - atomic.StoreInt64(&d.lastConnTime, time.Now().Unix()) + d.lastConnTime.Store(time.Now().Unix()) } func (d *SMB) cleanLastConnTime() { - atomic.StoreInt64(&d.lastConnTime, 0) + d.lastConnTime.Store(0) } func (d *SMB) getLastConnTime() time.Time { - return time.Unix(atomic.LoadInt64(&d.lastConnTime), 0) + return time.Unix(d.lastConnTime.Load(), 0) } func (d *SMB) initFS(ctx context.Context) error { diff --git a/drivers/strm/driver.go b/drivers/strm/driver.go index 4736ff1cf..1189fd9a7 100644 --- a/drivers/strm/driver.go +++ b/drivers/strm/driver.go @@ -219,9 +219,7 @@ func (d *Strm) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (* }, nil } - resultLink := *link - resultLink.SyncClosers = utils.NewSyncClosers(link) - return &resultLink, nil + return link.Clone(), nil } var _ driver.Driver = (*Strm)(nil) diff --git a/drivers/webdav/driver_test.go b/drivers/webdav/driver_test.go index 6251f7ff2..cccd16d5e 100644 --- a/drivers/webdav/driver_test.go +++ b/drivers/webdav/driver_test.go @@ -23,10 +23,10 @@ func TestGetMapsMissingPathToObjectNotFound(t *testing.T) { } func TestMakeDirAfterMissingWebDAVStat(t *testing.T) { - var mkcolCount int32 + var mkcolCount atomic.Int32 d, cleanup := newTestDriver(t, func(w http.ResponseWriter, r *http.Request) bool { if r.Method == "MKCOL" && (r.URL.Path == "/new" || r.URL.Path == "/new/") { - atomic.AddInt32(&mkcolCount, 1) + mkcolCount.Add(1) w.WriteHeader(http.StatusCreated) return true } @@ -37,7 +37,7 @@ func TestMakeDirAfterMissingWebDAVStat(t *testing.T) { if err := op.MakeDir(context.Background(), d, "/new"); err != nil { t.Fatalf("MakeDir failed: %v", err) } - if got := atomic.LoadInt32(&mkcolCount); got != 1 { + if got := mkcolCount.Load(); got != 1 { t.Fatalf("expected one MKCOL request, got %d", got) } } diff --git a/internal/fs/list_test.go b/internal/fs/list_test.go index ebaf4371e..d8c8e47fe 100644 --- a/internal/fs/list_test.go +++ b/internal/fs/list_test.go @@ -23,8 +23,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: true, }, - path: "/folder", - want: false, + path: "/folder", + want: false, reason: "nil user (treated as admin) should not hide", }, { @@ -38,8 +38,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: true, }, - path: "/folder", - want: false, + path: "/folder", + want: false, reason: "user with can_see_hides permission should not hide", }, { @@ -47,9 +47,9 @@ func TestWhetherHide(t *testing.T) { user: &model.User{ Role: model.GUEST, }, - meta: nil, - path: "/folder", - want: false, + meta: nil, + path: "/folder", + want: false, reason: "nil meta should not hide", }, { @@ -62,8 +62,8 @@ func TestWhetherHide(t *testing.T) { Hide: "", HSub: true, }, - path: "/folder", - want: false, + path: "/folder", + want: false, reason: "empty hide string should not hide", }, { @@ -76,8 +76,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: false, }, - path: "/folder", - want: true, + path: "/folder", + want: true, reason: "exact path match should hide for guest", }, { @@ -90,8 +90,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: true, }, - path: "/folder/subfolder", - want: true, + path: "/folder/subfolder", + want: true, reason: "sub path with HSub=true should hide for guest", }, { @@ -104,8 +104,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: false, }, - path: "/folder/subfolder", - want: false, + path: "/folder/subfolder", + want: false, reason: "sub path with HSub=false should not hide", }, { @@ -118,8 +118,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: true, }, - path: "/other", - want: false, + path: "/other", + want: false, reason: "non-sub path should not hide even with HSub=true", }, { @@ -133,8 +133,8 @@ func TestWhetherHide(t *testing.T) { Hide: "secret", HSub: true, }, - path: "/folder", - want: true, + path: "/folder", + want: true, reason: "user without can_see_hides permission should hide", }, } diff --git a/internal/mem/utils.go b/internal/mem/utils.go index b0a46d731..ef82f9958 100644 --- a/internal/mem/utils.go +++ b/internal/mem/utils.go @@ -17,7 +17,7 @@ func MemoryGrowCheck(growSize uint64) error { if conf.MinFreeMemory == 0 { return ErrNotEnoughMemory } - m, err, _ := singleflight.AnyGroup.Do("MemoryGrowCheck", func() (any, error) { + r, err, _ := singleflight.AnyGroup.Do("MemoryGrowCheck", func() (any, error) { m, err := mem.VirtualMemory() if err != nil { return nil, err @@ -25,18 +25,20 @@ func MemoryGrowCheck(growSize uint64) error { if m.Available < conf.MinFreeMemory { return nil, ErrNotEnoughMemory } - return m, nil + var res atomic.Uint64 + res.Store(m.Available) + return &res, nil }) if err != nil { return err } - memStat := m.(*mem.VirtualMemoryStat) + res := r.(*atomic.Uint64) for { - available := atomic.LoadUint64(&memStat.Available) + available := res.Load() if available < growSize || available-growSize < conf.MinFreeMemory { return ErrNotEnoughMemory } - if atomic.CompareAndSwapUint64(&memStat.Available, available, available-growSize) { + if res.CompareAndSwap(available, available-growSize) { return nil } } diff --git a/internal/model/args.go b/internal/model/args.go index 073c94a63..393a716fa 100644 --- a/internal/model/args.go +++ b/internal/model/args.go @@ -42,6 +42,20 @@ type Link struct { RequireReference bool `json:"-"` } +func (l *Link) Clone() *Link { + return &Link{ + URL: l.URL, + Header: l.Header, + RangeReader: l.RangeReader, + Expiration: l.Expiration, + Concurrency: l.Concurrency, + PartSize: l.PartSize, + ContentLength: l.ContentLength, + SyncClosers: utils.NewSyncClosers(l), + RequireReference: l.RequireReference, + } +} + type OtherArgs struct { Obj Obj Method string diff --git a/internal/net/request.go b/internal/net/request.go index 4fcdad0fd..0cfa7942e 100644 --- a/internal/net/request.go +++ b/internal/net/request.go @@ -113,13 +113,13 @@ type downloader struct { nextChunk int //next chunk id bufMap map[int]*buffer.PipeBuffer - written int64 //total bytes of file downloaded from remote + written atomic.Int64 //total bytes of file downloaded from remote concurrency int //剩余的并发数,递减。到0时停止并发 pos int64 maxPos int64 delayMu sync.Mutex - readingID int64 // 正在被读取的id + readingID atomic.Int64 // 正在被读取的id hc *hcache.HybridCache } @@ -294,7 +294,7 @@ func (d *downloader) sendChunkTask(newConcurrency bool) (err error) { func (d *downloader) interrupt() error { err := context.Cause(d.ctx) if err == nil { - if atomic.LoadInt64(&d.written) != d.params.Range.Length { + if d.written.Load() != d.params.Range.Length { err = fmt.Errorf("interrupted") } } else if errors.Is(err, context.Canceled) { @@ -333,7 +333,7 @@ func (d *downloader) popBuf(id int) *buffer.PipeBuffer { } func (d *downloader) finishBuf(nextId int, prev *buffer.PipeBuffer) (next *buffer.PipeBuffer) { - atomic.StoreInt64(&d.readingID, int64(nextId)) + d.readingID.Store(int64(nextId)) d.mu.Lock() shouldSendTask := d.bufMap[d.nextChunk] == nil @@ -479,7 +479,7 @@ func (d *downloader) tryDownloadChunk(params *HttpRequestParams, ch *chunk) (int } } d.mu.Unlock() - if int64(ch.id) != atomic.LoadInt64(&d.readingID) { //正在被读取的优先重试 + if int64(ch.id) != d.readingID.Load() { //正在被读取的优先重试 d.delayMu.Lock() defer d.delayMu.Unlock() if !d.delay(time.Millisecond * time.Duration(rand.Uint32N(300)+200)) { @@ -556,7 +556,7 @@ func (d *downloader) checkTotalBytes(resp *http.Response) error { } func (d *downloader) incrWritten(n int64) { - atomic.AddInt64(&d.written, n) + d.written.Add(n) } // Chunk represents a single chunk of data to write by the worker routine. diff --git a/pkg/aria2/rpc/call.go b/pkg/aria2/rpc/call.go index e8f7d4d6e..26358f1f3 100644 --- a/pkg/aria2/rpc/call.go +++ b/pkg/aria2/rpc/call.go @@ -262,8 +262,9 @@ type sendRequest struct { } var reqid = func() func() uint64 { - var id = uint64(time.Now().UnixNano()) + var id atomic.Uint64 + id.Store(uint64(time.Now().UnixNano())) return func() uint64 { - return atomic.AddUint64(&id, 1) + return id.Add(1) } }() diff --git a/pkg/errgroup/errgroup.go b/pkg/errgroup/errgroup.go index 7d72a1d8e..336af8623 100644 --- a/pkg/errgroup/errgroup.go +++ b/pkg/errgroup/errgroup.go @@ -15,7 +15,7 @@ type Group struct { ctx context.Context opts []retry.Option - success uint64 + success atomic.Uint64 wg sync.WaitGroup sem chan token @@ -41,7 +41,7 @@ func (g *Group) done() { <-g.sem } g.wg.Done() - atomic.AddUint64(&g.success, 1) + g.success.Add(1) } func (g *Group) Wait() error { @@ -140,7 +140,7 @@ func (g *Group) SetLimit(n int) *Group { } func (g *Group) Success() uint64 { - return atomic.LoadUint64(&g.success) + return g.success.Load() } func (g *Group) Err() error { diff --git a/pkg/generic_sync/map.go b/pkg/generic_sync/map.go index 96612f0cc..527b70626 100644 --- a/pkg/generic_sync/map.go +++ b/pkg/generic_sync/map.go @@ -72,12 +72,12 @@ var expunged = unsafe.Pointer(new(interface{})) // An entry is a slot in the map corresponding to a particular key. type entry[V any] struct { - // p points to the interface{} value stored for the entry. + // p points to the value stored for the entry. // // If p == nil, the entry has been deleted and m.dirty == nil. // - // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry - // is missing from m.dirty. + // If p == expungedFor[V](), the entry has been deleted, m.dirty != nil, and + // the entry is missing from m.dirty. // // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty // != nil, in m.dirty[key]. @@ -87,14 +87,20 @@ type entry[V any] struct { // m.dirty[key] unset. // // An entry's associated value can be updated by atomic replacement, provided - // p != expunged. If p == expunged, an entry's associated value can be updated + // p != expunged. If p == expungedFor[V](), an entry's associated value can be updated // only after first setting m.dirty[key] = e so that lookups using the dirty // map find the entry. - p unsafe.Pointer // *interface{} + p atomic.Pointer[V] +} + +func expungedFor[V any]() *V { + return (*V)(expunged) } func newEntry[V any](i V) *entry[V] { - return &entry[V]{p: unsafe.Pointer(&i)} + e := &entry[V]{} + e.p.Store(&i) + return e } // Load returns the value stored in the map for a key, or nil if no @@ -131,8 +137,8 @@ func (m *MapOf[K, V]) Has(key K) bool { } func (e *entry[V]) load() (value V, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { + p := e.p.Load() + if p == nil || p == expungedFor[V]() { return value, false } return *(*V)(p), true @@ -174,11 +180,11 @@ func (m *MapOf[K, V]) Store(key K, value V) { // unchanged. func (e *entry[V]) tryStore(i *V) bool { for { - p := atomic.LoadPointer(&e.p) - if p == expunged { + p := e.p.Load() + if p == expungedFor[V]() { return false } - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { + if e.p.CompareAndSwap(p, i) { return true } } @@ -189,14 +195,14 @@ func (e *entry[V]) tryStore(i *V) bool { // If the entry was previously expunged, it must be added to the dirty map // before m.mu is unlocked. func (e *entry[V]) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expunged, nil) + return e.p.CompareAndSwap(expungedFor[V](), nil) } // storeLocked unconditionally stores a value to the entry. // // The entry must be known not to be expunged. func (e *entry[V]) storeLocked(i *V) { - atomic.StorePointer(&e.p, unsafe.Pointer(i)) + e.p.Store(i) } // LoadOrStore returns the existing value for the key if present. @@ -243,24 +249,24 @@ func (m *MapOf[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { // If the entry is expunged, tryLoadOrStore leaves the entry unchanged and // returns with ok==false. func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == expunged { + p := e.p.Load() + if p == expungedFor[V]() { return actual, false, false } if p != nil { return *(*V)(p), true, true } - // Copy the interface after the first load to make this method more amenable + // Copy the value after the first load to make this method more amenable // to escape analysis: if we hit the "load" path or the entry is expunged, we - // shouldn'V bother heap-allocating. + // shouldn't bother heap-allocating. ic := i for { - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { + if e.p.CompareAndSwap(nil, &ic) { return i, false, true } - p = atomic.LoadPointer(&e.p) - if p == expunged { + p = e.p.Load() + if p == expungedFor[V]() { return actual, false, false } if p != nil { @@ -289,11 +295,11 @@ func (m *MapOf[K, V]) Delete(key K) { func (e *entry[V]) delete() (hadValue bool) { for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { + p := e.p.Load() + if p == nil || p == expungedFor[V]() { return false } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { + if e.p.CompareAndSwap(p, nil) { return true } } @@ -401,12 +407,12 @@ func (m *MapOf[K, V]) dirtyLocked() { } func (e *entry[V]) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) + p := e.p.Load() for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { + if e.p.CompareAndSwap(nil, expungedFor[V]()) { return true } - p = atomic.LoadPointer(&e.p) + p = e.p.Load() } - return p == expunged + return p == expungedFor[V]() } diff --git a/pkg/singleflight/signleflight_test.go b/pkg/singleflight/signleflight_test.go index a6098333d..472fc1d06 100644 --- a/pkg/singleflight/signleflight_test.go +++ b/pkg/singleflight/signleflight_test.go @@ -112,9 +112,9 @@ func TestDoDupSuppress(t *testing.T) { var g Group[string] var wg1, wg2 sync.WaitGroup c := make(chan string, 1) - var calls int32 + var calls atomic.Int32 fn := func() (string, error) { - if atomic.AddInt32(&calls, 1) == 1 { + if calls.Add(1) == 1 { // First invocation. wg1.Done() } @@ -149,7 +149,7 @@ func TestDoDupSuppress(t *testing.T) { // least reached the line before the Do. c <- "bar" wg2.Wait() - if got := atomic.LoadInt32(&calls); got <= 0 || got >= n { + if got := calls.Load(); got <= 0 || got >= n { t.Errorf("number of calls = %d; want over 0 and less than %d", got, n) } } @@ -223,18 +223,19 @@ func TestPanicDo(t *testing.T) { } const n = 5 - waited := int32(n) - panicCount := int32(0) + waited := atomic.Int32{} + waited.Store(int32(n)) + panicCount := atomic.Int32{} done := make(chan struct{}) for i := 0; i < n; i++ { go func() { defer func() { if err := recover(); err != nil { t.Logf("Got panic: %v\n%s", err, debug.Stack()) - atomic.AddInt32(&panicCount, 1) + panicCount.Add(1) } - if atomic.AddInt32(&waited, -1) == 0 { + if waited.Add(-1) == 0 { close(done) } }() @@ -245,8 +246,9 @@ func TestPanicDo(t *testing.T) { select { case <-done: - if panicCount != n { - t.Errorf("Expect %d panic, but got %d", n, panicCount) + count := panicCount.Load() + if count != int32(n) { + t.Errorf("Expect %d panic, but got %d", n, count) } case <-time.After(time.Second): t.Fatalf("Do hangs") @@ -261,7 +263,8 @@ func TestGoexitDo(t *testing.T) { } const n = 5 - waited := int32(n) + waited := atomic.Int32{} + waited.Store(int32(n)) done := make(chan struct{}) for i := 0; i < n; i++ { go func() { @@ -270,7 +273,7 @@ func TestGoexitDo(t *testing.T) { if err != nil { t.Errorf("Error should be nil, but got: %v", err) } - if atomic.AddInt32(&waited, -1) == 0 { + if waited.Add(-1) == 0 { close(done) } }() diff --git a/pkg/task/task.go b/pkg/task/task.go index 5b634f10c..24a7232cf 100644 --- a/pkg/task/task.go +++ b/pkg/task/task.go @@ -108,11 +108,11 @@ func (t *Task[K]) Cancel() { if t.state == SUCCEEDED || t.state == CANCELED { return } + // maybe can't cancel + t.state = CANCELING if t.cancel != nil { t.cancel() } - // maybe can't cancel - t.state = CANCELING } func WithCancelCtx[K comparable](task *Task[K]) *Task[K] { diff --git a/pkg/task/task_test.go b/pkg/task/task_test.go index 94f283094..876f7b441 100644 --- a/pkg/task/task_test.go +++ b/pkg/task/task_test.go @@ -10,8 +10,9 @@ import ( ) func TestTask_Manager(t *testing.T) { + var next atomic.Uint64 tm := NewTaskManager(3, func(id *uint64) { - atomic.AddUint64(id, 1) + *id = next.Add(1) }) id := tm.Submit(WithCancelCtx(&Task[uint64]{ Name: "test", @@ -35,8 +36,9 @@ func TestTask_Manager(t *testing.T) { } func TestTask_Cancel(t *testing.T) { + var next atomic.Uint64 tm := NewTaskManager(3, func(id *uint64) { - atomic.AddUint64(id, 1) + *id = next.Add(1) }) id := tm.Submit(WithCancelCtx(&Task[uint64]{ Name: "test", @@ -63,8 +65,9 @@ func TestTask_Cancel(t *testing.T) { } func TestTask_Retry(t *testing.T) { + var next atomic.Uint64 tm := NewTaskManager(3, func(id *uint64) { - atomic.AddUint64(id, 1) + *id = next.Add(1) }) num := 0 id := tm.Submit(WithCancelCtx(&Task[uint64]{ diff --git a/pkg/utils/hash.go b/pkg/utils/hash.go index 596e61e54..b1859f3a6 100644 --- a/pkg/utils/hash.go +++ b/pkg/utils/hash.go @@ -134,7 +134,7 @@ func fromTypes(types []*HashType) map[*HashType]hash.Hash { // single multiwriter, where one write will update all // the hashers. func toMultiWriter(h map[*HashType]hash.Hash) io.Writer { - // Convert to to slice + // Convert to slice var w = make([]io.Writer, 0, len(h)) for _, v := range h { w = append(w, v) @@ -187,7 +187,7 @@ func (m *MultiHasher) Size() int64 { // A HashInfo contains hash string for one or more hashType type HashInfo struct { - h map[*HashType]string `json:"hashInfo"` + h map[*HashType]string } func NewHashInfoByMap(h map[*HashType]string) HashInfo { diff --git a/pkg/utils/io.go b/pkg/utils/io.go index f398161f6..e4c6ebaf3 100644 --- a/pkg/utils/io.go +++ b/pkg/utils/io.go @@ -189,17 +189,17 @@ func NewClosers(c ...io.Closer) Closers { type SyncClosers struct { closers []io.Closer - ref int32 + ref atomic.Int32 } // if closed, return false func (c *SyncClosers) AcquireReference() bool { - ref := atomic.AddInt32(&c.ref, 1) + ref := c.ref.Add(1) if ref > 0 { // log.Debugf("AcquireReference %p: %d", c, ref) return true } - atomic.StoreInt32(&c.ref, closersClosed) + c.ref.Store(closersClosed) return false } @@ -207,16 +207,16 @@ const closersClosed = math.MinInt32 func (c *SyncClosers) Close() error { for { - ref := atomic.LoadInt32(&c.ref) + ref := c.ref.Load() if ref < 0 { return nil } if ref > 1 { - if atomic.CompareAndSwapInt32(&c.ref, ref, ref-1) { + if c.ref.CompareAndSwap(ref, ref-1) { // log.Debugf("ReleaseReference %p: %d", c, ref) return nil } - } else if atomic.CompareAndSwapInt32(&c.ref, ref, closersClosed) { + } else if c.ref.CompareAndSwap(ref, closersClosed) { break } } @@ -235,7 +235,7 @@ func (c *SyncClosers) Close() error { func (c *SyncClosers) Add(closer io.Closer) { if closer != nil { - if atomic.LoadInt32(&c.ref) < 0 { + if c.ref.Load() < 0 { panic("Not reusable") } c.closers = append(c.closers, closer) @@ -244,7 +244,7 @@ func (c *SyncClosers) Add(closer io.Closer) { func (c *SyncClosers) AddIfCloser(a any) { if closer, ok := a.(io.Closer); ok { - if atomic.LoadInt32(&c.ref) < 0 { + if c.ref.Load() < 0 { panic("Not reusable") } c.closers = append(c.closers, closer) @@ -255,7 +255,7 @@ var _ ClosersIF = (*SyncClosers)(nil) // 实现cache.Expirable接口 func (c *SyncClosers) Expired() bool { - return atomic.LoadInt32(&c.ref) < 0 + return c.ref.Load() < 0 } func (c *SyncClosers) Length() int { return len(c.closers) diff --git a/pkg/utils/path.go b/pkg/utils/path.go index 4ac3dde24..0890cf2a7 100644 --- a/pkg/utils/path.go +++ b/pkg/utils/path.go @@ -111,13 +111,13 @@ func GetPathHierarchy(path string) []string { hierarchy := []string{"/"} - parts := strings.SplitSeq(path, "/") var currentPath strings.Builder - for part := range parts { + for part := range strings.SplitSeq(path, "/") { if part == "" { continue } - currentPath.WriteString("/" + part) + currentPath.WriteString("/") + currentPath.WriteString(part) hierarchy = append(hierarchy, currentPath.String()) } diff --git a/server/static/static.go b/server/static/static.go index 29f97ff74..38eee2dfa 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -98,9 +98,9 @@ func initIndex(siteConfig SiteConfig) { manifestPath = siteConfig.BasePath + "/manifest.json" } replaceMap := map[string]string{ - "cdn: undefined": fmt.Sprintf("cdn: '%s'", siteConfig.Cdn), - "base_path: undefined": fmt.Sprintf("base_path: '%s'", siteConfig.BasePath), - `href="/manifest.json"`: fmt.Sprintf(`href="%s"`, manifestPath), + "cdn: undefined": fmt.Sprintf("cdn: '%s'", siteConfig.Cdn), + "base_path: undefined": fmt.Sprintf("base_path: '%s'", siteConfig.BasePath), + `href="/manifest.json"`: fmt.Sprintf(`href="%s"`, manifestPath), } conf.RawIndexHtml = replaceStrings(conf.RawIndexHtml, replaceMap) UpdateIndex() @@ -134,10 +134,10 @@ func UpdateIndex() { func ManifestJSON(c *gin.Context) { // Get site configuration to ensure consistent base path handling siteConfig := getSiteConfig() - + // Get site title from settings siteTitle := setting.GetStr(conf.SiteTitle) - + // Get logo from settings, use the first line (light theme logo) logoSetting := setting.GetStr(conf.Logo) logoUrl := strings.Split(logoSetting, "\n")[0] @@ -167,7 +167,7 @@ func ManifestJSON(c *gin.Context) { c.Header("Content-Type", "application/json") c.Header("Cache-Control", "public, max-age=3600") // cache for 1 hour - + if err := json.NewEncoder(c.Writer).Encode(manifest); err != nil { utils.Log.Errorf("Failed to encode manifest.json: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate manifest"}) @@ -181,7 +181,7 @@ func Static(r *gin.RouterGroup, noRoute func(handlers ...gin.HandlerFunc)) { initStatic() initIndex(siteConfig) folders := []string{"assets", "images", "streamer", "static"} - + if conf.Conf.Cdn == "" { utils.Log.Debug("Setting up static file serving...") r.Use(func(c *gin.Context) {