Hello
I am using a map which holds a list of client connections (to a
server). When a client connects a client gets added to the map and
also when a client disconnects.
In various parts of the code the map gets updated - the key is an int
and the value is a class.
As the map may be accessed by multiple threads I use a mutex to
control access - ie locking whenever an item is added or removed.
I have these questions:
1. Do I also need to lock if I just update and item? I access the
item via an iterator.
As already mentioned, threads are outside of standard so not guarantees
here.
Note first that here the locking problems appear on two levels: map and
item. For map you definitely have to have some locking.
If you obtained the item iterator while the map was locked, then there
are good chances that you can dereference the iterator later and get to
the item without locking. If you are obtaining the iterator in a
multithreaded regime, then you have to lock the whole map. Just imagine
what happens if one thread is searching for an item in the map search
tree and the other thread comes along and inserts a new node, modifying
the tree beneath your feet!
The dereferencing of iterators in different threads is still suspect. A
paranoid debugging implementation of <map> might check during this
operation that the corresponding map is alive and the iterator is valid
inside there. To be on safer side, you can convert the iterator into a
plain item pointer before releasing the map lock. This breaks any
connection to the map, you have a plain pointer and your application
logic is now responsible for that the pointer remains valid while used.
Of course, as the map is global, different threads can obtain a pointer
to the same item. If they always do only read-only access (physically),
then there is no problem and no need for locks. However, because one of
the threads finally modifies the item (when destroying it), the things
are not so simple. If your application logic cannot guarantee that the
items are only referenced by a single thread when deleted, then you
should hold the whole map lock always when working with any item in the
map. For read-only access the items can be copied out of the map, to
release the lock faster.
One way to achieve the above guarantee (single-thread visibility by
destruction) is to use reference-counted smartpointers to the items in
the map (these have to be threadsafe smartpointers, with locked
refcounter increment/decrement). This ensures that the object can be read
and destroyed safely (but not modified at the same time, for that you
would need other mutexes/locks).
2. Do I need to lock if I simply access the map via a const_iterator?
The 'const' in 'const_iterator' here is applied only to the items
themselves, so no change with respect to map locking. Also, the const can
be cast away and worked around by mutable members. What is important is
whether the items are modified physically while in map or not. Using
const_iterator can help here, but not more. Most importantly, you can't
delete the items from the map by const_iterator, so you probably cannot
follow this policy strictly.
hth
Paavo