Skip to content

Commit 106f6c5

Browse files
committed
[Personal-WP] Fix broadcast message loop in tab coordinator
Prevent infinite loop where checkForExistingTabs was being called repeatedly, causing continuous ping messages between BroadcastChannel instances in the same tab. Changes: - Add mutex lock and rate limiting (1s min interval) to checkForExistingTabs - Fix unstable useCallback dependency in useTabTracking hook
1 parent 6c6fac3 commit 106f6c5

File tree

2 files changed

+22
-5
lines changed

2 files changed

+22
-5
lines changed

packages/playground/personal-wp/src/lib/hooks/use-tab-tracking.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,19 @@ export function useTabTracking(
4848
}
4949
}, [hasOwnWorker]);
5050

51+
const onWorkerLostRef = useRef(options.onWorkerLost);
52+
onWorkerLostRef.current = options.onWorkerLost;
53+
5154
const checkWorkerLost = useCallback(() => {
5255
if (
5356
!hasOwnWorkerRef.current &&
5457
knownTabsRef.current.size === 0 &&
5558
!recentlyBecameDependentRef.current
5659
) {
5760
setWorkerLost(true);
58-
options.onWorkerLost?.();
61+
onWorkerLostRef.current?.();
5962
}
60-
}, [options]);
63+
}, []);
6164

6265
useEffect(() => {
6366
if (!activeSite || !tabInfo) {

packages/playground/personal-wp/src/lib/state/redux/tab-coordinator.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type TabCoordinatorMessage =
7979
const CHANNEL_NAME = 'playground-tab-coordinator';
8080
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
8181
const PING_TIMEOUT_MS = 150;
82+
const MIN_CHECK_INTERVAL_MS = 1000;
8283

8384
let channel: BroadcastChannel | null = null;
8485
let currentTabInfo: TabInfo | null = null;
@@ -87,6 +88,8 @@ let takeoverCallback: (() => void) | null = null;
8788
let backupRequestCallback: (() => Promise<boolean>) | null = null;
8889
let siteResetCallback: (() => void) | null = null;
8990
let beforeUnloadHandler: (() => void) | null = null;
91+
let isCheckingTabs = false;
92+
let lastCheckTime = 0;
9093

9194
/**
9295
* Initialize the tab coordinator for a specific site.
@@ -168,6 +171,8 @@ export function destroyTabCoordinator(): void {
168171
takeoverCallback = null;
169172
backupRequestCallback = null;
170173
siteResetCallback = null;
174+
isCheckingTabs = false;
175+
lastCheckTime = 0;
171176
}
172177

173178
/**
@@ -185,8 +190,15 @@ export async function checkForExistingTabs(siteSlug: string): Promise<{
185190
return { existingTabs: [], hasFreshTab: false, hasStaleTab: false };
186191
}
187192

188-
const existingTabs: TabInfo[] = [];
193+
// Safeguard: prevent concurrent checks and rate limit
189194
const now = Date.now();
195+
if (isCheckingTabs || now - lastCheckTime < MIN_CHECK_INTERVAL_MS) {
196+
return { existingTabs: [], hasFreshTab: false, hasStaleTab: false };
197+
}
198+
isCheckingTabs = true;
199+
lastCheckTime = now;
200+
201+
const existingTabs: TabInfo[] = [];
190202

191203
const pongHandler = (event: MessageEvent<TabCoordinatorMessage>) => {
192204
const message = event.data;
@@ -212,13 +224,15 @@ export async function checkForExistingTabs(siteSlug: string): Promise<{
212224

213225
channel.removeEventListener('message', pongHandler);
214226

227+
const checkTime = Date.now();
215228
const hasFreshTab = existingTabs.some(
216-
(tab) => now - tab.createdAt < ONE_DAY_MS && !tab.isDependentMode
229+
(tab) => checkTime - tab.createdAt < ONE_DAY_MS && !tab.isDependentMode
217230
);
218231
const hasStaleTab = existingTabs.some(
219-
(tab) => now - tab.createdAt >= ONE_DAY_MS && !tab.isDependentMode
232+
(tab) => checkTime - tab.createdAt >= ONE_DAY_MS && !tab.isDependentMode
220233
);
221234

235+
isCheckingTabs = false;
222236
return { existingTabs, hasFreshTab, hasStaleTab };
223237
}
224238

0 commit comments

Comments
 (0)