From ebe5cc916de1a38bbcb1259fd0699f771211ad99 Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Sun, 2 Apr 2017 17:41:21 +0200 Subject: [PATCH 1/8] Port of @PsiOmegaDelta's Baystation12/Baystation12#16820 Ports tg's garbage collector subsystem and Destroy() returning qdel hints. --- code/ATMOSPHERICS/pipes.dm | 28 +- code/__defines/qdel.dm | 17 + code/_helpers/mobs.dm | 2 +- .../ProcessScheduler/core/process.dm | 2 +- code/controllers/Processes/garbage.dm | 254 ------------- code/controllers/Processes/machinery.dm | 6 +- code/controllers/Processes/mob.dm | 2 +- code/controllers/Processes/nanoui.dm | 2 +- code/controllers/Processes/obj.dm | 2 +- code/controllers/Processes/scheduler.dm | 4 +- code/controllers/failsafe.dm | 4 +- code/controllers/master.dm | 2 +- code/controllers/master_controller.dm | 6 +- code/controllers/subsystems/garbage.dm | 354 ++++++++++++++++++ code/datums/helper_datums/global_iterator.dm | 1 + code/datums/repositories/decls.dm | 2 +- code/datums/weakref.dm | 6 +- code/game/atoms_movable.dm | 12 +- .../endgame/supermatter_cascade/universe.dm | 1 - code/game/machinery/CableLayer.dm | 2 +- .../atmoalter/portable_atmospherics.dm | 11 +- code/game/machinery/doors/door.dm | 3 +- code/game/machinery/floor_light.dm | 2 +- code/game/machinery/pipe/construction.dm | 18 +- code/game/mecha/mecha.dm | 16 +- .../objects/items/devices/transfer_valve.dm | 2 +- .../objects/items/weapons/flamethrower.dm | 13 +- .../weapons/material/material_weapons.dm | 2 +- code/game/objects/structures.dm | 2 +- code/game/objects/structures/lattice.dm | 2 +- code/game/turfs/turf.dm | 1 + code/modules/admin/view_variables/helpers.dm | 7 - code/modules/assembly/signaler.dm | 2 +- code/modules/client/client defines.dm | 4 + code/modules/client/client procs.dm | 3 + .../hydroponics/spreading/spreading_growth.dm | 2 +- code/modules/lighting/lighting_overlay.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 2 +- .../living/silicon/robot/drone/drone_items.dm | 2 +- code/modules/mob/mob.dm | 1 + code/modules/mob/mob_grab.dm | 12 +- code/modules/organs/internal/brain.dm | 6 +- code/modules/reagents/Chemistry-Holder.dm | 2 +- code/modules/reagents/Chemistry-Machinery.dm | 2 +- code/modules/reagents/Chemistry-Reagents.dm | 2 +- .../reagent_containers/food/snacks.dm | 4 +- code/modules/shieldgen/emergency_shield.dm | 6 +- code/modules/tables/tables.dm | 2 +- polaris.dme | 3 +- 49 files changed, 479 insertions(+), 366 deletions(-) create mode 100644 code/__defines/qdel.dm delete mode 100644 code/controllers/Processes/garbage.dm create mode 100644 code/controllers/subsystems/garbage.dm diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm index 105aaa8c8b8..c23fc165552 100644 --- a/code/ATMOSPHERICS/pipes.dm +++ b/code/ATMOSPHERICS/pipes.dm @@ -63,11 +63,11 @@ return parent.return_network(reference) /obj/machinery/atmospherics/pipe/Destroy() - qdel(parent) + qdel_null(parent) if(air_temporary) loc.assume_air(air_temporary) - ..() + . = ..() /obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) if (istype(src, /obj/machinery/atmospherics/pipe/tank)) @@ -227,10 +227,12 @@ /obj/machinery/atmospherics/pipe/simple/Destroy() if(node1) node1.disconnect(src) + node1 = null if(node2) node2.disconnect(src) + node1 = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/simple/pipeline_expansion() return list(node1, node2) @@ -461,12 +463,15 @@ /obj/machinery/atmospherics/pipe/manifold/Destroy() if(node1) node1.disconnect(src) + node1 = null if(node2) node2.disconnect(src) + node2 = null if(node3) node3.disconnect(src) + node3 = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference) if(reference == node1) @@ -705,14 +710,18 @@ /obj/machinery/atmospherics/pipe/manifold4w/Destroy() if(node1) node1.disconnect(src) + node1 = null if(node2) node2.disconnect(src) + node2 = null if(node3) node3.disconnect(src) + node3 = null if(node4) node4.disconnect(src) + node4 = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference) if(reference == node1) @@ -967,8 +976,9 @@ /obj/machinery/atmospherics/pipe/cap/Destroy() if(node) node.disconnect(src) + node = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference) if(reference == node) @@ -1081,8 +1091,9 @@ /obj/machinery/atmospherics/pipe/tank/Destroy() if(node1) node1.disconnect(src) + node1 = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/tank/pipeline_expansion() return list(node1) @@ -1251,8 +1262,9 @@ /obj/machinery/atmospherics/pipe/vent/Destroy() if(node1) node1.disconnect(src) + node1 = null - ..() + . = ..() /obj/machinery/atmospherics/pipe/vent/pipeline_expansion() return list(node1) diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm new file mode 100644 index 00000000000..168a6adf0c3 --- /dev/null +++ b/code/__defines/qdel.dm @@ -0,0 +1,17 @@ +//defines that give qdel hints. these can be given as a return in destory() or by calling + +#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion. +#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory. +#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it. +#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference. +#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste. +#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm. + //if TESTING is enabled, qdel will call this object's find_references() verb. +//defines for the gc_destroyed var + +#define GC_QUEUED_FOR_QUEUING -1 +#define GC_QUEUED_FOR_HARD_DEL -2 +#define GC_CURRENTLY_BEING_QDELETED -3 + +#define QDELETED(X) (!X || X.gc_destroyed) +#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index d7fd20ac770..8ac42dc0b92 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -228,7 +228,7 @@ Proc for attack log creation, because really why not . = 0 break - if(target_loc && (!target || target_loc != target.loc)) + if(target_loc && (QDELETED(target) || target_loc != target.loc)) . = 0 break diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm index edcb8d703c6..9f08241978d 100644 --- a/code/controllers/ProcessScheduler/core/process.dm +++ b/code/controllers/ProcessScheduler/core/process.dm @@ -387,6 +387,6 @@ exceptions[eid] = 0 /datum/controller/process/proc/catchBadType(var/datum/caught) - if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed)) + if(isnull(caught) || !istype(caught) || QDELETED(caught)) return // Only bother with types we can identify and that don't belong catchException("Type [caught.type] does not belong in process' queue") diff --git a/code/controllers/Processes/garbage.dm b/code/controllers/Processes/garbage.dm deleted file mode 100644 index 02f04113320..00000000000 --- a/code/controllers/Processes/garbage.dm +++ /dev/null @@ -1,254 +0,0 @@ -// The time a datum was destroyed by the GC, or null if it hasn't been -/datum/var/gcDestroyed - -#define GC_COLLECTIONS_PER_RUN 300 -#define GC_COLLECTION_TIMEOUT (30 SECONDS) -#define GC_FORCE_DEL_PER_RUN 30 - -var/datum/controller/process/garbage_collector/garbage_collector -var/list/delayed_garbage = list() - -/datum/controller/process/garbage_collector - var/garbage_collect = 1 // Whether or not to actually do work - var/total_dels = 0 // number of total del()'s - var/tick_dels = 0 // number of del()'s we've done this tick - var/soft_dels = 0 - var/hard_dels = 0 // number of hard dels in total - var/list/destroyed = list() // list of refID's of things that should be garbage collected - // refID's are associated with the time at which they time out and need to be manually del() - // we do this so we aren't constantly locating them and preventing them from being gc'd - - var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened. - // the types are stored as strings - -/datum/controller/process/garbage_collector/setup() - name = "garbage" - schedule_interval = 5 SECONDS - start_delay = 3 - - if(!garbage_collector) - garbage_collector = src - - for(var/garbage in delayed_garbage) - qdel(garbage) - delayed_garbage.Cut() - delayed_garbage = null - -#ifdef GC_FINDREF -world/loop_checks = 0 -#endif - -/datum/controller/process/garbage_collector/doWork() - if(!garbage_collect) - return - - tick_dels = 0 - var/time_to_kill = world.time - GC_COLLECTION_TIMEOUT - var/checkRemain = GC_COLLECTIONS_PER_RUN - var/remaining_force_dels = GC_FORCE_DEL_PER_RUN - - - while(destroyed.len && --checkRemain >= 0) - if(remaining_force_dels <= 0) - #ifdef GC_DEBUG - testing("GC: Reached max force dels per tick [dels] vs [maxDels]") - #endif - break // Server's already pretty pounded, everything else can wait 2 seconds - var/refID = destroyed[1] - var/GCd_at_time = destroyed[refID] - if(GCd_at_time > time_to_kill) - #ifdef GC_DEBUG - testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + collection_timeout]") - #endif - break // Everything else is newer, skip them - var/datum/A = locate(refID) - #ifdef GC_DEBUG - testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]") - #endif - if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake - // Something's still referring to the qdel'd object. Kill it. - testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --") - logging["[A.type]"]++ - del(A) - - hard_dels++ - remaining_force_dels-- - else - #ifdef GC_DEBUG - testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]") - #endif - soft_dels++ - tick_dels++ - total_dels++ - destroyed.Cut(1, 2) - SCHECK - -#undef GC_FORCE_DEL_PER_TICK -#undef GC_COLLECTION_TIMEOUT -#undef GC_COLLECTIONS_PER_TICK - -#ifdef GC_FINDREF -/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ) - . = 0 - for(var/V in D.vars) - if(V == "contents") - continue - if(istype(D.vars[V], /atom)) - var/atom/A = D.vars[V] - if(A in targ) - testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]") - . += 1 - else if(islist(D.vars[V])) - . += LookForListRefs(D.vars[V], targ, D, V) - -/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/list/targ, var/datum/D, var/V) - . = 0 - for(var/F in L) - if(istype(F, /atom)) - var/atom/A = F - if(A in targ) - testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]") - . += 1 - if(islist(F)) - . += LookForListRefs(F, targ, D, "[F] in list [V]") -#endif - -/datum/controller/process/garbage_collector/proc/AddTrash(datum/A) - if(!istype(A) || !isnull(A.gcDestroyed)) - return - #ifdef GC_DEBUG - testing("GC: AddTrash(\ref[A] - [A.type])") - #endif - A.gcDestroyed = world.time - destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list. - destroyed["\ref[A]"] = world.time - -/datum/controller/process/garbage_collector/statProcess() - ..() - stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued") - stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run") - - -// Tests if an atom has been deleted. -/proc/deleted(atom/A) - return !A || !isnull(A.gcDestroyed) - -// Should be treated as a replacement for the 'del' keyword. -// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. -/proc/qdel(var/datum/A) - if(!A) - return - if(!istype(A)) - warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.") - crash_with("qdel() passed object of type [A.type]. qdel() can only handle /datum types.") - del(A) - if(garbage_collector) - garbage_collector.total_dels++ - garbage_collector.hard_dels++ - else if(isnull(A.gcDestroyed)) - // Let our friend know they're about to get collected - . = !A.Destroy() - if(. && A) - A.finalize_qdel() - -/datum/proc/finalize_qdel() - del(src) - -/atom/finalize_qdel() - if(garbage_collector) - garbage_collector.AddTrash(src) - else - delayed_garbage |= src - -/icon/finalize_qdel() - del(src) - -/image/finalize_qdel() - del(src) - -/mob/finalize_qdel() - del(src) - -/turf/finalize_qdel() - del(src) - -// Default implementation of clean-up code. -// This should be overridden to remove all references pointing to the object being destroyed. -// Return true if the the GC controller should allow the object to continue existing. -/datum/proc/Destroy() - nanomanager.close_uis(src) - tag = null - return - -#ifdef TESTING -/client/var/running_find_references - -/mob/verb/create_thing() - set category = "Debug" - set name = "Create Thing" - - var/path = input("Enter path") - var/atom/thing = new path(loc) - thing.find_references() - -/atom/verb/find_references() - set category = "Debug" - set name = "Find References" - set background = 1 - set src in world - - if(!usr || !usr.client) - return - - if(usr.client.running_find_references) - testing("CANCELLED search for references to a [usr.client.running_find_references].") - usr.client.running_find_references = null - return - - if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No") - return - - // Remove this object from the list of things to be auto-deleted. - if(garbage_collector) - garbage_collector.destroyed -= "\ref[src]" - - usr.client.running_find_references = type - testing("Beginning search for references to a [type].") - var/list/things = list() - for(var/client/thing) - things += thing - for(var/datum/thing) - things += thing - for(var/atom/thing) - things += thing - testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)") - for(var/datum/thing in things) - if(!usr.client.running_find_references) return - for(var/varname in thing.vars) - var/variable = thing.vars[varname] - if(variable == src) - testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.") - else if(islist(variable)) - if(src in variable) - testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.") - testing("Completed search for references to a [type].") - usr.client.running_find_references = null - -/client/verb/purge_all_destroyed_objects() - set category = "Debug" - if(garbage_collector) - while(garbage_collector.destroyed.len) - var/datum/o = locate(garbage_collector.destroyed[1]) - if(istype(o) && o.gcDestroyed) - del(o) - garbage_collector.dels++ - garbage_collector.destroyed.Cut(1, 2) -#endif - -#ifdef GC_DEBUG -#undef GC_DEBUG -#endif - -#ifdef GC_FINDREF -#undef GC_FINDREF -#endif \ No newline at end of file diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm index 405615dc1fe..1fd1f8ce456 100644 --- a/code/controllers/Processes/machinery.dm +++ b/code/controllers/Processes/machinery.dm @@ -20,7 +20,7 @@ /datum/controller/process/machinery/proc/internal_process_machinery() for(last_object in machines) var/obj/machinery/M = last_object - if(M && !M.gcDestroyed) + if(M && !QDELETED(M)) if(M.process() == PROCESS_KILL) //M.inMachineList = 0 We don't use this debugging function machines.Remove(M) @@ -34,7 +34,7 @@ /datum/controller/process/machinery/proc/internal_process_power() for(last_object in powernets) var/datum/powernet/powerNetwork = last_object - if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed)) + if(istype(powerNetwork) && !QDELETED(powerNetwork)) powerNetwork.reset() SCHECK continue @@ -52,7 +52,7 @@ /datum/controller/process/machinery/proc/internal_process_pipenets() for(last_object in pipe_networks) var/datum/pipe_network/pipeNetwork = last_object - if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed)) + if(istype(pipeNetwork) && !QDELETED(pipeNetwork)) pipeNetwork.process() SCHECK continue diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm index 8681803ac44..6b623e77305 100644 --- a/code/controllers/Processes/mob.dm +++ b/code/controllers/Processes/mob.dm @@ -14,7 +14,7 @@ /datum/controller/process/mob/doWork() for(last_object in mob_list) var/mob/M = last_object - if(M && isnull(M.gcDestroyed)) + if(M && !QDELETED(M)) try M.Life() catch(var/exception/e) diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm index 49b9048c078..21d2f8e5f01 100644 --- a/code/controllers/Processes/nanoui.dm +++ b/code/controllers/Processes/nanoui.dm @@ -9,7 +9,7 @@ /datum/controller/process/nanoui/doWork() for(last_object in nanomanager.processing_uis) var/datum/nanoui/NUI = last_object - if(istype(NUI) && isnull(NUI.gcDestroyed)) + if(istype(NUI) && !QDELETED(NUI)) try NUI.process() catch(var/exception/e) diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm index 1010c8cc977..6032cbb5412 100644 --- a/code/controllers/Processes/obj.dm +++ b/code/controllers/Processes/obj.dm @@ -11,7 +11,7 @@ /datum/controller/process/obj/doWork() for(last_object in processing_objects) var/datum/O = last_object - if(O && isnull(O.gcDestroyed)) + if(!QDELETED(O)) try O:process() catch(var/exception/e) diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm index 276249bd558..fb9f85d7c46 100644 --- a/code/controllers/Processes/scheduler.dm +++ b/code/controllers/Processes/scheduler.dm @@ -30,8 +30,8 @@ // Note: We won't be informed about tasks being destroyed, but this is the best we can do. /datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target) scheduled_tasks = list() - for(var/st in target.scheduled_tasks) - if(!deleted(st) && istype(st, /datum/scheduled_task)) + for(var/datum/scheduled_task/st in target.scheduled_tasks) + if(!QDELETED(st) && istype(st)) schedule(st) scheduler = src diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 98c6a5d89d6..0589d5fa7ff 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -33,13 +33,13 @@ var/datum/controller/failsafe/Failsafe /datum/controller/failsafe/Initialize() set waitfor = 0 Failsafe.Loop() - if(!deleted(src)) + if(!QDELETED(src)) qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us /datum/controller/failsafe/Destroy() running = FALSE ..() - // return QDEL_HINT_HARDDEL_NOW // TODO - Once we port garbage.dm + return QDEL_HINT_HARDDEL_NOW /datum/controller/failsafe/proc/Loop() while(running) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 981cf6d2bb8..360b739badf 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -66,7 +66,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING /datum/controller/master/Destroy() ..() // Tell qdel() to Del() this object. - // return QDEL_HINT_HARDDEL_NOW // TODO - Once we port garbage.dm + return QDEL_HINT_HARDDEL_NOW /datum/controller/master/Shutdown() processing = FALSE diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 3075682438d..c2d04d9c939 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -49,7 +49,7 @@ datum/controller/game_controller/proc/setup_objects() admin_notice("Initializing objects", R_DEBUG) sleep(-1) for(var/atom/movable/object in world) - if(isnull(object.gcDestroyed)) + if(!QDELETED(object)) object.initialize() admin_notice("Initializing areas", R_DEBUG) @@ -80,8 +80,8 @@ datum/controller/game_controller/proc/setup_objects() admin_notice("Initializing turbolifts", R_DEBUG) for(var/thing in turbolifts) - if(!deleted(thing)) - var/obj/turbolift_map_holder/lift = thing + var/obj/turbolift_map_holder/lift = thing + if(!QDELETED(lift)) lift.initialize() sleep(-1) diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm new file mode 100644 index 00000000000..e849b64be62 --- /dev/null +++ b/code/controllers/subsystems/garbage.dm @@ -0,0 +1,354 @@ +var/datum/controller/subsystem/garbage_collector/SSgarbage + +/datum/controller/subsystem/garbage_collector + name = "Garbage" + priority = 15 + wait = 5 + flags = SS_FIRE_IN_LOBBY|SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT + + var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object + var/delslasttick = 0 // number of del()'s we've done this tick + var/gcedlasttick = 0 // number of things that gc'ed last tick + var/totaldels = 0 + var/totalgcs = 0 + + var/highest_del_time = 0 + var/highest_del_tickusage = 0 + + var/list/queue = list() // list of refID's of things that should be garbage collected + // refID's are associated with the time at which they time out and need to be manually del() + // we do this so we aren't constantly locating them and preventing them from being gc'd + + var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks + + var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened. + // the types are stored as strings + var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call + + var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT + // all types that did not respect qdel(A, force=TRUE) and returned one + // of the immortality qdel hints + var/list/noforcerespect = list() + +#ifdef TESTING + var/list/qdel_list = list() // list of all types that have been qdel()eted +#endif + +/datum/controller/subsystem/garbage_collector/New() + NEW_SS_GLOBAL(SSgarbage) + +/datum/controller/subsystem/garbage_collector/stat_entry(msg) + msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|" + msg += "GR:" + if (!(delslasttick+gcedlasttick)) + msg += "n/a|" + else + msg += "[round((gcedlasttick/(delslasttick+gcedlasttick))*100, 0.01)]%|" + + msg += "TD:[totaldels]|TG:[totalgcs]|" + if (!(totaldels+totalgcs)) + msg += "n/a|" + else + msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%" + ..(msg) + +/datum/controller/subsystem/garbage_collector/fire() + HandleToBeQueued() + if(state == SS_RUNNING) + HandleQueue() + +//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond. +//Don't attempt to optimize, not worth the effort. +/datum/controller/subsystem/garbage_collector/proc/HandleToBeQueued() + var/list/tobequeued = src.tobequeued + var/starttime = world.time + var/starttimeofday = world.timeofday + while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday) + if (MC_TICK_CHECK) + break + var/ref = tobequeued[1] + Queue(ref) + tobequeued.Cut(1, 2) + +/datum/controller/subsystem/garbage_collector/proc/HandleQueue() + delslasttick = 0 + gcedlasttick = 0 + var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del() + var/list/queue = src.queue + var/starttime = world.time + var/starttimeofday = world.timeofday + while(queue.len && starttime == world.time && starttimeofday == world.timeofday) + if (MC_TICK_CHECK) + break + var/refID = queue[1] + if (!refID) + queue.Cut(1, 2) + continue + + var/GCd_at_time = queue[refID] + if(GCd_at_time > time_to_kill) + break // Everything else is newer, skip them + queue.Cut(1, 2) + var/datum/A + A = locate(refID) + if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake + #ifdef GC_FAILURE_HARD_LOOKUP + A.find_references() + #endif + + // Something's still referring to the qdel'd object. Kill it. + var/type = A.type + testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --") + didntgc["[type]"]++ + var/time = world.timeofday + var/tick = world.tick_usage + var/ticktime = world.time + del(A) + tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100)) + + if (tick > highest_del_tickusage) + highest_del_tickusage = tick + time = world.timeofday - time + if (!time && TICK_DELTA_TO_MS(tick) > 1) + time = TICK_DELTA_TO_MS(tick)/100 + if (time > highest_del_time) + highest_del_time = time + if (time > 10) + log_game("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete)") + message_admins("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete).") + postpone(time/5) + break + ++delslasttick + ++totaldels + else + ++gcedlasttick + ++totalgcs + +/datum/controller/subsystem/garbage_collector/proc/QueueForQueuing(datum/A) + if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + tobequeued += A + A.gc_destroyed = GC_QUEUED_FOR_QUEUING + +/datum/controller/subsystem/garbage_collector/proc/Queue(datum/A) + if (!istype(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) + return + if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) + del(A) + return + var/gctime = world.time + var/refid = "\ref[A]" + + A.gc_destroyed = gctime + + if (queue[refid]) + queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list. + + queue[refid] = gctime + +/datum/controller/subsystem/garbage_collector/proc/HardQueue(datum/A) + if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + tobequeued += A + A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL + +/datum/controller/subsystem/garbage_collector/Recover() + if (istype(SSgarbage.queue)) + queue |= SSgarbage.queue + if (istype(SSgarbage.tobequeued)) + tobequeued |= SSgarbage.tobequeued + +// Should be treated as a replacement for the 'del' keyword. +// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. +/proc/qdel(datum/D, force=FALSE) + if(!D) + return +#ifdef TESTING + SSgarbage.qdel_list += D.type +#endif + if(!istype(D)) + log_debug("qdel() was passed [log_info_line(D)]. qdel() can only handle instances of (sub)type /datum.") + del(D) + else if(isnull(D.gc_destroyed)) + D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED + var/start_time = world.time + var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up. + if(world.time != start_time) + SSgarbage.sleptDestroy[D.type]++ + if(!D) + return + switch(hint) + if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. + SSgarbage.QueueForQueuing(D) + if (QDEL_HINT_IWILLGC) + D.gc_destroyed = world.time + return + if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory. + if(!force) + D.gc_destroyed = null //clear the gc variable (important!) + return + // Returning LETMELIVE after being told to force destroy + // indicates the objects Destroy() does not respect force + if(!SSgarbage.noforcerespect[D.type]) + SSgarbage.noforcerespect[D.type] = D.type + crash_with("[D.type] has been force deleted, but is \ + returning an immortal QDEL_HINT, indicating it does \ + not respect the force flag for qdel(). It has been \ + placed in the queue, further instances of this type \ + will also be queued.") + SSgarbage.QueueForQueuing(D) + if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() + SSgarbage.HardQueue(D) + if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. + del(D) + if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. + SSgarbage.QueueForQueuing(D) + #ifdef TESTING + D.find_references() + #endif + else + if(!SSgarbage.noqdelhint[D.type]) + SSgarbage.noqdelhint[D.type] = D.type + crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") + SSgarbage.QueueForQueuing(D) + else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") + +// Default implementation of clean-up code. +// This should be overridden to remove all references pointing to the object being destroyed. +// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE. +/datum/proc/Destroy(force=FALSE) + tag = null + nanomanager.close_uis(src) + return QDEL_HINT_QUEUE + +/datum/var/gc_destroyed //Time when this object was destroyed. + +#ifdef TESTING +/datum/var/running_find_references +/datum/var/last_find_references = 0 + +/datum/verb/find_refs() + set category = "Debug" + set name = "Find References" + set background = 1 + set src in world + + find_references(FALSE) + +/datum/proc/find_references(skip_alert) + running_find_references = type + if(usr && usr.client) + if(usr.client.running_find_references) + testing("CANCELLED search for references to a [usr.client.running_find_references].") + usr.client.running_find_references = null + running_find_references = null + //restart the garbage collector + SSgarbage.can_fire = 1 + SSgarbage.next_fire = world.time + world.tick_lag + return + + if(!skip_alert) + if(alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") == "No") + running_find_references = null + return + + //this keeps the garbage collector from failing to collect objects being searched for in here + SSgarbage.can_fire = 0 + + if(usr && usr.client) + usr.client.running_find_references = type + + testing("Beginning search for references to a [type].") + last_find_references = world.time + find_references_in_globals() + for(var/datum/thing in world) + DoSearchVar(thing, "WorldRef: [thing]") + testing("Completed search for references to a [type].") + if(usr && usr.client) + usr.client.running_find_references = null + running_find_references = null + + //restart the garbage collector + SSgarbage.can_fire = 1 + SSgarbage.next_fire = world.time + world.tick_lag + +/client/verb/purge_all_destroyed_objects() + set category = "Debug" + if(SSgarbage) + while(SSgarbage.queue.len) + var/datum/o = locate(SSgarbage.queue[1]) + if(istype(o) && o.gc_destroyed) + del(o) + SSgarbage.totaldels++ + SSgarbage.queue.Cut(1, 2) + +/datum/verb/qdel_then_find_references() + set category = "Debug" + set name = "qdel() then Find References" + set background = 1 + set src in world + + qdel(src) + if(!running_find_references) + find_references(TRUE) + +/client/verb/show_qdeleted() + set category = "Debug" + set name = "Show qdel() Log" + set desc = "Render the qdel() log and display it" + + var/dat = "List of things that have been qdel()eted this round

