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

Ref-Counting #5

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
7 changes: 1 addition & 6 deletions DMCompiler/DMStandard/_Standard.dm
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ proc/rand(L, H)
proc/rand_seed(Seed)
proc/range(Dist, Center)
proc/ref(Object)
proc/refcount(Object)
proc/replacetext(Haystack, Needle, Replacement, Start = 1, End = 0)
proc/replacetextEx(Haystack, Needle, Replacement, Start = 1, End = 0)
proc/rgb(R, G, B, A)
Expand Down Expand Up @@ -219,9 +220,3 @@ proc/lentext(T)

proc/winshow(player, window, show=1)
winset(player, window, "is-visible=[show ? "true" : "false"]")

proc/refcount(var/Object)
// woah that's a lot of refs
// i wonder if it's true??
return 100
// (it's not)
17 changes: 17 additions & 0 deletions OpenDreamRuntime/DreamValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,23 @@ public override int GetHashCode() {
public static bool operator !=(DreamValue a, DreamValue b) {
return !a.Equals(b);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IncrementDreamObjectRefCount() {
if (Type == DreamValueType.DreamObject && _refValue != null) {
DreamObject obj = (DreamObject)_refValue;
obj.IncrementRefCount();
}
}


[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void DecrementDreamObjectRefCount() {
if (Type == DreamValueType.DreamObject && _refValue != null) {
DreamObject obj = (DreamObject)_refValue;
obj.DecrementRefCount();
}
}
}

#region Serialization
Expand Down
29 changes: 28 additions & 1 deletion OpenDreamRuntime/Objects/DreamObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Utility;
using System.Threading;
using OpenDreamRuntime.Procs.DebugAdapter.Protocol;

namespace OpenDreamRuntime.Objects {
[Virtual]
Expand All @@ -21,6 +23,8 @@ public class DreamObject {
[Access(typeof(DreamObject))]
public bool Deleted;

public ulong RefCount { get; private set; }

public virtual bool ShouldCallNew => true;

// Shortcuts to IoC dependencies & entity systems
Expand Down Expand Up @@ -97,8 +101,14 @@ protected virtual void HandleDeletion() {

Tag = null;
Deleted = true;

//we release all relevant information, making this a very tiny object
Variables = null;
if (Variables != null) {
foreach (var value in Variables.Values)
value.DecrementDreamObjectRefCount();

Variables = null;
}

ObjectDefinition = null!;
}
Expand All @@ -110,6 +120,8 @@ public void Delete() {
if (TryGetProc("Del", out var delProc))
DreamThread.Run(delProc, this, null);

RefCount = 0;

HandleDeletion();
}

Expand Down Expand Up @@ -429,5 +441,20 @@ public override string ToString() {

return ObjectDefinition.Type;
}

public void IncrementRefCount() {
if (Deleted)
throw new InvalidOperationException("Invalid attempt at incrementing RefCount on a deleted DreamObject");

++RefCount;
}
public void DecrementRefCount() {
if (Deleted) throw new Exception("Invalid attempt at decrementing RefCount on a deleted DreamObject");
if (RefCount == 0) throw new Exception("Invalid attempt at decrementing DreamObject's refcount when it is at 0");

if (--RefCount == 0) {
Delete();
}
}
}
}
46 changes: 43 additions & 3 deletions OpenDreamRuntime/Objects/Types/DreamList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public DreamList(DreamObjectDefinition listDef, int size) : base(listDef) {
private DreamList(DreamObjectDefinition listDef, List<DreamValue> values, Dictionary<DreamValue, DreamValue>? associativeValues) : base(listDef) {
_values = values;
_associativeValues = associativeValues;

IEnumerable<DreamValue> allContainedValues = values;
if (associativeValues != null)
allContainedValues = allContainedValues.Concat(associativeValues.Values);

foreach(var value in allContainedValues)
value.IncrementDreamObjectRefCount();
}

public override void Initialize(DreamProcArguments args) {
Expand Down Expand Up @@ -114,17 +121,26 @@ public virtual DreamValue GetValue(DreamValue key) {
}

public virtual void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) {
value.IncrementDreamObjectRefCount();
if (key.TryGetValueAsInteger(out int keyInteger)) {
if (allowGrowth && keyInteger == _values.Count + 1) {
_values.Add(value);
} else {
_values[keyInteger - 1] = value;
}
} else {
key.IncrementDreamObjectRefCount();
if (!ContainsValue(key)) _values.Add(key);

_associativeValues ??= new Dictionary<DreamValue, DreamValue>(1);
DreamValue? oldValueNullable = null;
if (_associativeValues == null)
_associativeValues = new Dictionary<DreamValue, DreamValue>(1);
else if (_associativeValues.TryGetValue(key, out var oldValue))
oldValueNullable = oldValue;

_associativeValues[key] = value;
if (oldValueNullable.HasValue)
oldValueNullable.Value.DecrementDreamObjectRefCount();
}
}

Expand All @@ -138,6 +154,7 @@ public virtual void RemoveValue(DreamValue value) {
}

public virtual void AddValue(DreamValue value) {
value.IncrementDreamObjectRefCount();
_values.Add(value);
}

Expand Down Expand Up @@ -169,14 +186,19 @@ public virtual void Cut(int start = 1, int end = 0) {
if (end == 0 || end > (_values.Count + 1)) end = _values.Count + 1;

if (_associativeValues != null) {
for (int i = start; i < end; i++)
_associativeValues.Remove(_values[i - 1]);
for (int i = start; i < end; i++) {
var removedValue = _values[i - 1];
if (_associativeValues.Remove(removedValue, out var removedAssoc))
removedAssoc.DecrementDreamObjectRefCount();
removedValue.DecrementDreamObjectRefCount();
}
}

_values.RemoveRange(start - 1, end - start);
}

public void Insert(int index, DreamValue value) {
value.IncrementDreamObjectRefCount();
_values.Insert(index - 1, value);
}

Expand Down Expand Up @@ -375,6 +397,18 @@ public override DreamValue OperatorEquivalent(DreamValue b) {

return DreamValue.True;
}

protected override void HandleDeletion() {
base.HandleDeletion();

if (_associativeValues != null)
foreach (var assocValue in _associativeValues.Values)
assocValue.DecrementDreamObjectRefCount();

foreach (var value in _values)
value.DecrementDreamObjectRefCount();
}

#endregion Operators
}

Expand Down Expand Up @@ -442,6 +476,12 @@ public override DreamValue Initial(string name) {
public override bool IsSaved(string name) {
return _dreamObject.IsSaved(name);
}

protected override void HandleDeletion() {
base.HandleDeletion();

_dreamObject.DecrementRefCount();
}
}

// global.vars list
Expand Down
11 changes: 10 additions & 1 deletion OpenDreamRuntime/Procs/AsyncNativeProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ public void Initialize(AsyncNativeProc? proc, Func<State, Task<DreamValue>> task
Usr = usr;
arguments.Values.CopyTo(_arguments);
_argumentCount = arguments.Count;

if (src != null)
src.IncrementRefCount();

if (usr != null)
usr.IncrementRefCount();

foreach (var arg in arguments.Values)
arg.IncrementDreamObjectRefCount();
}

// Used to avoid reentrant resumptions in our proc
Expand Down Expand Up @@ -169,7 +178,7 @@ public DreamValue GetArgument(int argumentPosition, string argumentName) {
private readonly Dictionary<string, DreamValue>? _defaultArgumentValues;
private readonly Func<State, Task<DreamValue>> _taskFunc;

public AsyncNativeProc(int id, TreeEntry owningType, string name, List<string> argumentNames, Dictionary<string, DreamValue> defaultArgumentValues, Func<State, Task<DreamValue>> taskFunc)
public AsyncNativeProc(int id, TreeEntry owningType, string name, List<string> argumentNames, Dictionary<string, DreamValue>? defaultArgumentValues, Func<State, Task<DreamValue>> taskFunc)
: base(id, owningType, name, null, ProcAttributes.None, argumentNames, null, null, null, null, 0) {
_defaultArgumentValues = defaultArgumentValues;
_taskFunc = taskFunc;
Expand Down
52 changes: 49 additions & 3 deletions OpenDreamRuntime/Procs/DMProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ private DMProcState(DMProcState other, DreamThread thread) {
_stack = _dreamValuePool.Rent(other._stack.Length);
_localVariables = _dreamValuePool.Rent(other._localVariables.Length);
Array.Copy(other._localVariables, _localVariables, other._localVariables.Length);

IncrementRefCounts();
}

public void Initialize(DMProc proc, DreamThread thread, int maxStackSize, DreamObject? instance, DreamObject? usr, DreamProcArguments arguments) {
Expand All @@ -376,6 +378,24 @@ public void Initialize(DMProc proc, DreamThread thread, int maxStackSize, DreamO
for (int i = 0; i < ArgumentCount; i++) {
_localVariables[i] = arguments.GetArgument(i);
}

IncrementRefCounts();
}

private void IncrementRefCounts() {
Instance?.IncrementRefCount();
Usr?.IncrementRefCount();

for(var i = 0; i < _localVariables.Length; --i)
_localVariables[i].IncrementDreamObjectRefCount();
}

private void DecrementRefCounts() {
for(var i = _localVariables.Length - 1; i >= 0; --i)
_localVariables[i].DecrementDreamObjectRefCount();

Usr?.DecrementRefCount();
Instance?.DecrementRefCount();
}

public override unsafe ProcStatus Resume() {
Expand Down Expand Up @@ -421,6 +441,7 @@ public override unsafe ProcStatus Resume() {
}

public override void ReturnedInto(DreamValue value) {
value.IncrementDreamObjectRefCount();
Push(value);
}

Expand All @@ -442,6 +463,8 @@ public void Jump(int position) {
}

public void SetReturn(DreamValue value) {
value.IncrementDreamObjectRefCount();
Result.DecrementDreamObjectRefCount();
Result = value;
}

Expand Down Expand Up @@ -510,6 +533,7 @@ public override void Dispose() {
_stackIndex = 0;
_stack = null;

DecrementRefCounts();
_dreamValuePool.Return(_localVariables);
_localVariables = null;

Expand All @@ -527,6 +551,8 @@ public void SetArgument(int id, DreamValue value) {
if (id < 0 || id >= ArgumentCount)
throw new IndexOutOfRangeException($"Given argument id ({id}) was out of range");

value.IncrementDreamObjectRefCount();
_localVariables[id].DecrementDreamObjectRefCount();
_localVariables[id] = value;
}

Expand Down Expand Up @@ -700,23 +726,43 @@ private static void ThrowReferenceNotListIndex() {

public void AssignReference(DreamReference reference, DreamValue value) {
switch (reference.Type) {
case DMReference.Type.Self: Result = value; break;
case DMReference.Type.Self: Result = value;
value.IncrementDreamObjectRefCount();
Result.DecrementDreamObjectRefCount();
Result = value;
break;
case DMReference.Type.Argument: SetArgument(reference.Value, value); break;
case DMReference.Type.Local: _localVariables[ArgumentCount + reference.Value] = value; break;
case DMReference.Type.Local:
var localIndex = ArgumentCount + reference.Value;
value.IncrementDreamObjectRefCount();
_localVariables[localIndex].DecrementDreamObjectRefCount();
_localVariables[localIndex] = value;
break;
case DMReference.Type.SrcField: Instance.SetVariable(ResolveString(reference.Value), value); break;
case DMReference.Type.Global: DreamManager.Globals[reference.Value] = value; break;
case DMReference.Type.Global:
value.IncrementDreamObjectRefCount();
DreamManager.Globals[reference.Value].DecrementDreamObjectRefCount();
DreamManager.Globals[reference.Value] = value;
break;
case DMReference.Type.Src:
value.IncrementDreamObjectRefCount();
//TODO: src can be assigned to non-DreamObject values
var oldInstance = Instance;
if (!value.TryGetValueAsDreamObject(out Instance)) {
ThrowCannotAssignSrcTo(value);
}

oldInstance?.DecrementRefCount();
break;
case DMReference.Type.Usr:
value.IncrementDreamObjectRefCount();
//TODO: usr can be assigned to non-DreamObject values
var oldUsr = Usr;
if (!value.TryGetValueAsDreamObject(out Usr)) {
ThrowCannotAssignUsrTo(value);
}

oldUsr?.DecrementRefCount();
break;
case DMReference.Type.Field: {
DreamValue owner = Pop();
Expand Down
5 changes: 5 additions & 0 deletions OpenDreamRuntime/Procs/InitDreamObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public void Initialize(DreamThread thread, DreamObject dreamObject, DreamObject?
arguments.Values.CopyTo(_arguments);
_argumentCount = arguments.Count;
_stage = Stage.Init;

dreamObject.IncrementRefCount();
usr?.IncrementRefCount();
foreach(var arg in arguments.Values)
arg.IncrementDreamObjectRefCount();
}

private DreamObject _dreamObject;
Expand Down
1 change: 1 addition & 0 deletions OpenDreamRuntime/Procs/Native/DreamProcNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) {
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_rand_seed);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_range);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_ref);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_refcount);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_regex);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_replacetext);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_replacetextEx);
Expand Down
12 changes: 12 additions & 0 deletions OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,18 @@ public static DreamValue NativeProc_ref(NativeProc.Bundle bundle, DreamObject? s
return new DreamValue(bundle.DreamManager.CreateRef(bundle.GetArgument(0, "Object")));
}

[DreamProc("refcount")]
[DreamProcParameter("Object", Type = DreamValueTypeFlag.DreamObject)]
public static DreamValue NativeProc_refcount(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
var arg = bundle.GetArgument(0, "Object");
var refs = 0UL;
if (arg.TryGetValueAsDreamObject(out var dreamObject)) {
refs = dreamObject.RefCount;
}

return new DreamValue(refs);
}

[DreamProc("regex")]
[DreamProcParameter("pattern", Type = DreamValueTypeFlag.String | DreamValueTypeFlag.DreamObject)]
[DreamProcParameter("flags", Type = DreamValueTypeFlag.Float)]
Expand Down
2 changes: 1 addition & 1 deletion OpenDreamRuntime/Procs/NativeProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static (string, Dictionary<string, DreamValue>?, List<string>) GetNativeI
int intValue => new(intValue),
float floatValue => new(floatValue),
string stringValue => new(stringValue),
_ => throw new Exception($"Invalid default value {parameterAttribute.DefaultValue}")
_ => throw new Exception($"Invalid default value {parameterAttribute.DefaultValue}") // needed for refcounting
};

defaultArgumentValues.Add(parameterAttribute.Name, defaultValue);
Expand Down
Loading