diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index f21b5f30772..11be3f2fc52 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -863,8 +863,10 @@ const ( ) var ( + // checker is the checker function for instances map. + checker = func(v DB) bool { return v == nil } // instances is the management map for instances. - instances = gmap.NewStrAnyMap(true) + instances = gmap.NewKVMapWithChecker[string, DB](checker, true) // driverMap manages all custom registered driver. driverMap = map[string]Driver{} @@ -985,14 +987,14 @@ func Instance(name ...string) (db DB, err error) { if len(name) > 0 && name[0] != "" { group = name[0] } - v := instances.GetOrSetFuncLock(group, func() any { + v := instances.GetOrSetFuncLock(group, func() DB { db, err = NewByGroup(group) return db }) if v != nil { - return v.(DB), nil + return v, nil } - return + return nil, err } // getConfigNodeByGroup calculates and returns a configuration node of given group. It diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index 69d410a8f3f..6b1124f251f 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -50,8 +50,10 @@ const ( ) var ( + // configChecker checks whether the *Config is nil. + configChecker = func(v *Config) bool { return v == nil } // Configuration groups. - localConfigMap = gmap.NewStrAnyMap(true) + localConfigMap = gmap.NewKVMapWithChecker[string, *Config](configChecker, true) ) // SetConfig sets the global configuration for specified group. @@ -119,7 +121,7 @@ func GetConfig(name ...string) (config *Config, ok bool) { group = name[0] } if v := localConfigMap.Get(group); v != nil { - return v.(*Config), true + return v, true } return &Config{}, false } diff --git a/database/gredis/gredis_instance.go b/database/gredis/gredis_instance.go index 68a2f67e240..1f44368c365 100644 --- a/database/gredis/gredis_instance.go +++ b/database/gredis/gredis_instance.go @@ -14,8 +14,9 @@ import ( ) var ( - // localInstances for instance management of redis client. - localInstances = gmap.NewStrAnyMap(true) + // checker is the checker function for instances map. + checker = func(v *Redis) bool { return v == nil } + localInstances = gmap.NewKVMapWithChecker[string, *Redis](checker, true) ) // Instance returns an instance of redis client with specified group. @@ -26,7 +27,7 @@ func Instance(name ...string) *Redis { if len(name) > 0 && name[0] != "" { group = name[0] } - v := localInstances.GetOrSetFuncLock(group, func() any { + return localInstances.GetOrSetFuncLock(group, func() *Redis { if config, ok := GetConfig(group); ok { r, err := New(config) if err != nil { @@ -37,8 +38,4 @@ func Instance(name ...string) *Redis { } return nil }) - if v != nil { - return v.(*Redis) - } - return nil } diff --git a/i18n/gi18n/gi18n_instance.go b/i18n/gi18n/gi18n_instance.go index 8a41c34ad18..465888c91a0 100644 --- a/i18n/gi18n/gi18n_instance.go +++ b/i18n/gi18n/gi18n_instance.go @@ -14,9 +14,11 @@ const ( ) var ( + // checker is used for checking whether the value is nil. + checker = func(v *Manager) bool { return v == nil } // instances is the instances map for management // for multiple i18n instance by name. - instances = gmap.NewStrAnyMap(true) + instances = gmap.NewKVMapWithChecker[string, *Manager](checker, true) ) // Instance returns an instance of Resource. @@ -26,7 +28,7 @@ func Instance(name ...string) *Manager { if len(name) > 0 && name[0] != "" { key = name[0] } - return instances.GetOrSetFuncLock(key, func() any { + return instances.GetOrSetFuncLock(key, func() *Manager { return New() - }).(*Manager) + }) } diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 60ecf1dce0e..3a58745db8f 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -176,9 +176,12 @@ var ( // It is used for quick HTTP method searching using map. methodsMap = make(map[string]struct{}) + // checker is used for checking whether the value is nil. + checker = func(v *Server) bool { return v == nil } + // serverMapping stores more than one server instances for current processes. // The key is the name of the server, and the value is its instance. - serverMapping = gmap.NewStrAnyMap(true) + serverMapping = gmap.NewKVMapWithChecker[string, *Server](checker, true) // serverRunning marks the running server counts. // If there is no successful server running or all servers' shutdown, this value is 0. diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 57af5c3fb76..a486c981cbb 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -96,7 +96,7 @@ func GetServer(name ...any) *Server { if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } - v := serverMapping.GetOrSetFuncLock(serverName, func() any { + return serverMapping.GetOrSetFuncLock(serverName, func() *Server { s := &Server{ instance: serverName, plugins: make([]Plugin, 0), @@ -118,7 +118,6 @@ func GetServer(name ...any) *Server { s.Use(internalMiddlewareServerTracing) return s }) - return v.(*Server) } // Start starts listening on configured port. @@ -477,10 +476,9 @@ func Wait() { <-allShutdownChan // Remove plugins. - serverMapping.Iterator(func(k string, v any) bool { - s := v.(*Server) - if len(s.plugins) > 0 { - for _, p := range s.plugins { + serverMapping.Iterator(func(k string, v *Server) bool { + if len(v.plugins) > 0 { + for _, p := range v.plugins { intlog.Printf(ctx, `remove plugin: %s`, p.Name()) if err := p.Remove(); err != nil { intlog.Errorf(ctx, `%+v`, err) diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index f7cf1554900..60313218aa2 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -190,9 +190,9 @@ func forkRestartProcess(ctx context.Context, newExeFilePath ...string) error { // getServerFdMap returns all the servers name to file descriptor mapping as map. func getServerFdMap() map[string]listenerFdMap { sfm := make(map[string]listenerFdMap) - serverMapping.RLockFunc(func(m map[string]any) { + serverMapping.RLockFunc(func(m map[string]*Server) { for k, v := range m { - sfm[k] = v.(*Server).getListenerFdMap() + sfm[k] = v.getListenerFdMap() } }) return sfm @@ -263,11 +263,10 @@ func shutdownWebServersGracefully(ctx context.Context, signal os.Signal) { } else { glog.Printf(ctx, "pid[%d]: server gracefully shutting down by api", gproc.Pid()) } - serverMapping.RLockFunc(func(m map[string]any) { + serverMapping.RLockFunc(func(m map[string]*Server) { for _, v := range m { - server := v.(*Server) - server.doServiceDeregister() - for _, s := range server.servers { + v.doServiceDeregister() + for _, s := range v.servers { s.Shutdown(ctx) } } @@ -276,9 +275,9 @@ func shutdownWebServersGracefully(ctx context.Context, signal os.Signal) { // forceCloseWebServers forced shuts down all servers. func forceCloseWebServers(ctx context.Context) { - serverMapping.RLockFunc(func(m map[string]any) { + serverMapping.RLockFunc(func(m map[string]*Server) { for _, v := range m { - for _, s := range v.(*Server).servers { + for _, s := range v.servers { s.Close(ctx) } } diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index b5c03fe25b0..63cd8b2ef23 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -119,8 +119,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*Han array = strings.Split(path[1:], "/") } var ( - lastMiddlewareElem *glist.Element - parsedItemList = glist.New() + lastMiddlewareElem *glist.TElement[*HandlerItemParsed] + parsedItemList = glist.NewT[*HandlerItemParsed]() repeatHandlerCheckMap = make(map[int]struct{}, 16) ) @@ -245,7 +245,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*Han var index = 0 parsedItems = make([]*HandlerItemParsed, parsedItemList.Len()) for e := parsedItemList.Front(); e != nil; e = e.Next() { - parsedItems[index] = e.Value.(*HandlerItemParsed) + parsedItems[index] = e.Value index++ } } diff --git a/net/gtcp/gtcp_pool.go b/net/gtcp/gtcp_pool.go index 70b6ae735a6..6bccbdf682e 100644 --- a/net/gtcp/gtcp_pool.go +++ b/net/gtcp/gtcp_pool.go @@ -30,13 +30,14 @@ const ( ) var ( + poolChecker = func(v *gpool.Pool) bool { return v == nil } // addressPoolMap is a mapping for address to its pool object. - addressPoolMap = gmap.NewStrAnyMap(true) + addressPoolMap = gmap.NewKVMapWithChecker[string, *gpool.Pool](poolChecker, true) ) // NewPoolConn creates and returns a connection with pool feature. func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { - v := addressPoolMap.GetOrSetFuncLock(addr, func() any { + v := addressPoolMap.GetOrSetFuncLock(addr, func() *gpool.Pool { var pool *gpool.Pool pool = gpool.New(defaultPoolExpire, func() (any, error) { if conn, err := NewConn(addr, timeout...); err == nil { @@ -47,7 +48,7 @@ func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { }) return pool }) - value, err := v.(*gpool.Pool).Get() + value, err := v.Get() if err != nil { return nil, err } diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index e3d8ae77b2d..5b1c252c1ba 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -38,7 +38,12 @@ type Server struct { } // Map for name to server, for singleton purpose. -var serverMapping = gmap.NewStrAnyMap(true) +var ( + // checker is used for checking whether the value is nil. + checker = func(v *Server) bool { return v == nil } + // serverMapping is the map for name to server. + serverMapping = gmap.NewKVMapWithChecker[any, *Server](checker, true) +) // GetServer returns the TCP server with specified `name`, // or it returns a new normal TCP server named `name` if it does not exist. @@ -48,9 +53,9 @@ func GetServer(name ...any) *Server { if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } - return serverMapping.GetOrSetFuncLock(serverName, func() any { + return serverMapping.GetOrSetFuncLock(serverName, func() *Server { return NewServer("", nil) - }).(*Server) + }) } // NewServer creates and returns a new normal TCP server. diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index 9a23e27723d..5444ed80177 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -47,8 +47,10 @@ type Server struct { type ServerHandler func(conn *ServerConn) var ( + // checker is used for checking whether the value is nil. + checker = func(v *Server) bool { return v == nil } // serverMapping is used for instance name to its UDP server mappings. - serverMapping = gmap.NewStrAnyMap(true) + serverMapping = gmap.NewKVMapWithChecker[string, *Server](checker, true) ) // GetServer creates and returns an udp server instance with given name. @@ -57,12 +59,9 @@ func GetServer(name ...any) *Server { if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } - if s := serverMapping.Get(serverName); s != nil { - return s.(*Server) - } - s := NewServer("", nil) - serverMapping.Set(serverName, s) - return s + return serverMapping.GetOrSetFuncLock(serverName, func() *Server { + return NewServer("", nil) + }) } // NewServer creates and returns an udp server. diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 49daf72907a..baf4ec9222d 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -21,12 +21,12 @@ import ( // AdapterMemory is an adapter implements using memory. type AdapterMemory struct { - data *memoryData // data is the underlying cache data which is stored in a hash table. - expireTimes *memoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. - expireSets *memoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. - lru *memoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. - eventList *glist.List // eventList is the asynchronous event list for internal data synchronization. - closed *gtype.Bool // closed controls the cache closed or not. + data *memoryData // data is the underlying cache data which is stored in a hash table. + expireTimes *memoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. + expireSets *memoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. + lru *memoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. + eventList *glist.TList[*adapterMemoryEvent] // eventList is the asynchronous event list for internal data synchronization. + closed *gtype.Bool // closed controls the cache closed or not. } var _ Adapter = (*AdapterMemory)(nil) @@ -61,7 +61,7 @@ func doNewAdapterMemory() *AdapterMemory { data: newMemoryData(), expireTimes: newMemoryExpireTimes(), expireSets: newMemoryExpireSets(), - eventList: glist.New(true), + eventList: glist.NewT[*adapterMemoryEvent](true), closed: gtype.NewBool(), } // Here may be a "timer leak" if adapter is manually changed from adapter_memory adapter. @@ -414,7 +414,6 @@ func (c *AdapterMemory) syncEventAndClearExpired(ctx context.Context) { return } var ( - event *adapterMemoryEvent oldExpireTime int64 newExpireTime int64 ) @@ -422,11 +421,10 @@ func (c *AdapterMemory) syncEventAndClearExpired(ctx context.Context) { // Data expiration synchronization. // ================================ for { - v := c.eventList.PopFront() - if v == nil { + event := c.eventList.PopFront() + if event == nil { break } - event = v.(*adapterMemoryEvent) // Fetching the old expire set. oldExpireTime = c.expireTimes.Get(event.k) // Calculating the new expiration time set. diff --git a/os/gcache/gcache_adapter_memory_lru.go b/os/gcache/gcache_adapter_memory_lru.go index 8473a88c459..d72817ad02f 100644 --- a/os/gcache/gcache_adapter_memory_lru.go +++ b/os/gcache/gcache_adapter_memory_lru.go @@ -13,20 +13,23 @@ import ( "github.com/gogf/gf/v2/container/gmap" ) +// checker is used to check if the value is nil. +var checker = func(v *glist.Element) bool { return v == nil } + // memoryLru holds LRU info. // It uses list.List from stdlib for its underlying doubly linked list. type memoryLru struct { - mu sync.RWMutex // Mutex to guarantee concurrent safety. - cap int // LRU cap. - data *gmap.Map // Key mapping to the item of the list. - list *glist.List // Key list. + mu sync.RWMutex // Mutex to guarantee concurrent safety. + cap int // LRU cap. + data *gmap.KVMap[any, *glist.Element] // Key mapping to the item of the list. + list *glist.List // Key list. } // newMemoryLru creates and returns a new LRU manager. func newMemoryLru(cap int) *memoryLru { lru := &memoryLru{ cap: cap, - data: gmap.New(false), + data: gmap.NewKVMapWithChecker[any, *glist.Element](checker, false), list: glist.New(false), } return lru @@ -41,7 +44,7 @@ func (l *memoryLru) Remove(keys ...any) { defer l.mu.Unlock() for _, key := range keys { if v := l.data.Remove(key); v != nil { - l.list.Remove(v.(*glist.Element)) + l.list.Remove(v) } } } @@ -63,9 +66,8 @@ func (l *memoryLru) SaveAndEvict(keys ...any) (evictedKeys []any) { } func (l *memoryLru) doSaveAndEvict(key any) (evictedKey any) { - var element *glist.Element - if v := l.data.Get(key); v != nil { - element = v.(*glist.Element) + element := l.data.Get(key) + if element != nil { if element.Prev() == nil { // It this element is already on top of list, // it ignores the element moving. diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 8ab058c3f64..e71d49bc064 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -56,7 +56,7 @@ func Instance(name ...string) *Config { if len(name) > 0 && name[0] != "" { instanceName = name[0] } - return localInstances.GetOrSetFuncLock(instanceName, func() any { + return localInstances.GetOrSetFuncLock(instanceName, func() *Config { adapterFile, err := NewAdapterFile() if err != nil { intlog.Errorf(context.Background(), `%+v`, err) @@ -66,7 +66,7 @@ func Instance(name ...string) *Config { adapterFile.SetFileName(instanceName) } return NewWithAdapter(adapterFile) - }).(*Config) + }) } // SetAdapter sets the adapter of the current Config object. diff --git a/os/gcfg/gcfg_adapter_file.go b/os/gcfg/gcfg_adapter_file.go index 64be33238fd..459a4096759 100644 --- a/os/gcfg/gcfg_adapter_file.go +++ b/os/gcfg/gcfg_adapter_file.go @@ -46,8 +46,9 @@ const ( var ( supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml", "properties"} // All supported file types suffixes. - localInstances = gmap.NewStrAnyMap(true) // Instances map containing configuration instances. - customConfigContentMap = gmap.NewStrStrMap(true) // Customized configuration content. + checker = func(v *Config) bool { return v == nil } + localInstances = gmap.NewKVMapWithChecker[string, *Config](checker, true) // Instances map containing configuration instances. + customConfigContentMap = gmap.NewStrStrMap(true) // Customized configuration content. // Prefix array for trying searching in resource manager. resourceTryFolders = []string{ diff --git a/os/gcfg/gcfg_adapter_file_content.go b/os/gcfg/gcfg_adapter_file_content.go index 0ee43c7f134..c3bd240fffc 100644 --- a/os/gcfg/gcfg_adapter_file_content.go +++ b/os/gcfg/gcfg_adapter_file_content.go @@ -20,13 +20,11 @@ func (a *AdapterFile) SetContent(content string, fileNameOrPath ...string) { usedFileNameOrPath = fileNameOrPath[0] } // Clear file cache for instances which cached `name`. - localInstances.LockFunc(func(m map[string]any) { + localInstances.LockFunc(func(m map[string]*Config) { if customConfigContentMap.Contains(usedFileNameOrPath) { for _, v := range m { - if configInstance, ok := v.(*Config); ok { - if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { - fileConfig.jsonMap.Remove(usedFileNameOrPath) - } + if fileConfig, ok := v.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Remove(usedFileNameOrPath) } } } @@ -54,13 +52,11 @@ func (a *AdapterFile) RemoveContent(fileNameOrPath ...string) { usedFileNameOrPath = fileNameOrPath[0] } // Clear file cache for instances which cached `name`. - localInstances.LockFunc(func(m map[string]any) { + localInstances.LockFunc(func(m map[string]*Config) { if customConfigContentMap.Contains(usedFileNameOrPath) { for _, v := range m { - if configInstance, ok := v.(*Config); ok { - if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { - fileConfig.jsonMap.Remove(usedFileNameOrPath) - } + if fileConfig, ok := v.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Remove(usedFileNameOrPath) } } customConfigContentMap.Remove(usedFileNameOrPath) @@ -75,12 +71,10 @@ func (a *AdapterFile) RemoveContent(fileNameOrPath ...string) { func (a *AdapterFile) ClearContent() { customConfigContentMap.Clear() // Clear cache for all instances. - localInstances.LockFunc(func(m map[string]any) { + localInstances.LockFunc(func(m map[string]*Config) { for _, v := range m { - if configInstance, ok := v.(*Config); ok { - if fileConfig, ok := configInstance.GetAdapter().(*AdapterFile); ok { - fileConfig.jsonMap.Clear() - } + if fileConfig, ok := v.GetAdapter().(*AdapterFile); ok { + fileConfig.jsonMap.Clear() } } }) diff --git a/os/gcfg/gcfg_z_unit_instance_test.go b/os/gcfg/gcfg_z_unit_instance_test.go index 47bf3cbda06..b286b2cd624 100644 --- a/os/gcfg/gcfg_z_unit_instance_test.go +++ b/os/gcfg/gcfg_z_unit_instance_test.go @@ -94,7 +94,7 @@ func Test_Instance_EnvPath(t *testing.T) { t.Assert(Instance("c3") != nil, true) t.Assert(Instance("c3").MustGet(ctx, "my-config"), "3") t.Assert(Instance("c4").MustGet(ctx, "my-config"), "4") - localInstances = gmap.NewStrAnyMap(true) + localInstances = gmap.NewKVMapWithChecker[string, *Config](checker, true) }) } @@ -105,6 +105,6 @@ func Test_Instance_EnvFile(t *testing.T) { genv.Set("GF_GCFG_FILE", "c6.json") defer genv.Set("GF_GCFG_FILE", "") t.Assert(Instance().MustGet(ctx, "my-config"), "6") - localInstances = gmap.NewStrAnyMap(true) + localInstances = gmap.NewKVMapWithChecker[string, *Config](checker, true) }) } diff --git a/os/gfpool/gfpool.go b/os/gfpool/gfpool.go index 5eec01b27ef..b6bd6107848 100644 --- a/os/gfpool/gfpool.go +++ b/os/gfpool/gfpool.go @@ -36,6 +36,8 @@ type File struct { } var ( + // checker is used for checking whether the value is nil. + checker = func(v *Pool) bool { return v == nil } // Global file pointer pool. - pools = gmap.NewStrAnyMap(true) + pools = gmap.NewKVMapWithChecker[string, *Pool](checker, true) ) diff --git a/os/gfpool/gfpool_file.go b/os/gfpool/gfpool_file.go index b77f2fbf485..b0bc98d8adf 100644 --- a/os/gfpool/gfpool_file.go +++ b/os/gfpool/gfpool_file.go @@ -31,10 +31,10 @@ func Open(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file * // } pool := pools.GetOrSetFuncLock( fmt.Sprintf("%s&%d&%d&%d", path, flag, fpTTL, perm), - func() any { + func() *Pool { return New(path, flag, perm, fpTTL) }, - ).(*Pool) + ) return pool.File() } @@ -52,7 +52,7 @@ func Get(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file *F return nil } - fp, _ := f.(*Pool).pool.Get() + fp, _ := f.pool.Get() return fp.(*File) } diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 2ff8f7eb0c4..8eb011fecdd 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -27,22 +27,22 @@ import ( // Watcher is the monitor for file changes. type Watcher struct { - watcher *fsnotify.Watcher // Underlying fsnotify object. - events *gqueue.Queue // Used for internal event management. - cache *gcache.Cache // Used for repeated event filter. - nameSet *gset.StrSet // Used for AddOnce feature. - callbacks *gmap.StrAnyMap // Path(file/folder) to callbacks mapping. - closeChan chan struct{} // Used for watcher closing notification. + watcher *fsnotify.Watcher // Underlying fsnotify object. + events *gqueue.TQueue[*Event] // Used for internal event management. + cache *gcache.Cache // Used for repeated event filter. + nameSet *gset.StrSet // Used for AddOnce feature. + callbacks *gmap.KVMap[string, *glist.TList[*Callback]] // Path(file/folder) to callbacks mapping. + closeChan chan struct{} // Used for watcher closing notification. } // Callback is the callback function for Watcher. type Callback struct { - Id int // Unique id for callback object. - Func func(event *Event) // Callback function. - Path string // Bound file path (absolute). - name string // Registered name for AddOnce. - elem *glist.Element // Element in the callbacks of watcher. - recursive bool // Is bound to sub-path recursively or not. + Id int // Unique id for callback object. + Func func(event *Event) // Callback function. + Path string // Bound file path (absolute). + name string // Registered name for AddOnce. + elem *glist.TElement[*Callback] // Element in the callbacks of watcher. + recursive bool // Is bound to sub-path recursively or not. } // Event is the event produced by underlying fsnotify. @@ -82,10 +82,12 @@ const ( ) var ( - mu sync.Mutex // Mutex for concurrent safety of defaultWatcher. - defaultWatcher *Watcher // Default watcher. - callbackIdMap = gmap.NewIntAnyMap(true) // Global callback id to callback function mapping. - callbackIdGenerator = gtype.NewInt() // Atomic id generator for callback. + callBacksChecker = func(v *glist.TList[*Callback]) bool { return v == nil } // callBacksChecker checks whether the value is nil. + callbackIdMapChecker = func(v *Callback) bool { return v == nil } // callbackIdMapChecker checks whether the value is nil. + mu sync.Mutex // Mutex for concurrent safety of defaultWatcher. + defaultWatcher *Watcher // Default watcher. + callbackIdMap = gmap.NewKVMapWithChecker[int, *Callback](callbackIdMapChecker, true) // Global callback id to callback function mapping. + callbackIdGenerator = gtype.NewInt() // Atomic id generator for callback. ) // New creates and returns a new watcher. @@ -96,10 +98,10 @@ var ( func New() (*Watcher, error) { w := &Watcher{ cache: gcache.New(), - events: gqueue.New(), + events: gqueue.NewTQueue[*Event](), nameSet: gset.NewStrSet(true), closeChan: make(chan struct{}), - callbacks: gmap.NewStrAnyMap(true), + callbacks: gmap.NewKVMapWithChecker[string, *glist.TList[*Callback]](callBacksChecker, true), } if watcher, err := fsnotify.NewWatcher(); err == nil { w.watcher = watcher @@ -154,11 +156,7 @@ func RemoveCallback(callbackID int) error { if err != nil { return err } - callback := (*Callback)(nil) - if r := callbackIdMap.Get(callbackID); r != nil { - callback = r.(*Callback) - } - if callback == nil { + if callback := callbackIdMap.Get(callbackID); callback == nil { return gerror.NewCodef(gcode.CodeInvalidParameter, `callback for id %d not found`, callbackID) } w.RemoveCallback(callbackID) diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 9524d439dbc..b90fa7804ba 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -105,13 +105,11 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event recursive: !watchOption.NoRecursive, } // Register the callback to watcher. - w.callbacks.LockFunc(func(m map[string]any) { - list := (*glist.List)(nil) - if v, ok := m[path]; !ok { - list = glist.New(true) + w.callbacks.LockFunc(func(m map[string]*glist.TList[*Callback]) { + list, ok := m[path] + if !ok { + list = glist.NewT[*Callback](true) m[path] = list - } else { - list = v.(*glist.List) } callback.elem = list.PushBack(callback) }) @@ -155,10 +153,9 @@ func (w *Watcher) Remove(path string) error { for _, removedPath := range removedPaths { // remove the callbacks of the path. if value := w.callbacks.Remove(removedPath); value != nil { - list := value.(*glist.List) for { - if item := list.PopFront(); item != nil { - callbackIdMap.Remove(item.(*Callback).Id) + if item := value.PopFront(); item != nil { + callbackIdMap.Remove(item.Id) } else { break } @@ -180,13 +177,9 @@ func (w *Watcher) Remove(path string) error { // // Note that, it auto removes the path watching if there's no callback bound on it. func (w *Watcher) RemoveCallback(callbackID int) { - callback := (*Callback)(nil) - if r := callbackIdMap.Get(callbackID); r != nil { - callback = r.(*Callback) - } - if callback != nil { + if callback := callbackIdMap.Get(callbackID); callback != nil { if r := w.callbacks.Get(callback.Path); r != nil { - r.(*glist.List).Remove(callback.elem) + r.Remove(callback.elem) } callbackIdMap.Remove(callbackID) if callback.name != "" { diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index 730931b11d1..e3818a5e76e 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -9,7 +9,6 @@ package gfsnotify import ( "context" - "github.com/gogf/gf/v2/container/glist" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/internal/intlog" @@ -63,69 +62,68 @@ func (w *Watcher) eventLoop() { ) for { if v := w.events.Pop(); v != nil { - event := v.(*Event) // If there's no any callback of this path, it removes it from monitor, // as a path watching without callback is meaningless. - callbacks := w.getCallbacksForPath(event.Path) + callbacks := w.getCallbacksForPath(v.Path) if len(callbacks) == 0 { - _ = w.watcher.Remove(event.Path) + _ = w.watcher.Remove(v.Path) continue } switch { - case event.IsRemove(): + case v.IsRemove(): // It should check again the existence of the path. // It adds it back to the monitor if it still exists. - if fileExists(event.Path) { + if fileExists(v.Path) { // A watch will be automatically removed if the watched path is deleted or // renamed. // // It here adds the path back to monitor. // We need no worry about the repeat adding. - if err = w.watcher.Add(event.Path); err != nil { + if err = w.watcher.Add(v.Path); err != nil { intlog.Errorf(ctx, `%+v`, err) } else { intlog.Printf( ctx, "fake remove event, watcher re-adds monitor for: %s", - event.Path, + v.Path, ) } // Change the event to RENAME, which means it renames itself to its origin name. - event.Op = RENAME + v.Op = RENAME } - case event.IsRename(): + case v.IsRename(): // It should check again the existence of the path. // It adds it back to the monitor if it still exists. // Especially Some editors might do RENAME and then CHMOD when it's editing file. - if fileExists(event.Path) { + if fileExists(v.Path) { // A watch will be automatically removed if the watched path is deleted or // renamed. // // It might lose the monitoring for the path, so we add the path back to monitor. // We need no worry about the repeat adding. - if err = w.watcher.Add(event.Path); err != nil { + if err = w.watcher.Add(v.Path); err != nil { intlog.Errorf(ctx, `%+v`, err) } else { intlog.Printf( ctx, "fake rename event, watcher re-adds monitor for: %s", - event.Path, + v.Path, ) } // Change the event to CHMOD. - event.Op = CHMOD + v.Op = CHMOD } - case event.IsCreate(): + case v.IsCreate(): // ================================================================================= // Note that it here just adds the path to monitor without any callback registering, // because its parent already has the callbacks. // ================================================================================= - if w.checkRecursiveWatchingInCreatingEvent(event.Path) { + if w.checkRecursiveWatchingInCreatingEvent(v.Path) { // It handles only folders, watching folders also watching its sub files. - for _, subPath := range fileAllDirs(event.Path) { + for _, subPath := range fileAllDirs(v.Path) { if fileIsDir(subPath) { if err = w.watcher.Add(subPath); err != nil { intlog.Errorf(ctx, `%+v`, err) @@ -142,7 +140,7 @@ func (w *Watcher) eventLoop() { } // Calling the callbacks in multiple goroutines. for _, callback := range callbacks { - go w.doCallback(event, callback) + go w.doCallback(v, callback) } } else { break @@ -166,9 +164,8 @@ func (w *Watcher) checkRecursiveWatchingInCreatingEvent(path string) bool { break } if callbackItem := w.callbacks.Get(parentDirPath); callbackItem != nil { - for _, node := range callbackItem.(*glist.List).FrontAll() { - callback := node.(*Callback) - if callback.recursive { + for _, node := range callbackItem.FrontAll() { + if node.recursive { return true } } @@ -201,10 +198,7 @@ func (w *Watcher) doCallback(event *Event, callback *Callback) { func (w *Watcher) getCallbacksForPath(path string) (callbacks []*Callback) { // Firstly add the callbacks of itself. if item := w.callbacks.Get(path); item != nil { - for _, node := range item.(*glist.List).FrontAll() { - callback := node.(*Callback) - callbacks = append(callbacks, callback) - } + callbacks = append(callbacks, item.FrontAll()...) } // ============================================================================================================ // Secondly searches its direct parent for callbacks. @@ -214,10 +208,7 @@ func (w *Watcher) getCallbacksForPath(path string) (callbacks []*Callback) { // ============================================================================================================ dirPath := fileDir(path) if item := w.callbacks.Get(dirPath); item != nil { - for _, node := range item.(*glist.List).FrontAll() { - callback := node.(*Callback) - callbacks = append(callbacks, callback) - } + callbacks = append(callbacks, item.FrontAll()...) } // Lastly searches all the parents of directory of `path` recursively for callbacks. @@ -227,10 +218,9 @@ func (w *Watcher) getCallbacksForPath(path string) (callbacks []*Callback) { break } if item := w.callbacks.Get(parentDirPath); item != nil { - for _, node := range item.(*glist.List).FrontAll() { - callback := node.(*Callback) - if callback.recursive { - callbacks = append(callbacks, callback) + for _, node := range item.FrontAll() { + if node.recursive { + callbacks = append(callbacks, node) } } } diff --git a/os/glog/glog_instance.go b/os/glog/glog_instance.go index 4f6dad14e34..104308de110 100644 --- a/os/glog/glog_instance.go +++ b/os/glog/glog_instance.go @@ -14,8 +14,10 @@ const ( ) var ( + // Checker function for instances map. + checker = func(v *Logger) bool { return v == nil } // Instances map. - instances = gmap.NewStrAnyMap(true) + instances = gmap.NewKVMapWithChecker[string, *Logger](checker, true) ) // Instance returns an instance of Logger with default settings. @@ -25,7 +27,5 @@ func Instance(name ...string) *Logger { if len(name) > 0 && name[0] != "" { key = name[0] } - return instances.GetOrSetFuncLock(key, func() any { - return New() - }).(*Logger) + return instances.GetOrSetFuncLock(key, New) } diff --git a/os/gmlock/gmlock_locker.go b/os/gmlock/gmlock_locker.go index 424fcf056a7..0eaaa24109f 100644 --- a/os/gmlock/gmlock_locker.go +++ b/os/gmlock/gmlock_locker.go @@ -12,18 +12,20 @@ import ( "github.com/gogf/gf/v2/container/gmap" ) +var checker = func(v *sync.RWMutex) bool { return v == nil } + // Locker is a memory based locker. // Note that there's no cache expire mechanism for mutex in locker. // You need remove certain mutex manually when you do not want use it anymore. type Locker struct { - m *gmap.StrAnyMap + m *gmap.KVMap[string, *sync.RWMutex] } // New creates and returns a new memory locker. // A memory locker can lock/unlock with dynamic string key. func New() *Locker { return &Locker{ - m: gmap.NewStrAnyMap(true), + m: gmap.NewKVMapWithChecker[string, *sync.RWMutex](checker, true), } } @@ -43,7 +45,7 @@ func (l *Locker) TryLock(key string) bool { // Unlock unlocks the writing lock of the `key`. func (l *Locker) Unlock(key string) { if v := l.m.Get(key); v != nil { - v.(*sync.RWMutex).Unlock() + v.Unlock() } } @@ -63,7 +65,7 @@ func (l *Locker) TryRLock(key string) bool { // RUnlock unlocks the reading lock of the `key`. func (l *Locker) RUnlock(key string) { if v := l.m.Get(key); v != nil { - v.(*sync.RWMutex).RUnlock() + v.RUnlock() } } @@ -128,7 +130,7 @@ func (l *Locker) Clear() { // getOrNewMutex returns the mutex of given `key` if it exists, // or else creates and returns a new one. func (l *Locker) getOrNewMutex(key string) *sync.RWMutex { - return l.m.GetOrSetFuncLock(key, func() any { + return l.m.GetOrSetFuncLock(key, func() *sync.RWMutex { return &sync.RWMutex{} - }).(*sync.RWMutex) + }) } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index b547d31d766..9e2a3b2c1cb 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -12,6 +12,7 @@ import ( "sync" "github.com/gogf/gf/v2/container/gmap" + "github.com/gogf/gf/v2/container/gqueue" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/net/gtcp" @@ -42,9 +43,11 @@ const ( ) var ( + // checker is used for checking whether the value is nil. + checker = func(v *gqueue.TQueue[*MsgRequest]) bool { return v == nil } // commReceiveQueues is the group name to queue map for storing received data. - // The value of the map is type of *gqueue.Queue. - commReceiveQueues = gmap.NewStrAnyMap(true) + // The value of the map is type of *gqueue.TQueue[*MsgRequest]. + commReceiveQueues = gmap.NewKVMapWithChecker[string, *gqueue.TQueue[*MsgRequest]](checker, true) // commPidFolderPath specifies the folder path storing pid to port mapping files. commPidFolderPath string diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index 5271b4d3d3d..5c8b720a3d8 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -39,15 +39,10 @@ func Receive(group ...string) *MsgRequest { } else { groupName = defaultGroupNameForProcComm } - queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() any { - return gqueue.New(maxLengthForProcMsgQueue) - }).(*gqueue.Queue) - - // Blocking receiving. - if v := queue.Pop(); v != nil { - return v.(*MsgRequest) - } - return nil + queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() *gqueue.TQueue[*MsgRequest] { + return gqueue.NewTQueue[*MsgRequest](maxLengthForProcMsgQueue) + }) + return queue.Pop() } // receiveTcpListening scans local for available port and starts listening. @@ -110,7 +105,7 @@ func receiveTcpHandler(conn *gtcp.Conn) { } else { // Push to buffer queue. response.Code = 1 - v.(*gqueue.Queue).Push(msg) + v.Push(msg) } } else { // Empty package. diff --git a/os/gres/gres_instance.go b/os/gres/gres_instance.go index 7b58c497906..71ce4baa010 100644 --- a/os/gres/gres_instance.go +++ b/os/gres/gres_instance.go @@ -14,8 +14,10 @@ const ( ) var ( + // checker checks whether the value is nil. + checker = func(v *Resource) bool { return v == nil } // Instances map. - instances = gmap.NewStrAnyMap(true) + instances = gmap.NewKVMapWithChecker[string, *Resource](checker, true) ) // Instance returns an instance of Resource. @@ -25,7 +27,5 @@ func Instance(name ...string) *Resource { if len(name) > 0 && name[0] != "" { key = name[0] } - return instances.GetOrSetFuncLock(key, func() any { - return New() - }).(*Resource) + return instances.GetOrSetFuncLock(key, New) } diff --git a/os/grpool/grpool.go b/os/grpool/grpool.go index 1abf991950a..521a110fbb0 100644 --- a/os/grpool/grpool.go +++ b/os/grpool/grpool.go @@ -25,10 +25,10 @@ type RecoverFunc func(ctx context.Context, exception error) // Pool manages the goroutines using pool. type Pool struct { - limit int // Max goroutine count limit. - count *gtype.Int // Current running goroutine count. - list *glist.List // List for asynchronous job adding purpose. - closed *gtype.Bool // Is pool closed or not. + limit int // Max goroutine count limit. + count *gtype.Int // Current running goroutine count. + list *glist.TList[*localPoolItem] // List for asynchronous job adding purpose. + closed *gtype.Bool // Is pool closed or not. } // localPoolItem is the job item storing in job list. @@ -55,7 +55,7 @@ func New(limit ...int) *Pool { pool = &Pool{ limit: -1, count: gtype.NewInt(), - list: glist.New(true), + list: glist.NewT[*localPoolItem](true), closed: gtype.NewBool(), } timerDuration = grand.D( diff --git a/os/grpool/grpool_pool.go b/os/grpool/grpool_pool.go index 4296fc2c384..cb5d692cf73 100644 --- a/os/grpool/grpool_pool.go +++ b/os/grpool/grpool_pool.go @@ -104,18 +104,12 @@ func (p *Pool) checkAndForkNewGoroutineWorker() { func (p *Pool) asynchronousWorker() { defer p.count.Add(-1) - - var ( - listItem any - poolItem *localPoolItem - ) // Harding working, one by one, job never empty, worker never die. for !p.closed.Val() { - listItem = p.list.PopBack() + listItem := p.list.PopBack() if listItem == nil { return } - poolItem = listItem.(*localPoolItem) - poolItem.Func(poolItem.Ctx) + listItem.Func(listItem.Ctx) } } diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index 051ef2f2ee1..5e206061489 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -39,8 +39,10 @@ type SPathCacheItem struct { } var ( + // checker is the checking function for checking the value is nil or not. + checker = func(v *SPath) bool { return v == nil } // Path to searching object mapping, used for instance management. - pathsMap = gmap.NewStrAnyMap(true) + pathsMap = gmap.NewKVMapWithChecker[string, *SPath](checker, true) ) // New creates and returns a new path searching manager. @@ -65,9 +67,9 @@ func Get(root string, cache bool) *SPath { if root == "" { root = "/" } - return pathsMap.GetOrSetFuncLock(root, func() any { + return pathsMap.GetOrSetFuncLock(root, func() *SPath { return New(root, cache) - }).(*SPath) + }) } // Search searches file `name` under path `root`. diff --git a/os/gview/gview.go b/os/gview/gview.go index acd395b4828..4ab011b1a32 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -23,11 +23,11 @@ import ( // View object for template engine. type View struct { - searchPaths *garray.StrArray // Searching array for path, NOT concurrent-safe for performance purpose. - data map[string]any // Global template variables. - funcMap map[string]any // Global template function map. - fileCacheMap *gmap.StrAnyMap // File cache map. - config Config // Extra configuration for the view. + searchPaths *garray.StrArray // Searching array for path, NOT concurrent-safe for performance purpose. + data map[string]any // Global template variables. + funcMap map[string]any // Global template function map. + fileCacheMap *gmap.KVMap[string, *fileCacheItem] // File cache map. + config Config // Extra configuration for the view. } type ( @@ -41,7 +41,8 @@ const ( var ( // Default view object. - defaultViewObj *View + defaultViewObj *View + fileCacheItemChecker = func(v *fileCacheItem) bool { return v == nil } ) // checkAndInitDefaultView checks and initializes the default view object. @@ -69,7 +70,7 @@ func New(path ...string) *View { searchPaths: garray.NewStrArray(), data: make(map[string]any), funcMap: make(map[string]any), - fileCacheMap: gmap.NewStrAnyMap(true), + fileCacheMap: gmap.NewKVMapWithChecker[string, *fileCacheItem](fileCacheItemChecker, true), config: DefaultConfig(), } if len(path) > 0 && len(path[0]) > 0 { diff --git a/os/gview/gview_instance.go b/os/gview/gview_instance.go index cb75d5d423c..c36b0582deb 100644 --- a/os/gview/gview_instance.go +++ b/os/gview/gview_instance.go @@ -14,8 +14,9 @@ const ( ) var ( + checker = func(v *View) bool { return v == nil } // Instances map. - instances = gmap.NewStrAnyMap(true) + instances = gmap.NewKVMapWithChecker[string, *View](checker, true) ) // Instance returns an instance of View with default settings. @@ -25,7 +26,7 @@ func Instance(name ...string) *View { if len(name) > 0 && name[0] != "" { key = name[0] } - return instances.GetOrSetFuncLock(key, func() any { + return instances.GetOrSetFuncLock(key, func() *View { return New() - }).(*View) + }) } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index 93d1513768a..1f49bc33ab1 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -130,7 +130,7 @@ func (view *View) ParseWithOptions(ctx context.Context, opts Options) (result st return "", gerror.New(`template file cannot be empty`) } // It caches the file, folder, and content to enhance performance. - r := view.fileCacheMap.GetOrSetFuncLock(opts.File, func() any { + r := view.fileCacheMap.GetOrSetFuncLock(opts.File, func() *fileCacheItem { var ( path string folder string @@ -169,30 +169,29 @@ func (view *View) ParseWithOptions(ctx context.Context, opts Options) (result st if r == nil { return } - item := r.(*fileCacheItem) // It's not necessary continuing parsing if template content is empty. - if item.content == "" { + if r.content == "" { return "", nil } // If it's an Orphan option, it just parses the single file by ParseContent. if opts.Orphan { - return view.doParseContent(ctx, item.content, opts.Params) + return view.doParseContent(ctx, r.content, opts.Params) } // Get the template object instance for `folder`. var tpl any - tpl, err = view.getTemplate(item.path, item.folder, fmt.Sprintf(`*%s`, gfile.Ext(item.path))) + tpl, err = view.getTemplate(r.path, r.folder, fmt.Sprintf(`*%s`, gfile.Ext(r.path))) if err != nil { return "", err } // Using memory lock to ensure concurrent safety for template parsing. - gmlock.LockFunc("gview.Parse:"+item.path, func() { + gmlock.LockFunc("gview.Parse:"+r.path, func() { if view.config.AutoEncode { - tpl, err = tpl.(*htmltpl.Template).Parse(item.content) + tpl, err = tpl.(*htmltpl.Template).Parse(r.content) } else { - tpl, err = tpl.(*texttpl.Template).Parse(item.content) + tpl, err = tpl.(*texttpl.Template).Parse(r.content) } - if err != nil && item.path != "" { - err = gerror.Wrap(err, item.path) + if err != nil && r.path != "" { + err = gerror.Wrap(err, r.path) } }) if err != nil {