diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-18 13:13:25 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-18 13:13:25 +0300 |
| commit | 550f064f95113e072677b871b7de30ecf25d62b8 (patch) | |
| tree | 03e39a1bed71b4fc16ea62e7956a6268982f15a3 /internal/eventloop_state.go | |
| parent | 808c2549bfb17d22388a197d361a07ce0fc8f54f (diff) | |
fix task 45: bound pending handle cleanup
Diffstat (limited to 'internal/eventloop_state.go')
| -rw-r--r-- | internal/eventloop_state.go | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/internal/eventloop_state.go b/internal/eventloop_state.go index 9622fd1..5cb0a78 100644 --- a/internal/eventloop_state.go +++ b/internal/eventloop_state.go @@ -18,6 +18,15 @@ type fdTracker struct { age uint64 // monotonic counter for LRU ordering } +// pendingHandleTracker holds unresolved name_to_handle_at pathnames keyed by +// TID until the corresponding open_by_handle_at exit consumes them. +type pendingHandleTracker struct { + paths map[uint32]string + pathAges map[uint32]uint64 + maxCacheSize int + age uint64 +} + func newFDTracker(files map[int32]file.File) *fdTracker { if files == nil { files = make(map[int32]file.File) @@ -29,6 +38,13 @@ func newFDTracker(files map[int32]file.File) *fdTracker { } } +func newPendingHandleTracker() *pendingHandleTracker { + return &pendingHandleTracker{ + paths: make(map[uint32]string), + pathAges: make(map[uint32]uint64), + } +} + func (t *fdTracker) get(fd int32) (file.File, bool) { f, ok := t.files[fd] return f, ok @@ -135,6 +151,50 @@ func (t *fdTracker) deleteCacheKey(key uint64) { delete(t.procFdAges, key) } +func (t *pendingHandleTracker) set(tid uint32, pathname string) { + if t.paths == nil { + t.paths = make(map[uint32]string) + t.pathAges = make(map[uint32]uint64) + } + t.age++ + t.paths[tid] = pathname + t.pathAges[tid] = t.age + t.prune() +} + +func (t *pendingHandleTracker) consume(tid uint32) (string, bool) { + pathname, ok := t.paths[tid] + if !ok { + return "", false + } + delete(t.paths, tid) + delete(t.pathAges, tid) + return pathname, true +} + +func (t *pendingHandleTracker) delete(tid uint32) { + delete(t.paths, tid) + delete(t.pathAges, tid) +} + +func (t *pendingHandleTracker) prune() { + if t.paths == nil { + return + } + limit := t.limit() + if len(t.paths) <= limit { + return + } + trimOldestPendingHandles(t.paths, t.pathAges, trimTarget(limit)) +} + +func (t *pendingHandleTracker) limit() int { + if t.maxCacheSize > 0 { + return t.maxCacheSize + } + return defaultMaxPendingHandleEntries +} + // pairTracker holds the state for matching sys_enter events to their sys_exit // counterparts and computing inter-syscall durations per TID. type pairTracker struct { @@ -257,6 +317,26 @@ func trimOldestProcFdEntries(state map[uint64]*file.FdFile, ages map[uint64]uint } } +func trimOldestPendingHandles(state map[uint32]string, ages map[uint32]uint64, targetSize int) { + excess := len(state) - targetSize + if excess <= 0 { + return + } + type pendingHandleAge struct { + tid uint32 + age uint64 + } + oldest := make([]pendingHandleAge, 0, len(state)) + for tid := range state { + oldest = append(oldest, pendingHandleAge{tid: tid, age: ages[tid]}) + } + slices.SortFunc(oldest, func(a, b pendingHandleAge) int { return cmp.Compare(a.age, b.age) }) + for _, entry := range oldest[:excess] { + delete(state, entry.tid) + delete(ages, entry.tid) + } +} + func trimTarget(limit int) int { target := limit - limit/cacheTrimDivisor if target < 1 { |
