Let's say you're designing an interface IFoo, but you don't know offhand whether objects that implement IFoo will be thread-safe or not. In the pre-volatile world, this would probably be ok, because you can always use the decorator pattern later to add thread-safety:
Code: Select all
class IFoo
{
public:
virtual void bar() = 0;
};
class SingleThreadedFoo : public IFoo
{
...
};
class SynchronizingFoo : public IFoo
{
public:
SynchronizingFoo( IFoo* foo ) : m_foo( foo ) {}
void bar()
{
AutoLock lock( m_mutex );
m_foo->bar();
}
private:
std::auto_ptr<IFoo> m_foo;
Mutex m_mutex;
};
Code: Select all
class IFoo
{
public:
virtual void bar() = 0;
virtual void bar() volatile = 0;
};
class SingleThreadedFoo : public IFoo
{
public:
// Thread-unsafe version of bar().
void bar()
{
mutate_somehow( m_localState );
}
void bar() volatile
{
// BAD! We're lying about being thread-safe!
SingleThreadedFoo* nonVolatileThis =
const_cast<SingleThreadedFoo*>( this );
nonVolatileThis->bar();
}
private:
Baz m_localState;
};
...
volatile SingleThreadedFoo foo;
// Uh-oh.
run_thread1( foo );
run_thread2( foo );
So what if I keep the design of IFoo as it is? Then the problem is that my thread-safe wrapper instances cannot be declared volatile, which breaks the convention used in the article that all objects shared by multiple threads should be declared volatile.
Code: Select all
// This is how it should be done...
static volatile SynchronizingFoo foo( new SingleThreadedFoo() );
foo->bar(); // ILLEGAL, won't compile because bar is not volatile!
What would you do?
Alternatively, what do you think Bjarne would do?