[libcamera-devel] [RFC v1 5/7] py: Add 'nonblocking' argument to get_ready_requests()
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri Jun 24 15:53:08 CEST 2022
Hello,
On Fri, Jun 24, 2022 at 11:26:18AM +0100, David Plowman wrote:
> Hi everyone
>
> Just to add some background to this one...
>
> This has been causing me a little trouble in Picamera2 lately.
> get_ready_requests used to be non-blocking so I would always call it
> after stopping the camera to clear out any "lurking" requests. Since
> it became blocking I've had to stop that but it does mean that a stray
> request can be read out at an awkward time. This can lead to us
> getting the "wrong" image (and probably falling over when it's the
> wrong size or something), or even trying to queue it back to libcamera
> (while the camera is still stopped).
>
> Anyway, either solution works for me. Either I can flush out those
> requests after calling stop(), which is what I used to do. Or they
> could disappear "spontaneously". Both work for me!!
I'd like to make get_ready_requests() non-blocking unconditionally,
could that be done ?
> On Fri, 24 Jun 2022 at 11:13, Kieran Bingham wrote:
> > Quoting Tomi Valkeinen (2022-06-23 15:47:34)
> > > Add 'nonblocking' argument to get_ready_requests() which allows the user
> > > to ensure get_ready_requests() never blocks. This can be used e.g. after
> > > calling camera.stop(), to process or discard any ready or cancelled
> > > Requests.
> >
> > I guess I needed to read ahead for the comments I posted on the previous
> > patch ;-)
> >
> > > In fact, it probably should always be used after stopping the cameras,
> > > unless you have made sure that there are no unprocessed Requests. If you
> > > start the camera again, and you have left Requests unprocessed, you will
> > > get those "old" Requests when you expect to get the new Requests.
> > >
> > > It may be good to call this even if your script exits after stopping
> > > the cameras, as unprocessed Requests will keep the Cameras and related
> > > objects alive, and thus they won't be freed. As your script is exiting
> > > it's strictly speaking not an issue, but it does make tracking other
> > > "real" memory leaks more difficult.
Developers will forget to do so, so I think a better API would be nice.
> > > Perhaps the camera.start() should go and discard any old Requests
> > > related to that camera. For the exit issue I don't see any automatic
> > > solution.
> >
> > At the end of camera.stop() we should validate and ensure that all
> > Requests are released to the application. We should hold no internal
> > requests when camera.stop() completes.
> >
> > I.e. ... if we have things to discard at camera.start() - that's a bug I
> > believe.
> >
> > ahhh but here perhaps the issue is that the python code is the one that
> > has to 'retrieve' those, while in C++ they are returned via a signal?
> >
> > Is there any harm in discarding the requests at the end of camera.stop()
> > such that there is simply 'nothing left to process' after? Or does the
> > python application have to end up owning the requests to correctly
> > release them?
Sounds like an idea to explore. What's the drawback of clearing in
stop() ?
> > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> > > ---
> > > src/py/libcamera/py_camera_manager.cpp | 21 +++++++++++++++++++--
> > > src/py/libcamera/py_camera_manager.h | 4 +++-
> > > src/py/libcamera/py_main.cpp | 3 ++-
> > > 3 files changed, 24 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp
> > > index c9e5a99c..ba45f713 100644
> > > --- a/src/py/libcamera/py_camera_manager.cpp
> > > +++ b/src/py/libcamera/py_camera_manager.cpp
> > > @@ -5,6 +5,7 @@
> > >
> > > #include "py_camera_manager.h"
> > >
> > > +#include <poll.h>
> > > #include <sys/eventfd.h>
> > > #include <unistd.h>
> > >
> > > @@ -55,9 +56,10 @@ py::list PyCameraManager::getCameras()
> > > return l;
> > > }
> > >
> > > -std::vector<py::object> PyCameraManager::getReadyRequests()
> > > +std::vector<py::object> PyCameraManager::getReadyRequests(bool nonBlocking)
> > > {
> > > - readFd();
> > > + if (!nonBlocking || hasEvents())
> > > + readFd();
> > >
> > > std::vector<Request *> v;
> > > getRequests(v);
> > > @@ -113,3 +115,18 @@ void PyCameraManager::getRequests(std::vector<Request *> &v)
> > > std::lock_guard guard(reqlist_mutex_);
> > > swap(v, reqList_);
> > > }
> > > +
> > > +bool PyCameraManager::hasEvents()
> > > +{
> > > + struct pollfd pfd = {
> > > + .fd = eventFd_,
> > > + .events = POLLIN,
> > > + .revents = 0,
> > > + };
> > > +
> > > + int ret = poll(&pfd, 1, 0);
> > > + if (ret == -1)
> > > + throw std::system_error(errno, std::generic_category());
> > > +
> > > + return pfd.revents & POLLIN;
> > > +}
> > > diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h
> > > index b0b971ad..2396d236 100644
> > > --- a/src/py/libcamera/py_camera_manager.h
> > > +++ b/src/py/libcamera/py_camera_manager.h
> > > @@ -23,7 +23,7 @@ public:
> > >
> > > int eventFd() const { return eventFd_; }
> > >
> > > - std::vector<pybind11::object> getReadyRequests();
> > > + std::vector<pybind11::object> getReadyRequests(bool nonBlocking = false);
> > >
> > > void handleRequestCompleted(Request *req);
> > >
> > > @@ -36,4 +36,6 @@ private:
> > > void readFd();
> > > void pushRequest(Request *req);
> > > void getRequests(std::vector<Request *> &v);
> > > +
> > > + bool hasEvents();
> > > };
> > > diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> > > index 23018288..ee4ecb9b 100644
> > > --- a/src/py/libcamera/py_main.cpp
> > > +++ b/src/py/libcamera/py_main.cpp
> > > @@ -103,7 +103,8 @@ PYBIND11_MODULE(_libcamera, m)
> > > .def_property_readonly("cameras", &PyCameraManager::getCameras)
> > >
> > > .def_property_readonly("event_fd", &PyCameraManager::eventFd)
> > > - .def("get_ready_requests", &PyCameraManager::getReadyRequests);
> > > + .def("get_ready_requests", &PyCameraManager::getReadyRequests,
> > > + py::arg("nonblocking") = false);
> > >
> > > pyCamera
> > > .def_property_readonly("id", &Camera::id)
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list