Skip to content

Commit 28db22d

Browse files
committedJan 22, 2018
Additional fixes for deuping strings (#4970).
* Use deduped as both key and value. Hard reference to key is the responsibility of callers of freezeAndDedupString. * Remove use of Java 8+ atomic Map.compute API calls. This means 9.1 will have a race, but that should only affect concurrent loads that attempt to deduplicate the same currently-not- deduplicated strings.
1 parent fc97cd8 commit 28db22d

File tree

1 file changed

+26
-2
lines changed

1 file changed

+26
-2
lines changed
 

‎core/src/main/java/org/jruby/Ruby.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@
195195
import java.util.concurrent.TimeUnit;
196196
import java.util.concurrent.atomic.AtomicInteger;
197197
import java.util.concurrent.atomic.AtomicLong;
198+
import java.util.function.BiFunction;
199+
import java.util.function.Function;
198200
import java.util.regex.Pattern;
199201

200202
import static java.lang.invoke.MethodHandles.explicitCastArguments;
@@ -4577,14 +4579,36 @@ public RubyString freezeAndDedupString(RubyString string) {
45774579
RubyString deduped;
45784580

45794581
if (dedupedRef == null || (deduped = dedupedRef.get()) == null) {
4582+
// Never use incoming value as key
45804583
deduped = string.strDup(this);
45814584
deduped.setFrozen(true);
4582-
dedupMap.put(string, new WeakReference<RubyString>(deduped));
4583-
} else if (deduped.getEncoding() != string.getEncoding()) {
4585+
4586+
WeakReference<RubyString> weakref = new WeakReference<>(deduped);
4587+
WeakReference<RubyString> existing;
4588+
RubyString existingDeduped;
4589+
4590+
// Check if someone else beat us to it.
4591+
// NOTE: This still races because Map.compute* API is Java 8+.
4592+
while ((existing = dedupMap.putIfAbsent(deduped, weakref)) != null ) {
4593+
4594+
existingDeduped = existing.get();
4595+
4596+
if (existingDeduped != null) {
4597+
deduped = existingDeduped;
4598+
break;
4599+
}
4600+
4601+
// keep trying to put it if existing has been evacuated
4602+
}
4603+
}
4604+
4605+
if (deduped.getEncoding() != string.getEncoding()) {
45844606
// if encodings don't match, new string loses; can't dedup
4607+
// FIXME: This may never happen, if we are properly considering encoding in RubyString.hashCode
45854608
deduped = string.strDup(this);
45864609
deduped.setFrozen(true);
45874610
}
4611+
45884612
return deduped;
45894613
}
45904614

0 commit comments

Comments
 (0)
Please sign in to comment.