mscluster: reuse control buffers and cut per-property allocations (#2673)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-11 13:52:55 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a8adb6d7bc
commit 930609b0e5
11 changed files with 541 additions and 74 deletions
+20 -9
View File
@@ -210,7 +210,7 @@ func (c *Cluster) readObject(api *objectAPI, name objectName, deadline time.Time
}
}()
if err := readCommonProperties(api.control, handle, api.object, object.Values, deadline); err != nil {
if err := readCommonProperties(api.control, handle, api.object, object.Values, &c.scratch, deadline); err != nil {
resultErr = errors.Join(resultErr, err)
if errors.Is(err, context.DeadlineExceeded) {
@@ -222,7 +222,7 @@ func (c *Cluster) readObject(api *objectAPI, name objectName, deadline time.Time
name string
operation uint32
}{{"Characteristics", ctlGetCharacteristics}, {"Flags", ctlGetFlags}} {
data, err := objectBuffer(api.control, handle, api.object<<24|field.operation, 4, deadline)
data, err := c.scratch.objectControl(api.control, handle, api.object<<24|field.operation, 4, deadline)
switch {
case err != nil:
@@ -252,13 +252,13 @@ func (c *Cluster) readObject(api *objectAPI, name objectName, deadline time.Time
// readCommonProperties stores the single 32-bit values of the read-only and
// read/write common property lists in values. Other formats are skipped.
func readCommonProperties(control *windows.LazyProc, handle uintptr, object uint32, values map[string]uint32, deadline time.Time) error {
func readCommonProperties(control *windows.LazyProc, handle uintptr, object uint32, values map[string]uint32, scratch *controlScratch, deadline time.Time) error {
var resultErr error
for _, operation := range []uint32{ctlGetROCommonProperties, ctlGetCommonProperties} {
code := object<<24 | operation
data, err := objectBuffer(control, handle, code, propertyListBufferSize, deadline)
data, err := scratch.objectControl(control, handle, code, propertyListBufferSize, deadline)
if err != nil {
resultErr = errors.Join(resultErr, fmt.Errorf("%s %#x: %w", control.Name, code, err))
@@ -281,7 +281,7 @@ func readCommonProperties(control *windows.LazyProc, handle uintptr, object uint
continue
}
if value, err := list[0].Value32(); err == nil {
if value, ok := list[0].value32(); ok {
values[name] = value
}
}
@@ -290,10 +290,21 @@ func readCommonProperties(control *windows.LazyProc, handle uintptr, object uint
return resultErr
}
// objectBuffer calls a Cluster{Group,Node,Network}Control function. They share
// the ClusterResourceControl signature and return a DWORD status code.
func objectBuffer(proc *windows.LazyProc, handle uintptr, code uint32, size int, deadline time.Time) ([]byte, error) {
return controlBuffer(deadline, size, func(buffer []byte) (uint32, error) {
// objectControl calls a Cluster{Group,Node,Network}Control function starting
// with n bytes of the scratch buffer. The result is only valid until the next
// control.
func (s *controlScratch) objectControl(proc *windows.LazyProc, handle uintptr, code uint32, n int, deadline time.Time) ([]byte, error) {
data, err := objectBufferIn(proc, handle, code, s.get(n), deadline)
s.keep(data)
return data, err
}
// objectBufferIn calls a Cluster{Group,Node,Network}Control function. They
// share the ClusterResourceControl signature and return a DWORD status code.
// The result may share buffer's memory.
func objectBufferIn(proc *windows.LazyProc, handle uintptr, code uint32, buffer []byte, deadline time.Time) ([]byte, error) {
return controlBufferIn(deadline, buffer, func(buffer []byte) (uint32, error) {
var (
size uint32
pointer *byte