mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-10 15:36:47 +01:00
7aeab49ce5
Some concerns were brought up about the potential for performance concerns with wrapping Destroy() in a try catch. So let me make a trade. /datum is the parent of all objects in existence, and is spending a large chunk of time on proc overhead for 2.6 million qdel'ed objects. <img width="1196" height="811" alt="image" src="https://github.com/user-attachments/assets/06ca1596-2d56-40ca-9ec6-9215ed02113c" /> I'll trade you a faster global destroy() for the better logging so I can finish destroying the garbage collector lag. The cost reduction is from 0.01209ms per datum to 0.007525ms per datum, about a 28% time saving.
239 lines
8.2 KiB
Plaintext
239 lines
8.2 KiB
Plaintext
/datum
|
|
/**
|
|
* Tick count time when this object was destroyed.
|
|
*
|
|
* If this is non zero then the object has been garbage collected and is awaiting either
|
|
* a hard del by the GC subsystme, or to be autocollected (if it has no references)
|
|
*/
|
|
var/gc_destroyed
|
|
|
|
/// Open uis owned by this datum
|
|
/// Lazy, since this case is semi rare
|
|
var/list/open_uis
|
|
|
|
var/tmp/list/active_timers
|
|
|
|
/// Active timers with this datum as the target
|
|
var/list/_active_timers
|
|
|
|
/// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))
|
|
var/list/status_traits
|
|
|
|
/**
|
|
* Components attached to this datum
|
|
*
|
|
* Lazy associated list in the structure of `type -> component/list of components`
|
|
*/
|
|
var/list/_datum_components
|
|
/**
|
|
* Any datum registered to receive signals from this datum is in this list
|
|
*
|
|
* Lazy associated list in the structure of `signal -> registree/list of registrees`
|
|
*/
|
|
var/list/_listen_lookup
|
|
/// Lazy associated list in the structure of `target -> list(signal -> proctype)` that are run when the datum receives that signal
|
|
var/list/list/_signal_procs
|
|
|
|
/// Datum level flags
|
|
var/datum_flags = NONE
|
|
|
|
/**
|
|
* If set, a path at/above this one that expects not to be instantiated
|
|
*
|
|
* This is a `typepath`
|
|
*
|
|
* Do not instantiate a datum that has the path set as its abstract_type, this indicates
|
|
* that the datum is abstract and is not meant to be spawned/used directly
|
|
*/
|
|
var/abstract_type
|
|
|
|
/// A weak reference to another datum
|
|
var/datum/weakref/weak_reference
|
|
|
|
/*
|
|
* Lazy associative list of currently active cooldowns.
|
|
*
|
|
* cooldowns [ COOLDOWN_INDEX ] = add_timer()
|
|
* add_timer() returns the truthy value of -1 when not stoppable, and else a truthy numeric index
|
|
*/
|
|
var/list/cooldowns
|
|
|
|
/// Used to avoid unnecessary refstring creation in Destroy().
|
|
var/tmp/has_state_machine = FALSE
|
|
|
|
#ifdef REFERENCE_TRACKING
|
|
/// When was this datum last touched by a reftracker?
|
|
/// If this value doesn't match with the start of the search
|
|
/// We know this datum has never been seen before, and we should check it
|
|
var/last_find_references = 0
|
|
/// How many references we're trying to find when searching
|
|
var/references_to_clear = 0
|
|
#ifdef REFERENCE_TRACKING_DEBUG
|
|
///Stores info about where refs are found, used for sanity checks and testing
|
|
var/list/found_refs
|
|
#endif
|
|
#endif
|
|
|
|
// If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process)
|
|
// Create and destroy is weird and I wanna cover my bases
|
|
var/harddel_deets_dumped = FALSE
|
|
|
|
/**
|
|
* Default implementation of clean-up code.
|
|
*
|
|
* This should be overridden to remove all references pointing to the object being destroyed, if
|
|
* you do override it, make sure to call the parent and return it's return value by default
|
|
*
|
|
* Return an appropriate [QDEL_HINT][QDEL_HINT_QUEUE] to modify handling of your deletion;
|
|
* in most cases this is [QDEL_HINT_QUEUE].
|
|
*
|
|
* The base case is responsible for doing the following
|
|
* * Erasing timers pointing to this datum
|
|
* * Erasing compenents on this datum
|
|
* * Notifying datums listening to signals from this datum that we are going away
|
|
*
|
|
* Returns [QDEL_HINT_QUEUE]
|
|
*/
|
|
/datum/proc/Destroy(force=FALSE)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
|
|
tag = null
|
|
datum_flags &= ~DF_USE_TAG //In case something tries to REF us
|
|
weak_reference = null //ensure prompt GCing of weakref.
|
|
|
|
if(_active_timers)
|
|
var/list/timers = _active_timers
|
|
_active_timers = null
|
|
for(var/datum/timedevent/timer as anything in timers)
|
|
if (!timer || (timer.spent && !(timer.flags & TIMER_DELETE_ME)))
|
|
continue
|
|
qdel(timer)
|
|
|
|
#ifdef REFERENCE_TRACKING
|
|
#ifdef REFERENCE_TRACKING_DEBUG
|
|
found_refs = null
|
|
#endif
|
|
#endif
|
|
|
|
// Cleanup all events on non-turfs. These used to be procs, but have been removed to save proc overhead on 2.6 million destroy() calls
|
|
if (!isturf(src))
|
|
if(GLOB.global_listen_count && GLOB.global_listen_count[src])
|
|
GLOB.global_listen_count -= src
|
|
for(var/entry in GLOB.all_observable_events)
|
|
var/singleton/observ/event = entry
|
|
if(event.unregister_global(src))
|
|
log_debug("[event] - [src] was deleted while still registered to global events.")
|
|
if(!(--GLOB.global_listen_count[src]))
|
|
break
|
|
if(GLOB.event_sources_count && GLOB.event_sources_count[src])
|
|
GLOB.event_sources_count -= src
|
|
for(var/entry in GLOB.all_observable_events)
|
|
var/singleton/observ/event = entry
|
|
var/proc_owners = event.event_sources[src]
|
|
if(proc_owners)
|
|
for(var/proc_owner in proc_owners)
|
|
if(event.unregister(src, proc_owner))
|
|
log_debug("[event] - [src] was deleted while still being listened to by [proc_owner].")
|
|
if(!(--GLOB.event_sources_count[src]))
|
|
break
|
|
if(GLOB.event_listen_count && GLOB.event_listen_count[src])
|
|
GLOB.event_listen_count -= src
|
|
for(var/entry in GLOB.all_observable_events)
|
|
var/singleton/observ/event = entry
|
|
for(var/event_source in event.event_sources)
|
|
if(event.unregister(event_source, src))
|
|
log_debug("[event] - [src] was deleted while still listening to [event_source].")
|
|
if(!(--GLOB.event_listen_count[src]))
|
|
break
|
|
// End of event cleanup
|
|
|
|
//BEGIN: ECS SHIT
|
|
var/list/dc = _datum_components
|
|
if(dc)
|
|
for(var/component_key in dc)
|
|
var/component_or_list = dc[component_key]
|
|
if(islist(component_or_list))
|
|
for(var/datum/component/component as anything in component_or_list)
|
|
if (!component)
|
|
continue
|
|
qdel(component, FALSE)
|
|
else
|
|
var/datum/component/C = component_or_list
|
|
qdel(C, FALSE)
|
|
dc.Cut()
|
|
|
|
// Signal cleanup. This originally was a proc from /tg/ that had this warning label asking me not to override it.
|
|
/*
|
|
///Only override this if you know what you're doing. You do not know what you're doing
|
|
///This is a threat
|
|
*/
|
|
// Well proc overhead is a thing that exists when you're qdel'ing 2.6 million objects. And NOTHING overrides it or has any reason to override it.
|
|
if(length(_listen_lookup))
|
|
for(var/sig in _listen_lookup)
|
|
var/list/comps = _listen_lookup[sig]
|
|
if(length(comps))
|
|
for(var/datum/component/comp as anything in comps)
|
|
comp.UnregisterSignal(src, sig)
|
|
else
|
|
var/datum/component/comp = comps
|
|
comp.UnregisterSignal(src, sig)
|
|
_listen_lookup = null
|
|
|
|
for(var/target in _signal_procs)
|
|
UnregisterSignal(target, _signal_procs[target])
|
|
//END: ECS SHIT
|
|
|
|
return QDEL_HINT_QUEUE
|
|
|
|
/**
|
|
* Callback called by a timer to end an associative-list-indexed cooldown.
|
|
*
|
|
* Arguments:
|
|
* * source - datum storing the cooldown
|
|
* * index - string index storing the cooldown on the cooldowns associative list
|
|
*
|
|
* This sends a signal reporting the cooldown end.
|
|
*/
|
|
/proc/end_cooldown(datum/source, index)
|
|
if(QDELETED(source))
|
|
return
|
|
SEND_SIGNAL(source, COMSIG_CD_STOP(index))
|
|
TIMER_COOLDOWN_END(source, index)
|
|
|
|
|
|
/**
|
|
* Proc used by stoppable timers to end a cooldown before the time has ran out.
|
|
*
|
|
* Arguments:
|
|
* * source - datum storing the cooldown
|
|
* * index - string index storing the cooldown on the cooldowns associative list
|
|
*
|
|
* This sends a signal reporting the cooldown end, passing the time left as an argument.
|
|
*/
|
|
/proc/reset_cooldown(datum/source, index)
|
|
if(QDELETED(source))
|
|
return
|
|
SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index))
|
|
TIMER_COOLDOWN_END(source, index)
|
|
|
|
///Generate a tag for this /datum, if it implements one
|
|
///Should be called as early as possible, best would be in New, to avoid weakref mistargets
|
|
///Really just don't use this, you don't need it, global lists will do just fine MOST of the time
|
|
///We really only use it for mobs to make id'ing people easier
|
|
/datum/proc/GenerateTag()
|
|
datum_flags |= DF_USE_TAG
|
|
|
|
/// Return text from this proc to provide extra context to hard deletes that happen to it
|
|
/// Optional, you should use this for cases where replication is difficult and extra context is required
|
|
/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry)
|
|
/datum/proc/dump_harddel_info()
|
|
return
|
|
|
|
///images are pretty generic, this should help a bit with tracking harddels related to them
|
|
/image/dump_harddel_info()
|
|
if(harddel_deets_dumped)
|
|
return
|
|
harddel_deets_dumped = TRUE
|
|
return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]"
|