Weird behavior with custom `algorithm_with_properties<>`
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 28/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Estancado
- Stack tecnológico
- cpp
- Área
- operating-systems
Línea de trabajo
Comienza compilando y ejecutando el reproductor C++20 proporcionado; después, inspecciona boost/fiber/properties.hpp y los puntos de entrada algorithm_with_properties y round_robin que utiliza. Sigue la creación y destrucción alrededor de use_scheduling_algorithm, las fibras separadas y wait(). Se considera terminado cuando se hayan explicado el contexto adicional y el comportamiento de count/fallo de segmentación, y el comportamiento notificado se haya corregido o documentado claramente.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
I've been trying to hack up a way to wait for all fibers to complete, and I always end up with a count of 2 instead of 1 at the end. Here's a full example -- sorry, it's a couple of lines.
The basic idea is: I don't have a whole lot of hooks to know about the creation and destruction of fibers, and the scheduler itself only knows about ready fibers, not about waiting ones. So, I'm creating a new scheduler with "properties" -- the scheduler itself just calls an embedded round_robin, and the "properties" are used to track the creation/destruction of fibers, so I can update a counter in the constructor/destructor.
#include <boost/fiber/properties.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/algo/round_robin.hpp>
#include <boost/fiber/mutex.hpp>
#include <boost/fiber/condition_variable.hpp>
#include <boost/version.hpp>
#include <thread>
#include <iostream>
/************************************************************************/
class Scheduler;
/************************************************************************/
class Properties : public boost::fibers::fiber_properties
{
private:
Scheduler* scheduler=nullptr;
public:
Properties(boost::fibers::context* context)
: fiber_properties(context)
{
assert(false);
}
Properties(boost::fibers::context*, Scheduler&);
~Properties();
};
/************************************************************************/
class Scheduler : public boost::fibers::algo::algorithm_with_properties<Properties>
{
public:
boost::fibers::condition_variable condition;
unsigned int counter=0;
boost::fibers::algo::round_robin scheduler;
public:
Scheduler(Scheduler*& self)
{
self=this;
}
public:
virtual void awakened(boost::fibers::context* context, Properties&) noexcept // override
{
return scheduler.awakened(context);
}
virtual boost::fibers::context* pick_next() noexcept override
{
return scheduler.pick_next();
}
virtual bool has_ready_fibers() const noexcept override
{
return scheduler.has_ready_fibers();
}
virtual void suspend_until(std::chrono::steady_clock::time_point const& when) noexcept override
{
return scheduler.suspend_until(when);
}
virtual void notify() noexcept override
{
return scheduler.notify();
}
public:
virtual boost::fibers::fiber_properties* new_properties(boost::fibers::context* context) override
{
std::cout << "new_properties(" << context << ")" << std::endl;
return new Properties(context, *this);
}
void wait()
{
boost::fibers::mutex mutex;
std::unique_lock<decltype(mutex)> lock(mutex);
condition.wait(lock, [this](){ return counter==1; });
}
};
/************************************************************************/
Properties::Properties(boost::fibers::context* context, Scheduler& scheduler_)
: fiber_properties(context), scheduler(&scheduler_)
{
scheduler->counter++;
std::cout << "Fiber count: " << scheduler->counter << std::endl;
}
/************************************************************************/
Properties::~Properties()
{
scheduler->counter--;
std::cout << "Fiber count: " << scheduler->counter << std::endl;
scheduler->condition.notify_one();
}
/************************************************************************/
int main()
{
std::cout << "BOOST_VERSION: " << BOOST_VERSION << std::endl;
std::thread([](){
Scheduler* scheduler=nullptr;
boost::fibers::use_scheduling_algorithm<Scheduler>(scheduler);
for (int i=0; i<5; i++)
{
boost::fibers::fiber([i](){
boost::this_fiber::sleep_for(std::chrono::seconds(2+i));
}).detach();
}
std::cout << "Waiting..." << std::endl;
scheduler->wait();
std::cout << "All done!" << std::endl;
}).join();
return 0;
}
This is on a Debian "testing", to get a halfway-recent g++ and boost; I'm running this as:
stieber@debian:~$ g++ --version
g++ (Debian 12.2.0-14) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
stieber@debian:~$ g++ -ggdb -std=c++20 Test.cpp -l boost_fiber -l boost_context && ./a.out
BOOST_VERSION: 108100
new_properties(0x7f76cc020c00)
Fiber count: 1
new_properties(0x7f76d18c9f00)
Fiber count: 2
new_properties(0x7f76d18a8f00)
Fiber count: 3
new_properties(0x7f76d1887f00)
Fiber count: 4
new_properties(0x7f76d1866f00)
Fiber count: 5
new_properties(0x7f76d1845f00)
Fiber count: 6
Waiting...
Fiber count: 5
new_properties(0x7f76cc000b70)
Fiber count: 6
Fiber count: 5
Fiber count: 4
Fiber count: 3
Fiber count: 2
^C
stieber@debian:~$
Up until "waiting", all is expected -- I launch 5 fibers, and I have my initial fiber, for a total of 6.
Things start getting weird right away, though: I have no explanation for the "5 6 5" sequence, but I'm pretty sure I haven't started another fiber to explain the additional new_properties() that shows up in that phase. And, apparently, that ghost fiber doesn't end, so it's stuck at 2.
Also, if I change the wait() call to wait for 2 instead of 1, it counts down to 0, then segfaults. Maybe the "fake" property that showed up, getting cleaned up after the scheduler or something like that. I'll probably just try to wait for the 2 and replace the Scheduler* with a shared_ptr construct for now -- even if this is indeed a bug and not just me being dumb again, it would take an estimated 5-10 years for a corrected version to show up on Debian... and I don't want to wait that long.
Related observation:
- if I put another fiber-related operation other than launching fibers into the main fiber, it it seems to create the ghost fiber/context on the first such call. For example, starting the thread with a simple
boost::this_fiber::sleep_for(...)will create the fake fiber there, and the "waiting..." phase will just count down (but, of course, also stop at 2). - on the positive side, it appears to be "guaranteed" that there's exactly one such ghost fiber, so doing the workaround with waiting for 2 should be reliable. I should also be able to use this at runtime to determine whether the ghost fiber is created or not, so I can adapt to a potential change (doing a quick fiber-related call and taking the resulting fiber count as the base value to wait for before launching more fibers).
Unreleated notes:
- the documentation often leaves out the
algo::namespace part - the documentation says
awakenedisvirtual, but... it's not. That's why theoverrideis commented in my code.
- Lenguaje dominante
- C++
- Estrellas
- 512
- Forks
- 124
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de boostorg/fiber
-
Dificultad 4/5 3-5 días Aptitud para principiantes 20/100
-
Boost\libs\fiber\test failed to build due to error C2039 with /std:c++latest option on WindowsAbierto
Dificultad 4/5 3-5 días Aptitud para principiantes 35/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 42/100
-
Dificultad 4/5 3-5 días Aptitud para principiantes 35/100
-
Dificultad 4/5 3-5 días Aptitud para principiantes 25/100
Todos los issues de boostorg/fiber
Issues similares
-
Dificultad 1/5 1-3 horas Aptitud para principiantes 68/100
eic/TimeframeBuilder#102 ·
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 65/100
-
error not reported I/O low priority rt_lib
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
Los mantenedores suelen responder en 1 día
-
[XLA] hlo/pass/README.md says passes implement Run; since the NVI change they implement RunImplAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 91/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
objectionary/game-of-life#78 ·