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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions