void test_local (void) { void *parent; void *_ptr; TEST_GROUP ("nih_local"); /* Make sure that when a variable goes out of scope, it's freed. */ TEST_FEATURE ("with variable going out of scope"); do { nih_local void *ptr; ptr = nih_alloc (NULL, 100); nih_alloc_set_destructor (ptr, destructor_called); destructor_was_called = 0; } while (0); TEST_TRUE (destructor_was_called); /* Make sure that if a variable is referenced while in scope, it * is not freed. */ TEST_FEATURE ("with referenced variable"); parent = nih_alloc (NULL, 100); do { nih_local void *ptr; ptr = nih_alloc (NULL, 100); nih_ref (ptr, parent); _ptr = ptr; nih_alloc_set_destructor (ptr, destructor_called); destructor_was_called = 0; } while (0); TEST_FALSE (destructor_was_called); TEST_ALLOC_PARENT (_ptr, parent); nih_free (parent); /* Make sure we don't need to allocate the variable. */ TEST_FEATURE ("with NULL variable"); do { nih_local void *ptr = NULL; } while (0); }
/** * type_var_new: * @parent: parent object for new structure, * @type: C type, * @name: variable name. * * Allocates and returns a new TypeVar structure with the C type @type * and variable name @name, the strucure is not placed into any linked * list but will be removed from its containing list when freed. * * If @parent is not NULL, it should be a pointer to another object which * will be used as a parent for the returned structure. When all parents * of the returned structure are freed, the returned structure will also be * freed. * * Returns: the new TypeVar structure or NULL if insufficient memory. **/ TypeVar * type_var_new (const void *parent, const char *type, const char *name) { TypeVar *var; nih_assert (type != NULL); nih_assert (name != NULL); var = nih_new (parent, TypeVar); if (! var) return NULL; nih_list_init (&var->entry); var->type = nih_strdup (var, type); if (! var->type) { nih_free (var); return NULL; } var->name = nih_strdup (var, name); if (! var->name) { nih_free (var); return NULL; } var->array = FALSE; nih_alloc_set_destructor (var, nih_list_destroy); return var; }
/** * nih_error_steal: * * Returns the last unhandled error from the current context, and removes * it from the error context. To re-raise, it must be given to * nih_error_raise_error(). * * Returns: error object from current context. **/ NihError * nih_error_steal (void) { NihError *error; nih_assert (context_stack != NULL); nih_assert (CURRENT_CONTEXT->error != NULL); error = CURRENT_CONTEXT->error; CURRENT_CONTEXT->error = NULL; nih_alloc_set_destructor (error, NULL); return error; }
/** * session_new: * @parent: parent, * @chroot: full chroot path, * @user: user id. * * Create a new session. * * Return new Session, or NULL on error. **/ Session * session_new (const void *parent, const char *chroot, uid_t user) { Session *session; nih_assert ((chroot != NULL) || (user != 0)); session_init (); session = nih_new (parent, Session); if (! session) return NULL; nih_list_init (&session->entry); if (chroot) { session->chroot = nih_strdup (session, chroot); if (! session->chroot) { nih_free (session); return NULL; } } else { session->chroot = NULL; } session->user = user; session->conf_path = NULL; nih_alloc_set_destructor (session, nih_list_destroy); nih_list_add (sessions, &session->entry); return session; }
/** * _nih_error_raise_error: * @filename: filename where the error was raised, * @line: line number of @filename where the error was raised, * @function: function name the error was raised within, * @error: existing object to raise. * * Raises the existing error object in the current error context, * if an unhandled error already exists then an error message is emmitted * through the logging system; you should try to avoid this. * * This is normally used to raise a taken error that has not been handled, * or to raise a custom error object. * * The destructor of @error will be overwritten so that the context can * be cleared when the error is freed. * * This function should never be called directly, instead use the * nih_error_raise_error() macro to pass the correct arguments for @filename, * @line and @function. **/ void _nih_error_raise_error (const char *filename, int line, const char *function, NihError * error) { nih_assert (filename != NULL); nih_assert (line > 0); nih_assert (function != NULL); nih_assert (error != NULL); nih_assert (error->number > 0); nih_assert (error->message != NULL); nih_error_init (); nih_error_clear (); error->filename = filename; error->line = line; error->function = function; CURRENT_CONTEXT->error = error; nih_alloc_set_destructor (error, nih_error_destroy); }
void test_unref (void) { void *ptr1; void *ptr2; void *ptr3; TEST_FUNCTION ("nih_unref"); /* Check that we can remove a reference from an object with multiple * parents, which means the object will not be freed. */ TEST_FEATURE ("with multiple parents"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); ptr2 = nih_alloc (ptr1, 100); memset (ptr2, 'y', 100); ptr3 = nih_alloc (NULL, 100); memset (ptr2, 'z', 100); nih_ref (ptr2, ptr3); nih_alloc_set_destructor (ptr2, destructor_called); destructor_was_called = 0; nih_unref (ptr2, ptr1); TEST_FALSE (destructor_was_called); TEST_ALLOC_PARENT (ptr2, ptr3); nih_free (ptr1); nih_free (ptr3); /* Check that when we remove the last reference from an object, * the object is freed. */ TEST_FEATURE ("with last parent"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); ptr2 = nih_alloc (ptr1, 100); memset (ptr2, 'y', 100); nih_alloc_set_destructor (ptr2, destructor_called); destructor_was_called = 0; nih_unref (ptr2, ptr1); TEST_TRUE (destructor_was_called); nih_free (ptr1); /* Check that we have to remove the NULL reference from an object * for it to be freed. */ TEST_FEATURE ("with only NULL parent"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; nih_unref (ptr1, NULL); TEST_TRUE (destructor_was_called); /* Check that we can remove the NULL reference leaving a reference * to a different object. */ TEST_FEATURE ("with no parent and other parent"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); ptr2 = nih_alloc (NULL, 100); memset (ptr2, 'y', 100); nih_ref (ptr2, ptr1); nih_alloc_set_destructor (ptr2, destructor_called); destructor_was_called = 0; nih_unref (ptr2, NULL); TEST_FALSE (destructor_was_called); TEST_ALLOC_PARENT (ptr2, ptr1); TEST_FALSE (nih_alloc_parent (ptr2, NULL)); nih_free (ptr1); /* Check that an object with multiple NULL references must have * them both removed before it will be freed. */ TEST_FEATURE ("with multiple NULL parents"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); nih_ref (ptr1, NULL); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; nih_unref (ptr1, NULL); TEST_FALSE (destructor_was_called); nih_unref (ptr1, NULL); TEST_TRUE (destructor_was_called); /* Check that an object with multiple identical references must have * them both removed before it will be freed. */ TEST_FEATURE ("with multiple identical parents"); ptr1 = nih_alloc (NULL, 100); memset (ptr1, 'x', 100); ptr2 = nih_alloc (ptr1, 100); memset (ptr2, 'y', 100); nih_ref (ptr2, ptr1); nih_alloc_set_destructor (ptr2, destructor_called); destructor_was_called = 0; nih_unref (ptr2, ptr1); TEST_FALSE (destructor_was_called); nih_unref (ptr2, ptr1); TEST_TRUE (destructor_was_called); nih_free (ptr1); }
void test_discard (void) { void *ptr1; void *ptr2; int ret; TEST_FUNCTION ("nih_discard"); /* Check that nih_discard works if the block has no parent, freeing * the object. The destructor should get called and nih_discard * should return that return value. */ TEST_FEATURE ("with no parent"); ptr1 = nih_alloc (NULL, 10); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; ret = nih_discard (ptr1); TEST_TRUE (destructor_was_called); TEST_EQ (ret, 2); /* Check that nih_discard does nothing it the block has a parent. */ TEST_FEATURE ("with parent"); ptr2 = nih_alloc (NULL, 20); ptr1 = nih_alloc (ptr2, 10); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; ret = nih_discard (ptr1); TEST_FALSE (destructor_was_called); TEST_EQ (ret, 0); nih_free (ptr2); /* Check that the destructor on any children also gets called, which * is as good a indication as any that the children are being freed. */ TEST_FEATURE ("with destructor on child"); ptr1 = nih_alloc (NULL, 10); ptr2 = nih_alloc (ptr1, 10); nih_alloc_set_destructor (ptr2, child_destructor_called); child_destructor_was_called = 0; ret = nih_discard (ptr1); TEST_TRUE (child_destructor_was_called); TEST_EQ (ret, 0); /* Check that both destructors on parent and children are called, * and that the return value from nih_discard is that of the parent's. */ TEST_FEATURE ("with child and destructors"); ptr1 = nih_alloc (NULL, 10); ptr2 = nih_alloc (ptr1, 10); nih_alloc_set_destructor (ptr1, destructor_called); nih_alloc_set_destructor (ptr2, child_destructor_called); destructor_was_called = 0; child_destructor_was_called = 0; ret = nih_discard (ptr1); TEST_TRUE (destructor_was_called); TEST_TRUE (child_destructor_was_called); TEST_EQ (ret, 2); }
void test_free (void) { void * ptr1; void * ptr2; Parent *parent; int ret; TEST_FUNCTION ("nih_free"); /* Check that nih_free works if the block has no parent. The * destructor should get called and nih_free should return that * return value. */ TEST_FEATURE ("with no parent"); ptr1 = nih_alloc (NULL, 10); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; ret = nih_free (ptr1); TEST_TRUE (destructor_was_called); TEST_EQ (ret, 2); /* Check that nih_free works if the block has a parent. The * destructor should get called and nih_free should return that * return value. */ TEST_FEATURE ("with parent"); ptr2 = nih_alloc (NULL, 20); ptr1 = nih_alloc (ptr2, 10); nih_alloc_set_destructor (ptr1, destructor_called); destructor_was_called = 0; ret = nih_free (ptr1); TEST_TRUE (destructor_was_called); TEST_EQ (ret, 2); nih_free (ptr2); /* Check that the destructor on any children also gets called, which * is as good a indication as any that the children are being freed. */ TEST_FEATURE ("with destructor on child"); ptr1 = nih_alloc (NULL, 10); ptr2 = nih_alloc (ptr1, 10); nih_alloc_set_destructor (ptr2, child_destructor_called); child_destructor_was_called = 0; ret = nih_free (ptr1); TEST_TRUE (child_destructor_was_called); TEST_EQ (ret, 0); /* Check that both destructors on parent and children are called, * and that the return value from nih_free is that of the parent's. */ TEST_FEATURE ("with child and destructors"); ptr1 = nih_alloc (NULL, 10); ptr2 = nih_alloc (ptr1, 10); nih_alloc_set_destructor (ptr1, destructor_called); nih_alloc_set_destructor (ptr2, child_destructor_called); destructor_was_called = 0; child_destructor_was_called = 0; ret = nih_free (ptr1); TEST_TRUE (destructor_was_called); TEST_TRUE (child_destructor_was_called); TEST_EQ (ret, 2); /* Check that a child of an object may be included in a sibling * linked list allocated earlier. At the point the child destructor * is called, the sibling must not have been freed otherwise it * cannot cut itself out. */ TEST_FEATURE ("with child in older sibling list"); parent = nih_new (NULL, Parent); __nih_malloc = my_list_head_malloc; parent->list = nih_new (parent, NihList); nih_list_init (parent->list); __nih_malloc = malloc; parent->child = nih_new (parent, Child); nih_list_init (&parent->child->entry); nih_list_add (parent->list, &parent->child->entry); nih_alloc_set_destructor (parent->child, child_destructor_test); __nih_free = my_list_head_free; nih_free (parent); __nih_free = free; /* Check that a child of an object may be included in a sibling * linked list allocated later. At the point the child destructor * is called, the sibling must not have been freed otherwise it * cannot cut itself out. */ TEST_FEATURE ("with child in younger sibling list"); parent = nih_new (NULL, Parent); parent->child = nih_new (parent, Child); nih_list_init (&parent->child->entry); __nih_malloc = my_list_head_malloc; parent->list = nih_new (parent, NihList); nih_list_init (parent->list); __nih_malloc = malloc; nih_list_add (parent->list, &parent->child->entry); nih_alloc_set_destructor (parent->child, child_destructor_test); __nih_free = my_list_head_free; nih_free (parent); __nih_free = free; }