threadsafe lookup table,

Hello, I am writing a threadsafe lookup table with buckets of linked lists and am running into a Access violation (line 113) which I have trouble understanding why it is happening.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#pragma once

#define NUM_BUCKETS 32

#include <functional>
#include <vector>
#include <mutex>
#include <condition_variable>

template <typename K, typename V>
class threadsafe_lookup_table
{

private:
	struct node {
		std::shared_ptr<K> key;
		std::shared_ptr<V> data;
		std::shared_ptr<node> next;
	};

	std::vector < std::shared_ptr<node> > buckets;
	std::vector < std::mutex > bucket_lock;

	std::mutex lock_read;
	std::mutex lock_write;

	int hash_func(const K &key)
	{
		//return key % NUM_BUCKETS;
		return 1;
	}

public:

	threadsafe_lookup_table()
	{
		buckets = std::vector < std::shared_ptr<node> >(NUM_BUCKETS);
		bucket_lock = std::vector<std::mutex>(NUM_BUCKETS);
	}

	threadsafe_lookup_table(const threadsafe_lookup_table<K,V> &other)
	{
		std::lock_guard < std::mutex > o_r;
		std::lock_guard < std::mutex > o_w;
		std::lock(o_r, o_w);

		buckets = other.buckets;
	}

	void add_or_update_mapping(const K &key, const V &val)
	{
		int idx = hash_func(key);
		std::lock_guard<std::mutex> bl(bucket_lock[idx]);

		// First element
		if (buckets[idx] == nullptr) {
			std::shared_ptr<node> cur = std::make_shared<node>();
			cur->key = std::make_shared<K>(key);
			cur->data = std::make_shared<V>(val);
			cur->next = nullptr;
			buckets[idx] = cur;
			return;
		}

		node* cur;
		cur = buckets[idx].get();

		while (cur->next != nullptr)
		{
			if (*cur->key == key) {
				// Update
				*cur->data = val;
				return;
			}
			*cur = *cur->next;
		}
		// cur->next is null, but cur is still not null. check a last time
		if (*cur->key == key) {
			// Update
			*cur->data = val;
			return;
		}
		// Else, we are at the end, cur is our tail and we just need to add a new node
		cur->next = std::make_shared<node>();
		(*cur->next).data = std::make_shared<V>(val);
		(*cur->next).key = std::make_shared<K>(key);
		(*cur->next).next = nullptr;
	}

	void remove_mapping(const K &key)
	{
		int idx = hash_func(key);
		if (buckets[idx] == nullptr)
			return;

		std::lock_guard<std::mutex> bl(bucket_lock[idx]);

		node* cur;
		cur = buckets[idx].get();

		if (*cur->key == key) {
			// Delete head
			buckets[idx] = cur->next;
			return;
		}

		while (cur->next != nullptr)
		{
			if (*cur->next->key == key) {
				// Delete cur->next

				// This is the version with the Access violation error.
				 *cur->next = *cur->next->next; //# <-- this is where the Access violation happened

				/* I tried this implementation instead, but it gives me different errors, where it fails to assign nullptr to the ->next variable
				if (*cur->next->next == nullptr)
					// if we delete our last element
					*cur->next = nullptr;
				else
					// if we delete within the chain
					*cur->next = *cur->next->next;
					*/

				return;
			}
			*cur = *cur->next;
		}
	}

	void value_for(const K &key, V &val)
	{
		int idx = hash_func(key);

		node* cur;
		cur = buckets[idx].get();

		while (cur != nullptr)
		{
			if (*cur->key == key) {
				val = *cur->data;
				return;
			}
			*cur = *cur->next;
		}
	}

};


I am testing the class as <int,std::string>, adding a few key/value pairs and then trying to remove an existing one that was last inserted (fails)

Any help would be appreciated.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <string>

template < typename K, typename V > struct threadsafe_lookup_table
{
    void add_or_update_mapping( const K& key, const V& val )
    {
        std::lock_guard<std::mutex> aquire(lock) ;
        map[key] = val ;
    }

    void remove_mapping( const K& key )
    {
        std::lock_guard<std::mutex> aquire(lock) ;
        map.erase(key) ;
    }

    bool value_for( const K& key, V& val )
    {
        std::lock_guard<std::mutex> aquire(lock) ;
        const auto iter = map.find(key) ;
        if( iter != map.end() )
        {
            val = iter->second ;
            return true ;
        }
        else
        {
            val = {} ;
            return false ;
        }
    }

    private:
        std::unordered_map<K,V> map ;
        std::mutex lock ;
};

int main()
{
    threadsafe_lookup_table< int, std::string > table ;

    table.add_or_update_mapping( 1, "abcd" ) ;
    table.add_or_update_mapping( 2, "efg" ) ;
    table.add_or_update_mapping( 3, "ijkl" ) ;
    table.add_or_update_mapping( 1, "mnopq" ) ;

    std::string str ;
    table.value_for( 1, str ) ;
    std::cout << str << '\n' ; // mnopq

    table.remove_mapping(1) ;
    if( table.value_for( 1, str ) ) std::cout << str << '\n' ;
    else std::cout << "*** no such key\n" ; // *** no such key
}

http://coliru.stacked-crooked.com/a/2693df036cd15f99
Topic archived. No new replies allowed.