-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
maps have a terrible memory management model #441
Comments
My first thought for fixing this is for libqb to allocate its own version of the key and data, so it would be easy for it to free them when they are finished with. Add to which we need a flag or some way of marking a entry as 'deleted' after qb_map_rm() is called so that it wouldn't appear in other iterators or to qb_map_get() calls. This would also need adding the length of the value to qb_map_put(), and, maybe, passing it back from qb_map_get() and the iterators. Though the latter might not be necessary if we assume that callers know what they put in there ... debatable. |
glib has a similar situation with hash tables. Their solution is that tables can be created with either g_hash_table_new(), which leaves memory management to the caller, or g_hash_table_new_full(), which takes key free and value free functions as arguments. Then g_hash_table_destroy() will free either just the hash table object, or each key/value entry first then the object, depending on how it was created. |
But this ( The main problem with libqb callback is how easy is to forget that it's absolutely needed for safe operation. Basically API is not intuitive. |
glib just documents it and leaves it to the caller: "Note that neither keys nor values are copied when inserted into the GHashTable, so they must exist for the lifetime of the GHashTable. This means that the use of static strings is OK, but temporary strings ... should be copied with g_strdup() before being inserted." BTW Pacemaker doesn't use libqb maps, but we do use glib hash tables extensively, both with and without free functions. Using a table without free functions is useful for example when a map relates two existing data aggregates with the same lifetime as the map (e.g. lists A and B, and a map where A entries are keys and B entries are values, and all of them are members of the same struct). I think it would be fine to just stress the issue more heavily in the documentation. Alternatively if you want to be stricter, you could make all functions return an error unless the map has a free function registered (an aware caller could make that function no-op, but it would have to be a conscious choice). That could still be seen as breaking ABI compatibility, though. |
I think it's the fact that I've been doing quite a bit of Rust recently that makes be balk (or worse) when I see pointers thrown around semi-randomly with little thought for ownership or lifetimes. IMHO it's the sort of thing that gives C a bad name for security and reliability. |
Yup, such comment would be useful. Also we may add something like |
Yep, this sort of thing is an endless source of memory mischief in C. Depending on what you're looking to accomplish I see a few options:
|
I am very much in favour of 5 or 6 ;-) |
I have one question (before trying to find it in rust/glib docs). In corosync we use prefix callbacks ( This is quite unique feature and not sure if either rust or glib is implementing that. |
Good point. glib doesn't have that, or any change callbacks at all. It would be straightforward for corosync to implement glib wrappers that would check for prefixes, if we don't mind making the capability private to corosync. |
Thats good to know
straightforward yes, but probably pretty slow ;) QB trie is really nicely implemented in that respect (it's not "linked" list walking but trigger is stored in the element). |
True, libqb has the advantage here :) |
All of the libqb maps leave 'ownership' of the memory stored in them to the caller. So the claler can easily free things that are stored in the maps without libqb being aware of it.
This happens most easily when iterators are used. libqb adds a ref to items that are used in iterators so that they do not get removed from the map when qb_map_rm() is called. HOWEVER, the caller is quite likely to free any malloced memory used by th item after callimg qb_map_rm() leaving dangling pointers.
The maps have a special callback that tells the application when it is safe to free items, but apart from being unnecessarily messy (and easy to miss or forget), it means that calling qb_map_rm() on an object doesn't necessarily remove it from the map, which could be a cause of race conditions.
Fixing this is an API change so would need a soname bump, of course.
The text was updated successfully, but these errors were encountered: