Boost logo

Boost Users :

Subject: [Boost-users] Problems with boost::coroutines with lambda captures
From: Tamas.Ruszkai_at_[hidden]
Date: 2015-09-16 09:24:42


Hi Boost users,

I use boost 1.57 on Windows 64 bit platfrom with Visual Studio 2013. It seems to me that the boost::coroutines and the lambda capture somehow does not play nice together.

I want to do something like the iterator block in C# and I came up with the following code:


        template <typename TOutput>
        using GeneratorBlockYield = typename boost::coroutines::asymmetric_coroutine<TOutput>::push_type;

        template<typename TOutput>
        class GeneratorBlockEnumerator : public IEnumerator<TOutput>
        {
        public:
                explicit GeneratorBlockEnumerator(std::function<void(GeneratorBlockYield<TOutput>&)> generator)
                        : _firstMoveNextCall(true)
                        , _generatorPuller(generator)
                {
                }

                ~GeneratorBlockEnumerator() override = default;

                bool MoveNext() override
                {
                        auto generatorNotFinished = static_cast<bool>(_generatorPuller);
                        if (!generatorNotFinished)
                                return false;

                        if (_firstMoveNextCall)
                        {
                                _firstMoveNextCall = false;
                        }
                        else
                        {
                                _generatorPuller();
                        }

                        generatorNotFinished = static_cast<bool>(_generatorPuller);
                        return generatorNotFinished;
                }

                TOutput Current() const override
                {
                        return _generatorPuller.get();
                }

        private:
                bool _firstMoveNextCall;
                typename boost::coroutines::asymmetric_coroutine<TOutput>::pull_type _generatorPuller;
        };

Here is a test:

1, Create an vectotr with smart pointer
2, I capture that smart pointer inside the lambda, so the lambda has a copy of the smart pointer. This means the "useCount" shall show 2 strong references and it does.
3, I remove the lambda, so the captured smart pointer is released, so the next time I refresh "useCount" it shows 1, which is exactly what I have expected

4, I have only one reference ("input") to the object.
5, I create the iterator block which immediately executes, because coroutines immediately execute after they created.
6, In the gernerator block I create a copy of the smart pointer and I also have the captured smart pointer. With the original one together they shall make 3 strong references.
7, Now when I yield from the generator and return back to the main flow, the "useCount" only shows 2 strong references instead of 3.
I have realized that the 2 strong references are the original ("input") and the copy inside the generator block ("c"). The captured one somehow removed from the refrence counter, but its internal pointer is not set to null, so it is in a strange state - if I can trust the debugger.

Sumamry: somehow the captured smart pointer gets destroyed+corrupted when I yield from the coroutine, but the local copy survives. When I use just normally use lambdas to capture smart pointers without coroutines then it looks fine.


        void GeneratorErrortest()
        {
                auto input = std::make_shared<std::vector<int>>(5);

                std::function<void()> simpleLambda = [=]()
                {
                        auto a = input->size();
                        a = input->size();
                };

                auto useCount = input.use_count();
                simpleLambda = nullptr;
                useCount = input.use_count();

                using namespace DeferredLinq;
                auto numbersEnumerable = boost::make_shared<GeneratorBlockEnumerator<int>>([=](GeneratorBlockYield<int>& yield)
                {
                        auto c = input;
                        auto a = input->size();
                        yield(1);
                        a = input->size();
                });

                useCount = input.use_count();

                input.reset();

                numbersEnumerable->MoveNext();
                numbersEnumerable->MoveNext();
        }

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net