All of lore.kernel.org
 help / color / mirror / Atom feed
* [libgpiod][RFC PATCH] bindings: cxx: demote the line's parent chip reference to a weak_ptr
@ 2020-10-16  9:09 Bartosz Golaszewski
  2020-10-16 10:29 ` Helmut Grohne
  0 siblings, 1 reply; 9+ messages in thread
From: Bartosz Golaszewski @ 2020-10-16  9:09 UTC (permalink / raw)
  To: Helmut Grohne; +Cc: Kent Gibson, linux-gpio, Bartosz Golaszewski

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Currently the line object has the power to control the parent chip's
lifetime because it stores a hard reference (in the form of a shared
pointer) to the underlying struct gpiod_chip. This is wrong from the
relationship point-of-view - the chip should control the exposed lines,
not the other way around.

Demote the parent reference to a weak_ptr. Introduce a sub-class that
allows line and line_bulk objects to lock the chip by temporarily
converting this weak_ptr to a full shared_ptr - this way we don't risk
dropping the last reference to the parent chip when the line is calling
the underlying library functions. Chip deleted during that time will
expire right after the concerned line method returns.

This requires an API change - the global find_line() function now
returns a <line, chip> pair so that the caller gets the chance to grab
the chip's reference.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
Hi Helmut!

Please take a look at the following proposition of modifying the
relationship between chips and lines in C++ bindings. This is a follow-up
to our discussion under your suggestion for using time_point for
timestamps.

Bartosz

 bindings/cxx/chip.cpp                 |  6 ++++
 bindings/cxx/examples/gpiofindcxx.cpp |  6 ++--
 bindings/cxx/gpiod.hpp                | 31 +++++++++++++++++----
 bindings/cxx/iter.cpp                 |  2 +-
 bindings/cxx/line.cpp                 | 40 +++++++++++++++++++++------
 bindings/cxx/line_bulk.cpp            |  9 ++++++
 bindings/cxx/tests/tests-line.cpp     | 12 ++++----
 7 files changed, 81 insertions(+), 25 deletions(-)

diff --git a/bindings/cxx/chip.cpp b/bindings/cxx/chip.cpp
index 54c85da..1fc0723 100644
--- a/bindings/cxx/chip.cpp
+++ b/bindings/cxx/chip.cpp
@@ -69,6 +69,12 @@ chip::chip(::gpiod_chip* chip)
 
 }
 
+chip::chip(const ::std::weak_ptr<::gpiod_chip>& chip_ptr)
+	: _m_chip(chip_ptr)
+{
+
+}
+
 void chip::open(const ::std::string& device, int how)
 {
 	auto func = open_funcs.at(how);
diff --git a/bindings/cxx/examples/gpiofindcxx.cpp b/bindings/cxx/examples/gpiofindcxx.cpp
index 08fb62c..aeba29d 100644
--- a/bindings/cxx/examples/gpiofindcxx.cpp
+++ b/bindings/cxx/examples/gpiofindcxx.cpp
@@ -19,11 +19,11 @@ int main(int argc, char **argv)
 		return EXIT_FAILURE;
 	}
 
-	::gpiod::line line = ::gpiod::find_line(argv[1]);
-	if (!line)
+	auto ret = ::gpiod::find_line(argv[1]);
+	if (!ret.first)
 		return EXIT_FAILURE;
 
-	::std::cout << line.get_chip().name() << " " << line.offset() << ::std::endl;
+	::std::cout << ret.second.name() << " " << ret.first.offset() << ::std::endl;
 
 	return EXIT_SUCCESS;
 }
diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
index 8dfc172..50cdf7f 100644
--- a/bindings/cxx/gpiod.hpp
+++ b/bindings/cxx/gpiod.hpp
@@ -199,11 +199,13 @@ public:
 private:
 
 	chip(::gpiod_chip* chip);
+	chip(const ::std::weak_ptr<::gpiod_chip>& chip_ptr);
 
 	void throw_if_noref(void) const;
 
 	::std::shared_ptr<::gpiod_chip> _m_chip;
 
+	friend line;
 	friend chip_iter;
 	friend line_iter;
 };
@@ -438,10 +440,10 @@ public:
 	GPIOD_API int event_get_fd(void) const;
 
 	/**
-	 * @brief Get the reference to the parent chip.
-	 * @return Reference to the parent chip object.
+	 * @brief Get the parent chip.
+	 * @return Parent chip of this line.
 	 */
-	GPIOD_API const chip& get_chip(void) const;
+	GPIOD_API const chip get_chip(void) const;
 
 	/**
 	 * @brief Re-read the line info from the kernel.
@@ -526,7 +528,22 @@ private:
 	line_event make_line_event(const ::gpiod_line_event& event) const noexcept;
 
 	::gpiod_line* _m_line;
-	chip _m_chip;
+	::std::weak_ptr<::gpiod_chip> _m_owner;
+
+	class chip_locker
+	{
+	public:
+		chip_locker(const line& line);
+		~chip_locker(void) = default;
+
+		chip_locker(const chip_locker& other) = delete;
+		chip_locker(chip_locker&& other) = delete;
+		chip_locker& operator=(const chip_locker&& other) = delete;
+		chip_locker& operator=(chip_locker&& other) = delete;
+
+	private:
+		::std::shared_ptr<::gpiod_chip> _m_chip;
+	};
 
 	friend chip;
 	friend line_bulk;
@@ -536,9 +553,11 @@ private:
 /**
  * @brief Find a GPIO line by name. Search all GPIO chips present on the system.
  * @param name Name of the line.
- * @return Returns a line object - empty if the line was not found.
+ * @return Returns a <line, chip> pair where line is the line with given name
+ *         and chip is the line's owner. Both objects are empty if the line was
+ *         not found.
  */
-GPIOD_API line find_line(const ::std::string& name);
+GPIOD_API ::std::pair<line, chip> find_line(const ::std::string& name);
 
 /**
  * @brief Describes a single GPIO line event.
diff --git a/bindings/cxx/iter.cpp b/bindings/cxx/iter.cpp
index b1acc8c..7985910 100644
--- a/bindings/cxx/iter.cpp
+++ b/bindings/cxx/iter.cpp
@@ -115,7 +115,7 @@ line_iter& line_iter::operator++(void)
 {
 	::gpiod_line* next = ::gpiod_line_iter_next(this->_m_iter.get());
 
-	this->_m_current = next ? line(next, this->_m_current._m_chip) : line();
+	this->_m_current = next ? line(next, this->_m_current._m_owner) : line();
 
 	return *this;
 }
diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
index 52084bf..1ee4459 100644
--- a/bindings/cxx/line.cpp
+++ b/bindings/cxx/line.cpp
@@ -24,14 +24,14 @@ const ::std::map<int, int> bias_mapping = {
 
 line::line(void)
 	: _m_line(nullptr),
-	  _m_chip()
+	  _m_owner()
 {
 
 }
 
 line::line(::gpiod_line* line, const chip& owner)
 	: _m_line(line),
-	  _m_chip(owner)
+	  _m_owner(owner._m_chip)
 {
 
 }
@@ -39,6 +39,7 @@ line::line(::gpiod_line* line, const chip& owner)
 unsigned int line::offset(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return ::gpiod_line_offset(this->_m_line);
 }
@@ -46,6 +47,7 @@ unsigned int line::offset(void) const
 ::std::string line::name(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	const char* name = ::gpiod_line_name(this->_m_line);
 
@@ -55,6 +57,7 @@ unsigned int line::offset(void) const
 ::std::string line::consumer(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	const char* consumer = ::gpiod_line_consumer(this->_m_line);
 
@@ -64,6 +67,7 @@ unsigned int line::offset(void) const
 int line::direction(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	int dir = ::gpiod_line_direction(this->_m_line);
 
@@ -73,6 +77,7 @@ int line::direction(void) const
 int line::active_state(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	int active = ::gpiod_line_active_state(this->_m_line);
 
@@ -82,6 +87,7 @@ int line::active_state(void) const
 int line::bias(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return bias_mapping.at(::gpiod_line_bias(this->_m_line));
 }
@@ -89,6 +95,7 @@ int line::bias(void) const
 bool line::is_used(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return ::gpiod_line_is_used(this->_m_line);
 }
@@ -96,6 +103,7 @@ bool line::is_used(void) const
 bool line::is_open_drain(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return ::gpiod_line_is_open_drain(this->_m_line);
 }
@@ -103,6 +111,7 @@ bool line::is_open_drain(void) const
 bool line::is_open_source(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return ::gpiod_line_is_open_source(this->_m_line);
 }
@@ -128,6 +137,7 @@ void line::release(void) const
 bool line::is_requested(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	return ::gpiod_line_is_requested(this->_m_line);
 }
@@ -227,6 +237,7 @@ line_event line::make_line_event(const ::gpiod_line_event& event) const noexcept
 line_event line::event_read(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	::gpiod_line_event event_buf;
 	line_event event;
@@ -243,6 +254,7 @@ line_event line::event_read(void) const
 ::std::vector<line_event> line::event_read_multiple(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	/* 16 is the maximum number of events stored in the kernel FIFO. */
 	::std::array<::gpiod_line_event, 16> event_buf;
@@ -265,6 +277,7 @@ line_event line::event_read(void) const
 int line::event_get_fd(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	int ret = ::gpiod_line_event_get_fd(this->_m_line);
 
@@ -275,14 +288,15 @@ int line::event_get_fd(void) const
 	return ret;
 }
 
-const chip& line::get_chip(void) const
+const chip line::get_chip(void) const
 {
-	return this->_m_chip;
+	return chip(this->_m_owner);
 }
 
 void line::update(void) const
 {
 	this->throw_if_null();
+	line::chip_locker lock_chip(*this);
 
 	int ret = ::gpiod_line_update(this->_m_line);
 
@@ -294,7 +308,7 @@ void line::update(void) const
 void line::reset(void)
 {
 	this->_m_line = nullptr;
-	this->_m_chip.reset();
+	this->_m_owner.reset();
 }
 
 bool line::operator==(const line& rhs) const noexcept
@@ -323,14 +337,22 @@ void line::throw_if_null(void) const
 		throw ::std::logic_error("object not holding a GPIO line handle");
 }
 
-line find_line(const ::std::string& name)
+line::chip_locker::chip_locker(const line& line)
+	: _m_chip(line._m_owner)
 {
-	line ret;
+	
+}
+
+::std::pair<line, chip> find_line(const ::std::string& name)
+{
+	::std::pair<line, chip> ret;
 
 	for (auto& it: make_chip_iter()) {
-		ret = it.find_line(name);
-		if (ret)
+		ret.first = it.find_line(name);
+		if (ret.first) {
+			ret.second = it;
 			break;
+		}
 	}
 
 	return ret;
diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
index e77baa2..78615a1 100644
--- a/bindings/cxx/line_bulk.cpp
+++ b/bindings/cxx/line_bulk.cpp
@@ -101,6 +101,7 @@ void line_bulk::clear(void)
 void line_bulk::request(const line_request& config, const ::std::vector<int> default_vals) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	if (!default_vals.empty() && this->size() != default_vals.size())
 		throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
@@ -131,6 +132,7 @@ void line_bulk::request(const line_request& config, const ::std::vector<int> def
 void line_bulk::release(void) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	::gpiod_line_bulk bulk;
 
@@ -142,6 +144,7 @@ void line_bulk::release(void) const
 ::std::vector<int> line_bulk::get_values(void) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	::std::vector<int> values;
 	::gpiod_line_bulk bulk;
@@ -161,6 +164,7 @@ void line_bulk::release(void) const
 void line_bulk::set_values(const ::std::vector<int>& values) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	if (values.size() != this->_m_bulk.size())
 		throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
@@ -180,6 +184,7 @@ void line_bulk::set_config(int direction, ::std::bitset<32> flags,
 			   const ::std::vector<int> values) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	if (!values.empty() && this->_m_bulk.size() != values.size())
 		throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
@@ -206,6 +211,7 @@ void line_bulk::set_config(int direction, ::std::bitset<32> flags,
 void line_bulk::set_flags(::std::bitset<32> flags) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	::gpiod_line_bulk bulk;
 	int rv, gflags;
@@ -228,6 +234,7 @@ void line_bulk::set_flags(::std::bitset<32> flags) const
 void line_bulk::set_direction_input() const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	::gpiod_line_bulk bulk;
 	int rv;
@@ -243,6 +250,7 @@ void line_bulk::set_direction_input() const
 void line_bulk::set_direction_output(const ::std::vector<int>& values) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	if (values.size() != this->_m_bulk.size())
 		throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
@@ -262,6 +270,7 @@ void line_bulk::set_direction_output(const ::std::vector<int>& values) const
 line_bulk line_bulk::event_wait(const ::std::chrono::nanoseconds& timeout) const
 {
 	this->throw_if_empty();
+	line::chip_locker lock_chip(this->_m_bulk.front());
 
 	::gpiod_line_bulk bulk, event_bulk;
 	::timespec ts;
diff --git a/bindings/cxx/tests/tests-line.cpp b/bindings/cxx/tests/tests-line.cpp
index e2e4cbc..9841bea 100644
--- a/bindings/cxx/tests/tests-line.cpp
+++ b/bindings/cxx/tests/tests-line.cpp
@@ -24,16 +24,16 @@ TEST_CASE("Global find_line() function works", "[line]")
 
 	SECTION("line found")
 	{
-		auto line = ::gpiod::find_line("gpio-mockup-C-5");
-		REQUIRE(line.offset() == 5);
-		REQUIRE(line.name() == "gpio-mockup-C-5");
-		REQUIRE(line.get_chip().label() == "gpio-mockup-C");
+		auto ret = ::gpiod::find_line("gpio-mockup-C-5");
+		REQUIRE(ret.first.offset() == 5);
+		REQUIRE(ret.first.name() == "gpio-mockup-C-5");
+		REQUIRE(ret.second.label() == "gpio-mockup-C");
 	}
 
 	SECTION("line not found")
 	{
-		auto line = ::gpiod::find_line("nonexistent-line");
-		REQUIRE_FALSE(line);
+		auto ret = ::gpiod::find_line("nonexistent-line");
+		REQUIRE_FALSE(ret.first);
 	}
 }
 
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-11-04 15:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-16  9:09 [libgpiod][RFC PATCH] bindings: cxx: demote the line's parent chip reference to a weak_ptr Bartosz Golaszewski
2020-10-16 10:29 ` Helmut Grohne
2020-10-16 13:38   ` Bartosz Golaszewski
2020-10-19 12:17     ` Bartosz Golaszewski
2020-10-19 12:38       ` Helmut Grohne
2020-10-19 13:06         ` Bartosz Golaszewski
2020-10-20  5:57           ` Helmut Grohne
2020-10-20  6:58             ` Bartosz Golaszewski
2020-11-04 15:45               ` Bartosz Golaszewski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.