Skip to content

Conversation

@pks-t
Copy link
Contributor

@pks-t pks-t commented Nov 22, 2016

CommitOperations allow for committing files from several
different SVN Team providers at the same time. This is for
example useful to commit from multiple working copies at the same
time. When doing this, we have to determine for each resource to
be committed, to which SVN Team provider it belongs and add it to
this provider's transaction. For each of these providers, we also
have to get a different SVN client, as one client cannot handle
multiple repositories.

When we try to delete files from different providers, we get the
set of providers for the files to be committed. Afterwards, we
iterate through each provider, get an SVN client and then commit
the files that belong to this specific provider. After each
iteration, we dispose the SVN client again.

But in fact, we do not reset the SVN client to null, so we will
never retrieve the client for following providers. As we call
dispose after each iteration, this will lead to a double free
error in C++ code. The issue can be reproduced by commiting two
missing (not deleted) files from two different repositories at
once.

Fix the issue by resetting the SVN client to null after each
iteration.

@markphip
Copy link
Contributor

This patch ought to work fine, but looking at the code my guess would be that we intended to allocate the ISVNClient once and use it throughout and only dispose it at the end. I think the dispose stuff was added later to all the users of ISVNClient and was probably done carelessly here. I cannot think of any reason to keep creating new clients in this function. Do you want to rework it so that the client is constructed once, used as needed and then disposed at the end of the process?

I would not be shocked if there are a couple of other similar problems somewhere in the code. There are not too many that work the way commit needed to though.

@pks-t
Copy link
Contributor Author

pks-t commented Nov 23, 2016

I wasn't quite sure about the requirements here, so I went the safe way. I thought that we might be required to use different ISVNClients per SVNTeamProvider, as we request the clients via the resource's repository, which will usually be different for each team provider. Do you know the semantics here?

If it is actually safe to use an ISVNClient for different team providers, I will adjust accordingly.

@markphip
Copy link
Contributor

Getting the client from the Repository is just legacy code that pre-dates the 1.0 release. That code should probably be removed and callers updated to use SVNClientManager directly. There is nothing special about ISVNClient that makes it specific to any repository. So yes it is safe to use it for multiple projects.

I think that pre-1.0 the Repository contained the credentials, but we stopped supporting this a long, long time ago and rely on SVN's credential storage exclusively. Anyway, this is probably why the early design had this method to get a client.

@pks-t
Copy link
Contributor Author

pks-t commented Nov 25, 2016

Okay. This makes the fix even nicer, as there's already an unused svn client retrieved outside the loop

@markphip
Copy link
Contributor

This looks good in general. I need to go back and take a closer look at the original change where this was added. Looking at the comments now, it looks like we might have introduced a new SVNClient here for console logging purposes. I cannot immediately think of why without spending a bit more time to refresh my memory on how these classes work, but maybe when you get it from the Repository class it sets up the client with the notification handler and the way the existing svn client in this class is setup it does not. This all seems fixable regardless, I just want to make sure I can recall why this code was written in the first place.

@selsemore feel free to jump in on this too.

@pks-t
Copy link
Contributor Author

pks-t commented Nov 25, 2016

You're right, the repository location does actually set up a notification listener. So I'll have to re-add the notification listener to the existing client. Will do in the coming week.

    public ISVNClientAdapter getSVNClient() throws SVNException {
    	ISVNClientAdapter svnClient =
    		SVNProviderPlugin.getPlugin().getSVNClient();
    
    	svnClient.addNotifyListener(NotificationListener.getInstance());
    
    	return svnClient;
    }

@markphip
Copy link
Contributor

Thinking about this more, I wonder if your original patch is therefore not the simplest and best solution here? I do not think the overhead of constructing a new ISVNClient is too great that the optimization to reuse the existing one is worth complicating the code too much.

When committing a missing file from a repository, we lazily
initialize and then dispose a new logging SVNClient. When
committing missing files from multiple repositories at once,
though, we initialize the logging client once and then dispose it
for each repository provider, causing a double-free.

Fix the issue by disposing the client once only after all
resources have been removed.
@pks-t
Copy link
Contributor Author

pks-t commented Nov 30, 2016

We can also have the best of both worlds: lazily initialize the client as before but then only dispose it after the last iteration. So simply move the try-catch-finally up one level so that it contains the loop instead of the other way round. The diff looks somewhat intriguing, but it's really mostly whitespace changes.

@markphip markphip merged commit 9dccf99 into subclipse:master Nov 30, 2016
@markphip
Copy link
Contributor

Thanks

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

Successfully merging this pull request may close these issues.

2 participants