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:
// 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:
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:
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:
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:
interface Admin { void shutdown(); // Shut down server }
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.