Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import org.jruby.util.TypeConverter;

import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.ReentrantLock;

import static org.jruby.util.RubyStringBuilder.str;
Expand All @@ -94,7 +95,8 @@ public class RubySymbol extends RubyObject implements MarshalEncoding, EncodingC
private final ByteList symbolBytes;
private final int hashCode;
private String decodedString;
private RubyString rubyString;
private volatile RubyString rubyString;
private static final AtomicReferenceFieldUpdater<RubySymbol, RubyString> RUBY_STRING_UPDATER = AtomicReferenceFieldUpdater.newUpdater(RubySymbol.class, RubyString.class, "rubyString");
private transient Object constant;
private SymbolNameType type;

Expand Down Expand Up @@ -473,7 +475,7 @@ public IRubyObject to_s() {

@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return to_s(context.runtime);
return getNameString(context).strDup(context.runtime);
}

final RubyString to_s(Ruby runtime) {
Expand All @@ -482,22 +484,38 @@ final RubyString to_s(Ruby runtime) {

@JRubyMethod(name = "name")
public IRubyObject name(ThreadContext context) {
// FIXME: A race here where two in-flight to_s on same symbol can return a different instance.
if (rubyString == null) {
rubyString = RubyString.newStringShared(context.runtime, getBytes());
rubyString.setFrozen(true);
}
return getNameString(context);
}

private RubyString getNameString(ThreadContext context) {
RubyString currentString = rubyString;

if (currentString != null) {
return currentString;
}

return cacheNameString(context);
}

private RubyString cacheNameString(ThreadContext context) {
// atomically create and store an fstring for this symbol
Ruby runtime = context.runtime;
RubyString nameString = RubyString.newStringShared(runtime, getBytes());
nameString.scanForCodeRange();
nameString = runtime.freezeAndDedupString(nameString);
RUBY_STRING_UPDATER.compareAndSet(this, null, nameString);

return rubyString;
return rubyString;
}

@Deprecated
public IRubyObject id2name() {
return to_s(metaClass.runtime);
return name(getRuntime().getCurrentContext());
}

@JRubyMethod
public IRubyObject id2name(ThreadContext context) {
return to_s(context);
return name(context);
}

@Override
Expand Down