BugFix: correctly wait for Mixer to save file on exit

On the way, also improved Connection Manager ending properly.
This commit is contained in:
Bruno
2022-01-17 19:45:58 +01:00
parent 81e8d6d99c
commit ece7e04c7c
7 changed files with 98 additions and 85 deletions

View File

@@ -35,16 +35,12 @@
#endif #endif
Connection::Connection() : receiver_(nullptr) Connection::Connection() : asking_(false), receiver_(nullptr)
{ {
} }
Connection::~Connection() Connection::~Connection()
{ {
if (receiver_!=nullptr) {
receiver_->Break();
delete receiver_;
}
} }
bool Connection::init() bool Connection::init()
@@ -52,39 +48,43 @@ bool Connection::init()
// add default info for myself // add default info for myself
connections_.push_back(ConnectionInfo()); connections_.push_back(ConnectionInfo());
// try to open a socket at base handshake port if (receiver_ == nullptr) {
int trial = 0; // try to open a socket at base handshake port
while (trial < MAX_HANDSHAKE) { int trial = 0;
try { while (trial < MAX_HANDSHAKE) {
// increment the port to have unique ports try {
connections_[0].port_handshake = HANDSHAKE_PORT + trial; // increment the port to have unique ports
connections_[0].port_stream_request = STREAM_REQUEST_PORT + trial; connections_[0].port_handshake = HANDSHAKE_PORT + trial;
connections_[0].port_osc = OSC_DIALOG_PORT + trial; connections_[0].port_stream_request = STREAM_REQUEST_PORT + trial;
connections_[0].port_osc = OSC_DIALOG_PORT + trial;
// try to create listenning socket // try to create listenning socket
// through exception runtime if fails // through exception runtime if fails
receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS, receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS,
connections_[0].port_handshake ), &listener_ ); connections_[0].port_handshake ), &listener_ );
// validate hostname // validate hostname
connections_[0].name = APP_NAME "@" + NetworkToolkit::hostname() + connections_[0].name = APP_NAME "@" + NetworkToolkit::hostname() +
"." + std::to_string(connections_[0].port_handshake-HANDSHAKE_PORT); "." + std::to_string(connections_[0].port_handshake-HANDSHAKE_PORT);
// all good // all good
trial = MAX_HANDSHAKE; trial = MAX_HANDSHAKE;
}
catch (const std::runtime_error&) {
// arg, the receiver could not be initialized
// because the port was not available
receiver_ = nullptr;
}
// try again
trial++;
} }
catch (const std::runtime_error&) {
// arg, the receiver could not be initialized
// because the port was not available
receiver_ = nullptr;
}
// try again
trial++;
} }
// perfect, we could initialize the receiver // perfect, we could initialize the receiver
if (receiver_!=nullptr) { if (receiver_!=nullptr) {
// listen for answers // listen for answers
std::thread(listen).detach(); std::thread(listen).detach();
// regularly check for available streaming hosts // regularly check for available streaming hosts
asking_ = true;
std::thread(ask).detach(); std::thread(ask).detach();
// inform the application settings of our id // inform the application settings of our id
@@ -104,10 +104,31 @@ bool Connection::init()
void Connection::terminate() void Connection::terminate()
{ {
if (receiver_!=nullptr) // end ask loop
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
asking_ = false;
if ( ask_end_.wait_for(lck,std::chrono::seconds(2)) == std::cv_status::timeout)
g_printerr("Failed to terminate Connection manager (asker).");
// end receiver
if (receiver_!=nullptr) {
// request termination of receiver
receiver_->AsynchronousBreak(); receiver_->AsynchronousBreak();
// restore state of Streamer // wait for the listenner end notification
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
if ( listen_end_.wait_for(lck,std::chrono::seconds(2)) == std::cv_status::timeout)
g_printerr("Failed to terminate Connection manager (listener).");
// delete receiver and ready to initialize
delete receiver_;
receiver_ = nullptr;
}
// end Streamers
Streaming::manager().enable( false ); Streaming::manager().enable( false );
} }
@@ -175,6 +196,8 @@ void Connection::listen()
#endif #endif
if (Connection::manager().receiver_) if (Connection::manager().receiver_)
Connection::manager().receiver_->Run(); Connection::manager().receiver_->Run();
Connection::manager().listen_end_.notify_all();
} }
void Connection::ask() void Connection::ask()
@@ -191,7 +214,7 @@ void Connection::ask()
socket.SetEnableBroadcast(true); socket.SetEnableBroadcast(true);
// loop infinitely // loop infinitely
while(true) while(Connection::manager().asking_)
{ {
// broadcast on several ports // broadcast on several ports
for(int i=HANDSHAKE_PORT; i<HANDSHAKE_PORT+MAX_HANDSHAKE; i++) for(int i=HANDSHAKE_PORT; i<HANDSHAKE_PORT+MAX_HANDSHAKE; i++)
@@ -222,6 +245,7 @@ void Connection::ask()
} }
} }
Connection::manager().ask_end_.notify_all();
} }
void Connection::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, void Connection::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,

