First let's set the stage. This code is written with C++11 in mind, I assume lambda support. I also assume C++11 type mutex/condition_variable support, but since I didn't actually have that I wrote my own, so in this code their not explicitly in the std namespace. You should be able to to use std ones or boost ones or what have you by dropping in the appropriate using declarations. I also whipped up a simple scope guard doodad.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct at_scope_exit : private std::function<void()> { | |
at_scope_exit(std::function<void()> do_this) : std::function<void()>(do_this) | |
{} | |
~at_scope_exit() | |
{ | |
(*this)(); | |
} | |
}; |
Before I forget, you probably should have read the last post. So let's start with the simple versions of these, the with r do and the with r when Condition do.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class resource { | |
mutex m_; | |
condition_variable cv_; | |
public: | |
resource() {} | |
void with_do(std::function<void()> body) | |
{ | |
m_.lock(); | |
at_scope_exit u( [&]() { cv_.notify_all(); m_.unlock(); } ); | |
body(); | |
} | |
void with_when_do(std::function<bool()> cond, std::function<void()> body) | |
{ | |
with_do( [&]() { | |
while( !cond() ) | |
{ | |
cv_.wait(m_); | |
} | |
body(); }); | |
} | |
private: | |
resource(const resource&); | |
resource& operator=(resource); | |
}; |
That's actually not bad at all, no? Lambdas and std::function make this all a bit smoother than the C++ of yore would have. Ok, let's talk about problems. What happens when we nest? Actually, not at all what I expected! This blog post might turn out to be about compiler bugs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct counter : resource { int c; } r; | |
r.with_do([&]() | |
{ | |
r.c += 1; | |
r.with_do([&]() { r.c += 1; }); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
r.with_do([&]() | |
{ | |
r.c += 1; | |
counter& shadow = r; | |
r.with_do([&]() { shadow.c += 1; }); | |
}); |
fatal error C1001: An internal error has occurred in the compiler.
1> (compiler file 'msc1.cpp', line 1420)
1> To work around this problem, try simplifying or changing the program near the locations listed above.
1> Please choose the Technical Support command on the Visual C++
1> Help menu, or open the Technical Support help file for more information
I broke my compiler :(. Anyway, I suppose this is what happens when we try to point out a specific problem with a small example, we find new, different problems. We can solve this problem, and demonstrate what I really wanted to.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
auto incr = [&]() { r.c += 1; }; | |
r.with_do([&]() | |
{ | |
r.c += 1; | |
r.with_do(incr); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class resource { | |
recursive_mutex m_; | |
condition_variable cv_; | |
public: | |
resource() {} | |
void with_do(std::function<void()> body) | |
{ | |
m_.lock(); | |
at_scope_exit u( [&]() { cv_.notify_all(); m_.unlock(); } ); | |
body(); | |
} | |
void with_when_do(std::function<bool()> cond, std::function<void()> body) | |
{ | |
with_do( [&]() { | |
while( !cond() ) | |
{ | |
cv_.wait(m_); | |
} | |
body(); }); | |
} | |
void await(std::function<bool()> cond) | |
{ | |
while(!cond()) | |
{ | |
cv_.wait(m_); | |
} | |
} | |
private: | |
resource(const resource&); | |
resource& operator=(resource); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
r.await( [&r]() { return r.c != 0; }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void await(std::function<bool()> cond) | |
{ | |
with_when_do(cond, []() { }); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct counter : resource { int c; } r; | |
counter r2; | |
std::function<bool(int&)> is7 = [](int& x) -> bool { return x == 7; }; | |
r2.with_do([&]() { | |
if( r2.c != 7 ) | |
{ | |
std::cout << "Waiting for c to become 7.\n"; | |
} | |
r.await(std::bind(is7, std::ref(r2.c))); // r != r2 | |
std::cout << "c is 7!\n"; | |
}); |
No comments:
Post a Comment