py: Switch to non-blocking eventfd

Blocking wait can be easily implemented on top in Python, so rather than
supporting only blocking reads, or supporting both non-blocking and
blocking reads, let's support only non-blocking reads.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Tomi Valkeinen 2022-08-19 14:16:15 +03:00 committed by Laurent Pinchart
parent de7f1aa591
commit bf3dbaece9
6 changed files with 41 additions and 12 deletions

View file

@ -19,8 +19,9 @@ TIMEOUT_SEC = 3
def handle_camera_event(cm):
# cm.get_ready_requests() will not block here, as we know there is an event
# to read.
# cm.get_ready_requests() returns the ready requests, which in our case
# should almost always return a single Request, but in some cases there
# could be multiple or none.
reqs = cm.get_ready_requests()

View file

@ -14,6 +14,7 @@
import argparse
import libcamera as libcam
import selectors
import sys
# Number of frames to capture
@ -107,11 +108,18 @@ def main():
# The main loop. Wait for the queued Requests to complete, process them,
# and re-queue them again.
sel = selectors.DefaultSelector()
sel.register(cm.event_fd, selectors.EVENT_READ)
while frames_done < TOTAL_FRAMES:
# cm.get_ready_requests() blocks until there is an event and returns
# all the ready requests. Here we should almost always get a single
# cm.get_ready_requests() does not block, so we use a Selector to wait
# for a camera event. Here we should almost always get a single
# Request, but in some cases there could be multiple or none.
events = sel.select()
if not events:
continue
reqs = cm.get_ready_requests()
for req in reqs:

View file

@ -88,8 +88,9 @@ class CaptureContext:
camera_contexts: list[CameraCaptureContext] = []
def handle_camera_event(self):
# cm.get_ready_requests() will not block here, as we know there is an event
# to read.
# cm.get_ready_requests() returns the ready requests, which in our case
# should almost always return a single Request, but in some cases there
# could be multiple or none.
reqs = self.cm.get_ready_requests()

View file

@ -24,7 +24,7 @@ PyCameraManager::PyCameraManager()
cameraManager_ = std::make_unique<CameraManager>();
int fd = eventfd(0, EFD_CLOEXEC);
int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (fd == -1)
throw std::system_error(errno, std::generic_category(),
"Failed to create eventfd");
@ -62,7 +62,13 @@ py::list PyCameraManager::cameras()
std::vector<py::object> PyCameraManager::getReadyRequests()
{
readFd();
int ret = readFd();
if (ret == -EAGAIN)
return std::vector<py::object>();
if (ret != 0)
throw std::system_error(-ret, std::generic_category());
std::vector<py::object> py_reqs;
@ -96,12 +102,18 @@ void PyCameraManager::writeFd()
LOG(Python, Fatal) << "Unable to write to eventfd";
}
void PyCameraManager::readFd()
int PyCameraManager::readFd()
{
uint8_t buf[8];
if (read(eventFd_.get(), buf, 8) != 8)
throw std::system_error(errno, std::generic_category());
ssize_t ret = read(eventFd_.get(), buf, 8);
if (ret == 8)
return 0;
else if (ret < 0)
return -errno;
else
return -EIO;
}
void PyCameraManager::pushRequest(Request *req)

View file

@ -39,7 +39,7 @@ private:
LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_);
void writeFd();
void readFd();
int readFd();
void pushRequest(Request *req);
std::vector<Request *> getCompletedRequests();
};

View file

@ -207,9 +207,16 @@ class SimpleCaptureMethods(CameraTesterBase):
reqs = None
gc.collect()
sel = selectors.DefaultSelector()
sel.register(cm.event_fd, selectors.EVENT_READ)
reqs = []
while True:
events = sel.select()
if not events:
continue
ready_reqs = cm.get_ready_requests()
reqs += ready_reqs