Repository navigation
Conversation
- libgrass_parson library now depends on libgrass_gis - wrap G_malloc in private function adapting to G__parson_malloc - G_json_value_init_object and G_json_value_init_array always return allocated object - parson is initialized lazily using C11 atomic operation
|
Can you please specify here what is the big picture? Like all library functions result in fatal on non-recoverable errors... |
Yes, but not only library, modules as well use G_malloc. Current G_malloc's most advantageous feature is that it calls G_fatal_error on failure to malloc (with informative error message) and in the extension the caller of G_malloc is guaranteed to receive a non-null object, rendering return value checking superfluous (with help of It occurred to me now that |
|
I added a simple fallback for C11 compiler without support for atomic operations, this adds a small risk of undefined behaviour (in case of data race). While Parson library is not thread safe in itself, the lazy initialisation adds a potential, however minuscule, risk. |
wenzeslaus
left a comment
There was a problem hiding this comment.
This fits with the overall approach elsewhere, so probably fine as a consistent solution. The function scope should be limited. The text of the individual comments is mostly AI-written (reviewed by me).
| static _Atomic int parson_init_state = UNINITIALIZED; | ||
| #endif | ||
|
|
||
| void ensure_parson_initialized(void) |
There was a problem hiding this comment.
This function is only used within gjson.c, but it has external linkage, no G_ prefix, and no declaration in a header, so it becomes an exported symbol of libgrass_parson. Making it static does not affect the allocator registration: json_set_allocation_functions() receives function pointers, and calling through a pointer is independent of symbol linkage (G__parson_malloc is already static). While at it, a short comment stating what the atomics do and do not guarantee would help future readers.
| void ensure_parson_initialized(void) | |
| /* | |
| * Register G_malloc/G_free with parson exactly once. The atomic logic | |
| * makes only this one-time registration thread-safe; parson itself has | |
| * unsynchronized global state and makes no thread-safety guarantees. | |
| */ | |
| static void ensure_parson_initialized(void) |
| typedef enum { UNINITIALIZED, INITIALIZING, INITIALIZED } init_state_t; | ||
| static _Atomic int parson_init_state = UNINITIALIZED; |
There was a problem hiding this comment.
Naming this set of values makes sense, but the name is currently unused: the variable is _Atomic int and expected below is int. Using the type makes the declaration match the intent:
| typedef enum { UNINITIALIZED, INITIALIZING, INITIALIZED } init_state_t; | |
| static _Atomic int parson_init_state = UNINITIALIZED; | |
| typedef enum { UNINITIALIZED, INITIALIZING, INITIALIZED } init_state_t; | |
| static _Atomic init_state_t parson_init_state = UNINITIALIZED; |
With this, expected in ensure_parson_initialized() needs a matching type change; see the suggestion there.
| return; | ||
| } | ||
|
|
||
| int expected = UNINITIALIZED; |
There was a problem hiding this comment.
Follow-up to using init_state_t for parson_init_state above: the compare-exchange generic function requires the expected pointer to match the atomic variable's value type, so this needs to be:
| int expected = UNINITIALIZED; | |
| init_state_t expected = UNINITIALIZED; |
| if (atomic_compare_exchange_strong(&parson_init_state, &expected, | ||
| INITIALIZING)) { | ||
|
|
||
| json_set_allocation_functions(G__parson_malloc, G_free); | ||
|
|
||
| atomic_store_explicit(&parson_init_state, INITIALIZED, | ||
| memory_order_release); | ||
| } |
There was a problem hiding this comment.
Stray blank lines inside the block:
| if (atomic_compare_exchange_strong(&parson_init_state, &expected, | |
| INITIALIZING)) { | |
| json_set_allocation_functions(G__parson_malloc, G_free); | |
| atomic_store_explicit(&parson_init_state, INITIALIZED, | |
| memory_order_release); | |
| } | |
| if (atomic_compare_exchange_strong(&parson_init_state, &expected, | |
| INITIALIZING)) { | |
| json_set_allocation_functions(G__parson_malloc, G_free); | |
| atomic_store_explicit(&parson_init_state, INITIALIZED, | |
| memory_order_release); | |
| } |
| /* ***** WRAPPER FOR PARSON FUNCTIONS USED IN GRASS ************** */ | ||
| /* *************************************************************** */ | ||
|
|
||
| /// \since version 8.5 |
There was a problem hiding this comment.
The never-NULL guarantee is worth stating in the documentation as well, not only via the attribute:
| /// \since version 8.5 | |
| /// Never returns NULL; exits with G_fatal_error() on allocation failure. | |
| /// | |
| /// \since version 8.5 |
| return (G_JSON_Value *)json_value_init_object(); | ||
| } | ||
|
|
||
| /// \since version 8.5 |
There was a problem hiding this comment.
Same here:
| /// \since version 8.5 | |
| /// Never returns NULL; exits with G_fatal_error() on allocation failure. | |
| /// | |
| /// \since version 8.5 |
Use G_malloc and G_free internally in Parson.