/** usage: rule [ list inputs ] action [ name ... ] [ work_type ... ] [ target ... ] [ parallelism ... ] [ soft_target ... ] keyword args are optional DEFAULTS: name=<first token of action plus output list> type=TURBINE_ACTION_WORK target=TURBINE_RANK_ANY parallelism=1 The name is just for debugging soft_target will enable soft targeting mode and specify the target rank */ static int Turbine_Rule_Cmd(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) { const int BASIC_ARGS = 3; TCL_CONDITION(objc >= BASIC_ARGS, "turbine::c::rule requires at least %i args!", BASIC_ARGS); int rc; int inputs = 0, input_pairs = 0; adlb_datum_id input_list[TCL_TURBINE_MAX_INPUTS]; adlb_datum_id_sub input_pair_list[TCL_TURBINE_MAX_INPUTS]; char name_buffer[TURBINE_NAME_MAX]; // Get the action string int action_len; char* action = Tcl_GetStringFromObj(objv[2], &action_len); assert(action); action_len++; // Include null terminator struct rule_opts opts = {NULL, 0, 0, ADLB_DEFAULT_PUT_OPTS}; if (objc > BASIC_ARGS) { // User gave us a list of optional args rc = rule_opts_from_list(interp, objv, &opts, objv + BASIC_ARGS, objc - BASIC_ARGS, name_buffer, TURBINE_NAME_MAX, action); TCL_CHECK(rc); } else { rule_set_opts_default(&opts, action, name_buffer, TURBINE_NAME_MAX); } // Get the input list - done last so we can report name on error rc = turbine_extract_ids(interp, objv, objv[1], TCL_TURBINE_MAX_INPUTS, input_list, &inputs, input_pair_list, &input_pairs); TCL_CHECK_MSG(rc, "could not parse inputs list as ids or id/subscript " "pairs:\n in rule: %s inputs: \"%s\"", opts.name, Tcl_GetString(objv[1])); opts.opts.priority = ADLB_curr_priority; adlb_code ac = ADLB_Dput(action, action_len, opts.target, adlb_comm_rank, opts.work_type, opts.opts, opts.name, input_list, inputs, input_pair_list, input_pairs); TCL_CONDITION(ac == ADLB_SUCCESS, "could not process rule!"); // Free subscripts that were allocated for (int i = 0; i < input_pairs; i++) { free((void*)input_pair_list[i].subscript.key); } return TCL_OK; }
/* * Process worker keyword arguments. * Only modifies arguments if the keyword arg was encountered, i.e. * caller should initialise the arguments to their default values. * * This will validate any values received. */ static int worker_keyword_args(Tcl_Interp *interp, Tcl_Obj *const objv[], Tcl_Obj *dict, int *buffer_count, int *buffer_size) { int rc; Tcl_DictSearch search; Tcl_Obj *key_obj, *val_obj; int done; rc = Tcl_DictObjFirst(interp, dict, &search, &key_obj, &val_obj, &done); TCL_CHECK_MSG(rc, "Error iterating over dict: %s", Tcl_GetString(dict)); for (; !done; Tcl_DictObjNext(&search, &key_obj, &val_obj, &done)) { const char *key = Tcl_GetString(key_obj); if (strcmp(key, "buffer_count") == 0) { rc = Tcl_GetIntFromObj(interp, val_obj, buffer_count); TCL_CHECK_MSG(rc, "Expected integer value for buffer_count"); TCL_CONDITION(*buffer_count >= 0, "Positive value for " "buffer_count expected, but got %i", *buffer_count); } else if (strcmp(key, "buffer_size") == 0) { rc = Tcl_GetIntFromObj(interp, val_obj, buffer_size); TCL_CHECK_MSG(rc, "Expected integer value for buffer_size"); TCL_CONDITION(*buffer_size >= 0, "Positive value for buffer_size " "expected, but got %i", *buffer_size); } else { TCL_RETURN_ERROR("Invalid key for key-value argument: %s\n", key); return TCL_ERROR; } } Tcl_DictObjDone(&search); return TCL_OK; }
/* Extract IDs and ID/Sub pairs Ownership of subscript memory in id_subs is passed to caller. */ static int turbine_extract_ids(Tcl_Interp* interp, Tcl_Obj *const objv[], Tcl_Obj* list, int max, adlb_datum_id* ids, int* id_count, adlb_datum_id_sub* id_subs, int* id_sub_count) { Tcl_Obj** entry; int n; int code = Tcl_ListObjGetElements(interp, list, &n, &entry); assert(code == TCL_OK); TCL_CONDITION(n < max, "Rule IDs exceed supported max: %i > %i", n, max); for (int i = 0; i < n; i++) { Tcl_Obj *obj = entry[i]; // Parse, allocating memory for subscripts tcl_adlb_handle handle; code = ADLB_PARSE_HANDLE(obj, &handle, false); TCL_CHECK_MSG(code, "Error parsing handle %s", Tcl_GetString(obj)); if (handle.sub.val.key == NULL) { ids[(*id_count)++] = handle.id; } else { adlb_datum_id_sub *pair = &id_subs[(*id_sub_count)++]; pair->id = handle.id; pair->subscript.length = handle.sub.val.length; // check if key memory was allocated and owned by us if (handle.sub.buf.data == handle.sub.val.key) { pair->subscript.key = handle.sub.buf.data; } else { // Don't own data, alloc and copy // TODO: avoid malloc somehow? char *tmp_key = malloc(handle.sub.val.length); TCL_MALLOC_CHECK(tmp_key); memcpy(tmp_key, handle.sub.val.key, handle.sub.val.length); pair->subscript.key = tmp_key; } } } return TURBINE_SUCCESS; }
static int Python_Eval_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { TCL_ARGS(4); int rc; int persist; rc = Tcl_GetBooleanFromObj(interp, objv[1], &persist); TCL_CHECK_MSG(rc, "first arg should be integer!"); char* code = Tcl_GetString(objv[2]); char* expression = Tcl_GetString(objv[3]); Tcl_Obj* result = NULL; rc = python_eval(persist, code, expression, &result); TCL_CHECK(rc); Tcl_SetObjResult(interp, result); return TCL_OK; }
/** usage turbine::cache_store $td $type [ extra type info ] $value */ static int Turbine_Cache_Store_Cmd(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) { TCL_CONDITION(objc >= 4, "requires >= 4 args"); turbine_datum_id td; void* data = NULL; size_t length = 0; int argpos = 1; int error; const char *subscript; size_t subscript_len; error = ADLB_EXTRACT_HANDLE(objv[argpos++], &td, &subscript, &subscript_len); TCL_CHECK(error); if (subscript_len != 0) { // TODO: handle caching subscripts return TCL_OK; } adlb_data_type type; adlb_type_extra extra; error = adlb_type_from_obj_extra(interp, objv, objv[argpos++], &type, &extra); TCL_CHECK(error); TCL_CONDITION(argpos < objc, "not enough arguments"); error = turbine_tclobj2bin(interp, objv, td, type, extra, objv[argpos++], false, &data, &length); TCL_CHECK_MSG(error, "object extraction failed: <%"PRId64">", td); TCL_CONDITION(argpos == objc, "extra trailing arguments from %i", argpos); turbine_code rc = turbine_cache_store(td, type, data, length); TURBINE_CHECK(rc, "cache store failed: %"PRId64"", td); return TCL_OK; }
// Allocate a binary buffer and serialize a tcl object into it // for the specified ADLB type static int turbine_tclobj2bin(Tcl_Interp* interp, Tcl_Obj *const objv[], turbine_datum_id td, turbine_type type, adlb_type_extra extra, Tcl_Obj* obj, bool canonicalize, void** result, size_t* length) { adlb_binary_data data; int rc = adlb_tclobj2bin(interp, objv, type, extra, obj, canonicalize, NULL, &data); TCL_CHECK_MSG(rc, "failed serializing tcl object to ADLB <%"PRId64">: " "\"%s\"", td, Tcl_GetString(obj)); // Ensure we have ownership of a malloced buffer with the data adlb_data_code dc = ADLB_Own_data(NULL, &data); TCL_CONDITION(dc == ADLB_DATA_SUCCESS, "allocating binary buffer for " "<%"PRId64"> failed: %s", td, Tcl_GetString(obj)); assert(data.caller_data != NULL); *result = data.caller_data; *length = data.length; return TCL_OK; }
/** Translate one key value pair into an opts entry. Note that caller is responsible for copying any strings. */ static inline int rule_opt_from_kv(Tcl_Interp* interp, Tcl_Obj *const objv[], struct rule_opts* opts, Tcl_Obj* key, Tcl_Obj* val) { char* k = Tcl_GetString(key); int rc; switch (k[0]) { case 'a': if (strcmp(k, "accuracy") == 0) { return adlb_parse_accuracy(interp, val, &opts->opts.accuracy); } break; case 'n': if (strcmp(k, "name") == 0) { opts->name = Tcl_GetString(val); return TCL_OK; // printf("name: %s\n", opts->name); } break; case 'p': if (strcmp(k, "parallelism") == 0) { int t; rc = Tcl_GetIntFromObj(interp, val, &t); TCL_CHECK_MSG(rc, "parallelism argument must be integer"); opts->opts.parallelism = t; return TCL_OK; } break; case 't': if (strcmp(k, "target") == 0) { int t; rc = Tcl_GetIntFromObj(interp, val, &t); TCL_CHECK_MSG(rc, "target argument must be integer"); opts->target = t; return TCL_OK; } else if (strcmp(k, "type") == 0) { int t; rc = Tcl_GetIntFromObj(interp, val, &t); TCL_CHECK_MSG(rc, "type argument must be integer"); if (t == TURBINE_ADLB_WORK_TYPE_LOCAL) { // Ensure sent back here opts->work_type = TURBINE_ADLB_WORK_TYPE_WORK; opts->target = adlb_comm_rank; opts->opts.strictness = ADLB_TGT_STRICT_HARD; } else { opts->work_type = t; } return TCL_OK; } break; case 's': if (strcmp(k, "strictness") == 0) { return adlb_parse_strictness(interp, val, &opts->opts.strictness); } break; } TCL_RETURN_ERROR("rule options: unknown key: %s", k); return TCL_ERROR; // unreachable }