- Joined
- Nov 7, 2014
- Messages
- 571
Gul - Gui unfortunately leaks
This is a follow up on leandrotp's post about creating a "handle watcher". I think Gul is just that, albeit very naive, it scans the handle table for locations, groups, forces and rects and destroys them if they are "unreachable".
This is a follow up on leandrotp's post about creating a "handle watcher". I think Gul is just that, albeit very naive, it scans the handle table for locations, groups, forces and rects and destroys them if they are "unreachable".
JASS:
library Gul initializer init /*
*/ uses /*
*/ Memory, /* // both libraries can be obtained from:
*/ Version /* // https://www.hiveworkshop.com/threads/accessing-memory-from-the-script-its-time-of-the-revolution.279262/
*/
globals
// scan for leaks every this number of seconds
private constant real Leaks_Scan_Delay = 10.0
endglobals
globals
// these are not used but are needed to fool jasshelper
integer gul_typecast_integer
location gul_typecast_location
group gul_typecast_group
force gul_typecast_force
rect gul_typecast_rect
integer l__gul_typecast_integer
location l__gul_typecast_location
group l__gul_typecast_group
force l__gul_typecast_force
rect l__gul_typecast_rect
endglobals
private function set_integer takes integer h returns nothing
set l__gul_typecast_integer = h
return // prevent jasshelper from inlining this function
endfunction
private function typecast1 takes nothing returns nothing
local integer gul_typecast_location // jasshelper will implicitly rename this to l__gul_typecast_location
local location gul_typecast_integer // jasshelper will implicitly rename this to l__gul_typecast_integer
endfunction
//# +nosemanticerror
private function integer_to_location takes integer h returns location
call set_integer(h)
return l__gul_typecast_integer
endfunction
private function typecast2 takes nothing returns nothing
local integer gul_typecast_group // jasshelper will implicitly rename this to l__gul_typecast_group
local group gul_typecast_integer // jasshelper will implicitly rename this to l__gul_typecast_integer
endfunction
//# +nosemanticerror
private function integer_to_group takes integer h returns group
call set_integer(h)
return l__gul_typecast_integer
endfunction
private function typecast3 takes nothing returns nothing
local integer gul_typecast_force // jasshelper will implicitly rename this to l__gul_typecast_force
local force gul_typecast_integer // jasshelper will implicitly rename this to l__gul_typecast_integer
endfunction
//# +nosemanticerror
private function integer_to_force takes integer h returns force
call set_integer(h)
return l__gul_typecast_integer
endfunction
private function typecast4 takes nothing returns nothing
local integer gul_typecast_rect // jasshelper will implicitly rename this to l__gul_typecast_rect
local rect gul_typecast_integer // jasshelper will implicitly rename this to l__gul_typecast_integer
endfunction
//# +nosemanticerror
private function integer_to_rect takes integer h returns rect
call set_integer(h)
return l__gul_typecast_integer
endfunction
globals
private integer p_ef
private boolean done_ef
private force exec = CreateForce()
// we only destroy a handle if it's unreachable (has a reference count of 1)
// and has a handle table offset greater than this pivot's, i.e
// the handle must have been created after the pivot;
// we do it this way in order to avoid accidentally destroying handles created
// in blizzard.j's globals block or our own handles (exec and our timer)
//
private location pivot
private integer pivot_offset // pivot's offset into the handle table
endglobals
private function handle_table_base_p takes nothing returns integer
return Memory[Memory[GameState/4+7]/4+103]
endfunction
private function leaks_scan_ef takes nothing returns nothing
// make at most this many iterations so that we can avoid the op-limit
local integer N = 2000
// this many consecutive empty entries in the handle table denote its end;
// we do it this way because we don't know how many entries are in the handle table =)
local integer M = 5
local integer m
local integer p
local integer q
local integer rc // reference count
local integer a // handle pointer
local integer ty // handle type
local integer ht_base_p
// local integer hid // handle id
set ht_base_p = handle_table_base_p()
set p = p_ef
loop
set rc = Memory[p/4]
set a = Memory[(p+0x04)/4]
if a != 0 and rc == 1 and (p - ht_base_p > pivot_offset) then
// get the handle's type;
// note the unaligned memory read (Memory1);
set ty = Memory1[Memory[(Memory[a/4] + 0x1C)/4]/4]
// we don't have to destroy units because the game already does this =)
// if ty == '+w3u' then
if ty == '+loc' then
// creating handles "out of thin air", a handle table offset really
// set hid = 0x00100000 + (p - ht_base_p) / 0x0C
// call RemoveLocation(integer_to_location(hid))
call RemoveLocation(integer_to_location(0x00100000 + (p - ht_base_p) / 0x0C))
elseif ty == '+grp' then
call DestroyGroup(integer_to_group(0x00100000 + (p - ht_base_p) / 0x0C))
elseif ty == '+frc' then
call DestroyForce(integer_to_force(0x00100000 + (p - ht_base_p) / 0x0C))
elseif ty == '+rct' then
call RemoveRect(integer_to_rect(0x00100000 + (p - ht_base_p) / 0x0C))
// elseif ty == '+tmr' then
// we don't destroy "leaked" timers for now, because it might not be safe;
// does gui even leak timers?
endif
elseif a == 0 and rc ==0 then
set m = M - 1
set q = p + 0x0C
loop
exitwhen m == 0
if Memory[q/4] != 0 then
exitwhen true
endif
set q = q + 0x0C
set m = m - 1
endloop
if m == 0 then
set done_ef = true
exitwhen true
endif
endif
set p = p + 0x0C
endloop
set p_ef = p
endfunction
private function leaks_scan takes nothing returns nothing
set p_ef = handle_table_base_p()
set done_ef = false
loop
exitwhen done_ef
call ForForce(exec, function leaks_scan_ef)
endloop
endfunction
private function init takes nothing returns nothing
local integer N
local integer n
local location loc
local location prev_loc
local integer p
local integer pivot_p
call ForceAddPlayer(exec, Player(15))
call TimerStart(CreateTimer(), Leaks_Scan_Delay, true, function leaks_scan)
// eat up all the free handle ids so that we can put pivot at the end of the handle table
//
set N = 16
set n = N
set prev_loc = Location(0.0, 0.0)
loop
set loc = Location(0.0, 0.0)
if GetHandleId(prev_loc) + 1 == GetHandleId(loc) then
set n = n - 1
exitwhen n == 0
else
set n = N
endif
set prev_loc = loc
endloop
set pivot = loc
set pivot_offset = 0x0C * GetHandleId(pivot) - 0x00C00000
endfunction
endlibrary