Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalizer not invoked automatically with sharedworkers #673

Open
lauri865 opened this issue Nov 10, 2024 · 3 comments
Open

Finalizer not invoked automatically with sharedworkers #673

lauri865 opened this issue Nov 10, 2024 · 3 comments

Comments

@lauri865
Copy link

It doesn't seem like finalizer gets invoked for sharedworkers. I have the following worker, and the finalizer never gets invoked automatically.

declare const self: SharedWorkerGlobalScope;
import * as Comlink from "comlink";

export const api = {
  getToken: (token: string) => {
    return token;
  },
};

let id = 0;
self.addEventListener("connect", (event: MessageEvent): void => {
  id++;
  const tabId = id;
  console.log("tab connected", tabId);
  const port = event.ports[0];

  Comlink.expose(
    {
      ...api,
      [Comlink.finalizer]: () => {
        console.log("tab closed", tabId);
      },
    },
    port,
  );
});

It does work when I invoke it manually on beforeunload:

addEventListener("beforeunload", () => {
  instance[Comlink.releaseProxy]();
});

I also tried to wrap the port in weakRef myself before passing it to Comlink.expose, but no luck either. Was following this post: https://brightinventions.pl/blog/sharing-websocket-connections-between-browser-tabs-and-windows/

Environment:
MacOS 15
Chrome Version 130.0.6723.117 (Official Build) (arm64)

@lauri865
Copy link
Author

lauri865 commented Nov 11, 2024

I did manage to hack together a version with navigator.locks that seems to work.

Following this: whatwg/html#1766 (comment)

  • Acquire an infinite lock after Comlink.wrap with a random id
  • invoke "ready" function when the lock is acquired, which then tries to acquire the same lock
  • Lock gets released when the tab closes / crashes, we can then run a cleanup function for anything related to that tab

Bonus is that it should also be more performant than weakRef for ongoing tasks.

@surma
Copy link
Collaborator

surma commented Nov 11, 2024

Oh interesting. That might be a corner-case (i.e. working as intended) of the FinalizationRegistry, where the finalizer does not get called when you close a tab. If you were to just drop the proxy, it should work. In general, FinalizationRegistry makes no guarantee about calling the finalizers.

So if you want to be sure that the finalizer gets called, your approach with beforeunload seems like a good idea.

@lauri865
Copy link
Author

lauri865 commented Nov 11, 2024

WeakRefs should work (in theory) when closing the tab according to some blog posts online, but I didn't get around to test yet outside the Comlink setup.

Unfortunately beforeunload is not reliable enough from our testing. Locks works great though.

For us, running a cleanup after tabs is crucial for memory management. The shared worker can be running for months on end with variable number of tabs.

Anyways, we're happy now, but there could be some merit in incorporating a solution into the library. So many people are asking the question, yet there's no straightforward solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants