From b997fff7eb656d0af804284afc6405f3d6abae4f Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sat, 9 May 2026 17:01:47 +1200 Subject: [PATCH] Chore: Refactor Hub to use atomic clientCount for safe concurrent client tracking --- server/websockets/hub.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/websockets/hub.go b/server/websockets/hub.go index 2db98e2..4adec71 100644 --- a/server/websockets/hub.go +++ b/server/websockets/hub.go @@ -3,6 +3,7 @@ package websockets import ( "encoding/json" + "sync/atomic" "time" "github.com/axllent/mailpit/internal/logger" @@ -22,6 +23,9 @@ type Hub struct { // Unregister requests from clients. unregister chan *Client + + // clientCount is an atomic count of connected clients, safe for concurrent reads. + clientCount atomic.Int64 } // WebsocketNotification struct for responses @@ -48,12 +52,14 @@ func (h *Hub) Run() { if _, ok := h.Clients[client]; !ok { logger.Log().Debugf("[websocket] client %s connected", client.conn.RemoteAddr().String()) h.Clients[client] = true + h.clientCount.Add(1) } case client := <-h.unregister: if _, ok := h.Clients[client]; ok { logger.Log().Debugf("[websocket] client %s disconnected", client.conn.RemoteAddr().String()) delete(h.Clients, client) close(client.send) + h.clientCount.Add(-1) } case message := <-h.Broadcast: for client := range h.Clients { @@ -62,6 +68,7 @@ func (h *Hub) Run() { default: close(client.send) delete(h.Clients, client) + h.clientCount.Add(-1) } } } @@ -70,7 +77,7 @@ func (h *Hub) Run() { // Broadcast will spawn a broadcast message to all connected clients func Broadcast(t string, msg any) { - if MessageHub == nil || len(MessageHub.Clients) == 0 { + if MessageHub == nil || MessageHub.clientCount.Load() == 0 { return }