" + + var/tmplist = list() + for(var/elem in SSgarbage.qdel_list) + if(!(elem in tmplist)) + tmplist[elem] = 0 + tmplist[elem]++ + + for(var/path in tmplist) + dat += "[path] - [tmplist[path]] times
" + + usr << browse(dat, "window=qdeletedlog") + +/datum/proc/DoSearchVar(X, Xname) + if(usr && usr.client && !usr.client.running_find_references) return + if(istype(X, /datum)) + var/datum/D = X + if(D.last_find_references == last_find_references) + return + D.last_find_references = last_find_references + for(var/V in D.vars) + for(var/varname in D.vars) + var/variable = D.vars[varname] + if(variable == src) + testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]") + else if(islist(variable)) + if(src in variable) + testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]") +#ifdef GC_FAILURE_HARD_LOOKUP + for(var/I in variable) + DoSearchVar(I, TRUE) + else + DoSearchVar(variable, "[Xname]: [varname]") +#endif + else if(islist(X)) + if(src in X) + testing("Found [src.type] \ref[src] in list [Xname].") +#ifdef GC_FAILURE_HARD_LOOKUP + for(var/I in X) + DoSearchVar(I, Xname + ": list") +#else + CHECK_TICK +#endif + +//if find_references isn't working for some datum +//update this list using tools/GenerateGlobalVarAccess +/datum/proc/find_references_in_globals() + // TODO - Impement Global Variable Access + // for(var/global_var in _all_globals) + // DoSearchVar(readglobal(global_var), "Global: [global_var]") +#endif + + +/image/Destroy() + ..() + return QDEL_HINT_HARDDEL_NOW diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm index 3bf80f06d0d..d511b5d2337 100644 --- a/code/datums/helper_datums/global_iterator.dm +++ b/code/datums/helper_datums/global_iterator.dm @@ -155,4 +155,5 @@ Data storage vars: tag = null arg_list.Cut() stop() + return QDEL_HINT_LETMELIVE //Do not call ..() diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm index e87be74f531..61b24c21e9e 100644 --- a/code/datums/repositories/decls.dm +++ b/code/datums/repositories/decls.dm @@ -36,4 +36,4 @@ /decls/Destroy() crash_with("Prevented attempt to delete a decl instance: [log_info_line(src)]") - return 1 // Prevents Decl destruction \ No newline at end of file + return QDEL_HINT_LETMELIVE // Prevents Decl destruction diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index 1a7d844ca87..348d73b0b23 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -7,7 +7,9 @@ //obtain a weak reference to a datum /proc/weakref(datum/D) - if(D.gcDestroyed) + if(!istype(D)) + return + if(QDELETED(D)) return if(!D.weakref) D.weakref = new/weakref(D) @@ -22,7 +24,7 @@ /weakref/Destroy() // A weakref datum should not be manually destroyed as it is a shared resource, // rather it should be automatically collected by the BYOND GC when all references are gone. - return 0 + return QDEL_HINT_LETMELIVE /weakref/proc/resolve() var/datum/D = locate(ref) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index fe9f99b0328..64ae0722f83 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -23,16 +23,6 @@ if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING) initialize() -/atom/movable/Del() - if(isnull(gcDestroyed) && loc) - testing("GC: -- [type] was deleted via del() rather than qdel() --") - crash_with("GC: -- [type] was deleted via del() rather than qdel() --") // stick a stack trace in the runtime logs -// else if(isnull(gcDestroyed)) -// testing("GC: [type] was deleted via GC without qdel()") //Not really a huge issue but from now on, please qdel() -// else -// testing("GC: [type] was deleted via GC with qdel()") - ..() - /atom/movable/Destroy() . = ..() if(reagents) @@ -53,7 +43,7 @@ pulledby = null /atom/movable/proc/initialize() - if(!isnull(gcDestroyed)) + if(QDELETED(src)) crash_with("GC: -- [type] had initialize() called after qdel() --") /atom/movable/Bump(var/atom/A, yes) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index 6f549d15f4e..90ac43379fa 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -37,7 +37,6 @@ var/global/universe_has_ended = 0 // Apply changes when entering state /datum/universal_state/supermatter_cascade/OnEnter() set background = 1 - garbage_collector.garbage_collect = 0 world << "You are blinded by a brilliant flash of energy." world << sound('sound/effects/cascade.ogg') diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 2b4eae9a0d3..2e10ed3b1b0 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -73,7 +73,7 @@ visible_message("A red light flashes on \the [src].") return cable.use(amount) - if(deleted(cable)) + if(QDELETED(cable)) cable = null return 1 diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index b7bcc53e91e..deee8ed76b9 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -21,9 +21,9 @@ return 1 /obj/machinery/portable_atmospherics/Destroy() - qdel(air_contents) - qdel(holding) - ..() + qdel_null(air_contents) + qdel_null(holding) + . = ..() /obj/machinery/portable_atmospherics/initialize() . = ..() @@ -40,11 +40,6 @@ else update_icon() -/obj/machinery/portable_atmospherics/Destroy() - qdel(air_contents) - - ..() - /obj/machinery/portable_atmospherics/proc/StandardAirMix() return list( "oxygen" = O2STANDARD * MolesForPressure(), diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index cf7f96d52da..240610358fb 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -73,8 +73,7 @@ /obj/machinery/door/Destroy() density = 0 update_nearby_tiles() - ..() - return + . = ..() /obj/machinery/door/process() if(close_door_at && world.time >= close_door_at) diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index a134e68e3fe..fdaf284f3f2 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -150,7 +150,7 @@ var/list/floor_light_cache = list() var/area/A = get_area(src) if(A) on = 0 - ..() + . = ..() /obj/machinery/floor_light/cultify() default_light_colour = "#FF0000" diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 4abdb685d05..06228dd86f8 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -485,7 +485,7 @@ Buildable meters var/turf/T = P.loc P.level = !T.is_plating() ? 2 : 1 P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() @@ -504,7 +504,7 @@ Buildable meters var/turf/T = P.loc P.level = !T.is_plating() ? 2 : 1 P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() @@ -523,7 +523,7 @@ Buildable meters var/turf/T = P.loc P.level = !T.is_plating() ? 2 : 1 P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() @@ -542,7 +542,7 @@ Buildable meters var/turf/T = P.loc P.level = !T.is_plating() ? 2 : 1 P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() @@ -559,7 +559,7 @@ Buildable meters P.initialize_directions = pipe_dir //this var it's used to know if the pipe is bent or not P.initialize_directions_he = pipe_dir P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() @@ -594,7 +594,7 @@ Buildable meters var/turf/T = M.loc M.level = !T.is_plating() ? 2 : 1 M.initialize() - if (deleted(M)) + if (QDELETED(M)) usr << pipefailtext return 1 M.build_network() @@ -663,7 +663,7 @@ Buildable meters var/turf/T = M.loc M.level = !T.is_plating() ? 2 : 1 M.initialize() - if (deleted(M)) + if (QDELETED(M)) usr << pipefailtext return 1 M.build_network() @@ -740,7 +740,7 @@ Buildable meters P.initialize_directions = src.get_pdir() P.initialize_directions_he = src.get_hdir() P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext //"There's nothing to connect this pipe to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)" return 1 P.build_network() @@ -922,7 +922,7 @@ Buildable meters var/turf/T = P.loc P.level = !T.is_plating() ? 2 : 1 P.initialize() - if (deleted(P)) + if (QDELETED(P)) usr << pipefailtext return 1 P.build_network() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 09ff0075dbf..5d7d16a1261 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -140,18 +140,14 @@ cell = null internal_tank = null - qdel(pr_int_temp_processor) - qdel(pr_inertial_movement) - qdel(pr_give_air) - qdel(pr_internal_damage) - qdel(spark_system) - pr_int_temp_processor = null - pr_give_air = null - pr_internal_damage = null - spark_system = null + qdel_null(pr_int_temp_processor) + qdel_null(pr_inertial_movement) + qdel_null(pr_give_air) + qdel_null(pr_internal_damage) + qdel_null(spark_system) mechas_list -= src //global mech list - ..() + . = ..() //////////////////////// ////// Helpers ///////// diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 88b53bd342d..35697914ee7 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -169,7 +169,7 @@ valve_open = 0 - if(deleted(tank_one) || deleted(tank_two)) + if(QDELETED(tank_one) || QDELETED(tank_two)) return var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 84bee725588..649b5677cad 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -27,15 +27,10 @@ /obj/item/weapon/flamethrower/Destroy() - if(weldtool) - qdel(weldtool) - if(igniter) - qdel(igniter) - if(ptank) - qdel(ptank) - ..() - return - + qdel_null(weldtool) + qdel_null(igniter) + qdel_null(ptank) + . = ..() /obj/item/weapon/flamethrower/process() if(!lit) diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 31231964cbe..c778ec07a99 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -71,7 +71,7 @@ /obj/item/weapon/material/Destroy() processing_objects -= src - ..() + . = ..() /obj/item/weapon/material/apply_hit_effect() ..() diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 928246b2b5c..eab315809fe 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -10,7 +10,7 @@ /obj/structure/Destroy() if(parts) new parts(loc) - ..() + . = ..() /obj/structure/attack_hand(mob/user) if(breakable) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index ec4652901d4..35b8571a114 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -39,7 +39,7 @@ spawn(1) if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore. O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down. - ..() + . = ..() /obj/structure/lattice/ex_act(severity) switch(severity) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 3f2e0064f22..d34b210b997 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -47,6 +47,7 @@ /turf/Destroy() turfs -= src ..() + return QDEL_HINT_IWILLGC /turf/ex_act(severity) return 0 diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm index 67eefc018de..95945f82e00 100644 --- a/code/modules/admin/view_variables/helpers.dm +++ b/code/modules/admin/view_variables/helpers.dm @@ -1,8 +1,4 @@ -// Keep these two together, they *must* be defined on both -// If /client ever becomes /datum/client or similar, they can be merged -/client/proc/get_view_variables_header() - return "[src]" /datum/proc/get_view_variables_header() return "[src]" @@ -31,9 +27,6 @@ "} -// Same for these as for get_view_variables_header() above -/client/proc/get_view_variables_options() - return "" /datum/proc/get_view_variables_options() return "" diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 169ab9b6a02..1273b004a43 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -187,4 +187,4 @@ if(radio_controller) radio_controller.remove_object(src,frequency) frequency = 0 - ..() + . = ..() diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index b5596521328..5e5a1761285 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -1,4 +1,8 @@ /client + ////////////////////// + //BLACK MAGIC THINGS// + ////////////////////// + parent_type = /datum //////////////// //ADMIN THINGS// //////////////// diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index d908df1b3d9..d6f9375e984 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -181,6 +181,9 @@ clients -= src return ..() +/client/Destroy() + ..() + return QDEL_HINT_HARDDEL_NOW // here because it's similar to below diff --git a/code/modules/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm index fca9f3abafe..0dbe4c9eead 100644 --- a/code/modules/hydroponics/spreading/spreading_growth.dm +++ b/code/modules/hydroponics/spreading/spreading_growth.dm @@ -116,7 +116,7 @@ var/obj/effect/plant/child = new(get_turf(src),seed,parent) spawn(1) // This should do a little bit of animation. - if(deleted(child)) + if(QDELETED(child)) return //move out to the destination diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 60c684fd862..6d912cd8620 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -131,4 +131,4 @@ T.lighting_overlay = null T.luminosity = 1 - return ..() + . = ..() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 8a2513a54ab..9c372cf2e7d 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -443,7 +443,7 @@ var/global/list/damage_icon_parts = list() //For legacy support. /mob/living/carbon/human/regenerate_icons() ..() - if(transforming) return + if(transforming || QDELETED(src)) return update_mutations(0) update_body(0) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index c7fb1d67284..7d5c270f9b8 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -184,7 +184,7 @@ wrapped.force = 0.0 wrapped.attack(M,user) M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe. - if(deleted(wrapped) || wrapped.loc != src.loc) + if(QDELETED(wrapped) || wrapped.loc != src.loc) wrapped = null return 1 return 0 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7574bb4c774..454b58a55c1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -14,6 +14,7 @@ spellremove(src) ghostize() ..() + return QDEL_HINT_HARDDEL_NOW /mob/proc/remove_screen_obj_references() hands = null diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 80f68b979d5..be114228def 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -83,7 +83,9 @@ //This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/weapon/grab/proc/synch() +/obj/item/weapon/grab/proc/synch() //why is this needed? + if(QDELETED(src)) + return if(affecting) if(assailant.r_hand == src) hud.screen_loc = ui_rhand @@ -91,7 +93,7 @@ hud.screen_loc = ui_lhand /obj/item/weapon/grab/process() - if(gcDestroyed) // GC is trying to delete us, we'll kill our processing so we can cleanly GC + if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC return PROCESS_KILL confirm() @@ -221,6 +223,8 @@ animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) /obj/item/weapon/grab/proc/s_click(obj/screen/S) + if(QDELETED(src)) + return if(!affecting) return if(state == GRAB_UPGRADING) @@ -291,6 +295,8 @@ return 1 /obj/item/weapon/grab/attack(mob/M, mob/living/user) + if(QDELETED(src)) + return if(!affecting) return if(world.time < (last_action + 20)) @@ -336,7 +342,7 @@ /obj/item/weapon/grab/dropped() loc = null - if(!destroying) + if(!QDELETED(src)) qdel(src) /obj/item/weapon/grab/proc/reset_kill_state() diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 1390c89b291..e0809e32b59 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -67,10 +67,8 @@ brainmob.client.screen.len = null //clear the hud /obj/item/organ/internal/brain/Destroy() - if(brainmob) - qdel(brainmob) - brainmob = null - ..() + qdel_null(brainmob) + . = ..() /obj/item/organ/internal/brain/proc/transfer_identity(var/mob/living/carbon/H) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 49c1099184c..60efe9893d8 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -23,7 +23,7 @@ chemical_reagents_list[D.id] = D /datum/reagents/Destroy() - ..() + . = ..() if(chemistryProcess) chemistryProcess.active_holders -= src diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 78b33ef36ee..ccf7c62ac1e 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -488,7 +488,7 @@ var/amount_to_take = max(0,min(stack.amount,round(remaining_volume/REAGENTS_PER_SHEET))) if(amount_to_take) stack.use(amount_to_take) - if(deleted(stack)) + if(QDELETED(stack)) holdingitems -= stack beaker.reagents.add_reagent(sheet_reagents[stack.type], (amount_to_take*REAGENTS_PER_SHEET)) continue diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index 723db2ace77..f23e37f5167 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -118,8 +118,8 @@ return null /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references - ..() holder = null + . = ..() /* DEPRECATED - TODO: REMOVE EVERYWHERE */ diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index 3191442be80..ef07b03c4f5 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -210,8 +210,8 @@ /obj/item/weapon/reagent_containers/food/snacks/Destroy() if(contents) for(var/atom/movable/something in contents) - something.loc = get_turf(src) - ..() + something.dropInto(loc) + . = ..() //////////////////////////////////////////////////////////////////////////////// /// FOOD END diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 48721314294..d7ce2063b25 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -55,7 +55,7 @@ check_failure() set_opacity(1) - spawn(20) if(!deleted(src)) set_opacity(0) + spawn(20) if(!QDELETED(src)) set_opacity(0) ..() @@ -64,7 +64,7 @@ ..() check_failure() set_opacity(1) - spawn(20) if(!deleted(src)) set_opacity(0) + spawn(20) if(!QDELETED(src)) set_opacity(0) /obj/machinery/shield/ex_act(severity) switch(severity) @@ -114,7 +114,7 @@ //The shield becomes dense to absorb the blow.. purely asthetic. set_opacity(1) - spawn(20) if(!deleted(src)) set_opacity(0) + spawn(20) if(!QDELETED(src)) set_opacity(0) ..() return diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm index db445e9f509..cea2c935594 100644 --- a/code/modules/tables/tables.dm +++ b/code/modules/tables/tables.dm @@ -79,7 +79,7 @@ update_connections(1) // Update tables around us to ignore us (material=null forces no connections) for(var/obj/structure/table/T in oview(src, 1)) T.update_icon() - ..() + . = ..() /obj/structure/table/examine(mob/user) . = ..() diff --git a/polaris.dme b/polaris.dme index 942ada13d4b..9cb2df356de 100644 --- a/polaris.dme +++ b/polaris.dme @@ -37,6 +37,7 @@ #include "code\__defines\mobs.dm" #include "code\__defines\planets.dm" #include "code\__defines\process_scheduler.dm" +#include "code\__defines\qdel.dm" #include "code\__defines\research.dm" #include "code\__defines\species_languages.dm" #include "code\__defines\targeting.dm" @@ -152,7 +153,6 @@ #include "code\controllers\Processes\emergencyShuttle.dm" #include "code\controllers\Processes\event.dm" #include "code\controllers\Processes\game_master.dm" -#include "code\controllers\Processes\garbage.dm" #include "code\controllers\Processes\inactivity.dm" #include "code\controllers\Processes\lighting.dm" #include "code\controllers\Processes\machinery.dm" @@ -170,6 +170,7 @@ #include "code\controllers\Processes\vote.dm" #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" +#include "code\controllers\subsystems\garbage.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" #include "code\datums\browser.dm" From d4ebcac3f6964b1311e835d5378950c4da950919 Mon Sep 17 00:00:00 2001 From: Leshana Date: Mon, 5 Jun 2017 18:02:16 -0400 Subject: [PATCH 2/8] Update radiation system for the new garbage collector. --- code/controllers/Processes/radiation.dm | 18 +++++++++--------- code/datums/repositories/radiation.dm | 8 -------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/code/controllers/Processes/radiation.dm b/code/controllers/Processes/radiation.dm index 192acf5d5cb..71d9c60233e 100644 --- a/code/controllers/Processes/radiation.dm +++ b/code/controllers/Processes/radiation.dm @@ -15,10 +15,10 @@ /datum/controller/process/radiation/proc/sources_decay() var/list/sources = linked.sources for(var/thing in sources) - if(deleted(thing)) - sources.Remove(thing) - continue var/datum/radiation_source/S = thing + if(QDELETED(S)) + sources.Remove(S) + continue if(S.decay) S.update_rad_power(S.rad_power - config.radiation_decay_rate) if(S.rad_power <= config.radiation_lower_limit) @@ -29,22 +29,22 @@ /datum/controller/process/radiation/proc/cache_expires() var/list/resistance_cache = linked.resistance_cache for(var/thing in resistance_cache) - if(deleted(thing)) - resistance_cache.Remove(thing) - continue var/turf/T = thing + if(QDELETED(T)) + resistance_cache.Remove(T) + continue if((length(T.contents) + 1) != resistance_cache[T]) resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed. SCHECK - // Step 3 - Registered irradiatable things are checked for radiation +// Step 3 - Registered irradiatable things are checked for radiation /datum/controller/process/radiation/proc/irradiate_targets() var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway. if(length(linked.sources) > 0) for(var/thing in registered_listeners) - if(deleted(thing)) - continue var/atom/A = thing + if(QDELETED(A)) + continue var/turf/T = get_turf(thing) var/rads = linked.get_rads_at_turf(T) if(rads) diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm index 0245bb801cf..00c35324580 100644 --- a/code/datums/repositories/radiation.dm +++ b/code/datums/repositories/radiation.dm @@ -22,14 +22,6 @@ var/global/repository/radiation/radiation_repository = new() src.source_turf = null . = ..() -// TEMPORARY HACK - hard del()'ing sources is too expensive! Until we implement qdel() hints we need to override behavior here -/datum/radiation_source/finalize_qdel() - if(garbage_collector) - garbage_collector.AddTrash(src) - else - delayed_garbage |= src -// TEMPORARY HACK END - /datum/radiation_source/proc/update_rad_power(var/new_power = null) if(new_power != null && new_power != rad_power) rad_power = new_power From 733f69a4741b92f0309bb27d8c77f848531aca2b Mon Sep 17 00:00:00 2001 From: Leshana Date: Mon, 5 Jun 2017 17:55:34 -0400 Subject: [PATCH 3/8] Remove redundant cleanup of /atom/var/reagents * /datum/reagents/Destroy() does everything /datum/reagents/delete() does and more, and delete() is called only from /obj/effect/Destroy() which is *itself* redundant with its parent /atom/movable/Destroy() --- code/game/objects/effects/effect_system.dm | 5 ----- code/modules/reagents/Chemistry-Holder.dm | 6 ------ 2 files changed, 11 deletions(-) diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 2343871b95a..4c930d23cf2 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -13,11 +13,6 @@ would spawn and follow the beaker, even if it is carried or thrown. unacidable = 1//So effect are not targeted by alien acid. pass_flags = PASSTABLE | PASSGRILLE -/obj/effect/Destroy() - if(reagents) - reagents.delete() - return ..() - /datum/effect/effect/system var/number = 3 var/cardinals = 0 diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 60efe9893d8..d6347113790 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -79,12 +79,6 @@ total_volume += R.volume return -/datum/reagents/proc/delete() - for(var/datum/reagent/R in reagent_list) - R.holder = null - if(my_atom) - my_atom.reagents = null - /datum/reagents/proc/handle_reactions() if(chemistryProcess) chemistryProcess.mark_for_update(src) From 8752999a14f58515c4f9dd441311810f3b2d1bd3 Mon Sep 17 00:00:00 2001 From: Leshana Date: Mon, 5 Jun 2017 21:50:38 -0400 Subject: [PATCH 4/8] Atmospherics Destroy() cleanup - return qdel hints. --- .../components/binary_devices/binary_atmos_base.dm | 4 +--- .../components/binary_devices/pipeturbine.dm | 4 +--- code/ATMOSPHERICS/components/omni_devices/filter.dm | 2 +- code/ATMOSPHERICS/components/portables_connector.dm | 4 +--- .../components/trinary_devices/trinary_base.dm | 4 +--- code/ATMOSPHERICS/components/tvalve.dm | 4 +--- code/ATMOSPHERICS/components/unary/unary_base.dm | 4 +--- code/ATMOSPHERICS/components/unary/vent_pump.dm | 12 ++++-------- code/ATMOSPHERICS/components/unary/vent_scrubber.dm | 13 ++++--------- code/ATMOSPHERICS/components/valve.dm | 4 +--- code/ATMOSPHERICS/datum_pipeline.dm | 6 ++---- 11 files changed, 18 insertions(+), 43 deletions(-) diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 2ccfe0f2e87..ea604bc996a 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -42,7 +42,7 @@ obj/machinery/atmospherics/binary return null Destroy() - loc = null + . = ..() if(node1) node1.disconnect(src) @@ -54,8 +54,6 @@ obj/machinery/atmospherics/binary node1 = null node2 = null - ..() - initialize() if(node1 && node2) return diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm index 5768def1ec1..d0417e14dd2 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm @@ -36,7 +36,7 @@ initialize_directions = NORTH|SOUTH Destroy() - loc = null + . = ..() if(node1) node1.disconnect(src) @@ -48,8 +48,6 @@ node1 = null node2 = null - ..() - process() ..() if(anchored && !(stat&BROKEN)) diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm index b2966ef4314..feb1b5cb93c 100644 --- a/code/ATMOSPHERICS/components/omni_devices/filter.dm +++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm @@ -28,7 +28,7 @@ input = null output = null filters.Cut() - ..() + return ..() /obj/machinery/atmospherics/omni/filter/sort_ports() for(var/datum/omni_port/P in ports) diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index 4f6473fccf3..9668f7a5a3e 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -61,7 +61,7 @@ return null /obj/machinery/atmospherics/portables_connector/Destroy() - loc = null + . = ..() if(connected_device) connected_device.disconnect() @@ -72,8 +72,6 @@ node = null - ..() - /obj/machinery/atmospherics/portables_connector/initialize() if(node) return diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index fa066d978f7..717540db69d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -51,7 +51,7 @@ obj/machinery/atmospherics/trinary return null Destroy() - loc = null + . = ..() if(node1) node1.disconnect(src) @@ -67,8 +67,6 @@ obj/machinery/atmospherics/trinary node2 = null node3 = null - ..() - initialize() if(node1 && node2 && node3) return diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm index f93970a93fd..a7deb70abaa 100644 --- a/code/ATMOSPHERICS/components/tvalve.dm +++ b/code/ATMOSPHERICS/components/tvalve.dm @@ -100,7 +100,7 @@ return null /obj/machinery/atmospherics/tvalve/Destroy() - loc = null + . = ..() if(node1) node1.disconnect(src) @@ -116,8 +116,6 @@ node2 = null node3 = null - ..() - /obj/machinery/atmospherics/tvalve/proc/go_to_side() if(state) return 0 diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index 20d37b309c7..0372483c796 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -31,7 +31,7 @@ return null Destroy() - loc = null + . = ..() if(node) node.disconnect(src) @@ -39,8 +39,6 @@ node = null - ..() - initialize() if(node) return diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 00bc7e57728..c6121dea31a 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -80,7 +80,10 @@ /obj/machinery/atmospherics/unary/vent_pump/Destroy() unregister_radio(src, frequency) - ..() + if(initial_loc) + initial_loc.air_vent_info -= id_tag + initial_loc.air_vent_names -= id_tag + return ..() /obj/machinery/atmospherics/unary/vent_pump/high_volume name = "Large Air Vent" @@ -414,13 +417,6 @@ new /obj/item/pipe(loc, make_from=src) qdel(src) -/obj/machinery/atmospherics/unary/vent_pump/Destroy() - if(initial_loc) - initial_loc.air_vent_info -= id_tag - initial_loc.air_vent_names -= id_tag - ..() - return - #undef DEFAULT_PRESSURE_DELTA #undef EXTERNAL_PRESSURE_BOUND diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 06d1bce4373..3fccbb50f33 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -44,8 +44,10 @@ /obj/machinery/atmospherics/unary/vent_scrubber/Destroy() unregister_radio(src, frequency) - ..() - + if(initial_loc) + initial_loc.air_scrub_info -= id_tag + initial_loc.air_scrub_names -= id_tag + return ..() /obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) if(!check_icon_cache()) @@ -285,10 +287,3 @@ user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" else user << "You are too far away to read the gauge." - -/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() - if(initial_loc) - initial_loc.air_scrub_info -= id_tag - initial_loc.air_scrub_names -= id_tag - ..() - return diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm index 3cd77597b39..01e6e977bbc 100644 --- a/code/ATMOSPHERICS/components/valve.dm +++ b/code/ATMOSPHERICS/components/valve.dm @@ -72,7 +72,7 @@ return null /obj/machinery/atmospherics/valve/Destroy() - loc = null + . = ..() if(node1) node1.disconnect(src) @@ -84,8 +84,6 @@ node1 = null node2 = null - ..() - /obj/machinery/atmospherics/valve/proc/open() if(open) return 0 diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 44c7cd25905..0aece2d1398 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -10,14 +10,12 @@ datum/pipeline var/alert_pressure = 0 Destroy() - if(network) - qdel(network) + qdel_null(network) if(air && air.volume) temporarily_store_air() - qdel(air) - ..() + . = ..() proc/process()//This use to be called called from the pipe networks From 010cca1d44757d346b0880c485c47f7438330b67 Mon Sep 17 00:00:00 2001 From: Leshana Date: Mon, 5 Jun 2017 21:51:00 -0400 Subject: [PATCH 5/8] Pipe network datum Destroy() cleanup * Every pipe in a pipeline has a reference to the pipeline. This needs to be cleared, both so the pipeline can be gc'd, and also so the pipes don't try and keep using the qdel'd pipeline. * Same story for each pipeline in a pipe network, and each machine in a pipe network. * Pipe networks are also in the pipe_networks global list. While the controller would detect and remove it from the list on the next tick, cleaning up ourselves is the responsible thing to do. --- code/ATMOSPHERICS/datum_pipe_network.dm | 9 +++++++++ code/ATMOSPHERICS/datum_pipeline.dm | 2 ++ 2 files changed, 11 insertions(+) diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index 7fe586733ca..84414613428 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -16,6 +16,15 @@ datum/pipe_network ..() + Destroy() + pipe_networks -= src + for(var/datum/pipeline/line_member in line_members) + line_member.network = null + for(var/obj/machinery/atmospherics/normal_member in normal_members) + normal_member.reassign_network(src, null) + gases.Cut() // Do not qdel the gases, we don't own them + return ..() + proc/process() //Equalize gases amongst pipe if called for if(update) diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 0aece2d1398..0115c85c3dd 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -14,6 +14,8 @@ datum/pipeline if(air && air.volume) temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null . = ..() From e48b548f2b50236a35ee85bfb35b2708ea44a8e2 Mon Sep 17 00:00:00 2001 From: Leshana Date: Mon, 5 Jun 2017 21:55:18 -0400 Subject: [PATCH 6/8] More and more Destroy() cleanups all across the board - Return qdel hints. --- code/_helpers/unsorted.dm | 7 ++++++ code/_onclick/hud/ability_screen_objects.dm | 2 +- code/_onclick/hud/action.dm | 5 +++- code/_onclick/hud/hud.dm | 2 +- code/_onclick/hud/screen_objects.dm | 2 +- code/_onclick/hud/spell_screen_objects.dm | 4 ++-- code/controllers/communications.dm | 1 - code/controllers/voting.dm | 8 ++++++- code/datums/wires/wires.dm | 1 + code/game/machinery/doors/airlock_control.dm | 6 ++--- code/game/machinery/hologram.dm | 2 +- code/game/machinery/pipe/pipelayer.dm | 5 ++-- code/game/objects/buckling.dm | 5 ---- code/game/objects/items/devices/PDA/PDA.dm | 8 +++++-- code/game/objects/items/devices/PDA/cart.dm | 8 +++---- .../devices/communicator/communicator.dm | 2 +- .../objects/items/devices/radio/intercom.dm | 2 +- .../items/weapons/material/material_armor.dm | 2 +- code/game/objects/items/weapons/mop_deploy.dm | 12 +++++----- .../objects/items/weapons/storage/internal.dm | 2 +- .../objects/items/weapons/storage/storage.dm | 18 +++++++-------- .../game/objects/items/weapons/tanks/tanks.dm | 5 ++-- code/game/objects/structures.dm | 3 --- code/game/objects/structures/window.dm | 5 +--- .../spacesuits/rig/modules/utility.dm | 4 ++-- code/modules/lighting/lighting_corner.dm | 6 +++++ code/modules/lighting/lighting_overlay.dm | 23 +++++++++++-------- code/modules/mob/freelook/ai/eye.dm | 2 +- .../mob/freelook/ai/update_triggers.dm | 2 +- code/modules/mob/freelook/eye.dm | 6 ++++- .../mob/freelook/mask/update_triggers.dm | 2 +- code/modules/mob/freelook/update_triggers.dm | 4 ++-- code/modules/mob/freelook/visualnet.dm | 2 +- code/modules/mob/holder.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 23 ++++++++----------- code/modules/mob/living/silicon/silicon.dm | 2 +- .../mob/living/simple_animal/simple_animal.dm | 2 +- code/modules/mob/mob_grab.dm | 6 +---- code/modules/mob/modifiers.dm | 2 +- code/modules/power/breaker_box.dm | 2 +- code/modules/power/cable.dm | 2 +- code/modules/power/grid_checker.dm | 2 +- code/modules/power/lighting.dm | 2 +- code/modules/power/power.dm | 2 +- code/modules/power/singularity/collector.dm | 2 +- .../Chemistry-Reagents-Toxins.dm | 2 +- code/modules/tables/update_triggers.dm | 4 +--- code/world.dm | 3 ++- maps/RandomZLevels/stationCollision.dm | 4 ++-- maps/RandomZLevels/wildwest.dm | 2 +- maps/~map_system/maps.dm | 9 ++++++-- 51 files changed, 128 insertions(+), 113 deletions(-) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 2b75414a7e9..63bac6e134d 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1294,6 +1294,13 @@ var/mob/dview/dview_mob = new else living_mob_list -= src +/mob/dview/Destroy(var/force) + crash_with("Attempt to delete the dview_mob: [log_info_line(src)]") + if (!force) + return QDEL_HINT_LETMELIVE + global.dview_mob = new + return ..() + // call to generate a stack trace and print to runtime logs /proc/crash_with(msg) CRASH(msg) diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index 2081e011b35..d29ef9dd967 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -20,7 +20,7 @@ message_admins("ERROR: ability_master's New() was not given an owner argument. This is a bug.") /obj/screen/movable/ability_master/Destroy() - ..() + . = ..() //Get rid of the ability objects. remove_all_abilities() ability_objects.Cut() diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index 9781e9c236d..7e03151fd66 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -30,6 +30,9 @@ /datum/action/Destroy() if(owner) Remove(owner) + target = null + qdel_null(button) + return ..() /datum/action/proc/Grant(mob/living/T) if(owner) @@ -45,7 +48,7 @@ if(button) if(T.client) T.client.screen -= button - del(button) + qdel_null(button) T.actions.Remove(src) T.update_action_buttons() owner = null diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 45c6f7e277c..0f6990f6812 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -155,7 +155,7 @@ datum/hud/New(mob/owner) ..() /datum/hud/Destroy() - ..() + . = ..() grab_intent = null hurt_intent = null disarm_intent = null diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index e55323fb3a2..35ef300978c 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -46,7 +46,7 @@ var/obj/item/owner /obj/screen/item_action/Destroy() - ..() + . = ..() owner = null /obj/screen/item_action/Click() diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm index a599381730a..e79db287132 100644 --- a/code/_onclick/hud/spell_screen_objects.dm +++ b/code/_onclick/hud/spell_screen_objects.dm @@ -13,7 +13,7 @@ var/mob/spell_holder /obj/screen/movable/spell_master/Destroy() - ..() + . = ..() for(var/obj/screen/spell/spells in spell_objects) spells.spellmaster = null spell_objects.Cut() @@ -156,7 +156,7 @@ var/icon/last_charged_icon /obj/screen/spell/Destroy() - ..() + . = ..() spell = null last_charged_icon = null if(spellmaster) diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index 796fb0b6649..332ab9af527 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -314,7 +314,6 @@ var/global/datum/controller/radio/radio_controller devices_line -= null if (devices_line.len==0) devices -= devices_filter - del(devices_line) /datum/signal var/obj/source diff --git a/code/controllers/voting.dm b/code/controllers/voting.dm index aa06984f692..ba1bb23c33a 100644 --- a/code/controllers/voting.dm +++ b/code/controllers/voting.dm @@ -23,9 +23,15 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f /datum/controller/vote/New() if(vote != src) if(istype(vote)) - del(vote) + Recover() + qdel(vote) vote = src +/datum/controller/vote/Destroy() + ..() + // Tell qdel() to Del() this object. + return QDEL_HINT_HARDDEL_NOW + /datum/controller/vote/proc/process() //called by master_controller if(mode) // No more change mode votes after the game has started. diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 066c357a22e..c1ee225dce5 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -48,6 +48,7 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" /datum/wires/Destroy() holder = null + signallers.Cut() return ..() /datum/wires/proc/GenerateWires() diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index 805bb7dba9b..ba8aa85b2c7 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -145,7 +145,7 @@ obj/machinery/door/airlock/New() obj/machinery/door/airlock/Destroy() if(frequency && radio_controller) radio_controller.remove_object(src,frequency) - ..() + return ..() obj/machinery/airlock_sensor icon = 'icons/obj/airlock_machines.dmi' @@ -220,7 +220,7 @@ obj/machinery/airlock_sensor/New() obj/machinery/airlock_sensor/Destroy() if(radio_controller) radio_controller.remove_object(src,frequency) - ..() + return ..() obj/machinery/airlock_sensor/airlock_interior command = "cycle_interior" @@ -292,7 +292,7 @@ obj/machinery/access_button/New() obj/machinery/access_button/Destroy() if(radio_controller) radio_controller.remove_object(src, frequency) - ..() + return ..() obj/machinery/access_button/airlock_interior frequency = 1379 diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 158b718e833..e0156196d6a 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -202,7 +202,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/hologram/holopad/Destroy() for (var/mob/living/silicon/ai/master in masters) clear_holo(master) - ..() + return ..() /* Holographic project of everything else. diff --git a/code/game/machinery/pipe/pipelayer.dm b/code/game/machinery/pipe/pipelayer.dm index 40726825a19..c960879e540 100644 --- a/code/game/machinery/pipe/pipelayer.dm +++ b/code/game/machinery/pipe/pipelayer.dm @@ -23,9 +23,8 @@ update_icon() /obj/machinery/pipelayer/Destroy() - qdel(W) - W = null - ..() + qdel_null(W) + . = ..() /obj/machinery/pipelayer/RefreshParts() var/mb_rating = 0 diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 8dfe6270308..c963a902bb4 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -21,11 +21,6 @@ if(can_buckle && istype(M)) user_buckle_mob(M, user) -//Cleanup -/obj/Del() - unbuckle_mob() - return ..() - /obj/Destroy() unbuckle_mob() return ..() diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 88f69d80111..7f1d83cc4a8 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -1470,8 +1470,12 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/Destroy() PDAs -= src if (src.id && prob(90)) //IDs are kept in 90% of the cases - src.id.loc = get_turf(src.loc) - ..() + src.id.forceMove(get_turf(src.loc)) + else + qdel_null(src.id) + qdel_null(src.cartridge) + qdel_null(src.pai) + return ..() /obj/item/device/pda/clown/Crossed(AM as mob|obj) //Clown PDA is slippery. if (istype(AM, /mob/living)) diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 2c4368736e8..c53ba712db4 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -78,6 +78,10 @@ var/list/civilian_cartridges = list( var/message2 var/list/stored_data = list() +/obj/item/weapon/cartridge/Destroy() + qdel_null(radio) + return ..() + /obj/item/weapon/cartridge/engineering name = "\improper Power-ON cartridge" icon_state = "cart-e" @@ -164,10 +168,6 @@ var/list/civilian_cartridges = list( radio = new /obj/item/radio/integrated/signal(src) ..() -/obj/item/weapon/cartridge/signal/Destroy() - qdel(radio) - ..() - /obj/item/weapon/cartridge/quartermaster name = "\improper Space Parts & Space Vendors cartridge" desc = "Perfect for the Quartermaster on the go!" diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index 018be587055..8630d6a6026 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -803,7 +803,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list() if(exonet) exonet.remove_address() exonet = null - ..() + return ..() // Proc: update_icon() // Parameters: None diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 8e9a72d2038..106d1448344 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -96,7 +96,7 @@ /obj/item/device/radio/intercom/Destroy() processing_objects -= src - ..() + return ..() /obj/item/device/radio/intercom/attack_ai(mob/user as mob) src.add_fingerprint(user) diff --git a/code/game/objects/items/weapons/material/material_armor.dm b/code/game/objects/items/weapons/material/material_armor.dm index 1041fb5d1fd..990ae376591 100644 --- a/code/game/objects/items/weapons/material/material_armor.dm +++ b/code/game/objects/items/weapons/material/material_armor.dm @@ -42,7 +42,7 @@ Protectiveness | Armor % /obj/item/clothing/Destroy() processing_objects -= src - ..() + return ..() /obj/item/clothing/get_material() return material diff --git a/code/game/objects/items/weapons/mop_deploy.dm b/code/game/objects/items/weapons/mop_deploy.dm index 8baab875285..30a6991a531 100644 --- a/code/game/objects/items/weapons/mop_deploy.dm +++ b/code/game/objects/items/weapons/mop_deploy.dm @@ -26,7 +26,7 @@ T.dirt = 0 for(var/obj/effect/O in src) if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay)) - del(O) + qdel(O) /* //Reagent code changed at some point and the below doesn't work. To be fixed later. source.reagents.reaction(src, TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. source.reagents.remove_any(1) //reaction() doesn't use up the reagents @@ -47,16 +47,16 @@ return ..() -/obj/item/weapon/mop_deploy/Del() +/obj/item/weapon/mop_deploy/Destroy() processing_objects -= src - ..() + . = ..() /obj/item/weapon/mop_deploy/attack_self(mob/user as mob) user.drop_from_inventory(src) - spawn(1) if(src) del(src) + spawn(1) if(!QDELETED(src)) qdel(src) /obj/item/weapon/mop_deploy/dropped() - spawn(1) if(src) del(src) + spawn(1) if(!QDELETED(src)) qdel(src) /obj/item/weapon/mop_deploy/process() if(!creator || loc != creator || !creator.item_is_in_hands(src)) @@ -71,4 +71,4 @@ host.pinned -= src host.embedded -= src host.drop_from_inventory(src) - spawn(1) if(src) del(src) \ No newline at end of file + spawn(1) if(!QDELETED(src)) qdel(src) \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm index 8d1898fe4b2..1f1a5f6963e 100644 --- a/code/game/objects/items/weapons/storage/internal.dm +++ b/code/game/objects/items/weapons/storage/internal.dm @@ -12,7 +12,7 @@ /obj/item/weapon/storage/internal/Destroy() master_item = null - ..() + . = ..() /obj/item/weapon/storage/internal/attack_hand() return //make sure this is never picked up diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 3846a44d2b3..db55e95b44d 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -38,15 +38,15 @@ /obj/item/weapon/storage/Destroy() close_all() - qdel(boxes) - qdel(src.storage_start) - qdel(src.storage_continue) - qdel(src.storage_end) - qdel(src.stored_start) - qdel(src.stored_continue) - qdel(src.stored_end) - qdel(closer) - ..() + qdel_null(boxes) + qdel_null(src.storage_start) + qdel_null(src.storage_continue) + qdel_null(src.storage_end) + qdel_null(src.stored_start) + qdel_null(src.stored_continue) + qdel_null(src.stored_end) + qdel_null(closer) + . = ..() /obj/item/weapon/storage/MouseDrop(obj/over_object as obj) if(!canremove) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 57a5cf06a45..b8daa05d7f3 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -44,8 +44,7 @@ var/list/global/tank_gauge_cache = list() return /obj/item/weapon/tank/Destroy() - if(air_contents) - qdel(air_contents) + qdel_null(air_contents) processing_objects.Remove(src) @@ -53,7 +52,7 @@ var/list/global/tank_gauge_cache = list() var/obj/item/device/transfer_valve/TTV = loc TTV.remove_tank(src) - ..() + return ..() /obj/item/weapon/tank/examine(mob/user) . = ..(user, 0) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index eab315809fe..b41743799bf 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -49,9 +49,6 @@ if(climbable) verbs += /obj/structure/proc/climb_on -/obj/structure/Destroy() - ..() - /obj/structure/proc/climb_on() set name = "Climb structure" diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 809dd008a48..9175ddc28c7 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -355,12 +355,9 @@ density = 0 update_nearby_tiles() var/turf/location = loc - loc = null + . = ..() for(var/obj/structure/window/W in orange(location, 1)) W.update_icon() - loc = location - ..() - /obj/structure/window/Move() var/ini_dir = dir diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index cec738479ee..583f491d5e6 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -477,7 +477,7 @@ for(var/obj/item/weapon/mop_deploy/blade in M.contents) M.drop_from_inventory(blade) - del(blade) + qdel(blade) //Space Cleaner Launcher @@ -520,7 +520,7 @@ user << "You slot \the [input_device] into the suit module." user.drop_from_inventory(input_device) - del(input_device) + qdel(input_device) accepted_item.charges++ return 1 diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 63ce1232535..088c53e5216 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -131,3 +131,9 @@ /datum/lighting_corner/dummy/New() return + +/datum/lighting_corner/Destroy(var/force) + if (!force) + return QDEL_HINT_LETMELIVE + crash_with("Who decided to force qdel() a lighting corner? Why did you do this?") + return ..() diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 6d912cd8620..690eba93264 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -39,7 +39,7 @@ log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!") else log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!") - qdel(src) + qdel(src, force=TRUE) return // To the future coder who sees this and thinks @@ -121,14 +121,17 @@ /atom/movable/lighting_overlay/throw_at() return 0 -/atom/movable/lighting_overlay/Destroy() - total_lighting_overlays-- - global.lighting_update_overlays -= src - global.lighting_update_overlays_old -= src +/atom/movable/lighting_overlay/Destroy(var/force) + if (force) + total_lighting_overlays-- + global.lighting_update_overlays -= src + global.lighting_update_overlays_old -= src - var/turf/T = loc - if(istype(T)) - T.lighting_overlay = null - T.luminosity = 1 + var/turf/T = loc + if(istype(T)) + T.lighting_overlay = null + T.luminosity = 1 - . = ..() + return ..() + else + return QDEL_HINT_LETMELIVE diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm index 62f0d931520..a1c28a4af34 100644 --- a/code/modules/mob/freelook/ai/eye.dm +++ b/code/modules/mob/freelook/ai/eye.dm @@ -59,7 +59,7 @@ /mob/living/silicon/ai/Destroy() destroy_eyeobj() - ..() + return ..() /atom/proc/move_camera_by_click() if(istype(usr, /mob/living/silicon/ai)) diff --git a/code/modules/mob/freelook/ai/update_triggers.dm b/code/modules/mob/freelook/ai/update_triggers.dm index f4fea0aa58f..5c21c6784da 100644 --- a/code/modules/mob/freelook/ai/update_triggers.dm +++ b/code/modules/mob/freelook/ai/update_triggers.dm @@ -59,7 +59,7 @@ /obj/machinery/camera/Destroy() clear_all_networks() cameranet.cameras -= src - ..() + return ..() // Mobs /mob/living/silicon/ai/rejuvenate() diff --git a/code/modules/mob/freelook/eye.dm b/code/modules/mob/freelook/eye.dm index 0c38ad8033c..ff8681d128a 100644 --- a/code/modules/mob/freelook/eye.dm +++ b/code/modules/mob/freelook/eye.dm @@ -38,7 +38,11 @@ mob/observer/eye/Destroy() qdel(ghostimage) ghostimage = null updateallghostimages() - ..() + if(owner) + if(owner.eyeobj == src) + owner.eyeobj = null + owner = null + . = ..() /mob/observer/eye/Move(n, direct) if(owner == src) diff --git a/code/modules/mob/freelook/mask/update_triggers.dm b/code/modules/mob/freelook/mask/update_triggers.dm index 11423671640..1dd520e7f1d 100644 --- a/code/modules/mob/freelook/mask/update_triggers.dm +++ b/code/modules/mob/freelook/mask/update_triggers.dm @@ -25,7 +25,7 @@ /mob/living/Destroy() cultnet.updateVisibility(src, 0) - ..() + return ..() /mob/living/rejuvenate() var/was_dead = stat == DEAD diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm index 8408083ba8b..482025426eb 100644 --- a/code/modules/mob/freelook/update_triggers.dm +++ b/code/modules/mob/freelook/update_triggers.dm @@ -15,7 +15,7 @@ /turf/simulated/Destroy() updateVisibility(src) - ..() + return ..() /turf/simulated/New() ..() @@ -26,7 +26,7 @@ /obj/structure/Destroy() updateVisibility(src) - ..() + return ..() /obj/structure/New() ..() diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm index 24ad252eda1..0d53d1e48a5 100644 --- a/code/modules/mob/freelook/visualnet.dm +++ b/code/modules/mob/freelook/visualnet.dm @@ -14,7 +14,7 @@ /datum/visualnet/Destroy() visual_nets -= src - ..() + return ..() // Checks if a chunk has been Generated in x, y, z. /datum/visualnet/proc/chunkGenerated(x, y, z) diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index 1998e7fe650..5d0796f77c4 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -27,7 +27,7 @@ var/list/holder_mob_icon_cache = list() /obj/item/weapon/holder/Destroy() processing_objects.Remove(src) - ..() + return ..() /obj/item/weapon/holder/process() update_state() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 8583521f854..a22011e8abf 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -215,20 +215,15 @@ var/list/ai_verbs_hidden = list( // For why this exists, refer to https://xkcd.c /mob/living/silicon/ai/Destroy() ai_list -= src - qdel(eyeobj) - eyeobj = null - - qdel(psupply) - psupply = null - - qdel(aiMulti) - aiMulti = null - - qdel(aiRadio) - aiRadio = null - - qdel(aiCamera) - aiCamera = null + qdel_null(announcement) + qdel_null(eyeobj) + qdel_null(psupply) + qdel_null(aiPDA) + qdel_null(aiCommunicator) + qdel_null(aiMulti) + qdel_null(aiRadio) + qdel_null(aiCamera) + hack = null return ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 40b18525282..b74b7d6cf79 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -41,7 +41,7 @@ silicon_mob_list -= src for(var/datum/alarm_handler/AH in alarm_manager.all_handlers) AH.unregister_alarm(src) - ..() + return ..() /mob/living/silicon/proc/init_id() if(idcard) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 3ad6260813a..0e121e1481d 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -195,7 +195,7 @@ friends.Cut() //This one is not walk_list.Cut() languages.Cut() - ..() + return ..() //Client attached /mob/living/simple_animal/Login() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index be114228def..890ff2e8732 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -393,9 +393,6 @@ /obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) return mob_size_difference(A.mob_size, B.mob_size) -/obj/item/weapon/grab - var/destroying = 0 - /obj/item/weapon/grab/Destroy() animate(affecting, pixel_x = 0, pixel_y = 0, 4, 1, LINEAR_EASING) affecting.layer = 4 @@ -408,5 +405,4 @@ assailant = null qdel(hud) hud = null - destroying = 1 // stops us calling qdel(src) on dropped() - ..() + return ..() diff --git a/code/modules/mob/modifiers.dm b/code/modules/mob/modifiers.dm index f67ffc374b6..0b39c9e2754 100644 --- a/code/modules/mob/modifiers.dm +++ b/code/modules/mob/modifiers.dm @@ -73,7 +73,7 @@ /mob/living/Destroy() remove_all_modifiers(TRUE) - ..() + return ..() // Called by Life(). /mob/living/proc/handle_modifiers() diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index e133f82e6e2..2b10a9017d0 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -20,7 +20,7 @@ var/update_locked = 0 /obj/machinery/power/breakerbox/Destroy() - ..() + . = ..() for(var/datum/nano_module/rcon/R in world) R.FindDevices() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 2d53277590e..ebfa5330864 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -108,7 +108,7 @@ var/list/possible_cable_coil_colours = list( if(powernet) cut_cable_from_powernet() // update the powernets cable_list -= src //remove it from global cable list - ..() // then go ahead and delete the cable + return ..() // then go ahead and delete the cable // Ghost examining the cable -> tells him the power /obj/structure/cable/attack_ghost(mob/user) diff --git a/code/modules/power/grid_checker.dm b/code/modules/power/grid_checker.dm index 4d42d4ca3f2..b8a123e02fa 100644 --- a/code/modules/power/grid_checker.dm +++ b/code/modules/power/grid_checker.dm @@ -28,7 +28,7 @@ /obj/machinery/power/grid_checker/Destroy() qdel(wires) wires = null - ..() + return ..() /obj/machinery/power/grid_checker/update_icon() if(power_failing) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 09921cf58ba..850d7a84aa0 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -248,7 +248,7 @@ if(A) on = 0 // A.update_lights() - ..() + return ..() /obj/machinery/light/update_icon() diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 13213c2e7e3..76320db4796 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -19,7 +19,7 @@ disconnect_from_network() disconnect_terminal() - ..() + return ..() /////////////////////////////// // General procedures diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 47a9c08923e..53e3dc29c98 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -23,7 +23,7 @@ var/global/list/rad_collectors = list() /obj/machinery/power/rad_collector/Destroy() rad_collectors -= src - ..() + return ..() /obj/machinery/power/rad_collector/process() //so that we don't zero out the meter if the SM is processed first. diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 12540c2d049..b6c45475cc3 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -205,7 +205,7 @@ if(holder && holder.my_atom && ismob(holder.my_atom)) var/mob/M = holder.my_atom M.status_flags &= ~FAKEDEATH - ..() + return ..() /datum/reagent/toxin/fertilizer //Reagents used for plant fertilizers. name = "fertilizer" diff --git a/code/modules/tables/update_triggers.dm b/code/modules/tables/update_triggers.dm index a90e3eeb8a2..b7c087f9c0f 100644 --- a/code/modules/tables/update_triggers.dm +++ b/code/modules/tables/update_triggers.dm @@ -6,12 +6,10 @@ /obj/structure/window/Destroy() var/oldloc = loc - loc=null + . = ..() for(var/obj/structure/table/T in view(oldloc, 1)) T.update_connections() T.update_icon() - loc=oldloc - ..() /obj/structure/window/Move() var/oldloc = loc diff --git a/code/world.dm b/code/world.dm index 8d51f84b47b..a11d875c51f 100644 --- a/code/world.dm +++ b/code/world.dm @@ -26,7 +26,8 @@ var/global/datum/global_init/init = new () qdel(src) //we're done /datum/global_init/Destroy() - return 1 + global.init = null + return 2 // QDEL_HINT_IWILLGC /world mob = /mob/new_player diff --git a/maps/RandomZLevels/stationCollision.dm b/maps/RandomZLevels/stationCollision.dm index fa20f440ec8..0ebf12f169a 100644 --- a/maps/RandomZLevels/stationCollision.dm +++ b/maps/RandomZLevels/stationCollision.dm @@ -54,7 +54,7 @@ B.item_state = "melted" new /obj/item/weapon/paper/sc_safehint_paper_bible(B) new /obj/item/weapon/pen(B) - del(src) + qdel(src) /* * Guns - I'm making these specifically so that I dont spawn a pile of fully loaded weapons on the map. @@ -189,7 +189,7 @@ var/sc_safecode5 = "[rand(0,9)]" else if(istype(A,/obj/)) var/obj/O = A O.ex_act(1.0) - if(O) del(O) + if(O) qdel(O) else if(isturf(A)) var/turf/T = A if(T.intact) diff --git a/maps/RandomZLevels/wildwest.dm b/maps/RandomZLevels/wildwest.dm index b3ae83b9d7b..51ac498fb24 100644 --- a/maps/RandomZLevels/wildwest.dm +++ b/maps/RandomZLevels/wildwest.dm @@ -133,7 +133,7 @@ s.start() explosion(mob, 1, 0, 0, 0) spawn(0) - del(src) + qdel(src) /obj/effect/meatgrinder name = "Meat Grinder" diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm index 1a575b3578b..65797ebafaa 100644 --- a/maps/~map_system/maps.dm +++ b/maps/~map_system/maps.dm @@ -161,5 +161,10 @@ var/list/all_maps = list() if(transit_chance) map.accessible_z_levels["[z]"] = transit_chance -/datum/map_z_level/Destroy() - return TRUE // No. +/datum/map_z_level/Destroy(var/force) + crash_with("Attempt to delete a map_z_level instance [log_info_line(src)]") + if(!force) + return QDEL_HINT_LETMELIVE // No. + if (using_map.zlevels["[z]"] == src) + using_map.zlevels -= "[z]" + return ..() From d6b68dabdb5cec31ebc4de1d8a60f63956d6d904 Mon Sep 17 00:00:00 2001 From: Leshana Date: Wed, 7 Jun 2017 19:45:01 -0400 Subject: [PATCH 7/8] Standardize MC subsystem initialization boilerplate * Based on https://github.com/tgstation/tgstation/pull/25386 * Create macro for defining new subsystem types. * Create PreInit proc to handle setup that needs to be done before the map loads * Rename garbage_controller subsystem to garbage so it matches its global SSgarbage variable name. --- code/__defines/MC.dm | 8 ++++++++ code/controllers/subsystem.dm | 9 ++++++++- code/controllers/subsystems/garbage.dm | 26 ++++++++++++-------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm index 90724a2f984..08b8d223ec7 100644 --- a/code/__defines/MC.dm +++ b/code/__defines/MC.dm @@ -51,3 +51,11 @@ #define SS_PAUSED 3 //paused by mc_tick_check #define SS_SLEEPING 4 //fire() slept. #define SS_PAUSING 5 //in the middle of pausing + +// Standard way to define a global subsystem, keep boilerplate organized here! +#define SUBSYSTEM_DEF(X) var/datum/controller/subsystem/##X/SS##X;\ +/datum/controller/subsystem/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/##X diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index ce9558ca4f5..ff81fdc23e2 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -27,8 +27,15 @@ var/datum/controller/subsystem/queue_next var/datum/controller/subsystem/queue_prev -// Used to initialize the subsystem BEFORE the map has loaded +//Do not override /datum/controller/subsystem/New() + return + +// Used to initialize the subsystem BEFORE the map has loaded +// Called AFTER Recover if that is called +// Prefer to use Initialize if possible +/datum/controller/subsystem/proc/PreInit() + return //This is used so the mc knows when the subsystem sleeps. do not override. /datum/controller/subsystem/proc/ignite(resumed = 0) diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index e849b64be62..117786c40cc 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -1,6 +1,7 @@ -var/datum/controller/subsystem/garbage_collector/SSgarbage - -/datum/controller/subsystem/garbage_collector +// +// Garbage Collector Subsystem - Implements qdel() and the GC queue +// +SUBSYSTEM_DEF(garbage) name = "Garbage" priority = 15 wait = 5 @@ -34,10 +35,7 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage var/list/qdel_list = list() // list of all types that have been qdel()eted #endif -/datum/controller/subsystem/garbage_collector/New() - NEW_SS_GLOBAL(SSgarbage) - -/datum/controller/subsystem/garbage_collector/stat_entry(msg) +/datum/controller/subsystem/garbage/stat_entry(msg) msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|" msg += "GR:" if (!(delslasttick+gcedlasttick)) @@ -52,14 +50,14 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%" ..(msg) -/datum/controller/subsystem/garbage_collector/fire() +/datum/controller/subsystem/garbage/fire() HandleToBeQueued() if(state == SS_RUNNING) HandleQueue() //If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond. //Don't attempt to optimize, not worth the effort. -/datum/controller/subsystem/garbage_collector/proc/HandleToBeQueued() +/datum/controller/subsystem/garbage/proc/HandleToBeQueued() var/list/tobequeued = src.tobequeued var/starttime = world.time var/starttimeofday = world.timeofday @@ -70,7 +68,7 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage Queue(ref) tobequeued.Cut(1, 2) -/datum/controller/subsystem/garbage_collector/proc/HandleQueue() +/datum/controller/subsystem/garbage/proc/HandleQueue() delslasttick = 0 gcedlasttick = 0 var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del() @@ -124,12 +122,12 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage ++gcedlasttick ++totalgcs -/datum/controller/subsystem/garbage_collector/proc/QueueForQueuing(datum/A) +/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A) if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) tobequeued += A A.gc_destroyed = GC_QUEUED_FOR_QUEUING -/datum/controller/subsystem/garbage_collector/proc/Queue(datum/A) +/datum/controller/subsystem/garbage/proc/Queue(datum/A) if (!istype(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) return if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) @@ -145,12 +143,12 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage queue[refid] = gctime -/datum/controller/subsystem/garbage_collector/proc/HardQueue(datum/A) +/datum/controller/subsystem/garbage/proc/HardQueue(datum/A) if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) tobequeued += A A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL -/datum/controller/subsystem/garbage_collector/Recover() +/datum/controller/subsystem/garbage/Recover() if (istype(SSgarbage.queue)) queue |= SSgarbage.queue if (istype(SSgarbage.tobequeued)) From 60ffe4b00f13fc618bc44c73b82c921482d1d25e Mon Sep 17 00:00:00 2001 From: Leshana Date: Wed, 7 Jun 2017 23:27:27 -0400 Subject: [PATCH 8/8] Ports garbage collector improvements and bugfixes from tg * Writes a log of qdel() problems in a condensed format at the end of each round - tgstation/tgstation/pull/26135 * Moves hard dels into their own proc so that it can be profiled more easily, and uses it for the various types. Includes bugfixes too * https://github.com/tgstation/tgstation/pull/26210 * https://github.com/tgstation/tgstation/pull/26570 * Fix a bug with qdeling non-datums - https://github.com/tgstation/tgstation/pull/27434 * Fix typo - https://github.com/tgstation/tgstation/pull/27730 --- code/controllers/subsystems/garbage.dm | 75 +++++++++++++++++--------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 117786c40cc..b07337e8035 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -50,10 +50,28 @@ SUBSYSTEM_DEF(garbage) msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%" ..(msg) +/datum/controller/subsystem/garbage/Shutdown() + //Adds the del() log to world.log in a format condensable by the runtime condenser found in tools + if(didntgc.len || sleptDestroy.len) + var/list/dellog = list() + for(var/path in didntgc) + dellog += "Path : [path] \n" + dellog += "Failures : [didntgc[path]] \n" + if(path in sleptDestroy) + dellog += "Sleeps : [sleptDestroy[path]] \n" + sleptDestroy -= path + for(var/path in sleptDestroy) + dellog += "Path : [path] \n" + dellog += "Sleeps : [sleptDestroy[path]] \n" + log_misc(dellog.Join()) + /datum/controller/subsystem/garbage/fire() HandleToBeQueued() if(state == SS_RUNNING) HandleQueue() + + if (state == SS_PAUSED) //make us wait again before the next run. + state = SS_RUNNING //If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond. //Don't attempt to optimize, not worth the effort. @@ -98,24 +116,9 @@ SUBSYSTEM_DEF(garbage) var/type = A.type testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --") didntgc["[type]"]++ - var/time = world.timeofday - var/tick = world.tick_usage - var/ticktime = world.time - del(A) - tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100)) + + HardDelete(A) - if (tick > highest_del_tickusage) - highest_del_tickusage = tick - time = world.timeofday - time - if (!time && TICK_DELTA_TO_MS(tick) > 1) - time = TICK_DELTA_TO_MS(tick)/100 - if (time > highest_del_time) - highest_del_time = time - if (time > 10) - log_game("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete)") - message_admins("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete).") - postpone(time/5) - break ++delslasttick ++totaldels else @@ -128,10 +131,10 @@ SUBSYSTEM_DEF(garbage) A.gc_destroyed = GC_QUEUED_FOR_QUEUING /datum/controller/subsystem/garbage/proc/Queue(datum/A) - if (!istype(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) + if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) return if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) - del(A) + HardDelete(A) return var/gctime = world.time var/refid = "\ref[A]" @@ -143,6 +146,30 @@ SUBSYSTEM_DEF(garbage) queue[refid] = gctime +//this is purely to seperate things profile wise. +/datum/controller/subsystem/garbage/proc/HardDelete(datum/A) + var/time = world.timeofday + var/tick = world.tick_usage + var/ticktime = world.time + + var/type = A.type + var/refID = "\ref[A]" + + del(A) + + tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100)) + if (tick > highest_del_tickusage) + highest_del_tickusage = tick + time = world.timeofday - time + if (!time && TICK_DELTA_TO_MS(tick) > 1) + time = TICK_DELTA_TO_MS(tick)/100 + if (time > highest_del_time) + highest_del_time = time + if (time > 10) + log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)") + message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") + postpone(time/5) + /datum/controller/subsystem/garbage/proc/HardQueue(datum/A) if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) tobequeued += A @@ -157,15 +184,13 @@ SUBSYSTEM_DEF(garbage) // Should be treated as a replacement for the 'del' keyword. // Datums passed to this will be given a chance to clean up references to allow the GC to collect them. /proc/qdel(datum/D, force=FALSE) - if(!D) + if(!istype(D)) + del(D) return #ifdef TESTING SSgarbage.qdel_list += D.type #endif - if(!istype(D)) - log_debug("qdel() was passed [log_info_line(D)]. qdel() can only handle instances of (sub)type /datum.") - del(D) - else if(isnull(D.gc_destroyed)) + if(isnull(D.gc_destroyed)) D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED var/start_time = world.time var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up. @@ -196,7 +221,7 @@ SUBSYSTEM_DEF(garbage) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() SSgarbage.HardQueue(D) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. - del(D) + SSgarbage.HardDelete(D) if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. SSgarbage.QueueForQueuing(D) #ifdef TESTING