job::job(std::shared_ptr<job_metadata> job_meta, std::shared_ptr<worker_config> worker_conf, fs::path working_directory, fs::path source_path, fs::path result_path, std::shared_ptr<task_factory_interface> factory, std::shared_ptr<progress_callback_interface> progr_callback) : job_meta_(job_meta), worker_config_(worker_conf), working_directory_(working_directory), source_path_(source_path), result_path_(result_path), factory_(factory), progress_callback_(progr_callback) { // check construction parameters if they are in right format if (job_meta_ == nullptr) { throw job_exception("Job configuration cannot be null"); } else if (worker_config_ == nullptr) { throw job_exception("Worker configuration cannot be null"); } else if (factory_ == nullptr) { throw job_exception("Task factory pointer cannot be null"); } // if progress callback is null, we have to use default one init_progress_callback(); // check injected directories check_job_dirs(); // prepare variables which will be used in job config prepare_job_vars(); // construct system logger for this job init_logger(); // build job from given job configuration build_job(); }
void job_receiver::setFilenamePattern(const std::string& filenamepattern) { if (jobfilename!=NULL) delete jobfilename; jobfilenamepattern=filenamepattern; jobfilenamesize=filenamepattern.size()+20; jobfilename=new char[jobfilenamesize]; if (jobfilename==NULL) throw job_exception("could not allocate memory for filename"); }
wait_job::wait_job(const size_t n, const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs): control(n) { XMLCh* attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("time"); XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* length_attr=attrs->getNamedItem(attr_name); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name); if (length_attr==NULL) { attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("length"); length_attr=attrs->getNamedItem((XMLCh*)"time"); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name); if (length_attr==NULL) throw job_exception("length or time attribute required"); } char* length_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(length_attr->getNodeValue()); char* parse_end=NULL; sec=strtod(length_data,&parse_end); int length_check=strlen(length_data)-(parse_end-length_data); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_data); if (length_check!=0 || sec<=0) throw job_exception("wait: length attribute requires number>0"); }
single_pulse_experiment::single_pulse_experiment(size_t n,const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs): experiment(n) { t_before=0; frequency=1e4; sample_frequency=5e6; samples=1<<12; dac_value=3; // find pulse length XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* length_attr=attrs->getNamedItem((XMLCh*)"length"); if (length_attr==NULL) throw job_exception("length attribute required"); char* length_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(length_attr->getNodeValue()); pulse_length=strtod(length_data,NULL); }
std::string job::parse_job_var(const std::string &src) { std::string res = src; size_t start = 0; while ((start = res.find("${", start)) != std::string::npos) { size_t end = res.find("}", start + 1); size_t len = end - start - 2; if (end == std::string::npos) { throw job_exception("Not closed variable name: " + res.substr(start)); } if (job_variables_.find(res.substr(start + 2, len)) != job_variables_.end()) { // we found variable and can replace it in string res.replace(start, end - start + 1, job_variables_.at(res.substr(start + 2, len))); } // start++; // just to be sure we're not in cycle } return res; }
job_receiver::job_receiver(std::string the_jobfilenamepattern) { try { XERCES_CPP_NAMESPACE_QUALIFIER XMLPlatformUtils::Initialize(); } catch (const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) { char* ini_error=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); job_exception new_exception(std::string("xerces initialisation error: ")+ini_error); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&ini_error); throw new_exception; } jobfilename=NULL; setFilenamePattern(the_jobfilenamepattern); parser=new XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser(); if (parser==NULL) { delete jobfilename; throw job_exception("could not allocate parser"); } parser->setValidationScheme(XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser::Val_Always); parser->setDoNamespaces(true); errHandler = (XERCES_CPP_NAMESPACE_QUALIFIER ErrorHandler*) new XERCES_CPP_NAMESPACE_QUALIFIER HandlerBase(); parser->setErrorHandler(errHandler); }
void job::connect_tasks( std::shared_ptr<task_base> root, std::map<std::string, std::shared_ptr<task_base>> &unconn_tasks) { for (auto &elem : unconn_tasks) { const std::vector<std::string> &depend = elem.second->get_dependencies(); // connect all suitable task underneath root if (depend.size() == 0) { root->add_children(elem.second); elem.second->add_parent(root); } for (size_t i = 0; i < depend.size(); ++i) { try { auto ptr = unconn_tasks.at(depend.at(i)); ptr->add_children(elem.second); elem.second->add_parent(ptr); } catch (std::out_of_range) { throw job_exception("Non existing task-id (" + depend.at(i) + ") in dependency list"); } } } }
void job::check_job_dirs() { if (!fs::exists(working_directory_)) { throw job_exception("Working directory not exists"); } else if (!fs::is_directory(working_directory_)) { throw job_exception("Working directory is not directory"); } if (!fs::exists(source_path_)) { throw job_exception("Source code directory not exists"); } else if (!fs::is_directory(source_path_)) { throw job_exception("Source code directory is not directory"); } else if (fs::is_empty(source_path_)) { throw job_exception("Source code directory is empty"); } // check result files directory if (!fs::exists(result_path_)) { throw job_exception("Result files directory not exists"); } else if (!fs::is_directory(result_path_)) { throw job_exception("Result files directory is not directory"); } }
job* job_receiver::receive(const std::string& filename) { try { parser->parse(filename.c_str()); } catch(const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) { char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); job_exception je(std::string("XML error: ")+message); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); throw je; } catch(const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& toCatch) { char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.msg); job_exception je(std::string("XML DOM error: ")+message); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); throw je; } catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& toCatch) { // more verbose for parser errors char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); job_exception je(std::string("XML SAX Parser error: ")+message); char location[100]; snprintf(location,sizeof(location),", line %ld column %ld",toCatch.getLineNumber(),toCatch.getColumnNumber()); je.append(location); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); throw je; } catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXException& toCatch) { char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); job_exception je(std::string("XML SAX error: ")+message); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); throw je; } // extract root element, root attributes and root name XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc=parser->getDocument(); if (doc==NULL) throw job_exception("xml job document not found"); XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* rootelement=doc->getDocumentElement(); if (rootelement==NULL) throw job_exception("xml job root document not found"); char* docname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(rootelement->getNodeName()); XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* rootattrs=rootelement->getAttributes(); // check the job number XMLCh* docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no"); XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* jobno_attr=rootattrs->getNamedItem(docnoname); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname); if (jobno_attr==NULL) { docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no"); jobno_attr=rootattrs->getNamedItem(docnoname); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname); } size_t no=0; if (jobno_attr==NULL) fprintf(stderr,"Warning: job %" SIZETPRINTFLETTER ": root element has no job number\n",no); else { char* docno=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(jobno_attr->getNodeValue()); no=strtoul(docno,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docno); } job* this_job=NULL; // determine the job type by name if (strcasecmp(docname,"quit")==0) { this_job=new quit_job(no); } else if (strcasecmp(docname,"nop")==0) { this_job=new do_nothing_job(no); } else if (strcasecmp(docname,"pause")==0) { this_job=new pause_job(no); } /* pause */ else if (strcasecmp(docname,"restart")==0) { this_job=new restart_job(no); } /* restart */ else if (strcasecmp(docname,"wait")==0) { this_job=new wait_job(no,rootattrs); } /* wait */ else if (strcasecmp(docname,"experiment")==0) { try { this_job=new experiment(no, rootelement); } catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) { char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg); char domerrno[5]; snprintf(domerrno,5,"%d",de.code); job_exception je("sorry, something happend while parsing experiment job: "); je.append(domerrmsg); je.append(", code "); je.append(domerrno); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg); // cleanup missing throw je; } } else if (strcasecmp(docname,"configuration")==0) { try { this_job=new configuration(no, rootelement); } catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) { char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg); char domerrno[5]; snprintf(domerrno,5,"%d",de.code); job_exception je("sorry, something happend while parsing configuration job: "); je.append(domerrmsg); je.append(", code "); je.append(domerrno); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg); // cleanup missing throw je; } } XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docname); parser->reset(); parser->resetDocument(); parser->resetDocumentPool(); return this_job; }
state_atom* experiment::state_factory(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element) { if (element==NULL) return NULL; char* my_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(element->getNodeName()); if(strcasecmp(my_name,"state")==0) { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); // read parameters char* length_string=get_parameter(element,"length","time","t","l",NULL); if (length_string==NULL) throw job_exception("state requires length"); char* length_string_end; double length=strtod(length_string,&length_string_end); int result=strlen(length_string)-(length_string_end-length_string); // here, we could search for us, s or ms, d or min XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_string); // but now we do not accept anything if (result!=0) throw job_exception("state requires length as floating point value"); if (length<0) throw job_exception("state's length must be non-negative"); state* new_state=new state(length); new_state->parent=NULL; // read states defintions XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild(); while (one_child!=NULL) { if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { state_atom* new_one=state_factory(dynamic_cast<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child)); if (new_one!=NULL) new_state->push_back(new_one); } one_child=one_child->getNextSibling(); } return new_state; } else if(strcasecmp(my_name,"sequent")==0) { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); // read parameters size_t repeat=1; // read parameters char* repeat_string=get_parameter(element,"repeat","n",NULL); if (repeat_string!=NULL) { char* repeat_string_end; double repeat_d=strtod(repeat_string,&repeat_string_end); int result=strlen(repeat_string)-(repeat_string_end-repeat_string); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&repeat_string); // but now we do not accept anything if (result!=0 || repeat_d<0) throw job_exception("sequent requires loop count as a nonnegative integer value"); repeat=(size_t)floor(repeat_d); if (1.0*repeat-repeat_d!=0) fprintf(stderr,"rounding non integer towards lower integer"); } state_sequent* new_sequent=new state_sequent(repeat); new_sequent->parent=NULL; // read substates and subsequences XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild(); while (one_child!=NULL) { if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { state_atom* new_one=state_factory(dynamic_cast<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child)); if (new_one!=NULL) { state* new_state=dynamic_cast<state*>(new_one); if (new_state!=NULL) { new_state->parent=new_sequent; new_sequent->push_back(new_one); } else { fprintf(stderr,"experiment: found nonstate in sequent element\n"); delete new_one; } } } one_child=one_child->getNextSibling(); } return new_sequent; } else if(strcasecmp(my_name,"ttlout")==0) { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); // read parameters ttlout* ttls=new ttlout(); char* id=get_parameter(element,"id","i",NULL); if (id!=NULL) { ttls->id=strtol(id,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id); } char* value=get_parameter(element,"value",NULL); if (value!=NULL) { ttls->ttls=strtoul(value,NULL,0); // todo: another error message... XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&value); } char* channel=get_parameter(element,"channel",NULL); if (channel!=NULL) { char* state_value=get_parameter(element,"state",NULL); if (state_value!=NULL) { size_t number=strtoul(channel,NULL,0); char state_char=(state_value)[0]; fprintf(stderr, "been here\n"); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&state_value); if (state_char=='h' || state_char=='1') ttls->ttls.set(number,1); else ttls->ttls.set(number,0); } else { fprintf(stderr,"found invalid ttl state: ignoring\n"); delete ttls; ttls=NULL; } XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel); } return (state_atom*)ttls; } else if(strcasecmp(my_name,"analogout")==0) { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); analogout* aout=new analogout(); // read parameters char* channel=get_parameter(element,"channel","c","id","i",(char*)NULL); if (channel!=NULL) { aout->id=strtoul(channel,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel); } char* frequency=get_parameter(element,"frequency","f",(char*)NULL); if (frequency!=NULL) { aout->frequency=strtod(frequency,NULL); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency); } char* dac_value=get_parameter(element,"dac_value","d",(char*)NULL); if (dac_value!=NULL) { aout->dac_value=strtol(dac_value,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dac_value); } char* phase=get_parameter(element,"phase","p",(char*)NULL); if (phase!=NULL) { aout->phase=strtod(phase,NULL); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&phase); } char* amplitude=get_parameter(element,"amplitude","a",(char*)NULL); if (amplitude!=NULL) { aout->amplitude=strtod(amplitude,NULL); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&litude); } else aout->amplitude=1.0; return aout; } else if(strcasecmp(my_name,"analogin")==0) { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); analogin* ain=new analogin(); // read parameters char* id=get_parameter(element,"id","i",(char*)NULL); if (id!=NULL) { ain->id=strtoul(id,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id); } char* frequency=get_parameter(element,"f","frequency",(char*)NULL); if (frequency!=NULL) { ain->sample_frequency=strtod(frequency,NULL); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency); if (ain->sample_frequency<0) { delete ain; throw job_exception("frequency must be non-negative"); } } char* samples=get_parameter(element,"s","samples",(char*)NULL); if (samples!=NULL) { char* samples_startpos=samples; while (*samples_startpos!=0 && isspace(*samples_startpos)) ++samples_startpos; if (*samples_startpos=='-') { XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples); delete ain; throw job_exception("frequency must be non-negative"); } ain->samples=strtoul(samples,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples); } char* channels=get_parameter(element,"c","channels",(char*)NULL); if (channels!=NULL) { ain->channels=strtoul(channels,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channels); } else ain->channels=3; char* sensitivity=get_parameter(element,"sen","sensitivity",(char*)NULL); if (sensitivity!=NULL) { ain->sensitivity=strtod(sensitivity,NULL); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&sensitivity); } else ain->sensitivity=5.0; char* resolution=get_parameter(element,"r","res","resolution",(char*)NULL); if (resolution!=NULL) { ain->resolution=strtoul(resolution,NULL,0); XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&resolution); } else ain->resolution=12; return (state_atom*)ain; } return NULL; }
void job::build_job() { // check job info if (job_meta_->job_id == "") { throw job_exception("Job ID cannot be empty"); } else if (job_meta_->file_server_url == "") { throw job_exception("File server URL cannot be empty"); } else if (job_meta_->hwgroups.empty()) { throw job_exception("Job configuration has no specified hwgroup"); } // check if job is meant to be processed on this worker auto hw_group_it = std::find(job_meta_->hwgroups.begin(), job_meta_->hwgroups.end(), worker_config_->get_hwgroup()); if (hw_group_it == job_meta_->hwgroups.end()) { throw job_exception("Job is not supposed to be processed on this worker, hwgroups does not match"); } // create root task, which is logical root of evaluation size_t id = 0; root_task_ = factory_->create_internal_task(id++); // construct all tasks with their ids and check if they have all datas, but do not connect them std::map<std::string, std::shared_ptr<task_base>> unconnected_tasks; for (auto &task_meta : job_meta_->tasks) { if (task_meta->task_id == "") { throw job_exception("Task ID cannot be empty"); } else if (task_meta->priority == 0) { throw job_exception("Priority cannot be zero"); } else if (task_meta->binary == "") { throw job_exception("Command cannot be empty"); } // go through variables parsing task_meta->binary = parse_job_var(task_meta->binary); for (size_t i = 0; i < task_meta->cmd_args.size(); ++i) { task_meta->cmd_args.at(i) = parse_job_var(task_meta->cmd_args.at(i)); } std::shared_ptr<task_base> task; // distinguish internal/external command and construct suitable object if (task_meta->sandbox != nullptr) { // //////////////// // // external command // // //////////////// // auto sandbox = task_meta->sandbox; if (sandbox->name.empty()) { throw job_exception("Sandbox name cannot be empty"); } // first we have to get appropriate hwgroup limits std::shared_ptr<sandbox_limits> limits; auto hwit = sandbox->loaded_limits.find(worker_config_->get_hwgroup()); if (hwit != sandbox->loaded_limits.end()) { limits = hwit->second; // check and maybe modify limits process_task_limits(limits); } else { limits = std::make_shared<sandbox_limits>(worker_config_->get_limits()); } // go through variables parsing limits->std_input = parse_job_var(limits->std_input); limits->std_output = parse_job_var(limits->std_output); limits->std_error = parse_job_var(limits->std_error); limits->chdir = parse_job_var(limits->chdir); std::vector<std::tuple<std::string, std::string, sandbox_limits::dir_perm>> new_bnd_dirs; for (auto &bnd_dir : limits->bound_dirs) { new_bnd_dirs.push_back(std::tuple<std::string, std::string, sandbox_limits::dir_perm>{ parse_job_var(std::get<0>(bnd_dir)), parse_job_var(std::get<1>(bnd_dir)), std::get<2>(bnd_dir)}); } limits->bound_dirs = new_bnd_dirs; // ... and finally construct external task from given information create_params data = { worker_config_->get_worker_id(), id++, task_meta, limits, logger_, working_directory_.string()}; task = factory_->create_sandboxed_task(data); } else { // //////////////// // // internal command // // //////////////// // task = factory_->create_internal_task(id++, task_meta); if (task == nullptr) { throw job_exception("Unknown internal task: " + task_meta->binary); } } // add newly created task to container ready for connect with other tasks unconnected_tasks.insert(std::make_pair(task_meta->task_id, task)); } // constructed tasks in map have to have tree structure, so... make it and connect them connect_tasks(root_task_, unconnected_tasks); // all should be done now... just linear ordering is missing... try { helpers::topological_sort(root_task_, task_queue_); } catch (helpers::top_sort_exception &e) { throw job_exception(e.what()); } // remove unnecessary root task from begining of task queue if (!task_queue_.empty() && task_queue_.at(0)->get_task_id() == "") { task_queue_.erase(task_queue_.begin()); } else { // something bad is happening here, stop this job evaluation throw job_exception("Root task not present in first place after topological sort."); } }
void job::process_task_limits(std::shared_ptr<sandbox_limits> limits) { if (limits == nullptr) { throw job_exception("Internal error. Nullptr dereference in process_task_limits."); } auto worker_limits = worker_config_->get_limits(); std::string msg = " item is bigger than default worker value"; // we have to load defaults from worker_config if necessary and check for bigger limits than in worker_config if (limits->cpu_time == FLT_MAX) { limits->cpu_time = worker_limits.cpu_time; } else { if (limits->cpu_time > worker_limits.cpu_time) { throw job_exception("time" + msg); } } if (limits->wall_time == FLT_MAX) { limits->wall_time = worker_limits.wall_time; } else { if (limits->wall_time > worker_limits.wall_time) { throw job_exception("wall-time" + msg); } } if (limits->extra_time == FLT_MAX) { limits->extra_time = worker_limits.extra_time; } else { if (limits->extra_time > worker_limits.extra_time) { throw job_exception("extra-time" + msg); } } if (limits->stack_size == SIZE_MAX) { limits->stack_size = worker_limits.stack_size; } else { if (limits->stack_size > worker_limits.stack_size) { throw job_exception("stack-size" + msg); } } if (limits->memory_usage == SIZE_MAX) { limits->memory_usage = worker_limits.memory_usage; } else { if (limits->memory_usage > worker_limits.memory_usage) { throw job_exception("memory" + msg); } } if (limits->processes == SIZE_MAX) { limits->processes = worker_limits.processes; } else { if (limits->processes > worker_limits.processes) { throw job_exception("parallel" + msg); } } if (limits->disk_size == SIZE_MAX) { limits->disk_size = worker_limits.disk_size; } else { if (limits->disk_size > worker_limits.disk_size) { throw job_exception("disk-size" + msg); } } if (limits->disk_files == SIZE_MAX) { limits->disk_files = worker_limits.disk_files; } else { if (limits->disk_files > worker_limits.disk_files) { throw job_exception("disk-files" + msg); } } // union of bound directories and environs from worker configuration and job configuration limits->environ_vars.insert( limits->environ_vars.end(), worker_limits.environ_vars.begin(), worker_limits.environ_vars.end()); limits->bound_dirs.insert( limits->bound_dirs.end(), worker_limits.bound_dirs.begin(), worker_limits.bound_dirs.end()); }