View File

@@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <condition_variable>
#include "NetworkToolkit.h" #include "NetworkToolkit.h"
@@ -86,8 +87,11 @@ protected:
private: private:
static void ask(); static void ask();
std::atomic<bool> asking_;
std::condition_variable ask_end_;
static void listen(); static void listen();
RequestListener listener_; RequestListener listener_;
std::condition_variable listen_end_;
UdpListeningReceiveSocket *receiver_; UdpListeningReceiveSocket *receiver_;
std::vector< ConnectionInfo > connections_; std::vector< ConnectionInfo > connections_;

View File

@@ -105,9 +105,9 @@ void Mixer::update()
// get the session loaded by this loader // get the session loaded by this loader
merge( sessionImporters_.back().get() ); merge( sessionImporters_.back().get() );
// FIXME: shouldn't we delete the imported session? // FIXME: shouldn't we delete the imported session?
// done with this session loader
sessionImporters_.pop_back();
} }
// done with this session loader
sessionImporters_.pop_back();
} }
} }
@@ -119,9 +119,9 @@ void Mixer::update()
if (sessionLoaders_.back().valid()) { if (sessionLoaders_.back().valid()) {
// get the session loaded by this loader // get the session loaded by this loader
set( sessionLoaders_.back().get() ); set( sessionLoaders_.back().get() );
// done with this session loader
sessionLoaders_.pop_back();
} }
// done with this session loader
sessionLoaders_.pop_back();
busy_ = false; busy_ = false;
} }
} }
@@ -135,9 +135,9 @@ void Mixer::update()
// did we get a filename in return? // did we get a filename in return?
if (sessionSavers_.back().valid()) { if (sessionSavers_.back().valid()) {
filename = sessionSavers_.back().get(); filename = sessionSavers_.back().get();
// done with this session saver
sessionSavers_.pop_back();
} }
// done with this session saver
sessionSavers_.pop_back();
if (filename.empty()) if (filename.empty())
Log::Warning("Failed to save Session."); Log::Warning("Failed to save Session.");
// all ok // all ok

View File

@@ -118,11 +118,12 @@ bool Streaming::busy()
{ {
bool b = false; bool b = false;
streamers_lock_.lock(); if (streamers_lock_.try_lock()) {
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin(); std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end() && !b; ++sit) for (; sit != streamers_.end() && !b; ++sit)
b = (*sit)->busy() ; b = (*sit)->busy() ;
streamers_lock_.unlock(); streamers_lock_.unlock();
}
return b; return b;
} }
@@ -132,11 +133,12 @@ std::vector<std::string> Streaming::listStreams()
{ {
std::vector<std::string> ls; std::vector<std::string> ls;
streamers_lock_.lock(); if (streamers_lock_.try_lock()) {
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin(); std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end(); ++sit) for (; sit != streamers_.end(); ++sit)
ls.push_back( (*sit)->info() ); ls.push_back( (*sit)->info() );
streamers_lock_.unlock(); streamers_lock_.unlock();
}
return ls; return ls;
} }

