From: Carl Hetherington Date: Tue, 31 Dec 2013 15:44:51 +0000 (+0000) Subject: Merge 1.0 X-Git-Tag: v2.0.48~922^2~16 X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=ad49361b303d1ceff7048fa0e89ba609ca9ce376;hp=b2a9271256e09fcfedff3beea5fc73c04e7c0e14 Merge 1.0 --- diff --git a/ChangeLog b/ChangeLog index a8b8630b7..5aa58f5ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2013-12-30 Carl Hetherington + + * Put catches around a few threads which could have uncaught exceptions. + + * Add nascent dcpomatic_create command-line program to create films. + 2013-12-29 Carl Hetherington * Version 1.53 released. diff --git a/run/dcpomatic_create b/run/dcpomatic_create new file mode 100755 index 000000000..bf3c3c493 --- /dev/null +++ b/run/dcpomatic_create @@ -0,0 +1,18 @@ +#!/bin/bash + +export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH +if [ "$1" == "--debug" ]; then + shift + gdb --args build/src/tools/dcpomatic_create $* +elif [ "$1" == "--valgrind" ]; then + shift + valgrind --tool="memcheck" build/src/tools/dcpomatic_create $* +elif [ "$1" == "--callgrind" ]; then + shift + valgrind --tool="callgrind" build/src/tools/dcpomatic_create $* +elif [ "$1" == "--perf" ]; then + shift + perf record build/src/tools/dcpomatic_create $* +else + build/src/tools/dcpomatic_create $* +fi diff --git a/run/dcpomatic_server b/run/dcpomatic_server new file mode 100755 index 000000000..5856221b3 --- /dev/null +++ b/run/dcpomatic_server @@ -0,0 +1,10 @@ +#!/bin/bash + +export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH +if [ "$1" == "--debug" ]; then + gdb --args build/src/tools/dcpomatic_server +elif [ "$1" == "--valgrind" ]; then + valgrind --tool="memcheck" build/src/tools/dcpomatic_server +else + build/src/tools/dcpomatic_server +fi diff --git a/run/dcpomatic_server_gui b/run/dcpomatic_server_gui deleted file mode 100755 index b7b122e67..000000000 --- a/run/dcpomatic_server_gui +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH -if [ "$1" == "--debug" ]; then - gdb --args build/src/tools/dcpomatic_server_gui -elif [ "$1" == "--valgrind" ]; then - valgrind --tool="memcheck" build/src/tools/dcpomatic_server_gui -else - build/src/tools/dcpomatic_server_gui -fi diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 475c230da..ca9134c04 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -96,7 +96,6 @@ Encoder::process_begin () ServerFinder::instance()->connect (boost::bind (&Encoder::server_found, this, _1)); } - void Encoder::process_end () { @@ -199,9 +198,12 @@ Encoder::process_video (shared_ptr image, Eyes eyes, ColourConversi return; } - if (_writer->thrown ()) { - _writer->rethrow (); - } + _writer->rethrow (); + /* Re-throw any exception raised by one of our threads. If more + than one has thrown an exception, only one will be rethrown, I think; + but then, if that happens something has gone badly wrong. + */ + rethrow (); if (_writer->can_fake_write (_video_frames_out)) { _writer->fake_write (_video_frames_out, eyes); @@ -257,6 +259,7 @@ Encoder::terminate_threads () void Encoder::encoder_thread (optional server) +try { /* Number of seconds that we currently wait between attempts to connect to the server; not relevant for localhost @@ -338,6 +341,10 @@ Encoder::encoder_thread (optional server) _condition.notify_all (); } } +catch (...) +{ + store_current (); +} void Encoder::server_found (ServerDescription s) diff --git a/src/lib/encoder.h b/src/lib/encoder.h index d43e4e1d3..079174f89 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -56,7 +56,7 @@ class PlayerImage; * is supplied as uncompressed PCM in blocks of various sizes. */ -class Encoder : public boost::noncopyable +class Encoder : public boost::noncopyable, public ExceptionStore { public: Encoder (boost::shared_ptr f, boost::weak_ptr); diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index f4631c09b..c1240538f 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -230,17 +230,30 @@ public: PixelFormatError (std::string o, AVPixelFormat f); }; +/** A parent class for classes which have a need to catch and + * re-throw exceptions. This is intended for classes + * which run their own thread; they should do something like + * + * void my_thread () + * try { + * // do things which might throw exceptions + * } catch (...) { + * store_current (); + * } + * + * and then in another thread call rethrow(). If any + * exception was thrown by my_thread it will be stored by + * store_current() and then rethrow() will re-throw it where + * it can be handled. + */ class ExceptionStore { public: - bool thrown () const { - boost::mutex::scoped_lock lm (_mutex); - return _exception; - } - void rethrow () { boost::mutex::scoped_lock lm (_mutex); - boost::rethrow_exception (_exception); + if (_exception) { + boost::rethrow_exception (_exception); + } } protected: diff --git a/src/lib/player.cc b/src/lib/player.cc index 96d23a82b..c9f9acd94 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -343,16 +343,16 @@ void Player::flush () { TimedAudioBuffers tb = _audio_merger.flush (); - if (tb.audio) { + if (_audio && tb.audio) { Audio (tb.audio, tb.time); _audio_position += _film->audio_frames_to_time (tb.audio->frames ()); } - while (_video_position < _audio_position) { + while (_video && _video_position < _audio_position) { emit_black (); } - while (_audio_position < _video_position) { + while (_audio && _audio_position < _video_position) { emit_silence (_video_position - _audio_position); } diff --git a/src/lib/server_finder.cc b/src/lib/server_finder.cc index de90e0d5c..5b67d8048 100644 --- a/src/lib/server_finder.cc +++ b/src/lib/server_finder.cc @@ -47,6 +47,7 @@ ServerFinder::ServerFinder () void ServerFinder::broadcast_thread () +try { boost::system::error_code error; boost::asio::io_service io_service; @@ -88,9 +89,14 @@ ServerFinder::broadcast_thread () dcpomatic_sleep (10); } } +catch (...) +{ + store_current (); +} void ServerFinder::listen_thread () +try { while (1) { shared_ptr sock (new Socket (10)); @@ -117,6 +123,10 @@ ServerFinder::listen_thread () } } } +catch (...) +{ + store_current (); +} bool ServerFinder::server_found (string ip) const diff --git a/src/lib/server_finder.h b/src/lib/server_finder.h index 01e26f7df..202bee8f9 100644 --- a/src/lib/server_finder.h +++ b/src/lib/server_finder.h @@ -20,7 +20,7 @@ #include #include "server.h" -class ServerFinder +class ServerFinder : public ExceptionStore { public: void connect (boost::function); diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 4129b7a82..414ea72eb 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -335,9 +335,7 @@ Writer::finish () lock.unlock (); _thread->join (); - if (thrown ()) { - rethrow (); - } + rethrow (); delete _thread; _thread = 0; diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 66f795ddf..891c4623c 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -54,6 +54,7 @@ #include "lib/cinema.h" #include "lib/kdm.h" #include "lib/send_kdm_email_job.h" +#include "lib/server_finder.h" using std::cout; using std::string; @@ -632,8 +633,12 @@ class App : public wxApp f->Show (); ui_signaller = new wxUISignaller (this); - this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); + Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); + Bind (wxEVT_TIMER, boost::bind (&App::check, this)); + _timer.reset (new wxTimer (this)); + _timer->Start (1000); + return true; } catch (exception& e) @@ -670,6 +675,17 @@ class App : public wxApp { ui_signaller->ui_idle (); } + + void check () + { + try { + ServerFinder::instance()->rethrow (); + } catch (exception& e) { + error_dialog (0, std_to_wx (e.what ())); + } + } + + shared_ptr _timer; }; IMPLEMENT_APP (App) diff --git a/src/tools/dcpomatic_create.cc b/src/tools/dcpomatic_create.cc new file mode 100644 index 000000000..8be468b30 --- /dev/null +++ b/src/tools/dcpomatic_create.cc @@ -0,0 +1,128 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include "lib/version.h" +#include "lib/film.h" +#include "lib/util.h" +#include "lib/content_factory.h" +#include "lib/job_manager.h" +#include "lib/ui_signaller.h" +#include "lib/job.h" + +using std::string; +using std::cout; +using std::cerr; +using std::list; +using boost::shared_ptr; + +static void +help (string n) +{ + cerr << "Create a film directory (ready for making a DCP) from some content files.\n" + << "Syntax: " << n << " [OPTION] [ ...]\n" + << " -v, --version show DCP-o-matic version\n" + << " -h, --help show this help\n" + << " -n, --name film name\n" + << " -o, --output output directory (required)\n"; +} + +int +main (int argc, char* argv[]) +{ + string name; + boost::filesystem::path output; + + int option_index = 0; + while (1) { + static struct option long_options[] = { + { "version", no_argument, 0, 'v'}, + { "help", no_argument, 0, 'h'}, + { "name", required_argument, 0, 'n'}, + { "output", required_argument, 0, 'o'}, + { 0, 0, 0, 0} + }; + + int c = getopt_long (argc, argv, "vhn:o:", long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 'v': + cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; + exit (EXIT_SUCCESS); + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 'n': + name = optarg; + break; + case 'o': + output = optarg; + break; + } + } + + if (optind > argc) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + if (output.empty ()) { + cerr << "Missing required option -o or --output.\n" + << "Use " << argv[0] << " --help for help.\n"; + exit (EXIT_FAILURE); + } + + dcpomatic_setup (); + ui_signaller = new UISignaller (); + + shared_ptr film (new Film (output)); + if (!name.empty ()) { + film->set_name (name); + } + + for (int i = optind; i < argc; ++i) { + film->examine_and_add_content (content_factory (film, argv[i])); + } + + JobManager* jm = JobManager::instance (); + while (jm->work_to_do ()) { + ui_signaller->ui_idle (); + } + + if (jm->errors ()) { + list > jobs = jm->get (); + for (list >::iterator i = jobs.begin(); i != jobs.end(); ++i) { + if ((*i)->finished_in_error ()) { + cerr << (*i)->error_summary () << "\n" + << (*i)->error_details () << "\n"; + } + } + exit (EXIT_FAILURE); + } + + film->write_metadata (); + return 0; +} diff --git a/src/tools/dcpomatic_server.cc b/src/tools/dcpomatic_server.cc index 8c6a29461..a82478dfd 100644 --- a/src/tools/dcpomatic_server.cc +++ b/src/tools/dcpomatic_server.cc @@ -27,6 +27,7 @@ using std::cout; using std::string; +using std::exception; using boost::shared_ptr; using boost::thread; using boost::bind; @@ -133,7 +134,7 @@ private: } }; -class App : public wxApp +class App : public wxApp, public ExceptionStore { public: App () @@ -154,6 +155,10 @@ private: _icon = new TaskBarIcon; _thread = new thread (bind (&App::main_thread, this)); + + Bind (wxEVT_TIMER, boost::bind (&App::check, this)); + _timer.reset (new wxTimer (this)); + _timer->Start (1000); return true; } @@ -165,13 +170,29 @@ private: } void main_thread () - { + try { Server server (memory_log, false); server.run (Config::instance()->num_local_encoding_threads ()); + } catch (...) { + store_current (); + } + + void check () + { + try { + rethrow (); + } catch (exception& e) { + error_dialog (0, std_to_wx (e.what ())); + wxTheApp->ExitMainLoop (); + } catch (...) { + error_dialog (0, _("An unknown error has occurred with the DCP-o-matic server.")); + wxTheApp->ExitMainLoop (); + } } boost::thread* _thread; TaskBarIcon* _icon; + shared_ptr _timer; }; IMPLEMENT_APP (App) diff --git a/src/tools/wscript b/src/tools/wscript index eafad44ec..0fd336676 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -9,7 +9,7 @@ def configure(conf): conf.env.append_value('LINKFLAGS', ['-mconsole']) def build(bld): - for t in ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm']: + for t in ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm', 'dcpomatic_create']: obj = bld(features = 'cxx cxxprogram') obj.uselib = 'BOOST_THREAD OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC WXWIDGETS QUICKMAIL' obj.includes = ['..'] diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index 103d36d00..367d1edbb 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -140,11 +140,18 @@ ThreadedStaticText::~ThreadedStaticText () /** Run our thread and post the result to the GUI thread via AddPendingEvent */ void ThreadedStaticText::run (function fn) +try { wxCommandEvent ev (wxEVT_COMMAND_TEXT_UPDATED, _update_event_id); ev.SetString (std_to_wx (fn ())); GetEventHandler()->AddPendingEvent (ev); } +catch (...) +{ + /* Ignore exceptions; marginally better than the program quitting, but + only marginally. + */ +} /** Called in the GUI thread when our worker thread has finished */ void