tbb concurrent hash map find & insert - c++

I am currently using tbb's concurrent hash map to perform concurrent insertions into a hash map. Each key is a string and a value is a vector of integers. I would like to achieve the following: during insertions, if the key does not exist, I insert it and add the value to its vector. If it exists, I simply add the value to its vector.
After inspecting the tbb concurrent hash map API, I noticed that both the find and insert functions return booleans only. So how can I return a pointer to the key if it exists?

There are methods which require an accessor in theirs arguments. The accessor is basically a pointer coupled with a scoped_lock protecting concurrent access to the element. Without the lock, an element can be modified concurrently resulting in a data-race. So, never use a pointer to element in concurrent_hash_map directly (unless protected by the accessor).
Also, you don't need a find() method for your task since insert() methods create the element if it does not exist.
According to the Reference manual, the hash map has the following methods which will likely satisfy your needs:
bool insert( accessor& result, const Key& key ); // creates new element by default
bool insert( accessor& result, const value_type& value );// creates new element by copying
Here is an example:
{
hash_map_t::accessor a;
hash_map.insert( a, key ); // creates by default if not exists, acquires lock
a->second.my_vector.push_back( value ); // new or old entry, add to vector anyway
} // the accessor's lock is released here

During insertions, if the key does not exist then key inserted and added the value to its vector.
If it exists, return false and I simply add the value to its vector.
{
hash_map_t::accessor accessor;
bool result = hash_map.insert(accessor, std::make_pair(key, {value})); // creates by default if not exists, acquires lock
if(result == false)
accessor->second.push_back(value); // if key exists
} // the accessor's lock is released here

Related

Concurrent_hash_map implementation throwing SIGSEGV

I am trying to use tbb’s concurrent_hash_map to increase my application’s concurrent performance. Read up about it and implemented it according to my application but I am seeing crashes..
So, my application is a multi-threadedd application where I am storing pairs,the key is char* and the value is an integer. Pseudocode looks like this:
In .h file,
typedef tbb::concurrent_hash_map<const void*, unsigned, Hasher> tbb_concurrent_hash;
tbb_concurrent_hash concurrent_hash_table;
tbb_concurrent_hash::accessor write_lock;
tbb_concurrent_hash::const_accessor read_lock;
In .c file,
void storeName(const char * name) {
int id=0;
// This creates a pair object of name and index
std::pair object(name, 0);
// read_lock is a const accessor for reading. This find function searches for char* in the table and if not found, create a write_lock.
bool found = concurrent_hash_table.find(read_lock, name);
if (found == FALSE) {
concurrent_hash_table.insert(write_lock, name);
// Get integer id somehow.
id = somefunction();
write_lock->second = id;
write_lock.release();
} else {
// if the name is found in the table then get the value and release it later
id = read_lock->second;
read_lock.release();
}
}
As per my understanding, I am good with the implementation but as I said, there are multiple crashes coming when find returns me FALSE. Crash have traces of mutexs as well.
Your 'locks', i.e. accessors are declared global in .h file. so, basically you write to a shared scoped_lock instance... which logically leads to a data race. Accessors are like fused std::shared_ptr and std::scoped_lock classes in one, or simpler - a pointer for your result and a lock guard for the data it points. You don't want to use one global pointer from multiple threads. Declare them locally in a scope you want to have that access (and you'd not need to call .release() as well)
Another problem is the data race between find() and insert (). Two or more threads can decide that they have to insert since they found nothing. In this case, the first thread will insert the new element while other threads will return existing element because insert() acts as find() if there's existing element. The problem is that your code doesn't account for that.
I can see why you might want to double check using const_accessor as the read lock is more scalable. But instead, you might want to use bool insert( const_accessor& result, const value_type& value ); with read lock (const_accessor) and value_type instead of a key only, which will initialize the whole pair in the case when a new element is added.

How can I erase a key value pair in c++ map?

