hop-2012/experiments/cmsg/node.c

195 lines
5.0 KiB
C

/* Copyright 2010, 2011, 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>.
*
* This file is part of Hop.
*
* Hop is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hop is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hop. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <ucontext.h>
#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"
#include "messages.h"
static cmsg_bytes_t _container_name;
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(cmsg_bytes_t container_name) {
if (container_name.len == 0) {
unsigned char buf[CMSG_UUID_BUF_SIZE];
gen_uuid(buf);
_container_name = cmsg_bytes_malloc_dup(CMSG_BYTES(CMSG_UUID_BUF_SIZE, buf));
} else {
_container_name = cmsg_bytes_malloc_dup(container_name);
}
info("Local container name is <<%.*s>>\n", _container_name.len, _container_name.bytes);
init_hashtable(&node_class_table,
31,
NULL,
NULL);
init_hashtable(&directory,
10007,
node_incref,
node_decref);
}
cmsg_bytes_t local_container_name(void) {
return _container_name;
}
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) {
return send_node_release(node, message_post(sexp_bytes(name), body, token));
}
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;
}
int send_node_release(cmsg_bytes_t node, sexp_t *message) {
int result;
INCREF(message);
result = send_node(node, message);
DECREF(message, sexp_destructor);
return result;
}