toaruos/kernel/ds/tree.c
Kevin Lange 4d8335ad75 Implement proper wait/waitpid.
This is a pretty big commit, so let's run through it in parts:

- All of the userspace changes are to switch away from syscall_wait
  Mostly, this is to waitpid; some things were tweaked to do things
  "properly" instead of waiting for particular processes. Init has
  been fixed to do a proper spin wait.
- syscall_wait is gone - as are its uses. newlib bindings have been
  using just waitpid for a while now.
- waitpid now performs like a Unix waitpid
- process reaping is no longer a "do this on next change thing":
  it happens when a process is waited on, like it should
  (That means we can have real zombies: terminated processes that
  have not yet been waited on)
- Reparenting of children to init has been implemented, so you
  can fork-daemonize!

Overall, this is pretty big... So I hope it doesn't break everything.
2014-04-27 01:37:33 -07:00

190 lines
5.3 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* General-purpose tree implementation
*/
#include "tree.h"
#ifdef _KERNEL_
# include <system.h>
#else
# include <stddef.h>
# include <stdlib.h>
#endif
tree_t * tree_create(void) {
/* Create a new tree */
tree_t * out = malloc(sizeof(tree_t));
out->nodes = 0;
out->root = NULL;
return out;
}
void tree_set_root(tree_t * tree, void * value) {
/* Set the root node for a new tree. */
tree_node_t * root = tree_node_create(value);
tree->root = root;
tree->nodes = 1;
}
void tree_node_destroy(tree_node_t * node) {
/* Free the contents of a node and its children, but not the nodes themselves */
foreach(child, node->children) {
tree_node_destroy((tree_node_t *)child->value);
}
free(node->value);
}
void tree_destroy(tree_t * tree) {
/* Free the contents of a tree, but not the nodes */
if (tree->root) {
tree_node_destroy(tree->root);
}
}
void tree_node_free(tree_node_t * node) {
/* Free a node and its children, but not their contents */
if (!node) return;
foreach(child, node->children) {
tree_node_free(child->value);
}
free(node);
}
void tree_free(tree_t * tree) {
/* Free all of the nodes in a tree, but not their contents */
tree_node_free(tree->root);
}
tree_node_t * tree_node_create(void * value) {
/* Create a new tree node pointing to the given value */
tree_node_t * out = malloc(sizeof(tree_node_t));
out->value = value;
out->children = list_create();
out->parent = NULL;
return out;
}
void tree_node_insert_child_node(tree_t * tree, tree_node_t * parent, tree_node_t * node) {
/* Insert a node as a child of parent */
list_insert(parent->children, node);
node->parent = parent;
tree->nodes++;
}
tree_node_t * tree_node_insert_child(tree_t * tree, tree_node_t * parent, void * value) {
/* Insert a (fresh) node as a child of parent */
tree_node_t * out = tree_node_create(value);
tree_node_insert_child_node(tree, parent, out);
return out;
}
tree_node_t * tree_node_find_parent(tree_node_t * haystack, tree_node_t * needle) {
/* Recursive node part of tree_find_parent */
tree_node_t * found = NULL;
foreach(child, haystack->children) {
if (child->value == needle) {
return haystack;
}
found = tree_node_find_parent((tree_node_t *)child->value, needle);
if (found) {
break;
}
}
return found;
}
tree_node_t * tree_find_parent(tree_t * tree, tree_node_t * node) {
/* Return the parent of a node, inefficiently. */
if (!tree->root) return NULL;
return tree_node_find_parent(tree->root, node);
}
size_t tree_count_children(tree_node_t * node) {
/* return the number of children this node has */
if (!node) return 0;
if (!node->children) return 0;
size_t out = node->children->length;
foreach(child, node->children) {
out += tree_count_children((tree_node_t *)child->value);
}
return out;
}
void tree_node_parent_remove(tree_t * tree, tree_node_t * parent, tree_node_t * node) {
/* remove a node when we know its parent; update node counts for the tree */
tree->nodes -= tree_count_children(node) + 1;
list_delete(parent->children, list_find(parent->children, node));
tree_node_free(node);
}
void tree_node_remove(tree_t * tree, tree_node_t * node) {
/* remove an entire branch given its root */
tree_node_t * parent = node->parent;
if (!parent) {
if (node == tree->root) {
tree->nodes = 0;
tree->root = NULL;
tree_node_free(node);
}
}
tree_node_parent_remove(tree, parent, node);
}
void tree_remove(tree_t * tree, tree_node_t * node) {
/* Remove this node and move its children into its parent's list of children */
tree_node_t * parent = node->parent;
/* This is something we just can't do. We don't know how to merge our
* children into our "parent" because then we'd have more than one root node.
* A good way to think about this is actually what this tree struct
* primarily exists for: processes. Trying to remove the root is equivalent
* to trying to kill init! Which is bad. We immediately fault on such
* a case anyway ("Tried to kill init, shutting down!").
*/
if (!parent) return;
tree->nodes--;
list_delete(parent->children, list_find(parent->children, node));
foreach(child, node->children) {
/* Reassign the parents */
((tree_node_t *)child->value)->parent = parent;
}
list_merge(parent->children, node->children);
free(node);
}
void tree_remove_reparent_root(tree_t * tree, tree_node_t * node) {
/* Remove this node and move its children into the root children */
tree_node_t * parent = node->parent;
if (!parent) return;
tree->nodes--;
list_delete(parent->children, list_find(parent->children, node));
foreach(child, node->children) {
/* Reassign the parents */
((tree_node_t *)child->value)->parent = tree->root;
}
list_merge(tree->root->children, node->children);
free(node);
}
void tree_break_off(tree_t * tree, tree_node_t * node) {
tree_node_t * parent = node->parent;
if (!parent) return;
list_delete(parent->children, list_find(parent->children, node));
}
tree_node_t * tree_node_find(tree_node_t * node, void * search, tree_comparator_t comparator) {
if (comparator(node->value,search)) {
return node;
}
tree_node_t * found;
foreach(child, node->children) {
found = tree_node_find((tree_node_t *)child->value, search, comparator);
if (found) return found;
}
return NULL;
}
tree_node_t * tree_find(tree_t * tree, void * value, tree_comparator_t comparator) {
return tree_node_find(tree->root, value, comparator);
}