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

Client library deletes pointers without any notification to the calling application. #627

Open
gnif opened this issue Aug 18, 2020 · 8 comments

Comments

@gnif
Copy link

gnif commented Aug 18, 2020

// Cleanup remaining clients
jack_error("Jack server was closed but clients are still allocated, cleanup...");
for (int i = 0; i < CLIENT_NUM; i++) {
JackClient* client = JackGlobals::fClientTable[i];
if (client) {
jack_error("Cleanup client ref = %d", i);
client->Close();
delete client;
}
}

As the shutdown handler is to be treated like a signal, the cleanup of other connected clients must be deferred, however, if a recovery routine is attempted for a disconnected client, the above code destroys all the client pointers pulling the rug out from under the calling application. There is no way to determine if this "cleanup" has happened and that the old handles are now invalid.

Instead of deleting the objects, this should instead set a flag/state marking the objects as no longer valid, allowing the calling application to properly clean up.

@falkTX
Copy link
Member

falkTX commented Aug 18, 2020

Do you have some code where this is triggered?

@gnif
Copy link
Author

gnif commented Aug 18, 2020

https://github.com/qemu/qemu/blob/master/audio/jackaudio.c

Each client is created per virtual audio device, some have inputs, some have outputs, some have both. The life cycle of each virtual device is completely isolated and independent of the next. Restarting the application due to a Jack server failure is not acceptable as it means restarting the entire guest VM, as such reconnection/recovery of each client/connection is required on a client by client basis.

When the first client recovers and reconnects, it invalidates the client references that are still in use by the other devices which are expecting to clean up and also recover.

In short, not only is the above "cleanup" not documented anywhere, it goes against the standard design pattern of a client library and makes it impossible for the calling application to rely on the handles provided. At the very least the jack library should not delete the objects, keeping them valid, even if they have been cleaned up.

Edit: This code was introduced in 171a3c4, it seems it was added to fix bugs in applications written by third parties. Obviously I will need to work around this by not trying to close the old connections at all, which goes against all common programming conventions.

If this is fixed, please be sure to include a way to determine if this code is removed so that a client that properly implements the API can properly clean up (ie, jack version define)

@gnif
Copy link
Author

gnif commented Aug 18, 2020

Perhaps another option would be on connection to set a flag to specify the client is well behaved which disables this cleanup.

Please also note that this issue makes jack2 incompatible with jack1 as it appears that jack1 does not perform this "cleanup".

@falkTX
Copy link
Member

falkTX commented Oct 12, 2020

I am unsure what to do about this.
I think I understand why this is there. in the case of the server being restarted, jack2 wants to cleanup itself on the client side so it can reconnect cleanly.

@sletz under what circumstances was this needed?

@gnif I would like to have a proper solution that does not rely on adding something just to fix server-side mishaps. A special flag (I guess during registration?) feels exactly like that, though I cant really think of anything better.
Also, I am not sure if simply skipping the deleting of the client pointer is enough here, as the client object might reference stuff that is deleted on the next few lines.

@sletz
Copy link
Member

sletz commented Oct 12, 2020

This commit was done 12 years ago and the commit log is somewhat clear:
Client and library global context cleanup in case of incorrect shutdown handling (that is applications not correctly closing client after server has shutdown) .
I don't remember the exact use case but it was quite real. I guess this possible issue is a quite usual server/client model possible behaviour: @gnif what is the correct way to do it then?

@gnif
Copy link
Author

gnif commented Oct 12, 2020

Don't do any cleanup at all, just flag the object as "dead" and allow the application to clean up directly as per the normal alloc/free pattern. IMO the above-referenced code simply should be removed. It's not your library's job to work around a badly designed client.

@sletz
Copy link
Member

sletz commented Oct 13, 2020

Then I would vote for keeping client->Close(); since 1) it probably deallocate some stuff in shared memory and has to be done for the server to properly start again later on, and remove delete client; to avoid creating this dangling pointer (but at the price of a memory leak).
@falkTX I think you should check that 1) is actually true by writing a test case that simulates this in case of incorrect shutdown handling (that is applications not correctly closing client after server has shutdown) comment.

@falkTX
Copy link
Member

falkTX commented Oct 15, 2020

I have not had time to check on this, and today was the day set for the new release.
Going to pursue this afterwards.
My idea is to check if the client pointer destructor does anything, and trigger that during this part of cleanup too. also invalidate all pointers, leaving the client pointer valid but not able to do anything.

PR would be appreciated, otherwise I will try to fix this myself in some days/weeks.

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

3 participants