Detect if GenServer is terminating

I have already checked that Process.alive? returns true when terminate callback is running, so it will not help.

I also checked the Process.info. The :current_function of that info has {:gen_server, _, _} when process is running normal and {:proc_lib, _, _} when terminate callback is running. Can I use this info in this case? Or there is a better solution?

What are you trying to do? Can you modify the GenServer?

I have a GenServer that starts when user connects to websocket and stops when user disconnects. Name of GenServer is same as user id (I used Registry module for that)

Problem is, the terminate function of that GenServer must do several things that lasts few seconds. I can’t allow to do them in background.

When user connects to my websocket, I’m checking if process named after user id exists. If so, then I’m using that process, otherwise I’m creating a new process. So user can connect multiple times and still uses the same GenServer process.
When user disconnects from every websocket that he connected (multiple browser tabs), GenServer is stopped. When he has only one connection (one browser tab) and quickly disconnects and connects again (refreshes page) - terminate will be not done yet. So I have to wait until terminate ends.

First I must to know that termination happens

Yes, I can modify this GenServer

I think your reuse mechanism is the source of your problem. The issue is that even before terminate starts executing, messages could be piling up in the process mailbox - messages that are going to be lost because senders don’t know that the target process “is on it’s way out” the next time it comes out of the run queue. So you may need to rework the life-cycle of these “user” processes and how they are managed.

1 Like

There will be a race condition if you try to inspect the process state, it could enter terminate after you check the state but before you associate the websocket to it. Assuming you’re doing a GenServer call to associate the websocket to the server, you could wrap it in try/catch to catch a normal exit and try to start the server process again if it happens. That should handle the case where the call happens during terminate.

2 Likes

call Process.unregister/1 in handle_info(:timeout, state) or terminate/2 ?

but the new process could be created before the old process completed terminate.