We mentioned earlier that factoring the _names set and its mutex into a separate class instance does not really solve the cyclic dependency problem, at least not in general. To see why, suppose that we want to extend our factory with a new getDetails operation:

{zcode:slice}
// ...

struct Details {
    PhoneEntry* proxy;
    string name;
    string phNum;
};

sequence<Details> DetailsSeq;

interface PhoneEntryFactory {
    // ...

    DetailsSeq getDetails();
};
{zcode}

This type of operation is common in collection managers: instead of returning a simple list of proxies, getDetails returns a sequence of structures, each of which contains not only the object's proxy, but also some of the state of the corresponding object. The motivation for this is performance: with a plain list of proxies, the client, once it has obtained the list, is likely to immediately follow up with one or more remote calls for each object in the list in order to retrieve their state (for example, to display the list of objects to the user). Making all these additional remote procedure calls is inefficient, and an operation such as getDetails gets the job done with a single RPC instead.

To implement getDetails in the factory, we need to iterate over the set of entries and invoke the getNumber operation on each object. (These calls are collocated and therefore very efficient, so they do not suffer the performance problem that a client calling the same operations would suffer.) However, this is potentially dangerous because the following sequence of events is possible:

At this point, the server deadlocks: getDetails holds a lock on _namesMutex and waits for _m to become available, and destroy holds a lock on _m and waits for _namesMutex to become available, so neither thread can make progress.

To get rid of the deadlock, we have two options:

We will explore both options in the following pages.

Topics

See Also