Is Ice thread-safe?

The short answer is, "yes". All Ice APIs are thread-safe, in the sense that you can call them concurrently from different threads without running the risk of corrupting data structures that are internal to the Ice run time. For example, it is perfectly safe to do the following:

C++
// Executed by thread 1:
objectAdapter->add(servant, id);

// ...

// Executed concurrently by thread 2:
objectAdapter->remove(id); 

The concurrent calls to add and remove are safe because the Ice run time internally locks the Active Servant Map (ASM) before attempting to modify it, so the two API calls are serialized. In general, you never need to protect Ice-internal data structures from concurrent access.

However, Ice does not protect the integrity of application data. Consider the following Slice definition:

Slice
struct Item
{
     string name;
     // ...
}

interface ItemStore
{
     void put(string s, Item i);
     bool get(string s, out Item i);
}

The slice2cpp compiler generates the following signatures for the put and get operations:

C++
void put(const string& s, const Item& i);
bool get(const string& s, Item& i); 

Now, if you call put and get concurrently and pass the same item to each operation, you can end up in trouble:

C++
Item i;
i.name = "Joe";

// Pass i to thread 1 and thread 2...

// In thread 1:
itemStoreProxy->put("Joe", i);

// Concurrently in thread 2:
itemStoreProxy->get("Fred", i); 

Of course, the problem here is that the put operation in thread 1 may read the contents of i at the same time as the get operation in thread 2 modifies the contents of i. The most likely outcome is that that the put operation is passed a corrupted value of i but, depending on your CPU architecture and threading package, the consequences might be more serious, such as a core dump (either while get and put are executing, or later, when the application attempts to use i).

So, as usual, you must establish critical regions around concurrent access to application data, regardless of whether that access is performed by a thread of your own or a thread inside the Ice run time.

Note that "thread-safe" does not mean that you can blindly call any Ice API at any time. There are a few Ice API calls that can cause deadlock. For example:

Slice
interface Admin
{
     void shutdown(); // Shut down server
}
C++
void AdminI::shutdown(const Current& c)
{
     c.adapter->deactivate();
     // ...
     c.adapter->waitForDeactivate(); // Deadlock!
} 

This cannot possibly work because waitForDeactivate waits until all operations on the adapter have completed; if you call waitForDeactivate from within an operation on the adapter being deactivated, waitForDeactivate cannot complete until the operation has completed, and the operation cannot complete until waitForDeactivate has completed, so the code deadlocks. However, there are only a handful of operations that can cause this problem:

  • Communicator::destroy
  • Communicator::waitForShutdown
  • Adapter::waitForDeactivate
  • Adapter::waitForHold

If your code deadlocks due to incorrect use of these operations, the problem is easily diagnosed: one of the threads will be stuck in one of these methods and have an earlier stack frame that corresponds to the implementation of a Slice operation.

See Also