From 5394ffb1c59a7d85094d91f9be66673deb63707e Mon Sep 17 00:00:00 2001 From: David Freese Date: Fri, 2 Aug 2013 14:40:12 -0500 Subject: [PATCH] KML thread * Changed thread exit implementation to conditional compile -- used for building the OS X dmg --- src/dialogs/fl_digi.cxx | 3 ++ src/include/kmlserver.h | 4 +- src/include/main.h | 3 ++ src/main.cxx | 11 +++-- src/misc/kmlserver.cxx | 95 +++++++++++++++++++++++++++++++++++------ 5 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index 47a442d2..96646e32 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -143,6 +143,7 @@ #include "flmisc.h" #include "arq_io.h" +#include "kmlserver.h" #include "notifydialog.h" #include "macroedit.h" @@ -2806,6 +2807,8 @@ bool clean_exit(bool ask) { close_logbook(); MilliSleep(50); + exit_process(); + return true; } diff --git a/src/include/kmlserver.h b/src/include/kmlserver.h index 87be7143..35328a77 100644 --- a/src/include/kmlserver.h +++ b/src/include/kmlserver.h @@ -36,6 +36,8 @@ class KmlServer { protected: /// Counts the number of complete messages written. int m_nb_broadcasts ; + int exit_kml_server; + int request_broadcast; public: /// List of key-value pairs displayed for example in Google Earth balloons. struct CustomDataT : public std::vector< std::pair< std::string, std::string > > { @@ -55,7 +57,7 @@ public: } }; - KmlServer() : m_nb_broadcasts(0) {} + KmlServer() : m_nb_broadcasts(0), exit_kml_server(0), request_broadcast(0) {} virtual ~KmlServer() {} diff --git a/src/include/main.h b/src/include/main.h index 0d148739..879f2350 100644 --- a/src/include/main.h +++ b/src/include/main.h @@ -86,6 +86,9 @@ extern bool nbems_dirs_checked; // This inits or reinits everything related to KML: Reloads params etc... void kml_init(bool load_files = false); +// close down remaining threads just before exiting UI +extern void exit_process(); + int directory_is_created( const char * strdir ); #endif diff --git a/src/main.cxx b/src/main.cxx index 725a0d6f..8400ffef 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -513,8 +513,13 @@ int main(int argc, char ** argv) int ret = Fl::run(); - arq_close(); + return ret; +} +void exit_process() { + + KmlServer::Exit(); + arq_close(); XML_RPC_Server::stop(); if (progdefaults.usepskrep) @@ -524,11 +529,9 @@ int main(int argc, char ** argv) cbq[i]->detach(); delete cbq[i]; } + FSEL::destroy(); - KmlServer::Exit(); - - return ret; } void generate_option_help(void) { diff --git a/src/misc/kmlserver.cxx b/src/misc/kmlserver.cxx index efe8c905..5623050c 100644 --- a/src/misc/kmlserver.cxx +++ b/src/misc/kmlserver.cxx @@ -46,9 +46,22 @@ #include "strutil.h" #include "configuration.h" +#include "irrXML.h" + #include "timeops.h" -#include "irrXML.h" +/** Some platforms have problems with condition variables apparently. + * When cancelling a thread which waits in pthread_cond_timedwait, + * the thread is stuck. + * We replace it by an unconditional wait, and a test on a boolean + * which indicates if data was saved in the internal buffers. + * The consequence is that data are not immediately saved in KML files, + * and the user has to wait until the end of the delay. + */ + +#if !defined(__APPLE__) +# define FLDIGI_KML_CONDITION_VARIABLE 1 +#endif // ---------------------------------------------------------------------------- @@ -85,7 +98,10 @@ static time_t KmlFromTimestamp( const char * ts ) { if( 0 == strcmp( ts, KmlSrvUnique ) ) return KmlServer::UniqueEvent ; - tm objTm ; + /// So all fields are initialised with correct default values. + time_t timNow = time(NULL); + tm objTm = *gmtime( &timNow ); + int r = sscanf( ts, "%4d-%02d-%02dT%02d:%02dZ", &objTm.tm_year, &objTm.tm_mon, @@ -96,6 +112,7 @@ static time_t KmlFromTimestamp( const char * ts ) { objTm.tm_year -= 1900; objTm.tm_mon -= 1; objTm.tm_sec = 0; + time_t res = mktime( &objTm ); if( res < 0 ) throw std::runtime_error("Cannot make timestamp from " + std::string(ts) ); return res; @@ -185,12 +202,12 @@ static void KmlHeader( std::ostream & ostrm, const std::string & title ) { "" << title << "\n" ; } -// Appended at the end of each KML document. +/// Appended at the end of each KML document. static const char KmlFooter[] = "\n" "\n" ; -// Contains for example GIF images, and all the styles. Can be customised by the user. +/// Contains for example GIF images, and all the styles. Can be customised by the user. static const std::string namStyles = "styles.kml"; /** Used to code the data for reloading. The description tag is too complicated @@ -789,7 +806,6 @@ class KmlSrvImpl : public KmlServer { { PlacesMapItrSetT::const_iterator itNamNxt = itNamLast; ++itNamNxt ; - assert( (*itNamBeg)->first != (*itNamLast)->first ); if( ( itNamNxt == itStylNext ) || ( (*itNamNxt)->first != (*itNamLast)->first ) ) { // No point tracing a line with one point only. if( *itNamBeg != *itNamLast ) { @@ -852,15 +868,17 @@ class KmlSrvImpl : public KmlServer { /// This file copy does not need to be atomic because it happens once only. void CopyStyleFileIfNotExists(void) { - // Where the installed file is stored. Used as default. + /// Where the installed file is stored and never moved from. Used as default. std::string namSrc = PKGDATADIR "/kml/" + namStyles ; /// The use might customize its styles file: It will not be altered. std::string namDst = m_kml_dir + namStyles ; + /// Used to copy the master file to the user copy if needed. FILE * filSrc = NULL; FILE * filDst = fopen( namDst.c_str(), "r" ); - // If the file is there, leave as it is because it is maybe customize. + + /// If the file is there, leave as it is because it is maybe customize. if( filDst ) { LOG_INFO("Style file %s not altered", namDst.c_str() ); goto close_and_quit ; @@ -875,6 +893,8 @@ class KmlSrvImpl : public KmlServer { LOG_INFO("Cannot open source style file %s", namSrc.c_str() ); goto close_and_quit ; } + + /// Transient buffer to copy the file. char buffer[BUFSIZ]; size_t n; @@ -898,7 +918,7 @@ class KmlSrvImpl : public KmlServer { LOG_INFO("Creating baseFil=%s", baseFil.c_str() ); - // We do not need to make this file aomtic because it is read once only. + /// We do not need to make this file atomic because it is read once only. AtomicRenamer ar( baseFil ); KmlHeader( ar, "Fldigi"); @@ -918,7 +938,7 @@ class KmlSrvImpl : public KmlServer { PlacesMapT::const_iterator beg, PlacesMapT::const_iterator last ) { - // The polyline gets an id based on the beginning of the path, which will never change. + /// The polyline gets an id based on the beginning of the path, which will never change. ostrm << "second.KmlId() << ":Path\">" "" << beg->second.KmlId() << "" @@ -1102,7 +1122,7 @@ class KmlSrvImpl : public KmlServer { if( ! avoidNode.empty() ) break ; const char * msgTxt = xml->getNodeData(); - // LOG_INFO( "getNodeData=%s currState=%s", msgTxt, KmlRdToStr(currState) ); + LOG_DEBUG( "getNodeData=%s currState=%s", msgTxt, KmlRdToStr(currState) ); switch(currState) { case KMLRD_FOLDER_NAME : currFolderName = msgTxt ? msgTxt : "NullFolder"; @@ -1135,8 +1155,14 @@ class KmlSrvImpl : public KmlServer { if (!strcmp("Folder", nodeName)) { currState = KMLRD_FOLDER ; } else { + /// These tags are not meaningful for us. + if( strcmp( "kml", nodeName ) && + strcmp( "Document", nodeName ) && + strcmp( "name", nodeName ) ) + { LOG_INFO("Unexpected %s in document %s. currState=%s", nodeName, category.c_str(), KmlRdToStr(currState) ); + } } break; case KMLRD_FOLDER : @@ -1331,7 +1357,16 @@ class KmlSrvImpl : public KmlServer { return wasSaved ; } // KmlSrvImpl::RewriteKmlFileFull +#ifdef FLDIGI_KML_CONDITION_VARIABLE + /// This is signaled when geographic data is broadcasted. pthread_cond_t m_cond_queue ; +#else + /// This is set to true when geographic data is broadcasted. + bool m_bool_queue ; + + /// This tells that the subthread must leave at the first opportunity. + bool m_kml_must_leave; +#endif pthread_mutex_t m_mutex_write ; typedef std::list< PlacemarkT > PlacemarkListT ; @@ -1359,6 +1394,7 @@ class KmlSrvImpl : public KmlServer { // Endless loop until end of program, which cancels this subthread. for(;;) { +#ifdef FLDIGI_KML_CONDITION_VARIABLE struct timespec tmp_tim; { guard_lock myGuard( &m_mutex_write ); @@ -1373,6 +1409,23 @@ class KmlSrvImpl : public KmlServer { LOG_ERROR("pthread_cond_timedwait %s d=%d", strerror(errno), m_refresh_interval ); return (void *)"Error in pthread_cond_timed_wait"; } +#else + /// On the platforms where pthread_cond_timedwait has problems, everything behaves + // as if there was a timeout when data is saved. refresh_delay is never changed. + for( int i = 0; i < refresh_delay; ++i ) + { + MilliSleep( 1000 ); + if( m_kml_must_leave ) + { + LOG_INFO("Exit flag detected. Leaving"); + return (void *)"Exit flag detected"; + } + } + { + guard_lock myGuard( &m_mutex_write ); + r = m_bool_queue ? ETIMEDOUT : 0 ; + m_bool_queue = false; +#endif // Except if extremely slow init, the object should be ready now: Files loaded etc... if( ! m_loaded ) { @@ -1414,10 +1467,12 @@ class KmlSrvImpl : public KmlServer { // Reset the interval to the initial value. refresh_delay = m_refresh_interval ; +#ifdef FLDIGI_KML_CONDITION_VARIABLE } else { refresh_delay = tmp_tim.tv_sec - time(NULL); if( refresh_delay <= 0 ) refresh_delay = 1 ; // LOG_INFO("Interrupted when waiting. Restart with wait=%d", refresh ); +#endif } } // Endless loop. return NULL ; @@ -1494,7 +1549,7 @@ public: } // Now the object is usable. Theoretically should be protected by a mutex. - LOG_INFO("Object ready"); + LOG_DEBUG("Object ready"); /// Even if an exception was thrown when loading the previous file, it does not /// prevent to overwrite the old files with new and clean ones. @@ -1510,8 +1565,13 @@ public: , m_refresh_interval(-1) , m_balloon_style(0) { - LOG_INFO("Creation"); + LOG_DEBUG("Creation"); +#ifdef FLDIGI_KML_CONDITION_VARIABLE pthread_cond_init( &m_cond_queue, NULL ); +#else + m_bool_queue = false; + m_kml_must_leave = false; +#endif pthread_mutex_init( &m_mutex_write, NULL ); /// TODO: Add this thread to the other fldigi threads stored in cbq[]. @@ -1553,7 +1613,11 @@ public: LOG_ERROR("Category %s undefined", category.c_str()); } ptrMap->Enqueue( tmpKmlNam, currPM ); +#ifdef FLDIGI_KML_CONDITION_VARIABLE pthread_cond_signal( &m_cond_queue ); +#else + m_bool_queue = true; +#endif LOG_INFO("'%s' sz=%d time=%s nb_broad=%d m_merge_dist=%lf", descrTxt.c_str(), (int)ptrMap->size(), KmlTimestamp(evtTim).c_str(), @@ -1567,12 +1631,17 @@ public: LOG_INFO("Cancelling writer thread"); guard_lock myGuard( &m_mutex_write ); +#ifdef FLDIGI_KML_CONDITION_VARIABLE LOG_INFO("Cancelling subthread"); int r = pthread_cancel( m_writer_thread ); if( r ) { LOG_ERROR("pthread_cancel %s", strerror(errno) ); return ; } +#else + LOG_INFO("Setting exit flag."); + m_kml_must_leave = true ; +#endif } // LOG_INFO("Joining subthread"); void * retPtr; @@ -1592,7 +1661,9 @@ public: /// Here we are sure that the subthread is stopped. The subprocess is not called. RewriteKmlFileFull(); +#ifdef FLDIGI_KML_CONDITION_VARIABLE pthread_cond_destroy( &m_cond_queue ); +#endif pthread_mutex_destroy( &m_mutex_write ); }