Browse Source

linux-v4l2: Add eventfd to signal udev on shutdown

Currently we use signals based on os_event's but the udev code blocks in
a 1s select (now poll) that cannot be signalled without an FD. This adds
an extra eventfd which will be signalled along with the os_event to
trigger the poll.

Also switches from select to poll to avoid issues with large fd values.
Kurt Kartaltepe 3 years ago
parent
commit
f788e9f6b7
1 changed files with 21 additions and 9 deletions
  1. 21 9
      plugins/linux-v4l2/v4l2-udev.c

+ 21 - 9
plugins/linux-v4l2/v4l2-udev.c

@@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <sys/eventfd.h>
+#include <poll.h>
+#include <unistd.h>
 #include <libudev.h>
 
 #include <util/threading.h>
@@ -40,6 +43,7 @@ static pthread_mutex_t udev_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static pthread_t udev_thread;
 static os_event_t *udev_event;
+static int udev_event_fd;
 
 static signal_handler_t *udev_signalhandler = NULL;
 
@@ -108,8 +112,6 @@ static void *udev_event_thread(void *vptr)
 	UNUSED_PARAMETER(vptr);
 
 	int fd;
-	fd_set fds;
-	struct timeval tv;
 	struct udev *udev;
 	struct udev_monitor *mon;
 	struct udev_device *dev;
@@ -127,12 +129,14 @@ static void *udev_event_thread(void *vptr)
 	fd = udev_monitor_get_fd(mon);
 
 	while (os_event_try(udev_event) == EAGAIN) {
-		FD_ZERO(&fds);
-		FD_SET(fd, &fds);
-		tv.tv_sec = 1;
-		tv.tv_usec = 0;
+		struct pollfd fds[2];
 
-		if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0)
+		fds[0].fd = fd;
+		fds[0].events = POLLIN;
+		fds[1].fd = udev_event_fd;
+		fds[1].events = POLLIN;
+
+		if (poll(fds, 2, 1000) <= 0)
 			continue;
 
 		dev = udev_monitor_receive_device(mon);
@@ -158,13 +162,19 @@ void v4l2_init_udev(void)
 	if (udev_refs == 0) {
 		if (os_event_init(&udev_event, OS_EVENT_TYPE_MANUAL) != 0)
 			goto fail;
+		if ((udev_event_fd = eventfd(0, EFD_CLOEXEC)) < 0)
+			goto fail;
 		if (pthread_create(&udev_thread, NULL, udev_event_thread,
-				   NULL) != 0)
+				   NULL) != 0) {
+			close(udev_event_fd);
 			goto fail;
+		}
 
 		udev_signalhandler = signal_handler_create();
-		if (!udev_signalhandler)
+		if (!udev_signalhandler) {
+			close(udev_event_fd);
 			goto fail;
+		}
 		signal_handler_add_array(udev_signalhandler, udev_signals);
 	}
 	udev_refs++;
@@ -180,8 +190,10 @@ void v4l2_unref_udev(void)
 	/* unref udev monitor */
 	if (udev_refs && --udev_refs == 0) {
 		os_event_signal(udev_event);
+		eventfd_write(udev_event_fd, 1);
 		pthread_join(udev_thread, NULL);
 		os_event_destroy(udev_event);
+		close(udev_event_fd);
 
 		if (udev_signalhandler)
 			signal_handler_destroy(udev_signalhandler);