I'm working on a project that needs unique keys and values so I decided to use maps. Everything works expect for the case where someone may want to change the key value. I'm not sure why, but it causes a fragmentation fault. Can I not do this?
void Journal::set_id(int id){ // journal class
if(join.count(id)){ // join is: static map <int,string> join
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
Your logic is flawed.
void Journal::set_id(int id){
if(join.count(id)){
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
// When you hit this block of the code, there is nothing
// in the map corresponding to 'id'.
// join.find(id) returns the same iterator as join.end()
// Calling erase() with that iterator is causing you the
// problem.
// Don't call erase. Just insert the new item.
// join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
You have just checked to make sure that id is not being used as a key in the map. If it is, you issue an error. So now you know that id is not in the map. If id is not in the map, join.find(id) will return join.end(), so you really didn't need to call find at all. But more importantly, you then call join.erase(join.end()), which is an error.
See documention for std::map::erase() in cppreference:
The iterator pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for pos.
Rather than check whether the key is present, and insert it only if not found, you can simplify the code by just inserting the item, and then checking the return value to see if the insertion succeeded (which it won't if that key was already present).
void Journal::set_id(int id){
if (!(join.insert(std::make_pair(id, name)).second))
cout<<"Journal ID is already being used. Try again."<<endl;
}
This should also improve speed, since it only searches the tree once whereas code doing a count then an insert has to search it twice.

Checking whether an element exists in the stack

I'd like to know whether there is a way of checking if an element exists or not in the stack.
Assume that the stack interface has push, pop, isEmpty, getTop, member functions.
I know we can do it, if we get the top, compare it with that element and pop it, till it gets empty. But this method would be costy as we'd have to create another stacks to store the pop-ed elements and restore it again.
Here's some pseudo-code for a method that checks for whether or not an element is in the stack:
template<class T>
bool find (stack<T> source, T value)
{
while (!source.isEmpty() && source.top() != value)
source.pop();
if (!source.isEmpty())
return true;
return false;
}
It's critical that the source stack is passed by value, so that it isn't modified. Also, realize that this solution probably isn't as efficient as using a different container than stack and simply calling a method which checks for a value.

Retrieve object instance from map

I'm new to C++ and I'm trying to make a little game. I have this class named "UnitController" which stores multiple instances of the class "Unit" in a map. The class also has a method "getUnit" which should return one of the stored units.
It seems this method is only partially working. I think I get a copy of the unit instead of the requested instance.
Could anyone point me int the right direction?
#include "UnitController.h"
#include "Unit.h"
using namespace ci;
using std::map;
UnitController::UnitController()
{
}
void UnitController::addUnit( Vec2f position )
{
Unit mUnit = Unit();
mUnit.setup( position );
Units.insert( std::pair<int,Unit>( Units.size()+1, mUnit ) );
}
Unit UnitController::getUnit( int k )
{
Unit selectedUnit = Units[0];
return selectedUnit;
}
void UnitController::update()
{
for( map<int,Unit>::iterator u = Units.begin(); u != Units.end(); ++u ){
u->second.update();
}
}
void UnitController::draw()
{
for( map<int,Unit>::iterator u = Units.begin(); u != Units.end(); ++u ){
u->second.draw();
}
}
The method:
Unit UnitController::getUnit( int k )
{
Unit selectedUnit = Units[0];
return selectedUnit;
}
is returning a, possibly default, copy of the element with index 0 (do you mean to ignore k?). If you wish to avoid a copy being returned then return a reference instead to the element at index 0, not to the local variable selectedUnit:
Unit& UnitController::getUnit( int k )
{
return Units[k];
}
If the entry keyed by k is removed from the map then a caller that has a reference to the Unit of the entry now has a dangling reference, use of which is undefined behaviour. There a few things to consider to try and avoid this:
Does a client of UnitController require direct access to a Unit in the map? If not and the client only requires to update certain attributes of a Unit then modify the UnitController interface to support updates to Unit without providing a client direct access.
If a client does require direct access then consider using a std::shared_ptr<Unit> instead of a Unit for entry value type (and don't return by reference in this case). This would address the dangling reference problem but what does it mean for caller to have access to a Unit that is no longer in the UnitController?
operator[] will create an entry in the map for a key that does not currently exist:
Inserts a new element to the container using key as the key and a default constructed mapped value and returns a reference to the newly constructed mapped value. If an element with key key already exists, no insertion is performed and a reference to its mapped value is returned.
If you wish to not have this behaviour then use find() and decide on what action to take if an entry for key k does not exist.
In this code:
Unit UnitController::getUnit( int k )
{
Unit selectedUnit = Units[0];
return selectedUnit;
}
you return Unit by value, so you actually get a copy of the original Unit.
(Note also that you seem to have a bug, since you use 0 as key, instead of parameter k...)
If you want to modify the original Unit (i.e. the one stored in the map), you can return by reference (Unit &):
Unit& UnitController::getUnit(int key)
{
return Units[k];
}
As a side note, you can simplify your insertion code. Instead of using std::map::insert() method:
Units.insert( std::pair<int,Unit>( Units.size()+1, mUnit ) );
you can just use std::map::operator[] overload:
Units[ ...the key here... ] = mUnit;
You will indeed receive a deep copy of Unit.
Consider creating a reference counting pointer (aka smart pointer) encapsulating a Unit and set that to the value type of the map.
of course you get the copy. consider this:
void UnitController::addUnit( Vec2f position )
{
Unit mUnit = Unit(); // you create local instance of Unit
mUnit.setup( position );
// you add it to the map - actually the copy is created and stored in map
Units.insert( std::pair<int,Unit>( Units.size()+1, mUnit ) );
// mUnit is destroyed after the function exits
// your map would now contain a destroyed object if it wasn't a copy
}
Besides your getUnit method also returns a copy. this might be OK or not, depending on what the client does with it. if it changes the state of returned unit instance, then it will not be reflected in the copies withing the map.
You might want to use map<int,Unit*> but then you need to take care of lifetime of the original objects. You can't destroy it because pointer will point to destroyed object. Therefore, a proper solution is to use shared_ptr<Unit>
However, there are other problems with your design. You store the current size + 1 as the map key, which means that you use it as an index. Why not just use std::vector<shared_ptr<Unit>> instead?

Boost c++ property - if key does not exist then set to false

I am new to json parsing with boost using the property tree.
If I have this hash:
foo = {'test1',true}
ptree pt;
bool v = pt.get<bool>("test2");
I need to check a key exists and if not set it to false.
How do I do that gracefully?
Thanks
// bool optional
boost::optional<bool> v = pt.get_optional<bool>("test2");
// any type actually
boost::optional<std::string> v2 = pt.get_optional<std::string>("test3");
if (v) // key exists
bool bool_value = v.get();
else // not exists
v.set(false);
From boost documentation you can try to find the key and if not_found() then you can push a new key.
assoc_iterator not_found() ; Returns the not-found iterator.
Equivalent to end() in a real associative container.
const_assoc_iterator not_found() const; Returns the not-found
iterator. Equivalent to end() in a real associative container.
assoc_iterator find(const key_type & key) ; Find a child with the
given key, or not_found() if there is none. There is no guarantee
about which child is returned if multiple have the same key.
const_assoc_iterator find(const key_type & key) const; Find a child
with the given key, or not_found() if there is none. There is no
guarantee about which child is returned if multiple have the same key.