It seems that a `Glib::ConvertError` is thrown, and then the
function that catches the error causes another ConvertError in
`err.what()` (which presumably include the filename that
causes the error).
relevant part of the backtrace (macOS Crashdump):
```
Glib::ConvertError::throw_func(_GError*) + 56
Glib::Error::throw_exception(_GError*) + 292
Glib::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Glib::ustring const&) + 131
StringPrivate::Composition& StringPrivate::Composition::arg<Glib::ustring>(Glib::ustring const&) + 25
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > string_compose<Glib::ustring> (...) + 81
PBD::run_functor_for_paths(...) + 1313
```
Variables by these names are only used from the local wscript and when
running "waf configure", which already for other reasons only can run at
the top-level.
These variables are thus not mandatory and not used.
https://waf.io/book/ says
By default, the project name and version are set to noname and 1.0. To
change them, it is necessary to provide two additional variables in
the top-level project file
- and waf code inspection confirms that waf itself only will use the top
level APPNAME.
Also, the 'waf dist' comment doesn't seem relevant - especially after
this change - and is removed too.
(Note: libs/evoral/wscript and libs/temporal/wscript still use APPNAME
for other purposes.)
https://waf.io/book/ says
By default, the project name and version are set to noname and 1.0. To
change them, it is necessary to provide two additional variables in the
top-level project file
- and waf code inspection confirms that waf itself only will use the top
level VERSION.
Some wscripts will use
bld.env['VERSION']
but that will also just use the value set in the top wscript.
Done with ad hoc scripting hacks processing unused imports found by pyflakes:
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Logs.* but unused' | cut -d: -f1 | while read f; do sed -i 's/^import waflib.Logs as Logs,/import/g' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Options.* but unused' | cut -d: -f1 | while read f; do sed -i 's/import waflib.Options as Options, /import /g' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Options.* but unused' | cut -d: -f1 | while read f; do sed -i 's/^from waflib import Options,/from waflib import/g' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep ' imported but unused$' | sed "s/^\([^:]*\):[0-9]*:[0-9]* '\(.*\)'.*/\1 \2/g" | while read f lib; do sed -i "/^import $lib$/d" $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Options.* but unused' | cut -d: -f1 | while read f; do sed -i '/from waflib import Options$/d' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.TaskGen.* but unused' | cut -d: -f1 | while read f; do sed -i '/from waflib import TaskGen$/d' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Task.Task.* but unused' | cut -d: -f1 | while read f; do sed -i '/^from waflib.Task import Task$/d' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Tools.winres.* but unused' | cut -d: -f1 | while read f; do sed -i '/^from waflib.Tools import winres$/d' $f; done
for f in $( find * -name wscript ); do echo; pyflakes $f; done | grep 'waflib.Utils.* but unused' | cut -d: -f1 | while read f; do sed -i '/^import waflib.Utils as Utils$/d' $f; done
AbstractUI IS-A BaseUI IS-A EventLoop
IS-A sigc::trackable
If we have sent a call_slot() request to an EventLoop that has not executed
when the object involved in the call_slot() functor is destroyed, we need to
ensure that the request is invalidated.
To do this, We register "notify" callbacks with the sigc::trackable that is a
base class of the object involved in the functor given to
call_slot(). sigc::trackable will call these "notify" callbacks from its
destructor.
So when the call_slot() functor's relevant object dies, and its sigc::trackable
base class is destroyed, it will invoke all of its the "notify" callbacks, which will
in turn call EventLoop::invalidate_request() and this hopefully marks all the
queued call_slot() functors as "do not call".
However, invalidate_request() requires a lock, and access to the lock is
granted via a pure virtual, EventLoop::slot_invalidation_lock(). In the
heirarchy cited above, this is implemented by AbstractUI.
When we destroy an AbstractUI, ~AbstractUI() is called first, and this destroys
the lock and changes the VTT so that ::slot_invalidation_lock() becomes a pure
virtual again.
Eventually we will call ~trackable() which in turns runs all the "notify"
callbacks, and then removes them. But when these callbacks end up in
EventLoop::invalidate_request(), we try to call ::slot_invalidation_lock() and
C++ will abort because of its (now) pure virtual status.
Therefore, we must invoke the "notify" callbacks before the
::slot_invalidation_lock() becomes pure, and that means inside ~AbstractUI, as
an explicit call to trackable::notify_callbacks().
This has not appeared before (remarkably), but became an issue when the
Launchpad Pro support code's main object (derived from MIDISurface and hence
from AbstractUI) "failed" to use sub-objects for its various methods. So when
it connects to, for example, the Session::RouteAddedOrRemoved signal, it is
connecting itself (derived from a sigc::trackable). When the Launchpad Pro
object is destroyed, it tries to invalidate all the call_slot() requests, but
this requires access to an event loop lock - owned by the Launchpad Pro event
loop, which is already destroyed!
Other surfaces have generally avoided this by using other objects to provide
methods of dealing with signals from libardour objects.
we allow use of/dependency on sigc::trackable there, so this is
both legal but also sensible.
Leave the macro definition of invalidator(x) in gtkmm2ext/gui_thread.h
because it doesn't hurt and makes some sense for it to be there. No
reason for a source module that needs invalidator(x) to load EventLoop
decl.
This will allow PBD::Filearchive to properly report progress.
It is also a generally useful API and deserves to be in libpbd.
Temporarily keep Ardour::Progress as alias
There is no need to preallocate request buffers for these threads - the event
loops that require them can allocate them when they discover and register the
pre-registered threads. This also means that event loops do not need to
register request buffer factories.
The old code assumed that the thread that created a request buffer for a given
signal-emitting thread would be the latter thread, and thus a thread-local
pointer to the request buffer could be used. This turns out not to be true: the
GUI thread tends to be responsible for constructing the request buffers for
pre-registered threads.
That mechanism has been replaced by using a RWLock protected map using
pthread_t as the key and the request buffer as the value. This allows any
thread to create and register the request buffers used between any other pair
of threads (because the lookup always uses a pthread_t).
The symptoms of this problem were a signal emitted in an audioengine thread
that was propagated to the target thread, but when the target thread scans its
request buffers for requests, it finds nothing (because it didn't know about
the request buffer). In a sense, the signal was successfully delivered to the
target thread, but no meaningful work (i.e the signal handler) is performed.
This fixes some edge-cases when scanning recent sessions
in get_state_files_in_directory() and likely some other
places that use run_functor_for_paths in case the folder
contains files with non UTF-8 names.