Opened 9 years ago

Closed 9 years ago

#8685 closed Bugs (fixed)

lockfree spsc_queue iterator pop failure

Reported by: meyers@… Owned by: timblechmann
Milestone: To Be Determined Component: lockfree
Version: Boost 1.53.0 Severity: Problem
Keywords: Cc:

Description

In the case where the producer has populated with N items such that they wrap around the end of the ring buffer, and the consumer uses the pop() interface with an output iterator, the pop method splits the copying into two chunks.

The problem is that both calls to std::copy use the same destination value.

            size_t count0 = max_size - read_index;
            size_t count1 = avail - count0;

            std::copy(internal_buffer + read_index, internal_buffer + max_size, it);
            std::copy(internal_buffer, internal_buffer + count1, it);

What we see is pop() returns 2 (in the case where N == 2), but what's read is the last value added, then some old value which happened to be there from before. The N-1 value is lost.

Should the second call to std::copy be:

            std::copy(internal_buffer, internal_buffer + count1, it + count0);

Change History (4)

comment:1 by aubrey@…, 9 years ago

I confirm the fix is working.

Jason Aubrey

comment:2 by anonymous, 9 years ago

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>

using namespace std;

template <class Sequence, typename OutputIterator>
void test_copy(const Sequence &seq, OutputIterator out)
{
    size_t mid = seq.size() / 2;
    copy(begin(seq), begin(seq) + mid, out);
    copy(begin(seq) + mid, end(seq), out);
}

template <class Sequence, typename OutputIterator>
void test_copy_fix(const Sequence &seq, OutputIterator out)
{
    size_t mid = seq.size() / 2;
    copy(begin(seq), begin(seq) + mid, out);
    copy(begin(seq) + mid, end(seq), out + mid);
}


template <class Sequence>
void print_seq(const Sequence &seq, const string& name)
{
    cout << name << ": ";
    copy(begin(seq), end(seq), ostream_iterator<typename Sequence::value_type>(cout, " "));
    cout << '\n';
}

int main()
{
    vector<int> source = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    print_seq(source, "source");

    vector<int> dest1(10);
    test_copy(source, dest1.begin());
    print_seq(dest1, "dest1a");

    fill(begin(dest1), end(dest1), 0);
    test_copy_fix(source, dest1.begin());
    print_seq(dest1, "dest1b");

    dest1.clear();
    test_copy(source, back_inserter(dest1));
    print_seq(dest1, "dest1c");

    
    cout << "ostream: ";
    test_copy(source, ostream_iterator<int>(cout, " "));
    cout << '\n';

    // cout << "ostream_b: ";
    // test_copy_fix(source, ostream_iterator<int>(cout, " "));
    // cout << '\n';

    return 0;
}

Produces this output:

source: 1 2 3 4 5 6 7 8 9 
dest1a: 5 6 7 8 9 0 0 0 0 0 
dest1b: 1 2 3 4 5 6 7 8 9 0 
dest1c: 1 2 3 4 5 6 7 8 9 
ostream: 1 2 3 4 5 6 7 8 9 

The first "dest" was how I was using the spsc queue, and how I saw the "problem".

If the commented out lines are enabled, the code won't compile. This looks like my mistake in my usage of the spsc queue and output iterators.

in reply to:  2 comment:3 by meyers@…, 9 years ago

Replying to anonymous:

and by 'anonymous' I mean me.

Rob Meyers

comment:4 by timblechmann, 9 years ago

Resolution: fixed
Status: newclosed

already fixed in trunk and 1.54

Note: See TracTickets for help on using tickets.