-
Notifications
You must be signed in to change notification settings - Fork 14
Description
Managers have a way to add nodes, but not a way to remove them.
It is possible to attach callbacks to objects which gets called before they become freed by the go garbage collector, using runtime.AddCleanup (go 1.24) or runtime.SetFinalizer.
By using weak pointers it is possible for the manager to keep track of all nodes while also allowing the nodes to be reclaimed by the garbage collector. A cleanup function can be added to each node which closes the connection to the node and removes the node from the manager.
Configurations should not use weak pointers since you don't want any of the nodes in the configuration to close until the configuration itself is reclaimed.
Weak pointer problem
The nodes are currently accessible through the manager using node ids, making it possible to access a node without the node pointer as long as you have its node id. The garbage collector would clean up the node in this case and the node would be closed where it would otherwise be accessible.
This can be avoided if the manager does not expose its nodes, but that means that you have to store the nodes you need not to be closed somewhere outside the manager, and it also limit what you can do with node ids.
Example
func NewRawNode(addr string) (*RawNode, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
h := fnv.New32a()
_, _ = h.Write([]byte(tcpAddr.String()))
node := &RawNode{
id: h.Sum32(),
addr: tcpAddr.String(),
}
_ = runtime.AddCleanup(node, func(n *RawNode) {
m := n.mgr
if m == nil {
n.close()
return
}
m.mu.Lock()
defer m.mu.Unlock()
for i, n2 := range m.nodes {
if n2 == n {
m.nodes = append(m.nodes[:i], m.nodes[i+1:]...)
n.close()
return
}
}
panic("node does not exist in manager")
}, node)
return node, nil
}We don't need lookup, but need nodes for Close()
type RawManager struct {
mu sync.Mutex
nodes []weak.Pointer[RawNode]
closeOnce sync.Once
logger *log.Logger
opts managerOptions
nextMsgID uint64
}func (m *RawManager) AddNode(node *RawNode) error {
...
m.mu.Lock()
defer m.mu.Unlock()
m.nodes = append(m.nodes, weak.Make(node))
return nil
}