mirror of
https://github.com/fulpstation/fulpstation.git
synced 2025-12-10 10:01:40 +00:00
First pass at a qdel() garbage collection system for tgstation
Works pretty well. If it can't GC something, it'll just del() it and be done. Speed is amazing, holy shit. New procs you should be aware of: qdel(atom/movable) - sets up an object for garbage collection. Call this rather than del(atom/movable). atom/movable/Destroy() - called right before the object is GC'd, so it still has a loc. Also called if the object is del()'d. new controller - garbage.dm has all the details on this. Basically it nulls all references on GC'd objects and force del() them if necessary. Generally speaking, objects should use Destroy() for behavior prior to deletion rather than Del(). You should also always call the parent so the object gets the right gc_destroyed var set. ISSUES: Tries to GC mobs atm. This actually works for new players, not so much for humans/monkies/simple_animals/anything. I'm guessing it needs to clear out their mind and HUD and maybe other things. Gibbing is really bugged. It works, but the overlays just sit there for awhile and ugh. I'm very tempted just to del() mob/living and mob/camera and call it a day. qdel() equipment doesn't unequip the item. Pipes don't generally GC correctly. Debugging suggests they get referenced in many pipenets and that isn't cleared properly. However some do work fine. Need assistance here. Bots don't GC, probably in the radio controller. Lots of other shit doesn't GC but it's hard to find them because of the pipe spam. I think I'm calling Destroy() twice by accident.
This commit is contained in:
@@ -139,7 +139,7 @@ atom/movable/New()
|
||||
light = new(src)
|
||||
|
||||
//Objects with opacity will trigger nearby lights to update at next lighting process.
|
||||
atom/movable/Del()
|
||||
atom/movable/Destroy()
|
||||
if(opacity)
|
||||
if(isturf(loc))
|
||||
if(loc:lighting_lumcount > 1)
|
||||
|
||||
107
code/controllers/garbage.dm
Normal file
107
code/controllers/garbage.dm
Normal file
@@ -0,0 +1,107 @@
|
||||
#define GC_COLLECTIONS_PER_TICK 100 // maybe make this a config option at some point
|
||||
#define GC_COLLECTION_TIMEOUT 300 // deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
var/datum/controller/garbage_collector/garbage = new()
|
||||
var/list/uncollectable_vars=list(
|
||||
// "bounds", // bounds and its ilk are all caught by the issaved() check later on
|
||||
"contents",
|
||||
"gc_destroyed",
|
||||
"gender", // Causes runtimes if the logging is on
|
||||
"parent",
|
||||
"step_size",
|
||||
)
|
||||
// These are the vars left from /vg/'s implementation that aren't const or global
|
||||
// I dunno how many are necessary but since most of these are numbers anyways, I don't care.
|
||||
|
||||
/datum/controller/garbage_collector
|
||||
var/list/queue = list() // list of things that have yet to have all their vars nulled out
|
||||
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
|
||||
|
||||
/datum/controller/garbage_collector/proc/AddTrash(var/atom/movable/A)
|
||||
if(!istype(A))
|
||||
return
|
||||
// testing("GC: AddTrash([A.type])")
|
||||
queue |= A
|
||||
|
||||
/datum/controller/garbage_collector/proc/Pop()
|
||||
var/atom/movable/A = queue[1]
|
||||
if(!A)
|
||||
queue.Cut(1, 2)
|
||||
// testing("GC: Pop() given null")
|
||||
return
|
||||
if(!istype(A,/atom/movable))
|
||||
// testing("GC: -- Pop() given [A.type] --")
|
||||
queue.Cut(1, 2)
|
||||
del(A)
|
||||
return
|
||||
for(var/vname in A.vars)
|
||||
if(!issaved(A.vars[vname]))
|
||||
// testing("GC: Skipping [vname] in [A.type]: it's const|global|tmp")
|
||||
continue
|
||||
if(vname in uncollectable_vars)
|
||||
// testing("GC: Skipping [vname] in [A.type]: it's uncollectable")
|
||||
continue
|
||||
// testing("GC: Unsetting [vname] in [A.type]")
|
||||
A.vars[vname] = null
|
||||
// testing("GC: Pop([A.type]) - destroyed\[\ref[A]\] = [A.gc_destroyed] current time: [world.timeofday]")
|
||||
destroyed["\ref[A]"] = A.gc_destroyed
|
||||
queue.Cut(1, 2)
|
||||
|
||||
/datum/controller/garbage_collector/proc/process()
|
||||
var/i = 1
|
||||
while(queue.len && i <= GC_COLLECTIONS_PER_TICK)
|
||||
Pop()
|
||||
i++
|
||||
i = 1
|
||||
var/time_to_kill = world.timeofday - GC_COLLECTION_TIMEOUT // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
|
||||
if(time_to_kill < 1) // Within the first GC_COLLECTION_TIMEOUT deciseconds of midnight
|
||||
time_to_kill += MIDNIGHT_ROLLOVER
|
||||
while(i <= destroyed.len && i <= GC_COLLECTIONS_PER_TICK)
|
||||
var/refID = destroyed[i]
|
||||
var/GCd_at_time = destroyed[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
// testing("GC: [refID] not old enough, breaking at [world.timeofday] for [time_to_kill - GCd_at_time] deciseconds")
|
||||
i++
|
||||
break // Everything else is newer, skip them
|
||||
var/atom/A = locate(refID)
|
||||
// testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.timeofday]")
|
||||
if(A && A.gc_destroyed == 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 garbage collected and was force del() --")
|
||||
del(A)
|
||||
// else
|
||||
// testing("GC: [refID] properly GC'd at [world.timeofday] with timeout [GCd_at_time]")
|
||||
destroyed.Cut(i, ++i) // also increases i in general
|
||||
/**
|
||||
* NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
|
||||
* OTHER TYPES CANNOT BE QDEL'D BECAUSE THEIR LOC IS LOCKED OR THEY DON'T HAVE ONE.
|
||||
* While I'm leaving the above comment in since /atoms cannot be garbage collected, datums and lists can be garbage collected just fine.
|
||||
* Read the DM guide on it. Hit f1 and search for "garbage collection"
|
||||
*/
|
||||
/proc/qdel(var/atom/movable/A)
|
||||
if(!A)
|
||||
return
|
||||
if(!istype(A))
|
||||
warning("qdel() passed object of type [A.type]. qdel() can only handle /atom/movable types.")
|
||||
del(A)
|
||||
return
|
||||
if(!garbage)
|
||||
del(A)
|
||||
return
|
||||
// Let our friend know they're about to get fucked up.
|
||||
A.Destroy()
|
||||
garbage.AddTrash(A)
|
||||
|
||||
/* // If you can get this to run, report the results please.
|
||||
/client/verb/delete_everything()
|
||||
set name = "qdel() everything"
|
||||
set category = "Debug"
|
||||
set background = 1
|
||||
|
||||
if(input("Are you sure you want to do that?") as null|anything in list("Yes","No") != "Yes")
|
||||
return
|
||||
src << "qdel(everything)"
|
||||
for(var/atom/movable/everything in world)
|
||||
qdel(everything)
|
||||
*/
|
||||
@@ -27,6 +27,7 @@ datum/controller/game_controller
|
||||
var/nano_cost = 0
|
||||
var/events_cost = 0
|
||||
var/ticker_cost = 0
|
||||
var/gc_cost = 0
|
||||
var/total_cost = 0
|
||||
|
||||
var/last_thing_processed
|
||||
@@ -63,7 +64,6 @@ datum/controller/game_controller/New()
|
||||
if(!emergency_shuttle) emergency_shuttle = new /datum/shuttle_controller/emergency_shuttle()
|
||||
if(!supply_shuttle) supply_shuttle = new /datum/controller/supply_shuttle()
|
||||
|
||||
|
||||
datum/controller/game_controller/proc/setup()
|
||||
world.tick_lag = config.Ticklag
|
||||
|
||||
@@ -201,6 +201,12 @@ datum/controller/game_controller/proc/process()
|
||||
ticker.process()
|
||||
ticker_cost = (world.timeofday - timer) / 10
|
||||
|
||||
// GC
|
||||
timer = world.timeofday
|
||||
last_thing_processed = garbage.type
|
||||
garbage.process()
|
||||
gc_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TIMING
|
||||
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + ticker_cost
|
||||
|
||||
@@ -228,7 +234,7 @@ datum/controller/game_controller/proc/process_mobs()
|
||||
var/i = 1
|
||||
while(i<=mob_list.len)
|
||||
var/mob/M = mob_list[i]
|
||||
if(M)
|
||||
if(M && !M.gc_destroyed)
|
||||
last_thing_processed = M.type
|
||||
M.Life()
|
||||
i++
|
||||
@@ -250,7 +256,7 @@ datum/controller/game_controller/proc/process_machines()
|
||||
var/i = 1
|
||||
while(i<=machines.len)
|
||||
var/obj/machinery/Machine = machines[i]
|
||||
if(Machine)
|
||||
if(Machine && !Machine.gc_destroyed)
|
||||
last_thing_processed = Machine.type
|
||||
if(Machine.process() != PROCESS_KILL)
|
||||
if(Machine)
|
||||
@@ -264,7 +270,7 @@ datum/controller/game_controller/proc/process_objects()
|
||||
var/i = 1
|
||||
while(i<=processing_objects.len)
|
||||
var/obj/Object = processing_objects[i]
|
||||
if(Object)
|
||||
if(Object && !Object.gc_destroyed)
|
||||
last_thing_processed = Object.type
|
||||
Object.process()
|
||||
i++
|
||||
|
||||
@@ -49,13 +49,13 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
/obj/structure/plasticflaps/ex_act(severity)
|
||||
switch(severity)
|
||||
if (1)
|
||||
del(src)
|
||||
qdel(src)
|
||||
if (2)
|
||||
if (prob(50))
|
||||
del(src)
|
||||
qdel(src)
|
||||
if (3)
|
||||
if (prob(5))
|
||||
del(src)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/plasticflaps/mining //A specific type for mining that doesn't allow airflow because of them damn crates
|
||||
name = "airtight plastic flaps"
|
||||
@@ -67,7 +67,7 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
T.blocks_air = 1
|
||||
..()
|
||||
|
||||
Del() //lazy hack to set the turf to allow air to pass if it's a simulated floor
|
||||
Destroy() //lazy hack to set the turf to allow air to pass if it's a simulated floor //wow this is terrible
|
||||
var/turf/T = get_turf(loc)
|
||||
if(T)
|
||||
if(istype(T, /turf/simulated/floor))
|
||||
@@ -231,7 +231,7 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
var/crate_count = 0
|
||||
|
||||
centcom_message = ""
|
||||
|
||||
|
||||
for(var/atom/movable/MA in shuttle)
|
||||
if(MA.anchored) continue
|
||||
|
||||
@@ -286,7 +286,7 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
if(istype(A, /obj/item/stack/sheet/mineral/plasma))
|
||||
var/obj/item/stack/sheet/mineral/plasma/P = A
|
||||
plasma_count += P.amount
|
||||
|
||||
|
||||
if(istype(A, /obj/item/seeds))
|
||||
var/obj/item/seeds/S = A
|
||||
if(S.rarity == 0) // Mundane species
|
||||
@@ -303,8 +303,8 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
discoveredPlants[S.type] = S.potency
|
||||
centcom_message += "<font color=green>+[S.rarity]</font>: New species discovered: \"[capitalize(S.species)]\". Excellent work.<BR>"
|
||||
points += S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later
|
||||
del(MA)
|
||||
|
||||
qdel(MA)
|
||||
|
||||
if(plasma_count)
|
||||
centcom_message += "<font color=green>+[round(plasma_count/plasma_per_point)]</font>: Received [plasma_count] units of exotic material.<BR>"
|
||||
points += round(plasma_count / plasma_per_point)
|
||||
@@ -387,7 +387,7 @@ var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
// If it has multiple items, there's a 1% of each going missing... Not for secure crates or those large wooden ones, though.
|
||||
if(contains.len > 1 && prob(1) && !findtext(SP.containertype,"/secure/") && !findtext(SP.containertype,"/largecrate/"))
|
||||
slip.erroneous |= MANIFEST_ERROR_ITEM // This item was not included in the shipment!
|
||||
del(B2) // Lost in space... or the loading dock.
|
||||
qdel(B2) // Lost in space... or the loading dock.
|
||||
|
||||
//manifest finalisation
|
||||
slip.info += "</ul><br>"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
return
|
||||
|
||||
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras", "Events"))
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Garbage","Air","Jobs","Sun","Radio","Supply Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras", "Events"))
|
||||
set category = "Debug"
|
||||
set name = "Debug Controller"
|
||||
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
|
||||
@@ -47,6 +47,9 @@
|
||||
if("Lighting")
|
||||
debug_variables(lighting_controller)
|
||||
feedback_add_details("admin_verb","DLighting")
|
||||
if("Garbage")
|
||||
debug_variables(garbage)
|
||||
feedback_add_details("admin_verb","DGarbage")
|
||||
if("Air")
|
||||
debug_variables(air_master)
|
||||
feedback_add_details("admin_verb","DAir")
|
||||
|
||||
Reference in New Issue
Block a user