How to protect functions from being called unsynchronized

M

Marcel Müller

I would like to protect some functions of a class from being called
outside a synchronized context.

Of course I could write a proxy that forwards all functions with an
adjusted interface (see example below). But is is a bunch of work to
forward all of these methods. Note that

On the other hand I could use two interfaces one for synchronized class
instances (maybe the class itself) and a second one for the thread-safe
part. Unfortunately this causes a significant runtime overhead, since
even trivial functions would require a vtable lookup while they would
usually expanded inline else.

Any other ideas?

What a pity that one cannot define custom CV modifiers which would do
the job. :)
In fact I already abused volatile for strong thread safety. But this
should have no drawback since the volatile Info instances cannot be
accessed by ordinary statements anyway, since Info has no volatile methods.


Marcel



struct Info
{ Info() { }
Info(const volatile Info& r)
{ // Strongly thread safe access
}
const char* GetString() const;
// ...
};

class A
{ Info info;
protected:
void SyncMethod()
{ // ...
}
public:
void OrdinaryMethod()
{ // ...
}
const volatile Info& GetInfo() const // strong thread safety required
{ return info; }

public:
class Sync;
friend class Sync;
class Sync
{ A& a;
public:
Sync(A& a) : a(a)
{ // Aquire mutex
}
~Sync()
{ // Release mutex
}
public:
void SyncMethod()
{ a.SyncMethod();
}
void OrdinaryMethod()
{ a.OrdinaryMethod();
}
const Info& GetInfo() const // remove strong thread safety tag
{ return const_cast<Info&>(a.GetInfo());
}
Info& GetInfo()
{ return a.info; }
};
};

int main()
{
A a;
a.OrdinaryMethod();
//a.SyncMethod(); WRONG!
Info i(a.GetInfo()); // strong thread safety required
i.GetString(); // access Info content ...
{ // Hold mutex here
A::Sync a_sync(a);
a_sync.OrdinaryMethod();
a_sync.SyncMethod(); // valid
a_sync.GetInfo().GetString(); // no thread safety required
}
return 0;
}
 
I

Ian Collins

I would like to protect some functions of a class from being called
outside a synchronized context.

Of course I could write a proxy that forwards all functions with an
adjusted interface (see example below). But is is a bunch of work to
forward all of these methods. Note that

It is also the most robust solution.
On the other hand I could use two interfaces one for synchronized class
instances (maybe the class itself) and a second one for the thread-safe
part. Unfortunately this causes a significant runtime overhead, since
even trivial functions would require a vtable lookup while they would
usually expanded inline else.

Have you verified that? The above sounds like an attempt at premature
optimisation.
Any other ideas?

What a pity that one cannot define custom CV modifiers which would do
the job. :)
In fact I already abused volatile for strong thread safety. But this
should have no drawback since the volatile Info instances cannot be
accessed by ordinary statements anyway, since Info has no volatile methods.

Eh? What is "strong thread safety"? Something is either thread safe or
it isn't. The use of volatile does not imply thread safety.
 
M

Marcel Müller

Ian said:
It is also the most robust solution.

For sure. But the question is with how much advance.
Have you verified that? The above sounds like an attempt at premature
optimisation.

No I haven't verified that. But the calls, especially to GetInfo()
happen really often. In some code regions approx. once per code line and
inside a loop. (Calculation of recursive aggregate informations with
cascading auto update if some properties change.)

Eh? What is "strong thread safety"? Something is either thread safe or
it isn't.

Well, we had the same discussion in d.p.threads some time ago. In
principal you are right, but in conjunction with reference counting you
get an additional challenge. You cannot safely read a pointer from a
public location and increment the reference counter where it points to
lock free. In fact you have to protect any /reference/ to your reference
counted object with it's own mutex, rather that only the reference
counter - nearly impractical because of the exploding number of mutexes.
The ability to safely get a reference to an existing object that you do
/not/ currently own is called 'strong thread safety'.

However, there are tricks around this with two or three implicitly
atomic instructions per access. This is practical, but still not that
cheap on multi core architectures, because of the memory barriers.

The use of volatile does not imply thread safety.

I know. It is neither required nor sufficient.
But if I define methods that use volatile instances in a thread safe
way, then and only then it implies thread safety. This is a cheap trick
to avoid to call the expensive thread-safe methods where they are not
needed. In fact I utilize the otherwise mostly useless volatile modifier
for this purpose, because otherwise I would need another proxy for every
object which cannot be implemented in a generic way.


Marcel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top