diff --git a/src/torrent/utils/thread_interrupt.cc b/src/torrent/utils/thread_interrupt.cc index b28920260..cfce0424d 100644 --- a/src/torrent/utils/thread_interrupt.cc +++ b/src/torrent/utils/thread_interrupt.cc @@ -45,8 +45,14 @@ namespace torrent { +thread_interrupt::thread_interrupt(int fd) : + m_poking(false) { + m_fileDesc = fd; + get_fd().set_nonblock(); +} + thread_interrupt::~thread_interrupt() { - if (m_fileDesc != -1) + if (m_fileDesc == -1) return; ::close(m_fileDesc); @@ -55,6 +61,11 @@ thread_interrupt::~thread_interrupt() { bool thread_interrupt::poke() { + if (is_poking()) + return true; + + __sync_bool_compare_and_swap(&m_other->m_poking, false, true); + int result = ::send(m_fileDesc, "a", 1, 0); if (result == 0 || @@ -66,8 +77,7 @@ thread_interrupt::poke() { thread_interrupt::pair_type thread_interrupt::create_pair() { - int fd1; - int fd2; + int fd1, fd2; if (!SocketFd::open_socket_pair(fd1, fd2)) throw internal_error("Could not create socket pair for thread_interrupt: " + std::string(rak::error_number::current().c_str()) + "."); @@ -75,8 +85,8 @@ thread_interrupt::create_pair() { thread_interrupt* t1 = new thread_interrupt(fd1); thread_interrupt* t2 = new thread_interrupt(fd2); - t1->get_fd().set_nonblock(); - t2->get_fd().set_nonblock(); + t1->m_other = t2; + t2->m_other = t1; return pair_type(t1, t2); } @@ -89,6 +99,8 @@ thread_interrupt::event_read() { if (result == 0 || (result == -1 && !rak::error_number::current().is_blocked_momentary())) throw internal_error("Invalid result reading from thread_interrupt socket."); + + __sync_bool_compare_and_swap(&m_poking, true, false); } } diff --git a/src/torrent/utils/thread_interrupt.h b/src/torrent/utils/thread_interrupt.h index aa9def081..1fbb817f0 100644 --- a/src/torrent/utils/thread_interrupt.h +++ b/src/torrent/utils/thread_interrupt.h @@ -52,6 +52,8 @@ class LIBTORRENT_EXPORT lt_cacheline_aligned thread_interrupt : public Event { static pair_type create_pair(); + bool is_poking() const; + bool poke(); void event_read(); @@ -59,11 +61,20 @@ class LIBTORRENT_EXPORT lt_cacheline_aligned thread_interrupt : public Event { void event_error() {} private: + thread_interrupt(int fd); + SocketFd& get_fd() { return *reinterpret_cast(&m_fileDesc); } - thread_interrupt(int fd) { m_fileDesc = fd; } + thread_interrupt* m_other; + bool m_poking lt_cacheline_aligned; }; +inline bool +thread_interrupt::is_poking() const { + __sync_synchronize(); + return m_poking; +} + } #endif