1

I've looked at some other threads but they don't quite answer my question, I think...

I have a CircularBuffer object which I have separated from a ThreadSafeCircularBuffer object so that the CB logic is separated from the concurrency logic, mostly for a clear separation of logic/function and partly to make testing easier.

I would have thought that a ThreadSafeCircularBuffer is a CircularBuffer. But this leads me to an implementation problem which is represented by a small artificial example below:

#include <iostream>

class A
{
public:
    virtual void GetBytesAvailable()
    {
        std::cout << "GetBytesAvailable() from A\n";
    }

    virtual void doit()
    {
        std::cout << "doit() from A\n";
        GetBytesAvailable();
    }
};    

class B: public A
{
public:
    virtual void GetBytesAvailable() override
    {
        std::cout << "GetBytesAvailable() from B\n";
        std::cout << "LOCKED\n";
    }

    virtual void doit() override
    {
        std::cout << "doit() from B\n";
        std::cout << "LOCKED\n";
        A::doit();
    }
};    

int main()
{
    B b;
    b.doit();
}

The output is this, as one would expect in C++...

doit() from B   
LOCKED
doit() from 
GetBytesAvailable() from 
LOCKED // <-- Oops, this will break if lock not recursive!

Trouble is, I don't want to use a recursive lock.

I could solve this by re-writing A's doit() method as so:

    virtual void doit()
    {
        std::cout << "doit() from A\n";
        A::GetBytesAvailable();
     // ^^^
     // Make sure we call the non-locking function
    }

But it feels a little artificial... firstly, what if the override of GetBytesAvailable() should be called in a child class (although not in this case, I mean in the general case)? Also, its a bit clunky... class A shouldn't need to care about protecting against its children like this, I don't think.

So... this leads me to the idea of using a strategy pattern where the ThreadSafeCircularBuffer would create and instance of CircularBuffer or maybe even better be passed a CircularBuffer as a strategy. This is a "has-a" relationship. This would solve my problem but it seems to me like I should really have an "is-a" relationship.

Another possible solution is to have both objects inherit from an interface but then instead of making ThreadSafeCB a child of CB... this way they are both CB's, but the ThreadSafeCB would still have to use a "has-a" relationship under the hood to get the CB functionality.

How I could improve this design / is there is a better way of going about this?

  • Prefer composition over inheritance. As you already yourself noted, the has-a relationship in this case is more natural than the is-a relationship. – juhist Dec 28 '17 at 12:06
  • So where are you determining which thread has the lock? That is if both function are called by thread 1, then the second lock in doit is incorrect. However, if they are called by two different threads, then the lock in doit may be needed. – Robert Baron Dec 28 '17 at 12:18
  • @RobertBaron Hi, yep the second lock is incorrect as in the example I'm imagining one thread calling doit() resulting in the thread trying to take the mutex twice – Jimbo Dec 29 '17 at 10:48
1

Go with the interface and let the ThreadSafeCircularBuffer have an instance of CircularBuffer. The main reason for this is that it seems to be the simplest solution, but if you need to rationalize it:

The CircularBuffer class is a specific implementation of the abstract concept of a circular buffer. It's not really relevant that your ThreadSafeCircularBuffer is a CircularBuffer in the sense of that specific implementation.

As for passing the CircularBuffer in - I don't see a good reason for this, unless you might want to use different implementations of a CB in a thread-safe way. This, of course, would be all the more reason to use an interface.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.