[SOLVED]finaliser

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

[SOLVED]finaliser

Post by jickster » Fri Apr 13, 2018 5:35 pm

I'm transitioning to using finalisers but not clear on things.

I've declared the following type

Code: Select all

typedef struct _atiupy_can_message_obj_t {
	mp_obj_base_t base;
	mp_obj_t mp_obj_id;
	mp_obj_list_t * mp_list_data;
	mp_obj_dict_t * mp_dict_flags;
	mp_obj_int_t * mp_int_timestamp;
} atiupy_mp_obj_can_message_t;
This type is to be instantiated by the user like so

Code: Select all

msg1 = message()
I thought I would need a finaliser but now not clear if I do.
In C-code, in the `.make_new`, I currently do

Code: Select all

atiupy_mp_obj_can_message_t * new_msg = m_new_obj(atiupy_mp_obj_can_message_t);
new_msg->mp_list_data = mp_obj_new_list(0, NULL);
new_msg->mp_dict_flags = mp_obj_new_dict(0);
new_msg->mp_int_timestamp = mp_obj_new_int_from_uint(pRcvMsg->timestamp);
If I create a finaliser for `atiupy_mp_obj_can_message_t`, I'd have to free the pointers allocated
as part of the struct (like a destructor of a class) . . .

Code: Select all

STATIC mp_obj_t can_message_del(mp_obj_t self_in)
{
	atiupy_mp_obj_can_message_t * self_msg = self_in;
	// self_msg->mp_obj_id is a small int so nothing to "free"
	// free self_msg->mp_list_data
	// free self_msg->mp_dict_flags
	// free self_msg->mp_int_timestamp
	
	return mp_const_none;
}
. . . however, there's no API to delete `mp_type_list` or `mp_type_dict`.

QUESTION
This leads me to believe that structs that only contain `mp_type*` objects do not actually need a finaliser.
Is this correct?

If yes, how do these other objects get freed?
If no, how do I free these other objects?
Last edited by jickster on Mon Apr 16, 2018 8:17 pm, edited 1 time in total.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: finaliser

Post by dhylands » Fri Apr 13, 2018 9:29 pm

Your finalizer doesn't need to free anything. The finalizer is intended to do finalization stuff that the garbage collector can't do. Freeing memory when nobody else is using it is something that the gc will do for you.

The fact that the finalizer is being called means that the GC has decided to free up the block of memory containing the pointers, which means that if the pointers inside that block are the only references to the objects you think you need to free, then the gc will eventually free those objects as well.

Having said that, you can call m_free to free previously allocated memory, but generally speaking I wouldn't do that it there is any chance that python code might still have a reference of some type to the memory.

Things that you'd typically use a finalizer for is closing an open file or socket.

jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

Re: finaliser

Post by jickster » Mon Apr 16, 2018 8:17 pm

dhylands wrote:
Fri Apr 13, 2018 9:29 pm
Your finalizer doesn't need to free anything. The finalizer is intended to do finalization stuff that the garbage collector can't do. Freeing memory when nobody else is using it is something that the gc will do for you.

The fact that the finalizer is being called means that the GC has decided to free up the block of memory containing the pointers, which means that if the pointers inside that block are the only references to the objects you think you need to free, then the gc will eventually free those objects as well.

Having said that, you can call m_free to free previously allocated memory, but generally speaking I wouldn't do that it there is any chance that python code might still have a reference of some type to the memory.

Things that you'd typically use a finalizer for is closing an open file or socket.
Thank you!

jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

Re: finaliser

Post by jickster » Wed Apr 18, 2018 4:27 pm

dhylands wrote:
Fri Apr 13, 2018 9:29 pm
Your finalizer doesn't need to free anything. The finalizer is intended to do finalization stuff that the garbage collector can't do. Freeing memory when nobody else is using it is something that the gc will do for you.
But if I did free the pointers inside my finaliser, would it cause a race condition with the GC if those blocks have already been marked to be cleared?


Code: Select all

typedef struct _atiupy_can_message_obj_t {
	mp_obj_base_t base;
	mp_obj_t mp_obj_id;
	mp_obj_list_t * mp_list_data;
	mp_obj_dict_t * mp_dict_flags;
	mp_obj_int_t * mp_int_timestamp;
} atiupy_mp_obj_can_message_t;

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: [SOLVED]finaliser

Post by dhylands » Wed Apr 18, 2018 5:15 pm

The GC doesn't mark blocks to be freed. It marks blocks which are in-use.

Any block which isn't marked in-use is freed.

So the fact that you have pointers in your block means that the GC hasn't yet freed the block.

The issue will be if there are other pointers someplace else which are pointing to the block. If you free a object while there is another pointer someplace else pointing at that same object, then you've just corrupted the heap by creating a dangling pointer.

So the only place that you'll typically see m_free being used is to perform cleanup in a constructor. In this scenario you know that nobody else has a pointer to an object that you just constructed.

In the finalizer you have no idea is somebody else has a pointer or not, so it's generally best to not free anything and just let the GC take care of it. If it makes you feel better, nulling out the pointer won't cause any problems.

Post Reply