diff options
| author | Roberto I <roberto@inf.puc-rio.br> | 2026-01-11 15:36:03 -0300 |
|---|---|---|
| committer | Roberto I <roberto@inf.puc-rio.br> | 2026-01-11 15:36:03 -0300 |
| commit | 2a7cf4f319fc276f4554a8f6364e6b1ba4eb2ded (patch) | |
| tree | a99a8361664b0adda83186f04e2e9c98afd86b44 /testes | |
| parent | 5cfc725a8b61a6f96c7324f60ac26739315095ba (diff) | |
| download | lua-2a7cf4f319fc276f4554a8f6364e6b1ba4eb2ded.tar.gz lua-2a7cf4f319fc276f4554a8f6364e6b1ba4eb2ded.tar.bz2 lua-2a7cf4f319fc276f4554a8f6364e6b1ba4eb2ded.zip | |
Before calling a finalizer, Lua not only checks stack limits, but
actually ensures that a minimum number of slots are already allocated
for the call. (If it cannot ensure that, it postpones the finalizer.)
That avoids finalizers not running due to memory errors that the
programmer cannot control.
Diffstat (limited to 'testes')
| -rw-r--r-- | testes/gc.lua | 42 | ||||
| -rw-r--r-- | testes/memerr.lua | 19 | ||||
| -rw-r--r-- | testes/tracegc.lua | 9 |
3 files changed, 68 insertions, 2 deletions
diff --git a/testes/gc.lua b/testes/gc.lua index 62713dac..e50d9029 100644 --- a/testes/gc.lua +++ b/testes/gc.lua | |||
| @@ -707,4 +707,46 @@ end | |||
| 707 | 707 | ||
| 708 | collectgarbage(oldmode) | 708 | collectgarbage(oldmode) |
| 709 | 709 | ||
| 710 | |||
| 711 | if T then | ||
| 712 | print("testing stack issues when calling finalizers") | ||
| 713 | |||
| 714 | local X | ||
| 715 | local obj | ||
| 716 | |||
| 717 | local function initobj () | ||
| 718 | X = false | ||
| 719 | obj = setmetatable({}, {__gc = function () X = true end}) | ||
| 720 | end | ||
| 721 | |||
| 722 | local function loop (n) | ||
| 723 | if n > 0 then loop(n - 1) end | ||
| 724 | end | ||
| 725 | |||
| 726 | -- should not try to call finalizer without a CallInfo available | ||
| 727 | initobj() | ||
| 728 | loop(20) -- ensure stack space | ||
| 729 | T.resetCI() -- remove extra CallInfos | ||
| 730 | T.alloccount(0) -- cannot allocate more CallInfos | ||
| 731 | obj = nil | ||
| 732 | collectgarbage() -- will not call finalizer | ||
| 733 | T.alloccount() | ||
| 734 | assert(X == false) | ||
| 735 | collectgarbage() -- now will call finalizer (it was still pending) | ||
| 736 | assert(X == true) | ||
| 737 | |||
| 738 | -- should not try to call finalizer without stack space available | ||
| 739 | initobj() | ||
| 740 | loop(5) -- ensure enough CallInfos | ||
| 741 | T.reallocstack(0) -- remove extra stack slots | ||
| 742 | T.alloccount(0) -- cannot reallocate stack | ||
| 743 | obj = nil | ||
| 744 | collectgarbage() -- will not call finalizer | ||
| 745 | T.alloccount() | ||
| 746 | assert(X == false) | ||
| 747 | collectgarbage() -- now will call finalizer (it was still pending) | ||
| 748 | assert(X == true) | ||
| 749 | end | ||
| 750 | |||
| 751 | |||
| 710 | print('OK') | 752 | print('OK') |
diff --git a/testes/memerr.lua b/testes/memerr.lua index 9c940ca7..a55514a9 100644 --- a/testes/memerr.lua +++ b/testes/memerr.lua | |||
| @@ -282,6 +282,25 @@ testamem("growing stack", function () | |||
| 282 | return foo(100) | 282 | return foo(100) |
| 283 | end) | 283 | end) |
| 284 | 284 | ||
| 285 | |||
| 286 | collectgarbage() | ||
| 287 | collectgarbage() | ||
| 288 | global io, T, setmetatable, collectgarbage, print | ||
| 289 | |||
| 290 | local Count = 0 | ||
| 291 | testamem("finalizers", function () | ||
| 292 | local X = false | ||
| 293 | local obj = setmetatable({}, {__gc = function () X = true end}) | ||
| 294 | obj = nil | ||
| 295 | T.resetCI() -- remove extra CallInfos | ||
| 296 | T.reallocstack(18) -- remove extra stack slots | ||
| 297 | Count = Count + 1 | ||
| 298 | io.stderr:write(Count, "\n") | ||
| 299 | T.trick(io) | ||
| 300 | collectgarbage() | ||
| 301 | return X | ||
| 302 | end) | ||
| 303 | |||
| 285 | -- }================================================================== | 304 | -- }================================================================== |
| 286 | 305 | ||
| 287 | 306 | ||
diff --git a/testes/tracegc.lua b/testes/tracegc.lua index a8c929df..c1154f90 100644 --- a/testes/tracegc.lua +++ b/testes/tracegc.lua | |||
| @@ -1,10 +1,15 @@ | |||
| 1 | -- track collections | 1 | -- track collections |
| 2 | 2 | ||
| 3 | |||
| 3 | local M = {} | 4 | local M = {} |
| 4 | 5 | ||
| 5 | -- import list | 6 | -- import list |
| 6 | local setmetatable, stderr, collectgarbage = | 7 | local stderr, collectgarbage = io.stderr, collectgarbage |
| 7 | setmetatable, io.stderr, collectgarbage | 8 | |
| 9 | -- the debug version of setmetatable does not create any object (such as | ||
| 10 | -- a '__metatable' string), and so it is more appropriate to be used in | ||
| 11 | -- a finalizer | ||
| 12 | local setmetatable = require"debug".setmetatable | ||
| 8 | 13 | ||
| 9 | global none | 14 | global none |
| 10 | 15 | ||
