Channels ▼
RSS

C/C++

Weak References as Object Accessors


Let's devise a C++ implementation, using POSIX threads. The worker class would inherit from the base class RefTarget that would embody the functionality we're interested in. The class WeakReaf would implement a weak reference. Listing One is a reasonable class interface. The templates may be used to control the pointer types better but it's easier to show the example without them, with simple inheritance.

class RefTarget {
    friend class WeakRef;
public:
    RefTarget(void *owner); // this pointer will be held in the weak references
    ~RefTarget();
    WeakRef *newWeakRef(); // create a new weak reference
protected:
    void invalidate(); // called from the subclass destructor
    void freeWeakRef(WeakRef *wr); // notification from a WeakRef that it gets destroyed
private: // prevent the default and copy constructors and assignments
    RefTarget();
    RefTarget(const RefTarget&);
    void operator=(const RefTarget&);
};
class WeakRef {
    friend class RefTarget;
public:
    ~WeakRef();
    WeakRef *copy(); // create a copy of this reference
    void *grab(); // make the reference strong, return its current value
    void release(); // release the reference back to weak
protected:
    WeakRef(RefTarget *target); // called from copy() and from RefTarget
    bool invalidate1(); // called from RefTarget invalidation
    void invalidate2(); // called if invalidate1() returned true
private: // prevent the default and copy constructors and assignments
    // (they may be added but the code is simpler without them)
    WeakRef();
    WeakRef(const WeakRef&);
    void operator=(const WeakRef&);
};
Listing One

Listing Two shows how you use it. The listing includes only the parts related to the weak references, skipping most of the thread control and such. Note that the Worker invalidates its RefTarget at the start of the destructor. This must be done before the object starts being taken apart. Leaving things until the RefTarget destructor would be too late, since the superclass destructor gets called only after the subclass destructor. Invalidation insures of two things: that the new weak references stop being given out (or are given out as already invalid) and that all the existing weak references get invalidated. The weak references are invalidated in two steps, by the reasons that I discussed later.

class Worker : public RefTarget {
public:
    Worker() : RefTarget(this)
    { ... }
    ~Worker()
    {
        RefTarget::invalidate();
        ....
    }
    ...
};
class Listener {
public:
    Listener(WeakRef *ref) :
        ref_(ref), done_(false)
    { ... }
    ~Listener() 
    {
        delete ref_;
        ...
    }
    execute()
    {
        ...
        while(!done_) {
            ... accept connection ...
            Client * cl = new Client(connection, ref_.copy());
            ...
            cl.start(); // starts the thread with execute() method
        }
        ...
    }
protected:
    WeakRef *ref_;
    bool done_;
};
class Client {
public:
    Client(Connection *conn, WeakRef *ref) :
        conn_(conn), ref_(ref)
    { ... }
    ~Client()
    {
        delete ref_;
        ...
    }
    execute()
    {
        while(!conn_.eof()) {
            ... accept request ...
            error = 0;
            Worker *w = (Worker *)ref_->grab();
            if (w == 0)
                error = WORKER_GONE;
            else
                error = w->call(...);
            ref_->release();
            ... return result ...
        }
        ...
    }
protected:
    Connection *conn_;
    WeakRef *ref_;
};

// hypothetical main()
 ...
Worker *w = new Worker;
Listener l = new Listener(w->newWeakRef());
w->start();
l->start();
 ...
Listing Two

The WeakRef constructor is protected. It's never ever called directly. Instead all the creation happens either through the factory method newWeakRef() of the target object, or through the copy() method. This makes sure that nobody will create a reference to an object that is being or has been destroyed.

The WeakRef methods grab() and release() are always called in pairs, it makes both the usage and the implementation simpler. If the reference became invalid, grab() would return a NULL pointer. This should always be checked before performing any actions. But if it's not NULL then the worker object is guaranteed to stay put until release() is called.

An important assumption in this code is that one weak reference is used by one thread only. This thread must take care to always release the reference after grabbing it, and to release the reference before destroying it. If the reference needs to be passed to another thread, it should be copied first, and then the copy given to that other thread. Of course, other means may be used to implement a consistent access from multiple threads to the same reference, but making extra copies is easier.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video