173 lines
4.0 KiB
C
173 lines
4.0 KiB
C
/* Copyright (C) 2010 Tony Garnock-Jones. All rights reserved. */
|
|
|
|
#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"
|
|
|
|
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;
|
|
}
|