Make ServerFinder stop nicely when dcpomatic_cli quits.
authorCarl Hetherington <cth@carlh.net>
Thu, 29 Jan 2015 19:54:07 +0000 (19:54 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 29 Jan 2015 19:54:07 +0000 (19:54 +0000)
src/lib/cinema.cc
src/lib/dcpomatic_socket.h
src/lib/server_finder.cc
src/lib/server_finder.h
src/tools/dcpomatic_cli.cc

index f06db354343b3c971ce09e84bfba4ed950f08bf2..ce3077b2a041df9ab21e6600a59f4f770f78f164 100644 (file)
@@ -68,7 +68,7 @@ Cinema::remove_screen (shared_ptr<Screen> s)
 }
 
 Screen::Screen (cxml::ConstNodePtr node)
-       : name (node->string_child ("name"))
+       : name (node->string_child ("Name"))
 {
        if (node->optional_string_child ("Certificate")) {
                certificate = dcp::Certificate (node->string_child ("Certificate"));
index d2ee6d5555c634a8c58a31e47e81abbda0e746ec..b9859f600e40c16a76bbaa431a8ad4f57d655926 100644 (file)
  *  @brief A class to wrap a boost::asio::ip::tcp::socket with some things
  *  that are useful for DCP-o-matic.
  *
- *  This class wraps some things that I could not work out how to do with boost;
+ *  This class wraps some things that I could not work out how to do easily with boost;
  *  most notably, sync read/write calls with timeouts.
  */
-class Socket
+class Socket : public boost::noncopyable
 {
 public:
        Socket (int timeout = 30);
index ffc2d42dfb783c2f09197fac1ee0f47d4aa62a83..bef00702f800c8ae7d503304b058d6ee746d6b43 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <libcxml/cxml.h>
-#include <dcp/raw_convert.h>
 #include "server_finder.h"
 #include "exceptions.h"
 #include "util.h"
 #include "config.h"
 #include "cross.h"
 #include "ui_signaller.h"
+#include "dcpomatic_socket.h"
+#include <libcxml/cxml.h>
+#include <dcp/raw_convert.h>
+#include <boost/lambda/lambda.hpp>
 
 #include "i18n.h"
 
@@ -34,6 +36,7 @@ using std::vector;
 using std::cout;
 using boost::shared_ptr;
 using boost::scoped_array;
+using boost::weak_ptr;
 using dcp::raw_convert;
 
 ServerFinder* ServerFinder::_instance = 0;
@@ -42,11 +45,23 @@ ServerFinder::ServerFinder ()
        : _disabled (false)
        , _broadcast_thread (0)
        , _listen_thread (0)
+       , _stop (false)
 {
        _broadcast_thread = new boost::thread (boost::bind (&ServerFinder::broadcast_thread, this));
        _listen_thread = new boost::thread (boost::bind (&ServerFinder::listen_thread, this));
 }
 
+ServerFinder::~ServerFinder ()
+{
+       _stop = true;
+
+       _broadcast_thread->interrupt ();
+       _broadcast_thread->join ();
+
+       _listen_io_service.stop ();
+       _listen_thread->join ();
+}
+
 void
 ServerFinder::broadcast_thread ()
 try
@@ -64,7 +79,7 @@ try
 
        string const data = DCPOMATIC_HELLO;
        
-       while (true) {
+       while (!_stop) {
                if (Config::instance()->use_any_servers ()) {
                        /* Broadcast to look for servers */
                        try {
@@ -91,8 +106,12 @@ try
 
                        }
                }
-               
-               dcpomatic_sleep (10);
+
+               try {
+                       boost::thread::sleep (boost::get_system_time() + boost::posix_time::seconds (10));
+               } catch (boost::thread_interrupted& e) {
+                       return;
+               }
        }
 }
 catch (...)
@@ -102,53 +121,65 @@ catch (...)
 
 void
 ServerFinder::listen_thread ()
-try
-{
+try {
        using namespace boost::asio::ip;
 
-       boost::asio::io_service io_service;
-       boost::scoped_ptr<tcp::acceptor> acceptor;
        try {
-               acceptor.reset (new tcp::acceptor (io_service, tcp::endpoint (tcp::v4(), Config::instance()->server_port_base() + 1)));
+               _listen_acceptor.reset (new tcp::acceptor (_listen_io_service, tcp::endpoint (tcp::v4(), Config::instance()->server_port_base() + 1)));
        } catch (...) {
                boost::throw_exception (NetworkError (_("Could not listen for remote encode servers.  Perhaps another instance of DCP-o-matic is running.")));
        }
 
-       while (true) {
-               tcp::socket socket (io_service);
-               acceptor->accept (socket);
-
-               /* XXX: these reads should have timeouts, otherwise we will stop finding servers
-                  if one dies during this conversation
-               */
-
-               uint32_t length = 0;
-               boost::asio::read (socket, boost::asio::buffer (&length, sizeof (uint32_t)));
-               length = ntohl (length);
-
-               scoped_array<char> buffer (new char[length]);
-               boost::asio::read (socket, boost::asio::buffer (reinterpret_cast<uint8_t*> (buffer.get ()), length));
-               
-               string s (buffer.get());
-               shared_ptr<cxml::Document> xml (new cxml::Document ("ServerAvailable"));
-               xml->read_string (s);
-               
-               string const ip = socket.remote_endpoint().address().to_string ();
-               if (!server_found (ip)) {
-                       ServerDescription sd (ip, xml->number_child<int> ("Threads"));
-                       {
-                               boost::mutex::scoped_lock lm (_mutex);
-                               _servers.push_back (sd);
-                       }
-                       ui_signaller->emit (boost::bind (boost::ref (ServerFound), sd));
-               }
-       }
+       start_accept ();
+       _listen_io_service.run ();
 }
 catch (...)
 {
        store_current ();
 }
 
+void
+ServerFinder::start_accept ()
+{
+       shared_ptr<Socket> socket (new Socket ());
+       _listen_acceptor->async_accept (
+               socket->socket(),
+               boost::bind (&ServerFinder::handle_accept, this, boost::asio::placeholders::error, socket)
+               );
+}
+
+void
+ServerFinder::handle_accept (boost::system::error_code ec, shared_ptr<Socket> socket)
+{
+       if (ec) {
+               start_accept ();
+               return;
+       }
+       
+       uint32_t length;
+       socket->read (reinterpret_cast<uint8_t*> (&length), sizeof (uint32_t));
+       length = ntohl (length);
+       
+       scoped_array<char> buffer (new char[length]);
+       socket->read (reinterpret_cast<uint8_t*> (buffer.get()), length);
+       
+       string s (buffer.get());
+       shared_ptr<cxml::Document> xml (new cxml::Document ("ServerAvailable"));
+       xml->read_string (s);
+       
+       string const ip = socket->socket().remote_endpoint().address().to_string ();
+       if (!server_found (ip)) {
+               ServerDescription sd (ip, xml->number_child<int> ("Threads"));
+               {
+                       boost::mutex::scoped_lock lm (_mutex);
+                       _servers.push_back (sd);
+               }
+               ui_signaller->emit (boost::bind (boost::ref (ServerFound), sd));
+       }
+
+       start_accept ();
+}
+
 bool
 ServerFinder::server_found (string ip) const
 {
@@ -188,5 +219,9 @@ ServerFinder::instance ()
        return _instance;
 }
 
-       
-       
+void
+ServerFinder::drop ()
+{
+       delete _instance;
+       _instance = 0;
+}
index 6f02042cee6034827e4a6ad0d4b8ca9e0f0cc988..c0f1feb66e1d329167e55007ec8bdf4f6ac09521 100644 (file)
@@ -26,6 +26,7 @@ public:
        void connect (boost::function<void (ServerDescription)>);
 
        static ServerFinder* instance ();
+       static void drop ();
 
        void disable () {
                _disabled = true;
@@ -33,11 +34,14 @@ public:
 
 private:
        ServerFinder ();
+       ~ServerFinder ();
 
        void broadcast_thread ();
        void listen_thread ();
 
        bool server_found (std::string) const;
+       void start_accept ();
+       void handle_accept (boost::system::error_code ec, boost::shared_ptr<Socket> socket);
 
        boost::signals2::signal<void (ServerDescription)> ServerFound;
 
@@ -51,5 +55,9 @@ private:
        std::list<ServerDescription> _servers;
        mutable boost::mutex _mutex;
 
+       boost::asio::io_service _listen_io_service;
+       boost::shared_ptr<boost::asio::ip::tcp::acceptor> _listen_acceptor;
+       bool _stop;
+
        static ServerFinder* _instance;
 };
index 0b946181a09a84319a015a8928838b51525cb5a2..44abf4116d01403fc56963eee4d2a112d000a705 100644 (file)
@@ -216,6 +216,8 @@ main (int argc, char* argv[])
           indirectly holding onto codecs.
        */
        JobManager::drop ();
+
+       ServerFinder::drop ();
        
        return error ? EXIT_FAILURE : EXIT_SUCCESS;
 }