I and want to parallelise a seemingly straightforward problem using tbb::tasks. My tasks can be split into sub-tasks, the number of which cannot be chosen, but is determined by the state of the task (not known in advance). As a parent task does not require the results of its sub-tasks, I'd like to recycle the parent as its child. I couldn't find a good working example of this in the online documentation or examples, hence my question here. My current idea is to code along these lines:
struct my_task : tbb::task {
typedef implementation_defined task_data;
task_data DATA;
my_task(task_data const&data) : DATA(data) {}
void reset_state(task_data const&data) { DATA=data; }
bool is_small() const;
void serial_execution();
bool has_more_sub_tasks() const;
task_data parameters_for_next_sub_task();
tbb::task*execute()
{
if(is_small()) {
serial_execution();
return nullptr;
}
tbb::empty_task&Continuation = allocate_continuation(); // <-- correct?
task_data first_sub_task = parameters_for_next_sub_task();
int sub_task_counter = 1;
tbb::task_list further_sub_tasks;
for(; has_more_sub_tasks(); ++sub_task_counter)
further_sub_tasks.push_back(*new(Continuation.allocate_child())
my_task(parameters_for_next_sub_task());
Continuation.set_ref_count(sub_task_counter); // <-- correct?
spawn(further_sub_tasks);
recycle_as_child_of(Continuation); // <-- correct?
reset_state(first_sub_task); // change state
return this; // <-- correct?
}
};
my_task*root_task = new(tbb::task::allocate_root())
my_task(parameters_for_root_task());
tbb::task::spawn_root_and_wait(*root_task);
Is this a correct and/or the best way to do this? (note that in my code above the empty continuation task is neither spawned nor returned)
The line that creates the continuation should be:
tbb::empty_task&Continuation = *new( allocate_continuation() ) tbb::empty_task;
The logic between set_ref_count and reset_state looks correct.
Related
I have a ODBC wrapper interface that enables me to execute SQL queries in C++. In particular, I use
the named parameter idiom for
the select statements, for example:
Table.Select("foo").GroupBy("bar").OrderBy("baz");
To achieve this effect, the class Table_t returns a proxy object Select_t:
class Table_t
{
// ...
public:
Select_t Select(std::string const &Stmt)
{ return {*this, Stmt}; }
void Execute(std::string const &Stmt);
};
Select_t combines the basic statement with the additional clauses and executes the actual statement in the destructor:
class Select_t
{
private:
Table_t &Table;
std::string SelectStmt,
OrderStmt,
GroupStmt;
public:
Select_t(Table_t &Table_, std::string const &SelectStmt) :
Table(Table_), SelectStmt(SelectStmt_) {}
~Select_t()
{ /* Combine the statements */ Table.Execute(/* Combined statement */); }
Select_t &OrderBy(std::string const &OrderStmt_)
{ OrderStmt = OrderStmt_; return *this; }
Select_t &GroupBy(std::string const &GroupStmt_)
{ GroupStmt = GroupStmt_; return *this; }
};
The problem is that Table.Execute(Stmt) may throw and I must not throw in a destructor. Is there a
way I can work around that while retaining the named parameter idiom?
So far the only idea I came up with is to add an Execute function to Select_t, but I would prefer not to:
Table.Select("foo").GroupBy("bar").OrderBy("baz").Execute();
Throwing "inside" a destructor is not a problem; the problem is exceptions escaping from a destructor. You need to catch the ODBC exception, and decide how to communicate the error by another interface.
Actually, separating the concerns of the query object and its execution might be a good idea.
Lazy invocation can be very useful.
The Execute function could reasonably be a free function.
For example:
auto myquery = Table.Select("foo").GroupBy("bar").OrderBy("baz");
auto future_result = marshal_to_background_thread(myquery);
//or
auto result = Execute(myquery);
This would lend itself to re-use with respect to prepared statements.
e.g.
auto myquery = Prepare(Table.Select("foo").Where(Equals("name", Param(1))).OrderBy("baz"));
auto result = Execute(myquery, {1, "bob"});
result = Execute(myquery, {1, "alice"});
I am using Antlr4's C++ visitor api to traverse a parse tree. However, I'm struggling to get it functioning correctly. Namely, I'm not sure how to use the visitChildren(ParseTree *tree) call.
I'm given the context for each rule that I have defined. And I can traverse the tree using the contexts: context->accept[RuleContext]([RuleContext]* rule)
However, when I use those I continually visit the same node multiple times.
For instance:
program:
: nameRule
dateRule
( statements )*
EOF
;
nameRule
: NAME IDENTIFIER ;
dateRule
: DATE IDENTIFIER ;
statements:
: statementX
| statementY
| statementZ
;
statementX:
: // do something here
statementY:
: // do something here
statementZ:
: // do something here
IDENTIFIER, DATE, and NAME are terminals.
I build the Antlr parsing structure by the following:
void Parser::parse() {
ifstream file(FLAGS_c, ifstream::binary);
// Convert the file into ANTLR's format.
ANTLRInputStream stream = ANTLRInputStream(file);
// Give the input to the lexer.
MyLexer lexer = new MyLexer(&stream);
// Generate the tokens.
CommonTokenStream tokens(lexer);
file.close();
tokens.fill();
// Create the translation that will parse the input.
MyParser parser = new MyParser(&tokens);
parser->setBuildParseTree(true);
MyParser::ProgramContext *tree = parser->program();
auto *visitor = new MyVisitor();
visitor->visitProgram(tree);
}
So when I try to traverse this it looks similar to this, the class MyVisitor extends MyParserVisitor. MyVisitor is the visitor class I use to traverse the generated tree.
Any MyVisitor::visitProgram(ParserVisitor::ProgramContext *context) {
this->visitNameRule(context->nameRule());
this->visitDateRule(context->dateRule());
if (!this->statements.empty()) {
for (auto &it : this->statements) {
this->visitStatements(it);
}
}
return Any(context);
}
// Omitting name and date rules.
Any MyVisitor::visitStatements(ParserVisitor::StatementContext *context) {
this->visitStatementX(context->statementX());
this->visitStatementY(context->statementY());
this->visitStatementZ(context->statementZ());
return Any(context);
}
In this case, statements X, Y, and Z will be visited every time statements is visited. Even if they aren't present in the input program.
Is this the correct way to use this? If it isn't, then I assume the visitChildren(ParseTree *tree) is the correct api to use at each visitor function. But I don't understand how to get access to the ParseTree data structure from the *Context.
This question is not directly related to the C++ visitor, but a general visitor problem in ANTLR4. What you are doing is to shortcut the visitor walk in a way you are not intended to do. Don't explicitly visit the certain sub trees manually, but instead call the super implementation to let it do for you and collect the result in individual visitStatementXXX functions. Look at this implementation of a (very simple) expression evaluator, used in a unit test (written in C++). Here's a partial copy to demonstrate the principle:
class EvalParseVisitor : public MySQLParserBaseVisitor {
public:
std::vector<EvalValue> results; // One entry for each select item.
bool asBool(EvalValue in) {
if (!in.isNullType() && in.number != 0)
return true;
return false;
};
virtual Any visitSelectItem(MySQLParser::SelectItemContext *context) override {
Any result = visitChildren(context);
results.push_back(result.as<EvalValue>());
return result;
}
virtual Any visitExprNot(MySQLParser::ExprNotContext *context) override {
EvalValue value = visit(context->expr());
switch (value.type) {
case EvalValue::Null:
return EvalValue::fromNotNull();
case EvalValue::NotNull:
return EvalValue::fromNull();
default:
return EvalValue::fromBool(!asBool(value));
}
}
virtual Any visitExprAnd(MySQLParser::ExprAndContext *context) override {
EvalValue left = visit(context->expr(0));
EvalValue right = visit(context->expr(1));
if (left.isNullType() || right.isNullType())
return EvalValue::fromNull();
return EvalValue::fromBool(asBool(left) && asBool(right));
return visitChildren(context);
}
...
The essential part is the call to visit() which in turn iterates over the child nodes of the given context tree and triggers only visitor functions for elements that actually exist.
I need to define a vector of testmodes that I can then manipulate the values of through various methods.
Here is how I define the TestMode class:
class TestMode{
public:
TestMode(int val, int jamramaddr){
value=val;
jamramaddress=jamramaddr;
}
int getAddr(void){
return jamramaddress;
}
void setValue(int val){
value=val;
}
int getValue(void){
return value;
}
private:
int value;
int jamramaddress;
};
Pretty simple.
I then have a TestModeGroup class to perform actions on the vector of testmodes that I have created. That class looks like this:
class TestModeGroup{
public:
TestModeGroup(const std::vector<TestMode> &TestModes){
TestModeVector=TestModes;
}
//Compare the given jamramaddress against the known jamramaddress of the given testmode. If it is a match then update the testmode value
void compareAndStore(TestMode &TM){
int TM_address=TM.getAddr();
if(TM_address==JamRamAddress){
output("Match found! Old TM value %d", TM.getValue());
TM.setValue(JamRamData);
output("New TM value %d", TM.getValue());
}
}
//Commit the given testmode to the jamram with the latest known value
void writeTmBitToJamRam(TestMode &TM){
JamRamAddress=TM.getAddr();
JamRamData=TM.getValue();
apg_jam_ram_set(JamRamAddress,JamRamData);
}
//running TestModeGroupObject.store(address, data) will find which test mode that jamram address is for and set the appropriate test mode value for later printing.
//This is meant to be used in conjuction with the Excel spreadsheet method of entering test modes
void store(int address, int data){
JamRamAddress= address;
JamRamData = data;
output("Current JamRamAddress = %d JamRamData = %d", JamRamAddress, JamRamData);
apg_jam_ram_set(JamRamAddress,JamRamData);
for(std::vector<TestMode>::iterator it = TestModeVector.begin(); it!=TestModeVector.end(); ++it){
compareAndStore(*it);
}
}
//Running TestModeGroupObject.load() will commit all test mode changes to the jamram for test modes that are part of that object
void load(void){
for(std::vector<TestMode>::iterator it = TestModeVector.begin(); it!=TestModeVector.end(); ++it){
writeTmBitToJamRam(*it);
}
}
int getTMVal(TestMode &TM){
return TM.getValue();
}
private:
int JamRamAddress;
int JamRamData;
std::vector<TestMode> TestModeVector;
};
Here's how I defined the vector:
TestMode adm_testmodes[] = {TM_TWINWL,TM_TWINBL,ON_2WL,ON_2BL,WV_S1X,WV_S0X,TM_PCHG_RH_3,TM_PCHG_RH_2,TM_PCHG_RH_1,TM_PCHG_RH_0,TM_PCHG_RH_BYP,TM_PCHG_SF_3,TM_PCHG_SF_2,TM_PCHG_SF_1,TM_PCHG_SF_0,TM_PCHG_SF_BYP,
TM_PCHG_V04_3,TM_PCHG_V04_2,TM_PCHG_V04_1,TM_PCHG_V04_0,TM_PCHG_V04_BYP,TM_SA_DIS,TM_TS_NEGSLOPE,TM_TRIM_4,TM_TRIM_3,TM_TRIM_2,TM_TRIM_1,TM_TRIM_0,TM_TSSLP_2,TM_TSSLP_1,TM_TSSLP_0,
TM_WRV_N_2,TM_WRV_N_1,TM_WRV_N_0,TM_SAGAIN_EN,TM_REFTRIM_EN,TM_READ_DONE_OPT_EN,EnableCore_Read,SA_4,TM_OC_2,TM_OC_1,TM_OC_0,TM_WRLC_4,TM_WRLC_3,TM_WRLC_2,TM_WRLC_1,TM_WRLC_0,
TM_WRHC_4,TM_WRHC_3,TM_WRHC_2,TM_WRHC_1,TM_WRHC_0,TM_FTOP_3,TM_FTOP_2,TM_FTOP_1,TM_FTOP_0,TM_RISE_1,TM_RISE_0,TM_WRH_3,TM_WRH_2,TM_WRH_1,TM_WRH_0,TM_SET_4,TM_SET_3,TM_SET_2,TM_SET_1,TM_SET_0,
TM_REFSTART,TM_REFSEL_1,TM_REFSEL_0,TM_REFSL_5,TM_REFSL_4,TM_REFSL_3,TM_REFSL_2,TM_REFSL_1,TM_REFSL_0,TM_REFSH_5,TM_REFSH_4,TM_REFSH_3,TM_REFSH_2,TM_REFSH_1,TM_REFSH_0,TM_READ_DONE_ADD,
TM_READ_DONE_OPT,TM_READ_DONE_5,TM_READ_DONE_4,TM_READ_DONE_3,TM_SAGAIN_1,TM_SAGAIN_0,TM_REFR_5,TM_REFR_4,TM_REFR_3,TM_REFR_2,TM_REFR_1,TM_REFR_0
};
std::vector<TestMode> ADM_TMs (adm_testmodes, adm_testmodes + sizeof(adm_testmodes) / sizeof(TestMode));
TestModeGroup ADM_TestModeGroup(ADM_TMs);
So far so good. I can directly access all of the TestModes that are defined, change the value and have that change persist everywhere. The problem comes when I try to run the "store" function within the TestModeGroup class. It seems that I have a local copy of the TestModes that gets updated, but not the original TestMode. I'm sure that this is a pretty simple problem but I'm struggling. C++ is not my specialty, and OOP even less so.
Here's a quick example for a dummy testmode I created:
output("DUMMY_TESTMODE Initial Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_TESTMODE.setValue(1);
output("DUMMY_TESTMODE Set to 1 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_TESTMODE.setValue(0);
output("DUMMY_TESTMODE Set to 0 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_ADM_TestModeGroup.store(22,1);
output("DUMMY_TESTMODE Set to 1 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
doing the .setValue works fine, but doing the .store does not. When I print out the value it comes back as 0. Within the .store function I do a printout, though, and that gives the right value of 1. Somehow I think I am simply altering a copy of the original vector, but I just can't figure it out. I've been driving myself crazy and nobody I've talked to knows enough about C++ to help. Does anybody have any insight as to where I've screwed up?
Try changing TestModeGroup class:
class TestModeGroup{
public:
TestModeGroup(const std::vector<TestMode> &TestModes) : TestModeVector(TestModes)
{}
// ...
std::vector<TestMode> & TestModeVector;
};
If you want vector modifications done inside class to be applied to the original value passed to the constructor, you need to store a reference to the original object. Let me know if this helped :)
I've got a concurrent_unordered_map. I use the insert function (and no other) to try to insert into the map concurrently. However, many times, this crashes deep in the insert function internals. Here is some code:
class ModuleBase {
public:
virtual Wide::Parser::AST* GetAST() = 0;
virtual ~ModuleBase() {}
};
struct ModuleContents {
ModuleContents() {}
ModuleContents(ModuleContents&& other)
: access(other.access)
, base(std::move(other.base)) {}
Accessibility access;
std::unique_ptr<ModuleBase> base;
};
class Module : public ModuleBase {
public:
// Follows Single Static Assignment form. Once it's been written, do not write again.
Concurrency::samples::concurrent_unordered_map<Unicode::String, ModuleContents> contents;
Wide::Parser::AST* GetAST() { return AST; }
Wide::Parser::NamespaceAST* AST;
};
This is the function I use to actually insert into the map. There is more but it doesn't touch the map, only uses the return value of insert.
void CollateModule(Parser::NamespaceAST* module, Module& root, Accessibility access_level) {
// Build the new module, then try to insert it. If it comes back as existing, then we discard. Else, it was inserted and we can process.
Module* new_module = nullptr;
ModuleContents m;
{
if (module->dynamic) {
auto dyn_mod = MakeUnique<DynamicModule>();
dyn_mod->libname = module->libname->contents;
new_module = dyn_mod.get();
m.base = std::move(dyn_mod);
} else {
auto mod = MakeUnique<Module>();
new_module = mod.get();
m.base = std::move(mod);
}
new_module->AST = module;
m.access = access_level;
}
auto result = root.contents.insert(std::make_pair(module->name->name, std::move(m)));
This is the root function. It is called in parallel from many threads on different inputs, but with the same root.
void Collater::Context::operator()(Wide::Parser::NamespaceAST* input, Module& root) {
std::for_each(input->contents.begin(), input->contents.end(), [&](Wide::Parser::AST* ptr) {
if (auto mod_ptr = dynamic_cast<Wide::Parser::NamespaceAST*>(ptr)) {
CollateModule(mod_ptr, root, Accessibility::Public);
}
});
}
I'm not entirely sure wtf is going on. I've got one bit of shared state, and I only ever access it atomically, so why is my code dying?
Edit: This is actually completely my own fault. The crash was in the insert line, which I assumed to be the problem- but it wasn't. It wasn't related to the concurrency at all. I tested the return value of result the wrong way around- i.e., true for value existed, false for value did not exist, whereas the Standard defines true for insertion succeeded- i.e., value did not exist. This mucked up the memory management significantly, causing a crash- although exactly how it led to a crash in the unordered_map code, I don't know. Once I inserted the correct negation, it worked flawlessly. This was revealed because I didn't properly test the single-threaded version before jumping the concurrent fence.
One possibility is that you are crashing because of some problem with move semantics. Is the crash caused by a null pointer dereference? That would happen if you inadvertently accessed an object (e.g., ModuleContents) after it's been moved.
It's also possible that the crash is the result of a concurrency bug. The concurrent_unordered_map is thread safe in the sense that insertion and retrieval are atomic. However, whatever you are storing inside it is not automatically protected. So if multiple threads retrieve the same ModuleContents object, they will share the AST tree that's inside a Module. I'm not sure which references are modifiable, since I don't see any const pointers or references. Anything that is shared and modifiable must be protected by some synchronization mechanism (for instance, locks).
I'm keeping track of a player's "job" by setting his job to a number, and incrementing it by one if he changes job, and determining which job he currently is by whether the number is even or odd. (Only two jobs right now). However, I know there are better ways of doing this, and soon I'll need to implement for a third and fourth job, so I cannot keep using the even/odd check.
Here's my code for reference: (Please note that I only include relevant code)
GameModeState.cpp
// If changeJob's parameter number is 1, it increments the job. If number is 2, it only returns the current job
int GameModeState::changeJob(int number)
{
// Default job is even (landman)
static int job = 1;
if (number == 1)
{
job = (job+1);
return job;
}
else
{
return job;
}
}
int GameModeState::getJob()
{
int currentJob = (changeJob(2));
return currentJob;
}
// If the player opens the "stat sheet", it changes their job
void GameModeState::_statSheet(const String& message, const Awesomium::JSValue& input, Awesomium::JSValue& output)
{
changeJob(1);
}
GameModeState.h
class GameModeState : public GameState::State
{
public:
/// Changes the player's job if number is 1, or returns current job if number is 2
static int changeJob(int number);
/// Returns the current job number by calling changeJob appropriately
static int getJob();
private:
// Opening the player sheet will change the player's job
void _statSheet(const String& message, const Awesomium::JSValue& input, Awesomium::JSValue& output);
};
ZoneMovementState.cpp (This is where I check for current job)
#include "GameModeState.h"
#include <EnergyGraphics/ZoneParser.h>
void ZoneMovementState::_changeZone(const String& message, const Awesomium::JSValue& input, Awesomium::JSValue& output)
{
// If the number from getJob is even, the player is currently a geologist
if (GameModeState::getJob()%2 == 0)
{
ZoneParser::getSingleton().load("../media/zones/geology_zone.xml", false);
}
else //otherwise they are a landman
{
ZoneParser::getSingleton().load("../media/zones/landman_zone.xml", false);
}
transitionHandler->go();
}
I'm thinking either arrays or enums of the jobs will be the better way to deal with this, but I'm not sure how to implement this into my code. If you know a better way, please include examples or at least a point in the right direction. I will greatly appreciate it!
Don't use static variables to save anything like that inside a class. Use a member variable instead.
IMO the easiest way to do something like that and make it extensible is using a enum:
enum PlayerJob
JOB_NONE = 0,
JOB_GEOLOGIST,
JOB_LANDMAN,
...
NUM_JOBS // this element is optional but can be useful for range checking.
};
...
PlayerJob job = JOB_NONE;
...
switch(job)
{
case JOB_NONE:
break;
case JOB_GEOLOGIST:
...
break;
...
default:
error("Unhandled palyer job: %d", job);
break;
}
Also I'd think about somehow organizing such "job relevant" stuff into some kind of array or list or whatever to make it easier to call "job specific" things:
std::map<PlayerJob,std::string> jobzones;
jobzones.push_back(JOB_GEOLOGIST, "geozone.xml");
...
transitToZone(jobzones[job]);
Enums are nice, you may also think about using a std::stack or something similar for the GameState, so that you can push/pop etc.
You may want to look at the State pattern.