Skip to content

High: Memory Leak in Polling Implementation #146

@happybigmtn

Description

@happybigmtn

Summary

The polling implementation in useBoard.ts uses recursive setTimeout without proper cleanup, causing memory leaks and zombie intervals.

Affected Files

  • src/hooks/useBoard.ts (lines 286-325)
  • src/hooks/useCraps.ts (similar pattern)

Problem

// Current problematic pattern
const poll = async () => {
  // ... fetch logic
  timeoutRef.current = setTimeout(poll, POLL_INTERVAL);
};

useEffect(() => {
  poll();
  return () => clearTimeout(timeoutRef.current); // May not clear properly
}, []);

Issues:

  1. Recursive setTimeout can create zombie intervals on hot reload
  2. Cleanup in useEffect may not properly clear pending async operations
  3. No AbortController for in-flight requests

Impact

  • Memory grows over time
  • Multiple polling loops after hot reloads
  • Degraded performance after extended use

Proposed Fix

useEffect(() => {
  const controller = new AbortController();
  let timeoutId: NodeJS.Timeout;
  let isMounted = true;

  const poll = async () => {
    if (!isMounted) return;
    try {
      await fetchData({ signal: controller.signal });
    } finally {
      if (isMounted) {
        timeoutId = setTimeout(poll, POLL_INTERVAL);
      }
    }
  };

  poll();

  return () => {
    isMounted = false;
    controller.abort();
    clearTimeout(timeoutId);
  };
}, []);

Labels

bug, high-priority

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions