/* Copyright (C) 2010 Tony Garnock-Jones. All rights reserved. */ #include #include #include #include #include #include #include "cmsg_private.h" #include "ref.h" #include "sexp.h" #include "harness.h" #include "sexpio.h" #include "hashtable.h" #include "node.h" #include "meta.h" static hashtable_t node_class_table; static hashtable_t directory; static void *node_incref(void *arg) { return INCREF((node_t *) arg); } static void node_decref(void *arg) { DECREF((node_t *) arg, node_destructor); } void init_node(void) { init_hashtable(&node_class_table, 31, NULL, NULL); init_hashtable(&directory, 10007, node_incref, node_decref); } void register_node_class(node_class_t *nc) { cmsg_bytes_t key = cmsg_cstring_bytes(nc->name); if (hashtable_contains(&node_class_table, key)) { die("Duplicate node class name %s\n", nc->name); } hashtable_put(&node_class_table, key, nc); } node_class_t *lookup_node_class(cmsg_bytes_t name) { node_class_t *nc = NULL; hashtable_get(&node_class_table, name, (void **) &nc); return nc; } static void init_node_names(node_t *n) { init_hashtable(&n->names, 5, NULL, NULL); } node_t *new_node(node_class_t *nc, sexp_t *args, sexp_t **error_out) { node_t *n = malloc(sizeof(*n)); n->refcount = ZERO_REFCOUNT(); n->node_class = nc; n->extension = NULL; init_node_names(n); if (nc->extend != NULL) { sexp_t *error = nc->extend(n, args); if (error != NULL) { node_destructor(n); if (error_out != NULL) { *error_out = error; } else { warn("Creating node of class %s failed with ", nc->name); sexp_writeln(stderr_h, error); } return NULL; } } return n; } void node_destructor(node_t *n) { if (n->node_class->destroy != NULL) { n->node_class->destroy(n); } unbind_all_names_for_node(n); destroy_hashtable(&n->names); free(n); } node_t *lookup_node(cmsg_bytes_t name) { node_t *n = NULL; hashtable_get(&directory, name, (void **) &n); return n; } static void announce_binding(cmsg_bytes_t name, int onoff) { sexp_t *filter = sexp_bytes(name); INCREF(filter); announce_subscription(sexp_empty_bytes, filter, sexp_empty_bytes, sexp_empty_bytes, onoff); DECREF(filter, sexp_destructor); } int bind_node(cmsg_bytes_t name, node_t *n) { if (name.len == 0) { warn("Binding to empty name forbidden\n"); return 0; } if (hashtable_contains(&directory, name)) { return 0; } hashtable_put(&directory, name, n); hashtable_put(&n->names, name, NULL); info("Binding node <<%.*s>> of class %s\n", name.len, name.bytes, n->node_class->name); announce_binding(name, 1); return 1; } int unbind_node(cmsg_bytes_t name) { node_t *n = NULL; hashtable_get(&directory, name, (void **) &n); if (n == NULL) { return 0; } else { info("Unbinding node <<%.*s>> of class %s\n", name.len, name.bytes, n->node_class->name); hashtable_erase(&n->names, name); hashtable_erase(&directory, name); announce_binding(name, 0); return 1; } } static void unbind_on_destroy(void *context, cmsg_bytes_t key, void *value) { unbind_node(key); } void unbind_all_names_for_node(node_t *n) { hashtable_t names = n->names; init_node_names(n); hashtable_foreach(&names, unbind_on_destroy, NULL); destroy_hashtable(&names); } int post_node(cmsg_bytes_t node, cmsg_bytes_t name, sexp_t *body, sexp_t *token) { static sexp_t *post_atom = NULL; sexp_t *msg = NULL; int result; if (post_atom == NULL) { post_atom = INCREF(sexp_cstring("post")); } msg = sexp_cons(token, msg); msg = sexp_cons(body, msg); msg = sexp_cons(sexp_bytes(name), msg); msg = sexp_cons(post_atom, msg); INCREF(msg); result = send_node(node, msg); DECREF(msg, sexp_destructor); return result; } int send_node(cmsg_bytes_t node, sexp_t *message) { node_t *n = lookup_node(node); if (n == NULL) { return 0; } n->node_class->handle_message(n, message); return 1; }