In order to process data in an array using multithreading, I'd like to access each element of the array using an index. Each thread decrements the index and uses its current value to process the corresponding data in the array. The index is an atomic integer and decremented until it's -1 (or 0xFF). How can I prevent the value of the index to become less than -1?
data_type myData[MAX_DATA_COUNT];
std::atomic<uint16_t> data_index;
void process_array()
{
uint16_t index = data_index.fetch_sub(1); // problem starts here!
//
if(index != -1)
{
do_something_with(myData[index]); // process data at index
}
else
{
data_index = -1;
}
}
void worker_thread()
{
while(is_running){
wait_for_data();
process_array();
}
}
The problem is that multiple threads can subtract 1 from data_index and make it less than -1. How can I do this?
Use compare_exchange method. This is a standard way for modify variable only after successfull check.
void process_array()
{
uint16_t index = data_index.load();
while((index != -1) && !data_index.compare_exchange_weak(index, index - 1));
if(index != -1)
{
do_something_with(myData[index]); // process data at index
}
}
Related
Suppose I have some global:
std::atomic_int next_free_block;
and a number of threads each with access to a
std::atomic_int child_offset;
that may be shared between threads. I would like to allocate free blocks to child offsets in a contiguous manner, that is, I want to perform the following operation atomically:
if (child_offset != 0) child_offset = next_free_block++;
Obviously the above implementation does not work as multiple threads may enter the body of the if statement and then try to assign different blocks to child_offset.
I have also considered the following:
int expected = child_offset;
do {
if (expected == 0) break;
int updated = next_free_block++;
} while (!child_offset.compare_exchange_weak(&expected, updated);
But this also doesn't work because if the CAS fails, the side effect of incrementing next_free_block remains even if nothing is assigned to child_offset. This leaves gaps in the allocation of free blocks.
I am aware that I could do this with a mutex (or some kind of spin lock) around each child_offset and potentially DCLP, but I would like to know if this is possible to implement efficiently with atomic operations.
The use case for this is as follows: I have a large tree that I'm building in parallel. The tree is an array of the following:
struct tree_page {
atomic<uint32_t> allocated;
uint32_t child_offset[8];
uint32_t nodes[1015];
};
The tree is built level by level: first the nodes at depth 0 are created, then at depth 1, etc. A separate thread is dispatched for each non-leaf node at the previous step. If no more space is left in a page, a new page is allocated from the global next_free_page which points to the first unused page in the array of struct tree_page and is assigned to an element of child_ptr. A bit field is then set in the node word that indicates which element of the child_ptr array should be used to find the node's children.
The code I am trying to write looks like this:
int expected = allocated.load(relaxed), updated;
do {
updated = expected + num_children;
if (updated > NODES_PER_PAGE) {
expected = -1; break;
}
} while (!allocated.compare_exchange_weak(&expected, updated));
if (expected != -1) {
// successfully allocated in the same page
} else {
for (int i = 0; i < 8; ++i) {
// this is the operation I would like to be atomic
if (child_offset[i] == 0)
child_offset[i] = next_free_block++;
int offset = try_allocating_at_page(pages[child_offset[i]]);
if (offset != -1) {
// successfully allocated at child_offset i
// ...
break;
}
}
}
As far as I understood from you description you array of child_offset is filled with 0 initially and then filled with some concrete values concurrently by different threads.
In this case you can atomically "tag" value first and if you are successful assign valid value. Something like this:
constexpr int INVALID_VALUE = -1;
for (int i = 0; i < 8; ++i) {
int expected = 0;
// this is the operation I would like to be atomic
if (child_offset[i].compare_exchange_weak(expected, INVALID_VALUE)) {
child_offset[i] = next_free_block++;
}
// Not sure if this is needed in your environment, but just in case
if (child_offset[i] == INVALID_VALUE) continue;
...
}
This doesn't guarantee that all values in child_offset array will be in ascending order. But if you need that why not fill it without multithreading involved?
I've started learning C++ Sets and Iterators and I can't figure if I'm doing this correctly since I'm relatively new to programming.
I've created a Set of a struct with a custom comparator that puts the items in a decreasing order. Before receiving the input I don't know how many items my Set will contain. It can contain any number of items from 0 to 1000.
Here are the Setdefinitions:
typedef struct Pop {
int value_one; int node_value;
} Pop;
struct comparator {
bool operator() (const Pop& lhs, const Pop& rhs) const {
if (rhs.value_one == lhs.value_one) {
return lhs.node_value < rhs.node_value;
} else { return rhs.value_one < lhs.value_one;}
}
};
set<Pop, comparator> pop;
set<Pop>::iterator it;
And this is the algorithm. It should find a minimum value and print that value. If it does not find (the function do_some_work(...) returns 0), it should print "Zero work found!\n":
int minimum = (INT_MAX) / 2; int result;
int main(int argc, char** argv) {
//....
//After reading input and adding values to the SET gets to this part
Pop next;
Pop current;
for (it = pop.begin(); it != pop.end() && minimum != 1; it++) {
current = *it;
temp_it = it;
temp_it++;
if (temp_it != pop.end()) {
next = *temp_it;
// This function returns a integer value that can be any number from 0 to 5000.
// Besides this, it checks if the value found is less that the minimum (declared as global) and different of 0 and if so
// updates the minimum value. Even if the set as 1000 items and at the first iteration the value
// found is 1, minimum is updated with 1 and we should break out of the for loop.
result = do_some_work(current.node_value);
if (result > 0 && next.value_one < current.value_one) {
break;
}
} else {
result = do_some_work(current.node_value);
}
}
if (minimum != (INT_MAX) / 2) {
printf("%d\n", minimum);
} else {
printf("Zero work found!\n");
}
return 0;
}
Here are some possible outcomes.
If the Set is empty it should print Zero work found!
If the Set as one item and do_some_work(current.node_value) returns a value bigger than 0 it should printf("%d\n", minimum); or Zero work found! otherwise.
Imagine I have this Set (first position value_one and second position node_value:
4 2
3 6
3 7
3 8
3 10
2 34
If in the first iteration do_some_work(current.node_value) returns a value bigger than 0, since all other items value_one are smaller, it should break the loop, print the minimum and exit the program.
If in the first iteration do_some_work(current.node_value) returns 0, I advance in the Set and since there are 4 items with value_one as 3 I must analyze this 4 items because any of these can return a possible valid minimum value. If any of these updates the minimum value to 1, it should break the loop, print the minimum and exit the program.
In this case, the last item of the Set is only analysed if all other items return 0 or minimum value is set to 1.
For me this is both an algorithmic problem and a programming problem.
With this code, am I analysing all the possibilities and if minimum is 1, breaking the loop since if 1 is returned there's no need to check any other items?
I'm implementing a pointer / weak pointer mechanism using std::atomics for the reference counter (like this). For converting a weak pointer to a strong one I need to atomically
check if the strong reference counter is nonzero
if so, increment it
know whether something has changed.
Is there a way to do this using std::atomic_int? I think it has to be possible using one of the compare_exchange, but I can't figure it out.
Given the definition std::atomic<int> ref_count;
int previous = ref_count.load();
for (;;)
{
if (previous == 0)
break;
if (ref_count.compare_exchange_weak(previous, previous + 1))
break;
}
previous will hold the previous value. Note that compare_exchange_weak will update previous if it fails.
This should do it:
bool increment_if_non_zero(std::atomic<int>& i) {
int expected = i.load();
int to_be_loaded = expected;
do {
if(expected == 0) {
to_be_loaded = expected;
}
else {
to_be_loaded = expected + 1;
}
} while(!i.compare_exchange_weak(expected, to_be_loaded));
return expected;
}
How do I reset the value of blockIndex to its inital state when I call the method?
Say if I call it and pass in the value 4. I check if that value is greater than 9, if not I add the element at pos(0). But in tracing my function I see that it adds all the values of the vector. I just want it to add 1 element, then when it check if it is greater than 9, and it is not, revert it back to the initial value. How do I do this?
int NumCriticalVotes :: CountCriticalVotes(Vector<int> & blocks, int blockIndex)
{
if (blockIndex >= 9 && blocks.isEmpty())
{
return 1;
}
if (blocks.isEmpty()) //Fail case
{
return 0;
} else {
int element = blocks.get(0);
Vector<int> rest = blocks;
rest.remove(0);
return CountCriticalVotes(rest, blockIndex) || CountCriticalVotes(rest, blockIndex + element);
}
}
The code below is my own implementation a push_front method for my items array. I was wondering if someone could help me figure out how to implement the method so that i am just moving the indexes up or down. I need my items to stay in the array, rather then dumping them into a "front" varaible:
stack::stack(int capacity) : items(new item[capacity]), maxSize(capacity),
count(0), top(-1)
{
intFront = 0;
}
bool stack::pushFront(const int nPushFront)
{
if ( count == maxSize ) // indicates a full array
{
return false;
}
for ( int shift = 0; shift < count; )
{
if ( shift == top+1 )
{
intFront = items[top+1].n;
}
items->n = items[++shift].n;
items[shift].n = intFront;
if ( shift != maxSize-1 )
{
intFront = items[++shift].n;
items[shift].n = items->n;
}
}
++count;
items[top+1].n = nPushFront;
return true;
}
items->n is pointing to the struct member n, which is a int type varaible
As you can see im moving elements out of my array into temp varaibles. My question is how would i get around that? How would i just move my indexes up or down to push items to the front of the array? I am trying to get the contents of the array to stay in the array..
Any thoughts?
What you have there, or had there before, is a stack.
What you want, I presume, is a ring buffer.
When your index goes past the end of the array, you reset it back to 0. (It wraps.) And when it goes past the end index you set that index back to the start. You can insert elements after the end index, so long as it doesn't overlap the start one.