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

Make incremental builder counter atomic #724

Merged
merged 2 commits into from
Mar 17, 2017
Merged

Make incremental builder counter atomic #724

merged 2 commits into from
Mar 17, 2017

Conversation

Krzysztof-Cieslak
Copy link
Contributor

Fix #716

referenceCount <- referenceCount - 1
if referenceCount = 0 then
System.Threading.Interlocked.Decrement(referenceCount) |> ignore
if !referenceCount = 0 then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may race if someone else modifies it between these calls. I think the return value of Interlocked.Decrement should be used instead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is correct

@@ -1901,5 +1901,5 @@ type IncrementalBuilder(ctokCtor: CompilationThreadToken, frameworkTcImportsCach
| Some builder -> builder.IncrementUsageCount()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if it is decremented to 0 between getting it, and incrementing the count?
Then it may have been disposed already, but this would set the counter to 1 again.

I think you would need a central routine, which increments the counter first, checks if it was > 0 before, then returns Some (or None if it was at zero).

@@ -1367,7 +1367,7 @@ type IncrementalBuilder(ctokCtor: CompilationThreadToken, frameworkTcImportsCach
let assertNotDisposed() =
if disposed then
System.Diagnostics.Debug.Assert(false, "IncrementalBuild object has already been disposed!")
let mutable referenceCount = 0
let referenceCount = ref 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the count should start at 1. Decrementing it to 0 triggers a dispose, and incrementing it from 0 to 1 is illegal, because then the object has already been disposed in another thread..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think no one has access before the first increment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Nit: This doesn't need to be a ref - you can take the address of the mutable field)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but if you start at 1, then you can later make it illegal to increment from 0 to 1. (Because at 0, the object has already been disposed). This is just a preference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this PR we're just fixing the race condition, so let's stick with 0 :)

@Krzysztof-Cieslak
Copy link
Contributor Author

Pushed update:

  • Reversed backed to mutable value and pass address to Interlocked messages
  • Use returned value in DecrementUsageCount

@0x53A
Copy link
Contributor

0x53A commented Mar 16, 2017

I think it can still race at places like this: https://github.com/fsharp/FSharp.Compiler.Service/blob/b95f6fa386405ffba0cae9e3d6d60302dcaf2a2c/src/fsharp/vs/service.fs#L2569

It is first taken from the cache, then the count is incremented. But someone else could have disposed it between these two lines.

@dsyme
Copy link
Contributor

dsyme commented Mar 16, 2017

@0x53A I see. Perhaps getOrCreateBuilder and the creation process should return the decrementer

@Krzysztof-Cieslak
Copy link
Contributor Author

I think I'd like to get this merged as soon as possible as it's critical fix for Ionide, and, as far as I can tell, it's "good enough".

We can investigate more fixes later, if it's still problem.

@dsyme
Copy link
Contributor

dsyme commented Mar 16, 2017

@Krzysztof-Cieslak yes I will merge once green and fix up when releasing

@dsyme
Copy link
Contributor

dsyme commented Mar 16, 2017

@0x53A thanks for the careful review

@Krzysztof-Cieslak
Copy link
Contributor Author

Travis partial failures ... seems unrelated ;)

@rmunn
Copy link

rmunn commented Mar 17, 2017

@Krzysztof-Cieslak If there are unrelated failures, you can "kick" the build agents by closing and reopening a PR. This will cause all the build agents to rebuild, and you can get rid of a spurious build failure that way.

@dsyme dsyme merged commit e61fc4f into fsharp:master Mar 17, 2017
dsyme added a commit to dsyme/FSharp.Compiler.Service that referenced this pull request Apr 29, 2017
* remove redundant explicit copy

* fix build
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.

4 participants