diff options
author | Max Kellermann <max@duempel.org> | 2014-11-26 20:19:17 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-11-26 20:19:17 +0100 |
commit | 737a56a0302b1030e1071c7be9add7bdca955e32 (patch) | |
tree | 93d9b6d625fa1b0ce9e8bf8d13fbc829be5178d1 | |
parent | 12b6959ea201fea925c4376f961b8d54738292e2 (diff) | |
parent | 67cba251c8826409c82fb2ab75072dc4fca2b4dc (diff) | |
download | mpd-737a56a0302b1030e1071c7be9add7bdca955e32.tar.gz mpd-737a56a0302b1030e1071c7be9add7bdca955e32.tar.xz mpd-737a56a0302b1030e1071c7be9add7bdca955e32.zip |
Merge tag 'v0.19.5'
-rw-r--r-- | INSTALL | 3 | ||||
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | NEWS | 11 | ||||
-rw-r--r-- | android/AndroidManifest.xml | 4 | ||||
-rw-r--r-- | configure.ac | 24 | ||||
-rw-r--r-- | src/archive/plugins/ZzipArchivePlugin.cxx | 9 | ||||
-rw-r--r-- | src/decoder/DecoderList.cxx | 4 | ||||
-rw-r--r-- | src/decoder/plugins/Mp4v2DecoderPlugin.cxx | 330 | ||||
-rw-r--r-- | src/decoder/plugins/Mp4v2DecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/event/DeferredMonitor.hxx | 3 | ||||
-rw-r--r-- | src/input/plugins/AlsaInputPlugin.cxx | 2 | ||||
-rw-r--r-- | src/lib/nfs/Connection.cxx | 12 | ||||
-rw-r--r-- | src/lib/nfs/FileReader.cxx | 38 | ||||
-rw-r--r-- | src/lib/nfs/FileReader.hxx | 7 | ||||
-rw-r--r-- | src/lib/nfs/Manager.cxx | 30 | ||||
-rw-r--r-- | src/lib/nfs/Manager.hxx | 33 | ||||
-rw-r--r-- | test/run_input.cxx | 20 |
17 files changed, 140 insertions, 430 deletions
@@ -119,9 +119,6 @@ For AdLib playback. despotify - https://github.com/SimonKagstrom/despotify For Spotify playback. -MP4v2 - https://code.google.com/p/mp4v2/ -For MP4 playback. You will need FAAD2. - Optional Miscellaneous Dependencies ----------------------------------- diff --git a/Makefile.am b/Makefile.am index 52c314928..f4138116a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -833,7 +833,6 @@ libdecoder_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(WAVPACK_CFLAGS) \ $(MAD_CFLAGS) \ $(MPG123_CFLAGS) \ - $(MP4V2_CFLAGS) \ $(OPUS_CFLAGS) \ $(FFMPEG_CFLAGS) \ $(MPCDEC_CFLAGS) \ @@ -853,7 +852,6 @@ DECODER_LIBS = \ $(WAVPACK_LIBS) \ $(MAD_LIBS) \ $(MPG123_LIBS) \ - $(MP4V2_LIBS) \ $(OPUS_LIBS) \ $(FFMPEG_LIBS2) \ $(MPCDEC_LIBS) \ @@ -968,12 +966,6 @@ noinst_LIBRARIES += libmodplug_decoder_plugin.a DECODER_LIBS += libmodplug_decoder_plugin.a $(MODPLUG_LIBS) endif -if HAVE_MP4V2 -libdecoder_a_SOURCES += \ - src/decoder/plugins/Mp4v2DecoderPlugin.cxx \ - src/decoder/plugins/Mp4v2DecoderPlugin.hxx -endif - if ENABLE_SIDPLAY libdecoder_a_SOURCES += \ src/decoder/plugins/SidplayDecoderPlugin.cxx \ @@ -2193,4 +2185,11 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \ $(wildcard scripts/*.sh) \ $(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \ systemd/mpd.socket \ + android/AndroidManifest.xml \ + android/build.py \ + android/custom_rules.xml \ + android/res/values/strings.xml \ + android/src/Bridge.java \ + android/src/Loader.java \ + android/src/Main.java \ src/win32/mpd_win32_rc.rc.in src/win32/mpd.ico @@ -9,9 +9,14 @@ ver 0.20 (not yet released) - pulse: set channel map to WAVE-EX * reset song priority on playback -ver 0.19.5 (not yet released) +ver 0.19.5 (2014/11/26) +* input + - nfs: fix crash on connection failure +* archive + - zzip: fix crash after seeking * decoder - dsdiff, dsf, opus: fix deadlock while seeking + - mp4v2: remove because of incompatible license ver 0.19.4 (2014/11/18) * protocol @@ -144,6 +149,10 @@ ver 0.19 (2014/10/10) * install systemd unit for socket activation * Android port +ver 0.18.19 (2014/11/26) +* archive + - zzip: fix crash after seeking + ver 0.18.18 (2014/11/18) * decoder - ffmpeg: support opus diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 02401c699..8625bd76e 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.musicpd" android:installLocation="auto" - android:versionCode="8" - android:versionName="0.19.4"> + android:versionCode="9" + android:versionName="0.19.5"> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/> diff --git a/configure.ac b/configure.ac index 16ce66cf4..ce9619dce 100644 --- a/configure.ac +++ b/configure.ac @@ -382,11 +382,6 @@ AC_ARG_ENABLE(mikmod, [enable the mikmod decoder (default: disable)]),, enable_mikmod=no) -AC_ARG_ENABLE(mp4v2, - AS_HELP_STRING([--enable-mp4v2], - [enable libmp4v2 decoder plugin]),, - enable_mp4v2=auto) - AC_ARG_ENABLE(mpc, AS_HELP_STRING([--enable-mpc], [disable musepack (MPC) support (default: auto)]),, @@ -984,24 +979,6 @@ dnl -------------------------------- libmodplug ------------------------------- MPD_ENABLE_AUTO_PKG(modplug, MODPLUG, [libmodplug], [modplug decoder plugin], [libmodplug not found]) -dnl -------------------------------- libmp4v2 --------------------------------- -if test x$enable_aac = xyes; then - MPD_AUTO_LIB(mp4v2, MP4V2, mp4v2, MP4Create, [-lmp4v2], [], - [mp4v2], [libmp4v2 not found]) - - if test x$enable_mp4v2 = xyes; then - AC_DEFINE(HAVE_MP4V2, 1, [Define to use libmp4v2 for MP4 decoding]) - fi -else - if test x$enable_mp4v2 = xyes; then - AC_MSG_ERROR([MP4V2 requires AAC!]) - fi - - enable_mp4v2=no -fi - -AM_CONDITIONAL(HAVE_MP4V2, test x$enable_mp4v2 = xyes) - dnl -------------------------------- libopus ---------------------------------- MPD_ENABLE_AUTO_PKG(opus, OPUS, [opus ogg], [opus decoder plugin], [libopus not found]) @@ -1533,7 +1510,6 @@ printf '\n\t' results(sndfile, [libsndfile]) results(mikmod, [MikMod]) results(modplug, [MODPLUG]) -results(mp4v2, [MP4V2]) results(mad, [MAD]) results(mpg123, [MPG123]) results(mpc, [Musepack]) diff --git a/src/archive/plugins/ZzipArchivePlugin.cxx b/src/archive/plugins/ZzipArchivePlugin.cxx index b4dc7029f..21cb693d8 100644 --- a/src/archive/plugins/ZzipArchivePlugin.cxx +++ b/src/archive/plugins/ZzipArchivePlugin.cxx @@ -168,12 +168,13 @@ bool ZzipInputStream::Seek(offset_type new_offset, Error &error) { zzip_off_t ofs = zzip_seek(file, new_offset, SEEK_SET); - if (ofs != -1) { + if (ofs < 0) { error.Set(zzip_domain, "zzip_seek() has failed"); - offset = ofs; - return true; + return false; } - return false; + + offset = ofs; + return true; } /* exported structures */ diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx index 7f665fb88..e9c9ba685 100644 --- a/src/decoder/DecoderList.cxx +++ b/src/decoder/DecoderList.cxx @@ -37,7 +37,6 @@ #include "plugins/MadDecoderPlugin.hxx" #include "plugins/SndfileDecoderPlugin.hxx" #include "plugins/Mpg123DecoderPlugin.hxx" -#include "plugins/Mp4v2DecoderPlugin.hxx" #include "plugins/WildmidiDecoderPlugin.hxx" #include "plugins/MikmodDecoderPlugin.hxx" #include "plugins/ModplugDecoderPlugin.hxx" @@ -55,9 +54,6 @@ const struct DecoderPlugin *const decoder_plugins[] = { #ifdef ENABLE_MPG123 &mpg123_decoder_plugin, #endif -#ifdef HAVE_MP4V2 - &mp4v2_decoder_plugin, -#endif #ifdef ENABLE_VORBIS_DECODER &vorbis_decoder_plugin, #endif diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx deleted file mode 100644 index 34bccd243..000000000 --- a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2003-2014 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" /* must be first for large file support */ -#include "Mp4v2DecoderPlugin.hxx" -#include "../DecoderAPI.hxx" -#include "CheckAudioFormat.hxx" -#include "tag/TagHandler.hxx" -#include "fs/Path.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include <mp4v2/mp4v2.h> -#include <neaacdec.h> - -#include <cstdio> -#include <cstdlib> - -static constexpr Domain mp4v2_decoder_domain("mp4v2"); - -static MP4TrackId -mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, - AudioFormat &audio_format, Error &error) -{ - unsigned long sample_rate; - - const MP4TrackId tracks = MP4GetNumberOfTracks(handle); - - for (MP4TrackId id = 1; id <= tracks; id++) { - const char* track_type = MP4GetTrackType(handle, id); - - if (track_type == 0) - continue; - - const auto obj_type = MP4GetTrackEsdsObjectTypeId(handle, id); - - if (obj_type == MP4_INVALID_AUDIO_TYPE) - continue; - if (obj_type == MP4_MPEG4_AUDIO_TYPE) { - const auto mpeg_type = MP4GetTrackAudioMpeg4Type(handle, id); - if (!MP4_IS_MPEG4_AAC_AUDIO_TYPE(mpeg_type)) - continue; - } else if (!MP4_IS_AAC_AUDIO_TYPE(obj_type)) - continue; - - if (decoder == nullptr) - /* found audio track, no decoder */ - return id; - - unsigned char *buff = nullptr; - unsigned buff_size = 0; - - if (!MP4GetTrackESConfiguration(handle, id, &buff, &buff_size)) - continue; - - uint8_t channels; - int32_t nbytes = NeAACDecInit(decoder, buff, buff_size, - &sample_rate, &channels); - - free(buff); - - if (nbytes < 0) - /* invalid stream */ - continue; - - if (!audio_format_init_checked(audio_format, sample_rate, - SampleFormat::S16, - channels, - error)) - continue; - - return id; - } - - error.Set(mp4v2_decoder_domain, "no valid aac track found"); - - return MP4_INVALID_TRACK_ID; -} - -static NeAACDecHandle -mp4_faad_new(MP4FileHandle handle, AudioFormat &audio_format, Error &error) -{ - const NeAACDecHandle decoder = NeAACDecOpen(); - const NeAACDecConfigurationPtr config = - NeAACDecGetCurrentConfiguration(decoder); - config->outputFormat = FAAD_FMT_16BIT; - config->downMatrix = 1; - config->dontUpSampleImplicitSBR = 0; - NeAACDecSetConfiguration(decoder, config); - - const auto track = mp4_get_aac_track(handle, decoder, audio_format, error); - - if (track == MP4_INVALID_TRACK_ID) { - NeAACDecClose(decoder); - return nullptr; - } - - return decoder; -} - -static void -mp4_file_decode(Decoder &mpd_decoder, Path path_fs) -{ - const MP4FileHandle handle = MP4Read(path_fs.c_str()); - - if (handle == MP4_INVALID_FILE_HANDLE) { - FormatError(mp4v2_decoder_domain, - "unable to open file"); - return; - } - - AudioFormat audio_format; - Error error; - const NeAACDecHandle decoder = mp4_faad_new(handle, audio_format, error); - - if (decoder == nullptr) { - LogError(error); - MP4Close(handle); - return; - } - - const MP4TrackId track = mp4_get_aac_track(handle, nullptr, audio_format, error); - - /* initialize the MPD core */ - - const MP4Timestamp scale = MP4GetTrackTimeScale(handle, track); - const SongTime duration = SongTime::FromScale<uint64_t>(MP4GetTrackDuration(handle, track), - scale); - const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track); - - decoder_initialized(mpd_decoder, audio_format, true, duration); - - /* the decoder loop */ - - DecoderCommand cmd = DecoderCommand::NONE; - - for (MP4SampleId sample = 1; - sample < num_samples && cmd != DecoderCommand::STOP; - sample++) { - unsigned char *data = nullptr; - unsigned int data_length = 0; - - if (cmd == DecoderCommand::SEEK) { - const MP4Timestamp offset = - decoder_seek_time(mpd_decoder).ToScale(scale); - - sample = MP4GetSampleIdFromTime(handle, track, offset, - false); - decoder_command_finished(mpd_decoder); - } - - /* read */ - if (MP4ReadSample(handle, track, sample, &data, &data_length) == 0) { - FormatError(mp4v2_decoder_domain, "unable to read sample"); - break; - } - - /* decode it */ - NeAACDecFrameInfo frame_info; - const void *const decoded = NeAACDecDecode(decoder, &frame_info, data, data_length); - - if (frame_info.error > 0) { - FormatWarning(mp4v2_decoder_domain, - "error decoding AAC stream: %s", - NeAACDecGetErrorMessage(frame_info.error)); - break; - } - - if (frame_info.channels != audio_format.channels) { - FormatDefault(mp4v2_decoder_domain, - "channel count changed from %u to %u", - audio_format.channels, frame_info.channels); - break; - } - - if (frame_info.samplerate != audio_format.sample_rate) { - FormatDefault(mp4v2_decoder_domain, - "sample rate changed from %u to %lu", - audio_format.sample_rate, - (unsigned long)frame_info.samplerate); - break; - } - - /* update bit rate and position */ - unsigned bit_rate = 0; - - if (frame_info.samples > 0) { - bit_rate = frame_info.bytesconsumed * 8.0 * - frame_info.channels * audio_format.sample_rate / - frame_info.samples / 1000 + 0.5; - } - - /* send PCM samples to MPD */ - - cmd = decoder_data(mpd_decoder, nullptr, decoded, - (size_t)frame_info.samples * 2, - bit_rate); - - free(data); - } - - /* cleanup */ - NeAACDecClose(decoder); - MP4Close(handle); -} - -static inline void -mp4_safe_invoke_tag(const struct tag_handler *handler, void *handler_ctx, - TagType tag, const char *value) -{ - if (value != nullptr) - tag_handler_invoke_tag(handler, handler_ctx, tag, value); -} - -static bool -mp4_scan_file(Path path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - const MP4FileHandle handle = MP4Read(path_fs.c_str()); - - if (handle == MP4_INVALID_FILE_HANDLE) - return false; - - AudioFormat tmp_audio_format; - Error error; - const MP4TrackId id = mp4_get_aac_track(handle, nullptr, tmp_audio_format, error); - - if (id == MP4_INVALID_TRACK_ID) { - LogError(error); - MP4Close(handle); - return false; - } - - const MP4Timestamp scale = MP4GetTrackTimeScale(handle, id); - const SongTime dur = - SongTime::FromScale<uint64_t>(MP4GetTrackDuration(handle, id), - scale); - tag_handler_invoke_duration(handler, handler_ctx, dur); - - const MP4Tags* tags = MP4TagsAlloc(); - MP4TagsFetch(tags, handle); - - static constexpr struct { - const char *MP4Tags::*p; - TagType tag_type; - } mp4v2_tags[] = { - { &MP4Tags::name, TAG_NAME }, - { &MP4Tags::artist, TAG_ARTIST }, - { &MP4Tags::albumArtist, TAG_ALBUM_ARTIST }, - { &MP4Tags::album, TAG_ALBUM }, - { &MP4Tags::composer, TAG_COMPOSER }, - { &MP4Tags::comments, TAG_COMMENT }, - { &MP4Tags::genre, TAG_GENRE }, - { &MP4Tags::releaseDate, TAG_DATE }, - { &MP4Tags::sortArtist, TAG_ARTIST_SORT }, - { &MP4Tags::sortAlbumArtist, TAG_ALBUM_ARTIST_SORT }, - }; - - for (const auto &i : mp4v2_tags) - mp4_safe_invoke_tag(handler, handler_ctx, - i.tag_type, tags->*i.p); - - char buff[8]; /* tmp buffer for index to string. */ - if (tags->track != nullptr) { - sprintf(buff, "%d", tags->track->index); - tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, buff); - } - - if (tags->disk != nullptr) { - sprintf(buff, "%d", tags->disk->index); - tag_handler_invoke_tag(handler, handler_ctx, TAG_DISC, buff); - } - - MP4TagsFree(tags); - MP4Close(handle); - - return true; -} - -static const char *const mp4_suffixes[] = { - "mp4", - "m4a", - /* "m4p", encrypted */ - /* "m4b", audio book */ - /* "m4r", ring tones */ - /* "m4v", video */ - nullptr -}; - -static const char *const mp4_mime_types[] = { - "application/mp4", - "application/m4a", - "audio/mp4", - "audio/m4a", - /* "audio/m4p", */ - /* "audio/m4b", */ - /* "audio/m4r", */ - /* "audio/m4v", */ - nullptr -}; - -const struct DecoderPlugin mp4v2_decoder_plugin = { - "mp4v2", - nullptr, - nullptr, - nullptr, - mp4_file_decode, - mp4_scan_file, - nullptr, - nullptr, - mp4_suffixes, - mp4_mime_types -}; diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.hxx b/src/decoder/plugins/Mp4v2DecoderPlugin.hxx deleted file mode 100644 index 57585dec4..000000000 --- a/src/decoder/plugins/Mp4v2DecoderPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2014 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_DECODER_MP4V2_HXX -#define MPD_DECODER_MP4V2_HXX - -extern const struct DecoderPlugin mp4v2_decoder_plugin; - -#endif diff --git a/src/event/DeferredMonitor.hxx b/src/event/DeferredMonitor.hxx index 3d3ab22b7..c4aa605fc 100644 --- a/src/event/DeferredMonitor.hxx +++ b/src/event/DeferredMonitor.hxx @@ -21,9 +21,6 @@ #define MPD_SOCKET_DEFERRED_MONITOR_HXX #include "check.h" -#include "Compiler.h" - -#include <atomic> class EventLoop; diff --git a/src/input/plugins/AlsaInputPlugin.cxx b/src/input/plugins/AlsaInputPlugin.cxx index 82b96f7df..f03f745c6 100644 --- a/src/input/plugins/AlsaInputPlugin.cxx +++ b/src/input/plugins/AlsaInputPlugin.cxx @@ -43,6 +43,8 @@ #include <alsa/asoundlib.h> +#include <atomic> + #include <assert.h> #include <string.h> diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index c2c7ceb2b..06d2a4d2a 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -327,6 +327,10 @@ NfsConnection::DestroyContext() assert(GetEventLoop().IsInside()); assert(context != nullptr); + /* cancel pending DeferredMonitor that was scheduled to notify + new leases */ + DeferredMonitor::Cancel(); + if (SocketMonitor::IsDefined()) SocketMonitor::Cancel(); @@ -405,10 +409,10 @@ NfsConnection::OnSocketReady(unsigned flags) error.Format(nfs_domain, "NFS connection has failed: %s", nfs_get_error(context)); + BroadcastError(std::move(error)); + DestroyContext(); closed = true; - - BroadcastError(std::move(error)); } else if (SocketMonitor::IsDefined() && nfs_get_fd(context) < 0) { /* this happens when rpc_reconnect_requeue() is called after the connection broke, but autoreconnet was @@ -421,10 +425,10 @@ NfsConnection::OnSocketReady(unsigned flags) error.Format(nfs_domain, "NFS socket disappeared: %s", msg); + BroadcastError(std::move(error)); + DestroyContext(); closed = true; - - BroadcastError(std::move(error)); } assert(in_event); diff --git a/src/lib/nfs/FileReader.cxx b/src/lib/nfs/FileReader.cxx index 4837e1f0e..1b80f2c86 100644 --- a/src/lib/nfs/FileReader.cxx +++ b/src/lib/nfs/FileReader.cxx @@ -56,8 +56,18 @@ NfsFileReader::Close() return; } + /* this cancels State::MOUNT */ connection->RemoveLease(*this); + CancelOrClose(); +} + +void +NfsFileReader::CancelOrClose() +{ + assert(state != State::INITIAL && + state != State::DEFER); + if (state == State::IDLE) /* no async operation in progress: can close immediately */ @@ -164,6 +174,8 @@ NfsFileReader::OnNfsConnectionFailed(const Error &error) { assert(state == State::MOUNT); + state = State::INITIAL; + Error copy; copy.Set(error); OnNfsFileError(std::move(copy)); @@ -174,7 +186,7 @@ NfsFileReader::OnNfsConnectionDisconnected(const Error &error) { assert(state > State::MOUNT); - state = State::INITIAL; + CancelOrClose(); Error copy; copy.Set(error); @@ -246,6 +258,30 @@ NfsFileReader::OnNfsCallback(unsigned status, void *data) void NfsFileReader::OnNfsError(Error &&error) { + switch (state) { + case State::INITIAL: + case State::DEFER: + case State::MOUNT: + case State::IDLE: + assert(false); + gcc_unreachable(); + + case State::OPEN: + connection->RemoveLease(*this); + state = State::INITIAL; + break; + + case State::STAT: + connection->RemoveLease(*this); + connection->Close(fh); + state = State::INITIAL; + break; + + case State::READ: + state = State::IDLE; + break; + } + OnNfsFileError(std::move(error)); } diff --git a/src/lib/nfs/FileReader.hxx b/src/lib/nfs/FileReader.hxx index 7f43e0ecf..1495a2832 100644 --- a/src/lib/nfs/FileReader.hxx +++ b/src/lib/nfs/FileReader.hxx @@ -24,6 +24,7 @@ #include "Lease.hxx" #include "Callback.hxx" #include "event/DeferredMonitor.hxx" +#include "Compiler.h" #include <string> @@ -75,6 +76,12 @@ protected: virtual void OnNfsFileError(Error &&error) = 0; private: + /** + * Cancel the current operation, if any. The NfsLease must be + * unregistered already. + */ + void CancelOrClose(); + void OpenCallback(nfsfh *_fh); void StatCallback(const struct stat *st); diff --git a/src/lib/nfs/Manager.cxx b/src/lib/nfs/Manager.cxx index c5aecf48d..6d50cce18 100644 --- a/src/lib/nfs/Manager.cxx +++ b/src/lib/nfs/Manager.cxx @@ -29,8 +29,10 @@ NfsManager::ManagedConnection::OnNfsConnectionError(Error &&error) { FormatError(error, "NFS error on %s:%s", GetServer(), GetExportName()); - manager.connections.erase(manager.connections.iterator_to(*this)); - delete this; + /* defer deletion so the caller + (i.e. NfsConnection::OnSocketReady()) can still use this + object */ + manager.ScheduleDelete(*this); } inline bool @@ -59,7 +61,9 @@ NfsManager::Compare::operator()(const ManagedConnection &a, NfsManager::~NfsManager() { - assert(loop.IsInside()); + assert(GetEventLoop().IsInside()); + + CollectGarbage(); connections.clear_and_dispose([](ManagedConnection *c){ delete c; @@ -71,13 +75,13 @@ NfsManager::GetConnection(const char *server, const char *export_name) { assert(server != nullptr); assert(export_name != nullptr); - assert(loop.IsInside()); + assert(GetEventLoop().IsInside()); Map::insert_commit_data hint; auto result = connections.insert_check(LookupKey{server, export_name}, Compare(), hint); if (result.second) { - auto c = new ManagedConnection(*this, loop, + auto c = new ManagedConnection(*this, GetEventLoop(), server, export_name); connections.insert_commit(*c, hint); return *c; @@ -85,3 +89,19 @@ NfsManager::GetConnection(const char *server, const char *export_name) return *result.first; } } + +void +NfsManager::CollectGarbage() +{ + assert(GetEventLoop().IsInside()); + + garbage.clear_and_dispose([](ManagedConnection *c){ + delete c; + }); +} + +void +NfsManager::OnIdle() +{ + CollectGarbage(); +} diff --git a/src/lib/nfs/Manager.hxx b/src/lib/nfs/Manager.hxx index 612b01f9c..130c81aca 100644 --- a/src/lib/nfs/Manager.hxx +++ b/src/lib/nfs/Manager.hxx @@ -23,14 +23,16 @@ #include "check.h" #include "Connection.hxx" #include "Compiler.h" +#include "event/IdleMonitor.hxx" #include <boost/intrusive/set.hpp> +#include <boost/intrusive/slist.hpp> /** * A manager for NFS connections. Handles multiple connections to * multiple NFS servers. */ -class NfsManager { +class NfsManager final : IdleMonitor { struct LookupKey { const char *server; const char *export_name; @@ -38,6 +40,7 @@ class NfsManager { class ManagedConnection final : public NfsConnection, + public boost::intrusive::slist_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>, public boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> { NfsManager &manager; @@ -63,8 +66,6 @@ class NfsManager { const LookupKey b) const; }; - EventLoop &loop; - /** * Maps server and export_name to #ManagedConnection. */ @@ -74,9 +75,18 @@ class NfsManager { Map connections; + typedef boost::intrusive::slist<ManagedConnection> List; + + /** + * A list of "garbage" connection objects. Their destruction + * is postponed because they were thrown into the garbage list + * when callers on the stack were still using them. + */ + List garbage; + public: NfsManager(EventLoop &_loop) - :loop(_loop) {} + :IdleMonitor(_loop) {} /** * Must be run from EventLoop's thread. @@ -86,6 +96,21 @@ public: gcc_pure NfsConnection &GetConnection(const char *server, const char *export_name); + +private: + void ScheduleDelete(ManagedConnection &c) { + connections.erase(connections.iterator_to(c)); + garbage.push_front(c); + IdleMonitor::Schedule(); + } + + /** + * Delete all connections on the #garbage list. + */ + void CollectGarbage(); + + /* virtual methods from IdleMonitor */ + void OnIdle() override; }; #endif diff --git a/test/run_input.cxx b/test/run_input.cxx index de5bd9632..6864a5d64 100644 --- a/test/run_input.cxx +++ b/test/run_input.cxx @@ -54,11 +54,6 @@ tag_save(FILE *file, const Tag &tag) static int dump_input_stream(InputStream *is) { - Error error; - char buffer[4096]; - size_t num_read; - ssize_t num_written; - is->Lock(); /* print meta data */ @@ -76,7 +71,9 @@ dump_input_stream(InputStream *is) delete tag; } - num_read = is->Read(buffer, sizeof(buffer), error); + Error error; + char buffer[4096]; + size_t num_read = is->Read(buffer, sizeof(buffer), error); if (num_read == 0) { if (error.IsDefined()) LogError(error); @@ -84,11 +81,12 @@ dump_input_stream(InputStream *is) break; } - num_written = write(1, buffer, num_read); + ssize_t num_written = write(1, buffer, num_read); if (num_written <= 0) break; } + Error error; if (!is->Check(error)) { LogError(error); is->Unlock(); @@ -102,10 +100,6 @@ dump_input_stream(InputStream *is) int main(int argc, char **argv) { - Error error; - InputStream *is; - int ret; - if (argc != 2) { fprintf(stderr, "Usage: run_input URI\n"); return EXIT_FAILURE; @@ -129,6 +123,7 @@ int main(int argc, char **argv) archive_plugin_init_all(); #endif + Error error; if (!input_stream_global_init(error)) { LogError(error); return 2; @@ -139,7 +134,8 @@ int main(int argc, char **argv) Mutex mutex; Cond cond; - is = InputStream::OpenReady(argv[1], mutex, cond, error); + InputStream *is = InputStream::OpenReady(argv[1], mutex, cond, error); + int ret; if (is != NULL) { ret = dump_input_stream(is); delete is; |