3

The problem is like this:

I have an array of 500 pointers which point to 500 elements in a doubly linked list. There are 10 threads which run in parallel. Each thread runs 50 loops, and tries to free some element in the list.

The list is sorted (contain simple integers), and there are 10 other threads running in parallel, searching for the node that contains a particular integer and access the other satellite data in this node. So the node is like:

struct node
{
   int key;         // Key used to search this nodes
   int x,y,z;       // Satellite data
   struct node *prev;
   struct node *right;
};

The problem is easily solvable if I just lock the list before search / delete. But that is too coarse grained. How do I synchronize these threads so that I can achieve better concurrency?

Edits:

  1. This is not a homework question. I do not belong to academia.
  2. The array holding 500 pointers seems weird. I have made it like that to visualize my problems with least possible complexity.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Saurabh
  • 1,059
  • 10
  • 20
  • Possible duplicate of http://stackoverflow.com/questions/9081702/guard-simple-list-in-threaded-programming/9081775 – Tudor Feb 02 '12 at 16:25
  • Its a duplicate indeed. My question explains the scenario in more details. – Saurabh Feb 02 '12 at 16:27
  • 1
    The _best possible concurrency_ will depend very closely on your exact access pattern, and that isn't really clear from the question. Do you have 10 threads essentially traversing the same linked list, each looking for a different key to remove? If so, why - how long can it take to compare 500 integers? – Useless Feb 02 '12 at 16:28
  • I edited the question. There are 20 threads in all. 10 deletion threads and 10 search threads. Search threads search by looping through the list. Delete threads directly delete the pointer present in the list. The problem is just representative, I am looking for something better than a coarse grained lock. – Saurabh Feb 02 '12 at 16:39
  • So - the delete threads are deleting random items from the list, and you don't want that to hold up the searchers, is that it? – Useless Feb 02 '12 at 16:49
  • Refer http://stackoverflow.com/questions/9117007/concurrent-access-and-free-of-heap-object – Saurabh Feb 02 '12 at 17:32

3 Answers3

3

You might consider lock-free linked list using CompareAndSwap operation.

link to paper

peeyush
  • 2,841
  • 3
  • 24
  • 43
3

I can think of a couple of broad approaches which don't involve a global lock, and should allow some degree of forward progress:

1. mark but don't remove

When a deletion thread identifies its victim, mark it as deleted but leave it in place. When a search thread encounters a node with this deleted mark, it just ignores it.

You'll need to issue a write/release barrier after marking the node deleted, and an acquire barrier before inspecting the value: you'll need platform-specific, compiler-specific extensions, otherwise you're writing those barriers in assembler.

2. genuine removal with a lockfree list

As per the paper in Peeyush's answer; similar platform- or compiler-specific requirements for CAS, and significant care is required. Options such as refcounts or hazard pointers can allow the node to be genuinely deleted once no-one is looking at it. You may find you need to replace your prev/next pointers by short indices you can pack into a single word for CAS to work: this means bounding the number of nodes and allocating them in an array.

Also note that although every thread should be able to make progress with this sort of scheme, individual operations (eg. traversing to the next node) may become much more expensive due to the synchronisation requirements.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Thanks. First approach doesn't work me because the purpose of delete threads is to regain the memory. – Saurabh Feb 02 '12 at 17:20
  • On second approach, I will look for a CAS solution. Basically I have not been able to come up with a strategy where I keep a lock inside the node. And concurrently read and free it. – Saurabh Feb 02 '12 at 17:21
  • There is room for a hybrid approach if your consistency requirements are sufficiently relaxed: eg, remove it from the list (with release semantics) and have each searcher issue an acquire barrier when they start a new search. Eventually no search threads will traverse onto it, and you can reclaim the memory. It may appear different (ie, linked in one direction and not the other) in the meantime though. – Useless Feb 02 '12 at 17:42
  • Since this just bubbled up, note that you can do all this with `std::atomic` since C++11 (I was using C++0x well before this answer was written, but obviously it wasn't finalized and support was incomplete. I was certainly still using inline assembly or builtins instead of `std::atomic` then). – Useless Jul 02 '21 at 09:29
0

You need to lock any data that can change. If you will do a lot of work, create one lock per item in the list. A thread has to have the previous, the current, and the next item locked in order to remove the middle one. Make sure to always get locks in the same order to avoid deadlocks.

Other delete threads and the search threads will have to wait until the object is removed and the new links set up. Then the locks are released and they can continue.

Erik Ekman
  • 2,051
  • 12
  • 13