<close> annotation and disabling GC #133
-
The following comment is found in
and then we have the following lines of code: --[[
Destroys a string freeing its memory.
This must never be called on string literals.
This function is only needed to be called when not using the GC.
]]
function string:destroy(): void
if unlikely(self.size == 0) then return end
default_allocator:dealloc(self.data)
self.data = nilptr
self.size = 0
end
-- Effectively the same as `destroy`, called when a to-be-closed variable goes out of scope.
function string:__close(): void
self:destroy()
end Does this mean when our string object goes out of scope and we have our GC disabled, will it trigger |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 7 replies
-
Only if you use For example, the bellow will not leak memory: ## pragmas.nogc = true -- disable the GC
require 'string'
local function f()
local s: string <close> = string.upper('hello')
-- <close> will call destroy for us at the end of this scope, collecting string memory for us
end
f() This will also not leak memory: ## pragmas.nogc = true -- disable the GC
require 'string'
local function f()
local s: string = string.upper('hello')
s:destroy() -- collect string memory
end
f() However, this one will leak: ## pragmas.nogc = true -- disable the GC
require 'string'
local function f()
local s: string = string.upper('hello')
-- we forgot to destroy our string! it's memory is not collected, so it leaks
end
f() |
Beta Was this translation helpful? Give feedback.
-
No, this is not obvious, if we assign some external variable with ## pragmas.nogc = true -- disable the GC
require 'string'
local external_s: string
local function f()
local s: string = string.upper('hello')
external_s = s
-- we should not destroy `s`, because it is now "owned" by `external_s`, destroying it would make our app crash!
end
f()
print(external_s) You would have to track all assignments, and all argument passing, to effectively know if Custom allocators insteadI am in favor of the following approach for a temporary string, using custom allocators, here it's a pseudo code if you wonder: ## pragmas.nogc = true -- disable the GC
require 'allocators.heap'
require 'string'
-- `string.upper` variation to support custom allocators.
function string.upper_ex(s: string, allocator: auto): string
local function toupper(c: cint): cint <inline> return (@cuint)(c)-'a'_b < 26 and c & 0x5f or c end
if unlikely(s.size == 0) then return s end
local ret: string = {data = (@*[0]byte)(allocator:xalloc(s.size+1)), size = s.size}
ret.data[s.size] = 0
for i:usize=0,<s.size do ret.data[i] = (@byte)(toupper(s.data[i])) end
return ret
end
-- Temporary allocator for strings, used to allocate temporary strings
local temp_string_allocator: HeapAllocator(16384)
-- Function that creates a temporary string and prints it.
local function f()
local s: string = string.upper_ex('hello', temp_string_allocator)
print(s)
-- no need to deallocate the string, it will be deallocate in a the end of frame
end
local function frame()
f()
-- free all temporary memory!
temp_string_allocator:deallocall()
end
for i=1,10 do
frame()
end This approach is more efficient than using RAII, than using GC, than using ownership and than using reference counting. And it is how I believe people should do more for efficient software, specially in game world or realtime software. It is more efficient because you deallocate all temporary objects in a batch with Such a concept will be explored more in Nelua in the future, ideally all methods that allocate memory in the standard library will be able to work with a custom allocator, and the language will provide more utilities for custom allocators. This is not the focus at the moment, because the focus at the moment is making more Lua-like features, and we have the GC or manual memory management meanwhile. |
Beta Was this translation helpful? Give feedback.
-
About Linux kernel not using RAII...well...they actually do in a way; via constructor / destructor attributes.
About the rest, yes; I agree. As you have seen in #135, I have added GingerBill in it that explains in great detail about the (proper) use of memory. Last but not least, the Relative Pointers by Jonathan Blow demonstrates this concept you have just shown above and is really amazing! |
Beta Was this translation helpful? Give feedback.
-
I was playing with a code and accidentally caused a bug in my code which became food for thought which I would like to share. I introduced Here's the sample: require 'allocators.general'
require 'io'
local Object: type = @record{ x: integer, y: integer }
function Object:__close()
io.printf("object %p destroyed\n", self)
general_allocator:delete(self)
end
do
local o: *Object <close> = general_allocator:new(@Object)
-- The culprit line
general_allocator:delete(o)
-- ^^^^^^^^^^^^^^^^^^^^^^^
io.printf("object %p created\n", o)
io.printf("o.x initial value: %3d, address: %p\n", o.x, &o.x)
io.printf("o.y initial value: %3d, address: %p\n", o.y, &o.y)
o.x = 10
o.y = 20
io.printf(" o.x new value: %3d, address: %p\n", o.x, &o.x)
io.printf(" o.y new value: %3d, address: %p\n", o.y, &o.y)
end Is there a way, like It could be safer to prevent such action when we are dealing with manual memory management, especially with Do you have anything to suggest so I can improve my manual memory management use? |
Beta Was this translation helpful? Give feedback.
-
@edubart after all this time, I have just noticed the following in the generated code of the aforementioned code: As far as I can tell, a compound literal cannot be empty, unless you use C23 that have just introduced the support for empty list initialization, else if I'm not mistaken, it's an undefined behavior. Also, an empty struct is an undefined behavior (C17, 6.7.2.1, point 8). Was this the original intent with the generated code? |
Beta Was this translation helpful? Give feedback.
Only if you use
<close>
annotation on its declaration, that is, a string will be destroyed automatically only if you declare it with<close>
annotation.For example, the bellow will not leak memory:
This will also not leak memory: