diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h
index 8d5c35782ee3..f21c6dc016ec 100644
--- a/include/libcamera/base/utils.h
+++ b/include/libcamera/base/utils.h
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include <libcamera/base/class.h>
 #include <libcamera/base/private.h>
 
 #ifndef __DOXYGEN__
@@ -428,6 +429,43 @@ private:
 	std::vector<std::function<void()>> actions_;
 };
 
+#ifndef __DOXYGEN__
+template<typename EF>
+class scope_exit
+{
+public:
+	template<typename Fn,
+		 std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Fn>>, scope_exit> &&
+				  std::is_constructible_v<EF, Fn>> * = nullptr>
+	explicit scope_exit(Fn &&fn)
+		: exitFunction_(std::forward<Fn>(fn))
+	{
+		static_assert(std::is_nothrow_constructible_v<EF, Fn>);
+	}
+
+	~scope_exit()
+	{
+		if (active_)
+			exitFunction_();
+	}
+
+	void release()
+	{
+		active_ = false;
+	}
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(scope_exit)
+
+	EF exitFunction_;
+	bool active_ = true;
+};
+
+template<typename EF>
+scope_exit(EF) -> scope_exit<EF>;
+
+#endif /* __DOXYGEN__ */
+
 } /* namespace utils */
 
 #ifndef __DOXYGEN__
