Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

buffered_channel calls deleter twice when using iterators #291

Open
YaZasnyal opened this issue Feb 2, 2022 · 3 comments
Open

buffered_channel calls deleter twice when using iterators #291

YaZasnyal opened this issue Feb 2, 2022 · 3 comments

Comments

@YaZasnyal
Copy link

YaZasnyal commented Feb 2, 2022

version: 1.75.0 (probably 1.78.0)
compiler: gcc 9.4.0

Hi, I have found some unexpected behavior when using buffered_channel::iterator. The destructor is called twice when using them.

This happens first time when operator++ is called and second time in increment()

iterator & operator++() {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
increment_();
return * this;
}

void increment_( bool initial = false) {
BOOST_ASSERT( nullptr != chan_);
try {
if ( ! initial) {
reinterpret_cast< value_type * >( std::addressof( storage_) )->~value_type();
}
::new ( static_cast< void * >( std::addressof( storage_) ) ) value_type{ chan_->value_pop() };
} catch ( fiber_error const&) {
chan_ = nullptr;
}
}

Got this on version 1.75.0 but it seems that it is still present.

Minimal reproducible example:

#include <memory>
#include <vector>

#include <boost/fiber/all.hpp>

class Foo
{
public:
  Foo(int i) : i_(i) {std::cout << "Foo()\n";}
  ~Foo() {std::cout << "~Foo()\n";}
  void PrintI() {std::cout << "Foo::operator()(): " << i_ << "\n";}

private:
  int i_ = 0;
};
using FooSptr = std::shared_ptr<Foo>;

int main(int, char**)
{
  boost::fibers::buffered_channel<FooSptr> chan(16);

  boost::fibers::fiber f([&]() {
    std::vector<FooSptr> fooVec; // keep objects for some time
    for(auto& foo : chan)
    {
      fooVec.push_back(foo);
    }

    for (auto& foo : fooVec)
    {
      foo->PrintI();
    }
    fooVec.clear();
    std::cout << "Fiber finished\n";
  });

  chan.push(std::make_shared<Foo>(0));
  chan.push(std::make_shared<Foo>(1));

  chan.close();
  std::cout << "chan.close();\n";

  f.join();

  return 0;
}

In this example I pass some shared_ptr's through the channel and put it in an array to extend it's lifetime. But counter decrements twice and objects gets destroyed.

Expected result:

Foo()
Foo()
chan.close();
Foo::operator()(): 0
Foo::operator()(): 1
~Foo()
~Foo()
Fiber finished

Actual result:

Foo()
Foo()
chan.close();
~Foo()
~Foo()
Foo::operator()(): 32
Foo::operator()(): 1953066354
Fiber finished
@YaZasnyal
Copy link
Author

I made a little investigation and think that 37b5f77 (#209) should be reverted because e440623 (#258) already covers all situations where deletion is required.

@ttti07
Copy link

ttti07 commented Dec 28, 2022

I'm suffering the same issue, so double free corruption is occuring. (my current boost version is 1.80)
Even boost::fibers::buffered_channel<std::string> does not work at all, which used to work well in previous versions. (maybe <=1.70)
I think there must be some test cases for T = std::string or T = std::shared_ptr<X> in test_buffered_channel_post.cpp file...

@YaZasnyal
Copy link
Author

Unfortunately this problem is still present but there is a workaround. You still can pop elements like this:

FooSptr foo;
while(chan.pop(foo) == boost::fibers::channel_op_status::success)
{
  fooVec.push_back(foo);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants