#define UNLANG_IGNORE ((unlang_t *) -1)
+static unsigned int unlang_number = 0;
+
+/*
+ * For simplicity, this is just array[unlang_number]. Once we
+ * call unlang_thread_instantiate(), the "unlang_number" above MUST
+ * NOT change.
+ */
+static _Thread_local unlang_thread_t *unlang_thread_array;
+
+static fr_rb_tree_t *unlang_instruction_tree = NULL;
+
/* Here's where we recognize all of our keywords: first the rcodes, then the
* actions */
fr_table_num_sorted_t const mod_rcode_table[] = {
/*
- * Compile one entry of a module call.
+ * Compile one unlang instruction
*/
static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci)
{
module_method_t method;
bool policy;
unlang_op_compile_t compile;
+ unlang_t *c;
if (cf_item_is_section(ci)) {
cs = cf_item_to_section(ci);
name = cf_section_name1(cs);
compile = (unlang_op_compile_t) fr_table_value_by_str(unlang_section_keywords, name, NULL);
- if (compile) return compile(parent, unlang_ctx, ci);
+ if (compile) {
+ c = compile(parent, unlang_ctx, ci);
+ allocate_number:
+ if (!c) return NULL;
+ if (c == UNLANG_IGNORE) return UNLANG_IGNORE;
+
+ c->number = unlang_number++;
+ return c;
+ }
/*
* Forbid pair keywords as section names, e.g. "break { ... }"
*/
if (((name[0] == '%') && ((name[1] == '{') || (name[1] == '('))) ||
(cf_pair_attr_quote(cp) == T_BACK_QUOTED_STRING)) {
- return compile_tmpl(parent, unlang_ctx, cp);
+ c = compile_tmpl(parent, unlang_ctx, cp);
+ goto allocate_number;
}
compile = (unlang_op_compile_t)fr_table_value_by_str(unlang_pair_keywords, name, NULL); /* Cast for -Wpedantic */
- if (compile) return compile(parent, unlang_ctx, ci);
+ if (compile) {
+ c = compile(parent, unlang_ctx, ci);
+ goto allocate_number;
+ }
/*
* Forbid section keywords as pair names, e.g. bare "update"
* named redundant / load-balance subsection defined in
* "instantiate".
*/
- if (subcs) return compile_function(parent, unlang_ctx, ci, subcs, component, policy);
+ if (subcs) {
+ c = compile_function(parent, unlang_ctx, ci, subcs, component, policy);
+ goto allocate_number;
+ }
/*
* Not a function. It must be a real module.
&unlang_ctx2.section_name1, &unlang_ctx2.section_name2,
realname);
if (inst) {
- return compile_module(parent, &unlang_ctx2, ci, inst, method, realname);
+ c = compile_module(parent, &unlang_ctx2, ci, inst, method, realname);
+ goto allocate_number;
}
/*
.type = UNLANG_TYPE_GROUP,
.len = sizeof(unlang_group_t),
.type_name = "unlang_group_t",
- };
+ };
+
/*
* Don't compile it twice, and don't print out debug
* messages twice.
return (fr_table_value_by_str(unlang_pair_keywords, name, NULL) != NULL);
}
+
+static int8_t instruction_cmp(void const *one, void const *two)
+{
+ unlang_t const *a = one;
+ unlang_t const *b = two;
+
+ return CMP(a->number, b->number);
+}
+
+
+void unlang_compile_init()
+{
+ unlang_instruction_tree = fr_rb_talloc_alloc(NULL, unlang_t, instruction_cmp, NULL);
+}
+
+void unlang_compile_free()
+{
+ TALLOC_FREE(unlang_instruction_tree);
+}
+
+
+/** Create thread-specific data structures for unlang
+ *
+ */
+int unlang_thread_instantiate(TALLOC_CTX *ctx)
+{
+ fr_rb_iter_inorder_t iter;
+ unlang_t *instruction;
+
+ if (unlang_thread_array) {
+ fr_strerror_const("already initialized");
+ return -1;
+ }
+
+ MEM(unlang_thread_array = talloc_zero_array(ctx, unlang_thread_t, unlang_number + 1));
+// talloc_set_destructor(unlang_thread_array, _unlang_thread_array_free);
+
+ /*
+ * Instantiate each instruction with thread-specific data.
+ */
+ for (instruction = fr_rb_iter_init_inorder(&iter, unlang_instruction_tree);
+ instruction;
+ instruction = fr_rb_iter_next_inorder(&iter)) {
+ unlang_op_t *op;
+
+ unlang_thread_array[instruction->number].instruction = instruction;
+
+ op = &unlang_ops[instruction->type];
+ if (!op->thread_instantiate) continue;
+
+ /*
+ * Allocate any thread-specific instance data.
+ */
+ if (op->thread_inst_size) {
+ MEM(unlang_thread_array[instruction->number].thread_inst = talloc_zero_array(unlang_thread_array, uint8_t, op->thread_inst_size));
+ talloc_set_name_const(unlang_thread_array[instruction->number].thread_inst, op->thread_inst_type);
+ }
+
+ if (op->thread_instantiate(instruction, unlang_thread_array[instruction->number].thread_inst) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
char const *debug_name; //!< Printed in log messages when the node is executed.
unlang_type_t type; //!< The specialisation of this node.
bool closed; //!< whether or not this section is closed to new statements
+ unsigned int number; //!< unique node number
unlang_actions_t actions; //!< Priorities, etc. for the various return codes.
};
*/
typedef void (*unlang_dump_t)(request_t *request, unlang_stack_frame_t *frame);
+typedef int (*unlang_thread_instantiate_t)(unlang_t const *instruction, void *thread_inst);
+
/** An unlang operation
*
* These are like the opcodes in other interpreters. Each operation, when executed
unlang_dump_t dump; //!< Dump additional information about the frame state.
+ unlang_thread_instantiate_t thread_instantiate; //!< per-thread instantiation function
+ size_t thread_inst_size;
+ char const *thread_inst_type;
+
+
bool debug_braces; //!< Whether the operation needs to print braces
///< in debug mode.
size_t frame_state_pool_size; //!< The total size of the pool to alloc.
} unlang_op_t;
+typedef struct {
+ unlang_t const *instruction; //!< instruction which we're executing
+ void *thread_inst; //!< thread-specific instance data
+ /*
+ * And various other stats
+ */
+} unlang_thread_t;
+
typedef struct {
request_t *request;
int depth; //!< of this retry structure
int unlang_op_init(void);
void unlang_op_free(void);
+
/** @} */
/** @name io shims