View File

@@ -141,7 +141,6 @@ UserInterface::UserInterface()
currentTextEdit.clear(); currentTextEdit.clear();
screenshot_step = 0; screenshot_step = 0;
pending_save_on_exit = false; pending_save_on_exit = false;
close_and_exit = false;
sessionopendialog = nullptr; sessionopendialog = nullptr;
sessionimportdialog = nullptr; sessionimportdialog = nullptr;
@@ -154,7 +153,6 @@ bool UserInterface::Init()
return false; return false;
pending_save_on_exit = false; pending_save_on_exit = false;
close_and_exit = false;
// Setup Dear ImGui context // Setup Dear ImGui context
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
@@ -685,28 +683,24 @@ bool UserInterface::TryClose()
if (DialogToolkit::FileDialog::busy()) if (DialogToolkit::FileDialog::busy())
return false; return false;
// always stop all recordings
FrameGrabbing::manager().stopAll();
// force close if trying to close again although it is already pending for save // force close if trying to close again although it is already pending for save
if (pending_save_on_exit) { if (pending_save_on_exit)
close_and_exit = true;
return true; return true;
}
// determine if a pending save of session is required // save on exit
pending_save_on_exit = ( Settings::application.recentSessions.save_on_exit pending_save_on_exit = false;
&& !Mixer::manager().session()->empty() if (Settings::application.recentSessions.save_on_exit && !Mixer::manager().session()->empty())
&& Mixer::manager().session()->filename().empty() );
// if no pending save of session is needed, close
if (!pending_save_on_exit)
{ {
// stop all recordings // determine if a pending save of session is required
FrameGrabbing::manager().stopAll(); if (Mixer::manager().session()->filename().empty())
// need to wait for user to give a filename
// save on exit pending_save_on_exit = true;
if (Settings::application.recentSessions.save_on_exit) else
// ok to save the session
Mixer::manager().save(false); Mixer::manager().save(false);
close_and_exit = true;
} }
// say we can close if no pending save of session is needed // say we can close if no pending save of session is needed
@@ -784,8 +778,7 @@ void UserInterface::NewFrame()
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
if (ImGui::Button(MENU_QUIT, ImVec2(ImGui::GetWindowContentRegionWidth(), 0))) { if (ImGui::Button(MENU_QUIT, ImVec2(ImGui::GetWindowContentRegionWidth(), 0))) {
pending_save_on_exit = false; Rendering::manager().close();
close_and_exit = true;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::Spacing(); ImGui::Spacing();
@@ -793,21 +786,6 @@ void UserInterface::NewFrame()
} }
} }
// Asked to close_and_exit
if (close_and_exit){
if (!ImGui::IsPopupOpen("Closing"))
ImGui::OpenPopup("Closing");
if (ImGui::BeginPopupModal("Closing", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Please wait...");
ImGui::EndPopup();
}
// exit only after everything is closed
if (!FrameGrabbing::manager().busy() && !Mixer::manager().busy()) {
Rendering::manager().close();
}
}
// navigator bar first // navigator bar first
navigator.Render(); navigator.Render();
} }

View File

@@ -405,7 +405,6 @@ protected:
int target_view_navigator; int target_view_navigator;
unsigned int screenshot_step; unsigned int screenshot_step;
bool pending_save_on_exit; bool pending_save_on_exit;
bool close_and_exit;
// Dialogs // Dialogs
DialogToolkit::OpenSessionDialog *sessionopendialog; DialogToolkit::OpenSessionDialog *sessionopendialog;

View File

@@ -162,6 +162,12 @@ int main(int argc, char *argv[])
/// ///
UserInterface::manager().Terminate(); UserInterface::manager().Terminate();
///
/// MIXER TERMINATE
///
while (Mixer::manager().busy())
Mixer::manager().update();
/// ///
/// RENDERING TERMINATE /// RENDERING TERMINATE
/// ///