From 7b30be0058ee1809cd78900484c68cc5cf0e3a1a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 10 Oct 2017 14:27:59 -0400 Subject: [PATCH] Merge pull request #31494 from MrStonedOne/tick_contention_tug_of_war In which the stoner one gets stoned and tries to address tick contention... again --- code/__DEFINES/_tick.dm | 2 +- code/__DEFINES/time.dm | 6 ++- code/__HELPERS/mobs.dm | 6 +-- code/__HELPERS/unsorted.dm | 19 +++++---- code/controllers/master.dm | 41 ++++++++++++------- code/datums/browser.dm | 2 +- code/datums/explosion.dm | 4 +- code/datums/martial/wrestling.dm | 4 +- code/datums/mind.dm | 2 +- .../clock_cult/clock_effects/clock_sigils.dm | 7 ++-- .../gamemodes/clock_cult/clock_scripture.dm | 2 +- code/game/machinery/dance_machine.dm | 4 +- code/game/objects/items.dm | 2 +- code/game/objects/items/storage/storage.dm | 4 +- code/game/objects/structures/barsigns.dm | 2 +- code/game/turfs/turf.dm | 2 +- code/modules/client/asset_cache.dm | 6 +-- code/modules/client/client_procs.dm | 5 +-- .../hostile/megafauna/blood_drunk_miner.dm | 2 +- code/modules/projectiles/projectile.dm | 2 +- .../spells/spell_types/touch_attacks.dm | 2 +- 21 files changed, 72 insertions(+), 54 deletions(-) diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 93ac8ed70d..20eb648fe6 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -1,5 +1,5 @@ #define TICK_LIMIT_RUNNING 80 -#define TICK_LIMIT_TO_RUN 78 +#define TICK_LIMIT_TO_RUN 70 #define TICK_LIMIT_MC 70 #define TICK_LIMIT_MC_INIT_DEFAULT 98 diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 28017de85c..987d52e0cd 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -20,4 +20,8 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using #define HOURS MINUTES*60 -#define TICKS *world.tick_lag \ No newline at end of file +#define TICKS *world.tick_lag + +#define DS2TICKS(DS) (DS/world.tick_lag) + +#define TICKS2DS(T) (T TICKS) \ No newline at end of file diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index de606b137e..4800f35b65 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -321,7 +321,7 @@ Proc for attack log creation, because really why not var/starttime = world.time . = 1 while (world.time < endtime) - stoplag() + stoplag(1) if (progress) progbar.update(world.time - starttime) if(QDELETED(user) || QDELETED(target)) @@ -382,7 +382,7 @@ Proc for attack log creation, because really why not var/starttime = world.time . = 1 while (world.time < endtime) - stoplag() + stoplag(1) if (progress) progbar.update(world.time - starttime) @@ -437,7 +437,7 @@ Proc for attack log creation, because really why not . = 1 mainloop: while(world.time < endtime) - sleep(1) + stoplag(1) if(progress) progbar.update(world.time - starttime) if(QDELETED(user) || !targets) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 6c7bb0cda6..473817e12c 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -504,10 +504,10 @@ Turf and target are separate in case you want to teleport some distance from a t /atom/proc/GetAllContents(list/output=list()) . = output - output += src - for(var/i in 1 to contents.len) - var/atom/thing = contents[i] - thing.GetAllContents(output) + output += src + for(var/i in 1 to contents.len) + var/atom/thing = contents[i] + thing.GetAllContents(output) //Step-towards method of determining whether one atom can see another. Similar to viewers() /proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. @@ -1225,16 +1225,19 @@ proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) //Increases delay as the server gets more overloaded, //as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful -#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta,1)), 1) +#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) -/proc/stoplag() +//returns the number of ticks slept +/proc/stoplag(initial_delay) if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT)) sleep(world.tick_lag) return 1 + if (!initial_delay) + initial_delay = world.tick_lag . = 0 - var/i = 1 + var/i = DS2TICKS(initial_delay) do - . += round(i*DELTA_CALC) + . += Ceiling(i*DELTA_CALC) sleep(i*world.tick_lag*DELTA_CALC) i *= 2 while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index b92853a2c4..5607dde491 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -33,7 +33,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/init_time var/tickdrift = 0 - var/sleep_delta + var/sleep_delta = 1 var/make_runtime = 0 @@ -53,7 +53,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/restart_clear = 0 var/static/restart_timeout = 0 var/static/restart_count = 0 - + //current tick limit, assigned before running a subsystem. //used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits var/static/current_ticklimit = TICK_LIMIT_RUNNING @@ -276,35 +276,45 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration = 1 var/error_level = 0 - var/sleep_delta = 0 + var/sleep_delta = 1 var/list/subsystems_to_check //the actual loop. + while (1) tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag))) + var/starting_tick_usage = TICK_USAGE if (processing <= 0) current_ticklimit = TICK_LIMIT_RUNNING sleep(10) continue - //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later - // because sleeps are processed in the order received, so longer sleeps are more likely to run first - if (TICK_USAGE > TICK_LIMIT_MC) - sleep_delta += 2 + //Anti-tick-contention heuristics: + //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later. + // (because sleeps are processed in the order received, longer sleeps are more likely to run first) + if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit. + sleep_delta *= 2 current_ticklimit = TICK_LIMIT_RUNNING * 0.5 - sleep(world.tick_lag * (processing + sleep_delta)) + sleep(world.tick_lag * (processing * sleep_delta)) continue - sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0) - if (last_run + (world.tick_lag * processing) > world.time) - sleep_delta += 1 - if (TICK_USAGE > (TICK_LIMIT_MC*0.5)) + //Byond resumed us late. assume it might have to do the same next tick + if (last_run + Ceiling(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time) sleep_delta += 1 + sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta + + if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) //we ran 3/4 of the way into the tick + sleep_delta += 1 + + //debug if (make_runtime) var/datum/controller/subsystem/SS SS.can_fire = 0 + if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time)) new/datum/controller/failsafe() // (re)Start the failsafe. + + //now do the actual stuff if (!queue_head || !(iteration % 3)) var/checking_runlevel = current_runlevel if(cached_runlevel != checking_runlevel) @@ -321,6 +331,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new subsystems_to_check = current_runlevel_subsystems else subsystems_to_check = tickersubsystems + if (CheckQueue(subsystems_to_check) <= 0) if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) log_world("MC: SoftReset() failed, crashing") @@ -351,8 +362,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration++ last_run = world.time src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) - current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc. - sleep(world.tick_lag * (processing + sleep_delta)) + current_ticklimit = TICK_LIMIT_RUNNING + if (processing * sleep_delta <= world.tick_lag) + current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick + sleep(world.tick_lag * (processing * sleep_delta)) diff --git a/code/datums/browser.dm b/code/datums/browser.dm index cae6e04e9b..48fb688e53 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -177,7 +177,7 @@ /datum/browser/alert/proc/wait() while (opentime && selectedbutton <= 0 && (!timeout || opentime+timeout > world.time)) - stoplag() + stoplag(1) /datum/browser/alert/Topic(href,href_list) if (href_list["close"] || !user || !user.client) diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index 9869b43d8f..b9ea00da22 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -61,14 +61,14 @@ GLOBAL_LIST_EMPTY(explosions) flash_range = min(GLOB.MAX_EX_FLASH_RANGE, flash_range) flame_range = min(GLOB.MAX_EX_FLAME_RANGE, flame_range) - //DO NOT REMOVE THIS SLEEP, IT BREAKS THINGS + //DO NOT REMOVE THIS STOPLAG, IT BREAKS THINGS //not sleeping causes us to ex_act() the thing that triggered the explosion //doing that might cause it to trigger another explosion //this is bad //I would make this not ex_act the thing that triggered the explosion, //but everything that explodes gives us their loc or a get_turf() //and somethings expect us to ex_act them so they can qdel() - sleep(1) //tldr, let the calling proc call qdel(src) before we explode + stoplag() //tldr, let the calling proc call qdel(src) before we explode EX_PREPROCESS_EXIT_CHECK diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index 02e2d9614d..f489f99564 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -199,7 +199,7 @@ set waitfor = FALSE if (D) animate(D, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0) - sleep (15) + sleep(15) if (D) animate(D, transform = null, time = 1, loop = 0) @@ -258,7 +258,7 @@ D.pixel_y = 0 return 0 - sleep (1) + sleep(1) if (A && D) A.pixel_x = 0 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 8f33465a57..1b050f9aa8 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1300,7 +1300,7 @@ if (istype(M)) for(var/datum/disease/transformation/jungle_fever/JF in M.viruses) JF.cure(0) - sleep(0) //because deleting of virus is doing throught spawn(0) //What + stoplag() //because deleting of virus is doing throught spawn(0) //What log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm index d5b9ce4401..3091d6071a 100644 --- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm +++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm @@ -123,10 +123,9 @@ if(glow_type) glow = new glow_type(get_turf(src)) animate(glow, alpha = 255, time = convert_time) - var/I = 0 - while(I < convert_time && get_turf(L) == get_turf(src)) - I++ - sleep(1) + var/end_time = world.time+convert_time + while(world.time < end_time && get_turf(L) == get_turf(src)) + stoplag(1) if(get_turf(L) != get_turf(src)) if(glow) qdel(glow) diff --git a/code/game/gamemodes/clock_cult/clock_scripture.dm b/code/game/gamemodes/clock_cult/clock_scripture.dm index 8e8cd912e7..049a49f5f4 100644 --- a/code/game/gamemodes/clock_cult/clock_scripture.dm +++ b/code/game/gamemodes/clock_cult/clock_scripture.dm @@ -319,7 +319,7 @@ Applications: 8 servants, 3 caches, and 100 CV qdel(progbar) else progbar.update(end_time - world.time) - sleep(1) + stoplag(1) if(slab) if(slab.slab_ability) successful = slab.slab_ability.successful diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index f919bdbab8..4c90f3ae4e 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -381,7 +381,7 @@ initial_matrix = matrix(M.transform) initial_matrix.Translate(-3,0) animate(M, transform = initial_matrix, time = 1, loop = 0) - sleep (1) + sleep(1) M.lying_fix() @@ -428,7 +428,7 @@ initial_matrix = matrix(M.transform) initial_matrix.Translate(-3,0) animate(M, transform = initial_matrix, time = 1, loop = 0) - sleep (1) + sleep(1) M.lying_fix() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 76e1468751..e158c1f90a 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -344,7 +344,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/datum/progressbar/progress = new(user, len, loc) while (do_after(user, 10, TRUE, S, FALSE, CALLBACK(src, .proc/handle_mass_pickup, S, things, loc, rejections, progress))) - sleep(1) + stoplag(1) qdel(progress) diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm index 309479dd86..cb9e6e2d63 100644 --- a/code/game/objects/items/storage/storage.dm +++ b/code/game/objects/items/storage/storage.dm @@ -88,7 +88,7 @@ var/list/things = src_object.contents.Copy() var/datum/progressbar/progress = new(user, things.len, src) while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(src, .proc/handle_mass_item_insertion, things, src_object, user, progress))) - sleep(1) + stoplag(1) qdel(progress) orient2hud(user) src_object.orient2hud(user) @@ -489,7 +489,7 @@ var/list/things = contents.Copy() var/datum/progressbar/progress = new(usr, things.len, T) while (do_after(usr, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress))) - sleep(1) + stoplag(1) qdel(progress) /obj/item/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress) diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm index 6845a1c9f0..a6cc3d0464 100644 --- a/code/game/objects/structures/barsigns.dm +++ b/code/game/objects/structures/barsigns.dm @@ -120,7 +120,7 @@ return emagged = TRUE to_chat(user, "You emag the barsign. Takeover in progress...") - sleep(100) //10 seconds + sleep(10 SECONDS) set_sign(new /datum/barsign/hiddensigns/syndibarsign) req_access = list(ACCESS_SYNDICATE) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 66406b88be..77ef696714 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -330,7 +330,7 @@ var/list/things = src_object.contents.Copy() var/datum/progressbar/progress = new(user, things.len, src) while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /obj/item/storage.proc/mass_remove_from_storage, src, things, progress))) - sleep(1) + stoplag(1) qdel(progress) return TRUE diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index d5cf3aaf2c..7ea6c60000 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -61,7 +61,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the var/t = 0 var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. + stoplag(1) // Lock up the caller until this is received. t++ if(client) @@ -112,7 +112,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the var/t = 0 var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. + stoplag(1) // Lock up the caller until this is received. t++ if(client) @@ -131,7 +131,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the if (register_asset) register_asset(file,files[file]) send_asset(client,file) - sleep(0) //queuing calls like this too quickly can cause issues in some client versions + stoplag(0) //queuing calls like this too quickly can cause issues in some client versions //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. //if it's an icon or something be careful, you'll have to copy it before further use. diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index e755ff28ac..9b694d58b7 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -631,10 +631,9 @@ GLOBAL_LIST(external_rsc_urls) /client/Stat() . = ..() if (holder) - sleep(1) + stoplag(1) else - sleep(5) - stoplag() + stoplag(5) //send resources to the client. It's here in its own proc so we can move it around easiliy if need be /client/proc/send_resources() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 815b20c0c5..1980e57dbe 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -161,7 +161,7 @@ Difficulty: Medium /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop() if(next_move <= world.time) - sleep(1) + stoplag(1) .() //retry return sleep((next_move - world.time) * 1.5) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index db8a93d540..5a6116bf66 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -228,7 +228,7 @@ while(loc) if(paused) next_run = world.time - sleep(1) + stoplag(1) continue if((!( current ) || loc == current)) diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm index 06a699e54f..46563da53d 100644 --- a/code/modules/spells/spell_types/touch_attacks.dm +++ b/code/modules/spells/spell_types/touch_attacks.dm @@ -21,7 +21,7 @@ return 0 while(attached_hand) //hibernate untill the spell is actually used charge_counter = 0 - sleep(1) + stoplag(1) /obj/effect/proc_holder/spell/targeted/touch/proc/ChargeHand(mob/living/carbon/user) attached_hand = new hand_path(src)