kopia lustrzana https://github.com/jamescoxon/dl-fldigi
KML thread
* Changed thread exit implementation to conditional compile
-- used for building the OS X dmg
pull/1/head
rodzic
edc0c9fbc6
commit
67948117a0
|
|
@ -141,6 +141,7 @@
|
||||||
#include "flmisc.h"
|
#include "flmisc.h"
|
||||||
|
|
||||||
#include "arq_io.h"
|
#include "arq_io.h"
|
||||||
|
#include "kmlserver.h"
|
||||||
|
|
||||||
#include "notifydialog.h"
|
#include "notifydialog.h"
|
||||||
#include "macroedit.h"
|
#include "macroedit.h"
|
||||||
|
|
@ -2828,6 +2829,8 @@ bool clean_exit(bool ask) {
|
||||||
close_logbook();
|
close_logbook();
|
||||||
MilliSleep(50);
|
MilliSleep(50);
|
||||||
|
|
||||||
|
exit_process();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ class KmlServer {
|
||||||
protected:
|
protected:
|
||||||
/// Counts the number of complete messages written.
|
/// Counts the number of complete messages written.
|
||||||
int m_nb_broadcasts ;
|
int m_nb_broadcasts ;
|
||||||
|
int exit_kml_server;
|
||||||
|
int request_broadcast;
|
||||||
public:
|
public:
|
||||||
/// List of key-value pairs displayed for example in Google Earth balloons.
|
/// List of key-value pairs displayed for example in Google Earth balloons.
|
||||||
struct CustomDataT : public std::vector< std::pair< std::string, std::string > > {
|
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() {}
|
virtual ~KmlServer() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@ extern bool nbems_dirs_checked;
|
||||||
// This inits or reinits everything related to KML: Reloads params etc...
|
// This inits or reinits everything related to KML: Reloads params etc...
|
||||||
void kml_init(bool load_files = false);
|
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 );
|
int directory_is_created( const char * strdir );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
11
src/main.cxx
11
src/main.cxx
|
|
@ -522,8 +522,13 @@ int main(int argc, char ** argv)
|
||||||
|
|
||||||
int ret = Fl::run();
|
int ret = Fl::run();
|
||||||
|
|
||||||
arq_close();
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_process() {
|
||||||
|
|
||||||
|
KmlServer::Exit();
|
||||||
|
arq_close();
|
||||||
XML_RPC_Server::stop();
|
XML_RPC_Server::stop();
|
||||||
|
|
||||||
if (progdefaults.usepskrep)
|
if (progdefaults.usepskrep)
|
||||||
|
|
@ -533,11 +538,9 @@ int main(int argc, char ** argv)
|
||||||
cbq[i]->detach();
|
cbq[i]->detach();
|
||||||
delete cbq[i];
|
delete cbq[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
FSEL::destroy();
|
FSEL::destroy();
|
||||||
|
|
||||||
KmlServer::Exit();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_option_help(void) {
|
void generate_option_help(void) {
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,22 @@
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#include "irrXML.h"
|
||||||
|
|
||||||
#include "timeops.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 ;
|
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",
|
int r = sscanf( ts, "%4d-%02d-%02dT%02d:%02dZ",
|
||||||
&objTm.tm_year,
|
&objTm.tm_year,
|
||||||
&objTm.tm_mon,
|
&objTm.tm_mon,
|
||||||
|
|
@ -96,6 +112,7 @@ static time_t KmlFromTimestamp( const char * ts ) {
|
||||||
objTm.tm_year -= 1900;
|
objTm.tm_year -= 1900;
|
||||||
objTm.tm_mon -= 1;
|
objTm.tm_mon -= 1;
|
||||||
objTm.tm_sec = 0;
|
objTm.tm_sec = 0;
|
||||||
|
|
||||||
time_t res = mktime( &objTm );
|
time_t res = mktime( &objTm );
|
||||||
if( res < 0 ) throw std::runtime_error("Cannot make timestamp from " + std::string(ts) );
|
if( res < 0 ) throw std::runtime_error("Cannot make timestamp from " + std::string(ts) );
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -185,12 +202,12 @@ static void KmlHeader( std::ostream & ostrm, const std::string & title ) {
|
||||||
"<name>" << title << "</name>\n" ;
|
"<name>" << title << "</name>\n" ;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appended at the end of each KML document.
|
/// Appended at the end of each KML document.
|
||||||
static const char KmlFooter[] =
|
static const char KmlFooter[] =
|
||||||
"</Document>\n"
|
"</Document>\n"
|
||||||
"</kml>\n" ;
|
"</kml>\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";
|
static const std::string namStyles = "styles.kml";
|
||||||
|
|
||||||
/** Used to code the data for reloading. The description tag is too complicated
|
/** 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;
|
PlacesMapItrSetT::const_iterator itNamNxt = itNamLast;
|
||||||
++itNamNxt ;
|
++itNamNxt ;
|
||||||
assert( (*itNamBeg)->first != (*itNamLast)->first );
|
|
||||||
if( ( itNamNxt == itStylNext ) || ( (*itNamNxt)->first != (*itNamLast)->first ) ) {
|
if( ( itNamNxt == itStylNext ) || ( (*itNamNxt)->first != (*itNamLast)->first ) ) {
|
||||||
// No point tracing a line with one point only.
|
// No point tracing a line with one point only.
|
||||||
if( *itNamBeg != *itNamLast ) {
|
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.
|
/// This file copy does not need to be atomic because it happens once only.
|
||||||
void CopyStyleFileIfNotExists(void) {
|
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 ;
|
std::string namSrc = PKGDATADIR "/kml/" + namStyles ;
|
||||||
|
|
||||||
/// The use might customize its styles file: It will not be altered.
|
/// The use might customize its styles file: It will not be altered.
|
||||||
std::string namDst = m_kml_dir + namStyles ;
|
std::string namDst = m_kml_dir + namStyles ;
|
||||||
|
|
||||||
|
/// Used to copy the master file to the user copy if needed.
|
||||||
FILE * filSrc = NULL;
|
FILE * filSrc = NULL;
|
||||||
FILE * filDst = fopen( namDst.c_str(), "r" );
|
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 ) {
|
if( filDst ) {
|
||||||
LOG_INFO("Style file %s not altered", namDst.c_str() );
|
LOG_INFO("Style file %s not altered", namDst.c_str() );
|
||||||
goto close_and_quit ;
|
goto close_and_quit ;
|
||||||
|
|
@ -875,6 +893,8 @@ class KmlSrvImpl : public KmlServer {
|
||||||
LOG_INFO("Cannot open source style file %s", namSrc.c_str() );
|
LOG_INFO("Cannot open source style file %s", namSrc.c_str() );
|
||||||
goto close_and_quit ;
|
goto close_and_quit ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transient buffer to copy the file.
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
|
|
@ -898,7 +918,7 @@ class KmlSrvImpl : public KmlServer {
|
||||||
|
|
||||||
LOG_INFO("Creating baseFil=%s", baseFil.c_str() );
|
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 );
|
AtomicRenamer ar( baseFil );
|
||||||
|
|
||||||
KmlHeader( ar, "Fldigi");
|
KmlHeader( ar, "Fldigi");
|
||||||
|
|
@ -918,7 +938,7 @@ class KmlSrvImpl : public KmlServer {
|
||||||
PlacesMapT::const_iterator beg,
|
PlacesMapT::const_iterator beg,
|
||||||
PlacesMapT::const_iterator last ) {
|
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
|
ostrm
|
||||||
<< "<Placemark id=\"" << beg->second.KmlId() << ":Path\">"
|
<< "<Placemark id=\"" << beg->second.KmlId() << ":Path\">"
|
||||||
"<name>" << beg->second.KmlId() << "</name>"
|
"<name>" << beg->second.KmlId() << "</name>"
|
||||||
|
|
@ -1102,7 +1122,7 @@ class KmlSrvImpl : public KmlServer {
|
||||||
if( ! avoidNode.empty() ) break ;
|
if( ! avoidNode.empty() ) break ;
|
||||||
|
|
||||||
const char * msgTxt = xml->getNodeData();
|
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) {
|
switch(currState) {
|
||||||
case KMLRD_FOLDER_NAME :
|
case KMLRD_FOLDER_NAME :
|
||||||
currFolderName = msgTxt ? msgTxt : "NullFolder";
|
currFolderName = msgTxt ? msgTxt : "NullFolder";
|
||||||
|
|
@ -1135,8 +1155,14 @@ class KmlSrvImpl : public KmlServer {
|
||||||
if (!strcmp("Folder", nodeName)) {
|
if (!strcmp("Folder", nodeName)) {
|
||||||
currState = KMLRD_FOLDER ;
|
currState = KMLRD_FOLDER ;
|
||||||
} else {
|
} 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",
|
LOG_INFO("Unexpected %s in document %s. currState=%s",
|
||||||
nodeName, category.c_str(), KmlRdToStr(currState) );
|
nodeName, category.c_str(), KmlRdToStr(currState) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KMLRD_FOLDER :
|
case KMLRD_FOLDER :
|
||||||
|
|
@ -1331,7 +1357,16 @@ class KmlSrvImpl : public KmlServer {
|
||||||
return wasSaved ;
|
return wasSaved ;
|
||||||
} // KmlSrvImpl::RewriteKmlFileFull
|
} // KmlSrvImpl::RewriteKmlFileFull
|
||||||
|
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
|
/// This is signaled when geographic data is broadcasted.
|
||||||
pthread_cond_t m_cond_queue ;
|
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 ;
|
pthread_mutex_t m_mutex_write ;
|
||||||
|
|
||||||
typedef std::list< PlacemarkT > PlacemarkListT ;
|
typedef std::list< PlacemarkT > PlacemarkListT ;
|
||||||
|
|
@ -1359,6 +1394,7 @@ class KmlSrvImpl : public KmlServer {
|
||||||
// Endless loop until end of program, which cancels this subthread.
|
// Endless loop until end of program, which cancels this subthread.
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
struct timespec tmp_tim;
|
struct timespec tmp_tim;
|
||||||
{
|
{
|
||||||
guard_lock myGuard( &m_mutex_write );
|
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 );
|
LOG_ERROR("pthread_cond_timedwait %s d=%d", strerror(errno), m_refresh_interval );
|
||||||
return (void *)"Error in pthread_cond_timed_wait";
|
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...
|
// Except if extremely slow init, the object should be ready now: Files loaded etc...
|
||||||
if( ! m_loaded ) {
|
if( ! m_loaded ) {
|
||||||
|
|
@ -1414,10 +1467,12 @@ class KmlSrvImpl : public KmlServer {
|
||||||
|
|
||||||
// Reset the interval to the initial value.
|
// Reset the interval to the initial value.
|
||||||
refresh_delay = m_refresh_interval ;
|
refresh_delay = m_refresh_interval ;
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
} else {
|
} else {
|
||||||
refresh_delay = tmp_tim.tv_sec - time(NULL);
|
refresh_delay = tmp_tim.tv_sec - time(NULL);
|
||||||
if( refresh_delay <= 0 ) refresh_delay = 1 ;
|
if( refresh_delay <= 0 ) refresh_delay = 1 ;
|
||||||
// LOG_INFO("Interrupted when waiting. Restart with wait=%d", refresh );
|
// LOG_INFO("Interrupted when waiting. Restart with wait=%d", refresh );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} // Endless loop.
|
} // Endless loop.
|
||||||
return NULL ;
|
return NULL ;
|
||||||
|
|
@ -1494,7 +1549,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the object is usable. Theoretically should be protected by a mutex.
|
// 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
|
/// 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.
|
/// prevent to overwrite the old files with new and clean ones.
|
||||||
|
|
@ -1510,8 +1565,13 @@ public:
|
||||||
, m_refresh_interval(-1)
|
, m_refresh_interval(-1)
|
||||||
, m_balloon_style(0)
|
, m_balloon_style(0)
|
||||||
{
|
{
|
||||||
LOG_INFO("Creation");
|
LOG_DEBUG("Creation");
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
pthread_cond_init( &m_cond_queue, NULL );
|
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 );
|
pthread_mutex_init( &m_mutex_write, NULL );
|
||||||
|
|
||||||
/// TODO: Add this thread to the other fldigi threads stored in cbq[].
|
/// 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());
|
LOG_ERROR("Category %s undefined", category.c_str());
|
||||||
}
|
}
|
||||||
ptrMap->Enqueue( tmpKmlNam, currPM );
|
ptrMap->Enqueue( tmpKmlNam, currPM );
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
pthread_cond_signal( &m_cond_queue );
|
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",
|
LOG_INFO("'%s' sz=%d time=%s nb_broad=%d m_merge_dist=%lf",
|
||||||
descrTxt.c_str(), (int)ptrMap->size(),
|
descrTxt.c_str(), (int)ptrMap->size(),
|
||||||
KmlTimestamp(evtTim).c_str(),
|
KmlTimestamp(evtTim).c_str(),
|
||||||
|
|
@ -1567,12 +1631,17 @@ public:
|
||||||
LOG_INFO("Cancelling writer thread");
|
LOG_INFO("Cancelling writer thread");
|
||||||
guard_lock myGuard( &m_mutex_write );
|
guard_lock myGuard( &m_mutex_write );
|
||||||
|
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
LOG_INFO("Cancelling subthread");
|
LOG_INFO("Cancelling subthread");
|
||||||
int r = pthread_cancel( m_writer_thread );
|
int r = pthread_cancel( m_writer_thread );
|
||||||
if( r ) {
|
if( r ) {
|
||||||
LOG_ERROR("pthread_cancel %s", strerror(errno) );
|
LOG_ERROR("pthread_cancel %s", strerror(errno) );
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
LOG_INFO("Setting exit flag.");
|
||||||
|
m_kml_must_leave = true ;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// LOG_INFO("Joining subthread");
|
// LOG_INFO("Joining subthread");
|
||||||
void * retPtr;
|
void * retPtr;
|
||||||
|
|
@ -1592,7 +1661,9 @@ public:
|
||||||
/// Here we are sure that the subthread is stopped. The subprocess is not called.
|
/// Here we are sure that the subthread is stopped. The subprocess is not called.
|
||||||
RewriteKmlFileFull();
|
RewriteKmlFileFull();
|
||||||
|
|
||||||
|
#ifdef FLDIGI_KML_CONDITION_VARIABLE
|
||||||
pthread_cond_destroy( &m_cond_queue );
|
pthread_cond_destroy( &m_cond_queue );
|
||||||
|
#endif
|
||||||
pthread_mutex_destroy( &m_mutex_write );
|
pthread_mutex_destroy( &m_mutex_write );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue