Compare commits

...
Author SHA1 Message Date
Automatic Release Builder 6e0c39bb68 new version: 2020.3.4 2020-11-30 11:17:52 +00:00
Julian Smith f20b416cfe simgear/props/props.cxx: use rmutex to protect SGPropertyNodeListeners.
Also added asserts to check _num_iterators always >= 0.
2020-11-29 20:16:46 +00:00
Scott Giese 47e06b5216 METAR: mitigate wind sensor failures 2020-11-29 16:25:01 +00:00
James Turner bfcdf22705 TerraSync: fix crashes with null file return
Not sure how this is happening, but, check for a null file object
in FileGetRequest::onDone.
2020-11-29 16:23:44 +00:00
Stuart Buchanan d0db407faa Set minimum expiry time on STG nodes. 2020-11-29 16:23:40 +00:00
Automatic Release Builder d95b1c0441 new version: 2020.3.3 2020-11-12 11:14:23 +00:00
James Turner 837ba86d57 DNSClient: own requests, and cancel them on timeout
Fixes crashes where a request times-out, but then is completed by
UDN sometime afterwards, with a free-d object. Have the DNS::Client own
requests, and be able to retrieve the udns_query to cancel them, in 
the timeout case.

Fixes a couple of Sentry reports.
2020-11-12 09:39:58 +00:00
Automatic Release Builder 0cb1b463e1 Catalogs: fix ownership of new Catalogs
When doing the initial download of a Catalog, ensure we still keep
an owning ref to it.
2020-11-11 21:18:57 +00:00
10 changed files with 184 additions and 76 deletions
+1 -1
View File
@@ -1 +1 @@
2020.3.2 2020.3.4
+3
View File
@@ -218,6 +218,9 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
#define SG_OBJECT_RANGE_ROUGH 9000.0 #define SG_OBJECT_RANGE_ROUGH 9000.0
#define SG_OBJECT_RANGE_DETAILED 1500.0 #define SG_OBJECT_RANGE_DETAILED 1500.0
/** Minimum expiry time of PagedLOD within the Tile. Overridden by /sim/rendering/plod-minimum-expiry-time-secs **/
#define SG_TILE_MIN_EXPIRY 180.0
/** Radius of scenery tiles in m **/ /** Radius of scenery tiles in m **/
#define SG_TILE_RADIUS 14000.0 #define SG_TILE_RADIUS 14000.0
+21 -6
View File
@@ -646,16 +646,21 @@ bool SGMetar::scanWind()
double gust = NaN; double gust = NaN;
if (*m == 'G') { if (*m == 'G') {
m++; m++;
if (!scanNumber(&m, &i, 2, 3)) if (!strncmp(m, "//", 2)) // speed not measurable
m += 2, i = -1;
else if (!scanNumber(&m, &i, 2, 3))
return false; return false;
gust = i;
if (i != -1)
gust = i;
} }
double factor; double factor;
if (!strncmp(m, "KT", 2)) if (!strncmp(m, "KT", 2))
m += 2, factor = SG_KT_TO_MPS; m += 2, factor = SG_KT_TO_MPS;
else if (!strncmp(m, "KMH", 3)) else if (!strncmp(m, "KMH", 3)) // invalid Km/h
m += 3, factor = SG_KMH_TO_MPS; m += 3, factor = SG_KMH_TO_MPS;
else if (!strncmp(m, "KPH", 3)) // ?? else if (!strncmp(m, "KPH", 3)) // invalid Km/h
m += 3, factor = SG_KMH_TO_MPS; m += 3, factor = SG_KMH_TO_MPS;
else if (!strncmp(m, "MPS", 3)) else if (!strncmp(m, "MPS", 3))
m += 3, factor = 1.0; m += 3, factor = 1.0;
@@ -680,18 +685,28 @@ bool SGMetar::scanVariability()
{ {
char *m = _m; char *m = _m;
int from, to; int from, to;
if (!scanNumber(&m, &from, 3))
if (!strncmp(m, "///", 3)) // direction not measurable
m += 3, from = -1;
else if (!scanNumber(&m, &from, 3))
return false; return false;
if (*m++ != 'V') if (*m++ != 'V')
return false; return false;
if (!scanNumber(&m, &to, 3))
if (!strncmp(m, "///", 3)) // direction not measurable
m += 3, to = -1;
else if (!scanNumber(&m, &to, 3))
return false; return false;
if (!scanBoundary(&m)) if (!scanBoundary(&m))
return false; return false;
_m = m; _m = m;
_wind_range_from = from; _wind_range_from = from;
_wind_range_to = to; _wind_range_to = to;
_grpcount++; _grpcount++;
return true; return true;
} }
+9
View File
@@ -81,6 +81,15 @@ void test_sensor_failure_wind()
SGMetar m1("2020/10/23 16:55 LIVD 231655Z /////KT 9999 OVC025 10/08 Q1020 RMK OVC VIS MIN 9999 BLU"); SGMetar m1("2020/10/23 16:55 LIVD 231655Z /////KT 9999 OVC025 10/08 Q1020 RMK OVC VIS MIN 9999 BLU");
SG_CHECK_EQUAL(m1.getWindDir(), -1); SG_CHECK_EQUAL(m1.getWindDir(), -1);
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), -1, TEST_EPSILON); SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), -1, TEST_EPSILON);
SGMetar m2("2020/10/21 16:55 LIVD 211655Z /////KT CAVOK 07/03 Q1023 RMK SKC VIS MIN 9999 BLU");
SG_CHECK_EQUAL(m2.getWindDir(), -1);
SG_CHECK_EQUAL_EP2(m2.getWindSpeed_kt(), -1, TEST_EPSILON);
SGMetar m3("2020/11/17 16:00 CYAZ 171600Z 14040G//KT 10SM -RA OVC012 12/11 A2895 RMK NS8 VIA CYXY SLP806 DENSITY ALT 900FT");
SG_CHECK_EQUAL(m3.getWindDir(), 140);
SG_CHECK_EQUAL_EP2(m3.getWindSpeed_kt(), 40, TEST_EPSILON);
SG_CHECK_EQUAL_EP2(m3.getGustSpeed_kt(), SGMetarNaN, TEST_EPSILON);
} }
void test_wind_unit_not_specified() void test_wind_unit_not_specified()
+85 -47
View File
@@ -61,6 +61,10 @@ public:
struct dns_ctx * ctx; struct dns_ctx * ctx;
static size_t instanceCounter; static size_t instanceCounter;
using RequestVec = std::vector<Request_ptr>;
RequestVec _activeRequests;
}; };
size_t Client::ClientPrivate::instanceCounter = 0; size_t Client::ClientPrivate::instanceCounter = 0;
@@ -78,6 +82,11 @@ Request::~Request()
{ {
} }
void Request::cancel()
{
_cancelled = true;
}
bool Request::isTimeout() const bool Request::isTimeout() const
{ {
return (time(NULL) - _start) > _timeout_secs; return (time(NULL) - _start) > _timeout_secs;
@@ -114,18 +123,20 @@ static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
{ {
SRVRequest * r = static_cast<SRVRequest*>(data); SRVRequest * r = static_cast<SRVRequest*>(data);
if (result) { if (result) {
r->cname = result->dnssrv_cname; if (!r->isCancelled()) {
r->qname = result->dnssrv_qname; r->cname = result->dnssrv_cname;
r->ttl = result->dnssrv_ttl; r->qname = result->dnssrv_qname;
for (int i = 0; i < result->dnssrv_nrr; i++) { r->ttl = result->dnssrv_ttl;
SRVRequest::SRV_ptr srv(new SRVRequest::SRV); for (int i = 0; i < result->dnssrv_nrr; i++) {
r->entries.push_back(srv); SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
srv->priority = result->dnssrv_srv[i].priority; r->entries.push_back(srv);
srv->weight = result->dnssrv_srv[i].weight; srv->priority = result->dnssrv_srv[i].priority;
srv->port = result->dnssrv_srv[i].port; srv->weight = result->dnssrv_srv[i].weight;
srv->target = result->dnssrv_srv[i].name; srv->port = result->dnssrv_srv[i].port;
srv->target = result->dnssrv_srv[i].name;
}
std::sort(r->entries.begin(), r->entries.end(), sortSRV);
} }
std::sort( r->entries.begin(), r->entries.end(), sortSRV );
free(result); free(result);
} }
r->setComplete(); r->setComplete();
@@ -134,11 +145,16 @@ static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
void SRVRequest::submit( Client * client ) void SRVRequest::submit( Client * client )
{ {
// if service is defined, pass service and protocol // if service is defined, pass service and protocol
if (!dns_submit_srv(client->d->ctx, getDn().c_str(), _service.empty() ? NULL : _service.c_str(), _service.empty() ? NULL : _protocol.c_str(), 0, dnscbSRV, this )) { auto q = dns_submit_srv(client->d->ctx, getDn().c_str(), _service.empty() ? NULL : _service.c_str(),
_service.empty() ? NULL : _protocol.c_str(),
0, dnscbSRV, this);
if (!q) {
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn()); SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
return; return;
} }
_start = time(NULL); _start = time(NULL);
_query = q;
} }
TXTRequest::TXTRequest( const std::string & dn ) : TXTRequest::TXTRequest( const std::string & dn ) :
@@ -151,22 +167,24 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
{ {
TXTRequest * r = static_cast<TXTRequest*>(data); TXTRequest * r = static_cast<TXTRequest*>(data);
if (result) { if (result) {
r->cname = result->dnstxt_cname; if (!r->isCancelled()) {
r->qname = result->dnstxt_qname; r->cname = result->dnstxt_cname;
r->ttl = result->dnstxt_ttl; r->qname = result->dnstxt_qname;
for (int i = 0; i < result->dnstxt_nrr; i++) { r->ttl = result->dnstxt_ttl;
//TODO: interprete the .len field of dnstxt_txt? for (int i = 0; i < result->dnstxt_nrr; i++) {
auto rawTxt = reinterpret_cast<char*>(result->dnstxt_txt[i].txt); //TODO: interprete the .len field of dnstxt_txt?
if (!rawTxt) { auto rawTxt = reinterpret_cast<char*>(result->dnstxt_txt[i].txt);
continue; if (!rawTxt) {
} continue;
}
const string txt{rawTxt}; const string txt{rawTxt};
r->entries.push_back(txt); r->entries.push_back(txt);
string_list tokens = simgear::strutils::split( txt, "=", 1 ); string_list tokens = simgear::strutils::split(txt, "=", 1);
if( tokens.size() == 2 ) { if (tokens.size() == 2) {
r->attributes[tokens[0]] = tokens[1]; r->attributes[tokens[0]] = tokens[1];
} }
}
} }
free(result); free(result);
} }
@@ -176,11 +194,13 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
void TXTRequest::submit( Client * client ) void TXTRequest::submit( Client * client )
{ {
// protocol and service an already encoded in DN so pass in NULL for both // protocol and service an already encoded in DN so pass in NULL for both
if (!dns_submit_txt(client->d->ctx, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) { auto q = dns_submit_txt(client->d->ctx, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this);
if (!q) {
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn()); SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
return; return;
} }
_start = time(NULL); _start = time(NULL);
_query = q;
} }
@@ -195,27 +215,29 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
{ {
NAPTRRequest * r = static_cast<NAPTRRequest*>(data); NAPTRRequest * r = static_cast<NAPTRRequest*>(data);
if (result) { if (result) {
r->cname = result->dnsnaptr_cname; if (!r->isCancelled()) {
r->qname = result->dnsnaptr_qname; r->cname = result->dnsnaptr_cname;
r->ttl = result->dnsnaptr_ttl; r->qname = result->dnsnaptr_qname;
for (int i = 0; i < result->dnsnaptr_nrr; i++) { r->ttl = result->dnsnaptr_ttl;
if( !r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service ) for (int i = 0; i < result->dnsnaptr_nrr; i++) {
continue; if (!r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service)
continue;
//TODO: case ignore and result flags may have more than one flag //TODO: case ignore and result flags may have more than one flag
if( !r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags ) if (!r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags)
continue; continue;
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR); NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
r->entries.push_back(naptr); r->entries.push_back(naptr);
naptr->order = result->dnsnaptr_naptr[i].order; naptr->order = result->dnsnaptr_naptr[i].order;
naptr->preference = result->dnsnaptr_naptr[i].preference; naptr->preference = result->dnsnaptr_naptr[i].preference;
naptr->flags = result->dnsnaptr_naptr[i].flags; naptr->flags = result->dnsnaptr_naptr[i].flags;
naptr->service = result->dnsnaptr_naptr[i].service; naptr->service = result->dnsnaptr_naptr[i].service;
naptr->regexp = result->dnsnaptr_naptr[i].regexp; naptr->regexp = result->dnsnaptr_naptr[i].regexp;
naptr->replacement = result->dnsnaptr_naptr[i].replacement; naptr->replacement = result->dnsnaptr_naptr[i].replacement;
}
std::sort(r->entries.begin(), r->entries.end(), sortNAPTR);
} }
std::sort( r->entries.begin(), r->entries.end(), sortNAPTR );
free(result); free(result);
} }
r->setComplete(); r->setComplete();
@@ -223,11 +245,13 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
void NAPTRRequest::submit( Client * client ) void NAPTRRequest::submit( Client * client )
{ {
if (!dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this )) { auto q = dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this);
if (!q) {
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn()); SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
return; return;
} }
_start = time(NULL); _start = time(NULL);
_query = q;
} }
@@ -242,6 +266,7 @@ Client::Client() :
void Client::makeRequest(const Request_ptr& r) void Client::makeRequest(const Request_ptr& r)
{ {
d->_activeRequests.push_back(r);
r->submit(this); r->submit(this);
} }
@@ -252,6 +277,19 @@ void Client::update(int waitTimeout)
return; return;
dns_ioevent(d->ctx, now); dns_ioevent(d->ctx, now);
// drop our owning ref to completed requests,
// and cancel any which timed out
auto it = std::remove_if(d->_activeRequests.begin(), d->_activeRequests.end(),
[this](const Request_ptr& r) {
if (r->isTimeout()) {
dns_cancel(d->ctx, reinterpret_cast<struct dns_query*>(r->_query));
return true;
}
return r->isComplete();
});
d->_activeRequests.erase(it, d->_activeRequests.end());
} }
} // of namespace DNS } // of namespace DNS
+14 -4
View File
@@ -40,28 +40,38 @@ namespace DNS
{ {
class Client; class Client;
using UDNSQueryPtr = void*;
class Request : public SGReferenced class Request : public SGReferenced
{ {
public: public:
Request( const std::string & dn ); Request( const std::string & dn );
virtual ~Request(); virtual ~Request();
std::string getDn() const { return _dn; } const std::string& getDn() const { return _dn; }
int getType() const { return _type; } int getType() const { return _type; }
bool isComplete() const { return _complete; } bool isComplete() const { return _complete; }
bool isTimeout() const; bool isTimeout() const;
void setComplete( bool b = true ) { _complete = b; } void setComplete( bool b = true ) { _complete = b; }
bool isCancelled() const { return _cancelled; }
virtual void submit( Client * client) = 0; virtual void submit( Client * client) = 0;
void cancel();
std::string cname; std::string cname;
std::string qname; std::string qname;
unsigned ttl; unsigned ttl;
protected: protected:
friend class Client;
UDNSQueryPtr _query = nullptr;
std::string _dn; std::string _dn;
int _type; int _type;
bool _complete; bool _complete;
time_t _timeout_secs; time_t _timeout_secs;
time_t _start; time_t _start;
bool _cancelled = false;
}; };
typedef SGSharedPtr<Request> Request_ptr; typedef SGSharedPtr<Request> Request_ptr;
@@ -69,7 +79,7 @@ class NAPTRRequest : public Request
{ {
public: public:
NAPTRRequest( const std::string & dn ); NAPTRRequest( const std::string & dn );
virtual void submit( Client * client ); void submit(Client* client) override;
struct NAPTR : SGReferenced { struct NAPTR : SGReferenced {
int order; int order;
@@ -92,7 +102,7 @@ class SRVRequest : public Request
public: public:
SRVRequest( const std::string & dn ); SRVRequest( const std::string & dn );
SRVRequest( const std::string & dn, const string & service, const string & protocol ); SRVRequest( const std::string & dn, const string & service, const string & protocol );
virtual void submit( Client * client ); void submit(Client* client) override;
struct SRV : SGReferenced { struct SRV : SGReferenced {
int priority; int priority;
@@ -112,7 +122,7 @@ class TXTRequest : public Request
{ {
public: public:
TXTRequest( const std::string & dn ); TXTRequest( const std::string & dn );
virtual void submit( Client * client ); void submit(Client* client) override;
typedef std::vector<string> TXT_list; typedef std::vector<string> TXT_list;
typedef std::map<std::string,std::string> TXT_Attribute_map; typedef std::map<std::string,std::string> TXT_Attribute_map;
+4 -1
View File
@@ -994,7 +994,10 @@ HTTPRepository::failure() const
} }
void onDone() override { void onDone() override {
file->close(); if (file) {
file->close();
}
if (responseCode() == 200) { if (responseCode() == 200) {
std::string hash = std::string hash =
strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH); strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
+11 -6
View File
@@ -722,12 +722,6 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
auto catIt = d->catalogs.find(aCat->id()); auto catIt = d->catalogs.find(aCat->id());
d->fireRefreshStatus(aCat, aReason); d->fireRefreshStatus(aCat, aReason);
if (aReason == Delegate::STATUS_IN_PROGRESS) {
d->refreshing.insert(aCat);
} else {
d->refreshing.erase(aCat);
}
if (aCat->isUserEnabled() && if (aCat->isUserEnabled() &&
(aReason == Delegate::STATUS_REFRESHED) && (aReason == Delegate::STATUS_REFRESHED) &&
(catIt == d->catalogs.end())) (catIt == d->catalogs.end()))
@@ -761,6 +755,17 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
} }
} // of catalog is disabled } // of catalog is disabled
// remove from refreshing /after/ checking for enable / disabled, since for
// new catalogs, the reference in d->refreshing might be our /only/
// reference to the catalog. Once the refresh is done (either failed or
// succeeded) the Catalog will be in either d->catalogs or
// d->disabledCatalogs
if (aReason == Delegate::STATUS_IN_PROGRESS) {
d->refreshing.insert(aCat);
} else {
d->refreshing.erase(aCat);
}
if (d->refreshing.empty()) { if (d->refreshing.empty()) {
d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED); d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
d->firePackagesChanged(); d->firePackagesChanged();
+24 -5
View File
@@ -19,6 +19,10 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <iterator> #include <iterator>
#include <exception> // can't use sg_exception becuase of PROPS_STANDALONE
#include <mutex>
#include <thread>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -51,11 +55,15 @@ using namespace simgear;
struct SGPropertyNodeListeners struct SGPropertyNodeListeners
{ {
/* Protect _num_iterators and _items. We use a recursive mutex to allow
nested access to work as normal. */
std::recursive_mutex _rmutex;
/* This keeps a count of the current number of nested invocations of /* This keeps a count of the current number of nested invocations of
forEachListener(). If non-zero, other code higher up the stack is iterating forEachListener(). If non-zero, other code higher up the stack is iterating
_items[] so for example code must not erase items in the vector. */ _items[] so for example code must not erase items in the vector. */
int _num_iterators = 0; int _num_iterators = 0;
std::vector<SGPropertyChangeListener *> _items; std::vector<SGPropertyChangeListener *> _items;
}; };
@@ -2406,6 +2414,7 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
if (_listeners == 0) if (_listeners == 0)
_listeners = new SGPropertyNodeListeners; _listeners = new SGPropertyNodeListeners;
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
/* If there's a nullptr entry (a listener that was unregistered), we /* If there's a nullptr entry (a listener that was unregistered), we
overwrite it. This ensures that listeners that routinely unregister+register overwrite it. This ensures that listeners that routinely unregister+register
themselves don't make _listeners->_items grow unnecessarily. Otherwise simply themselves don't make _listeners->_items grow unnecessarily. Otherwise simply
@@ -2429,9 +2438,13 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
{ {
if (_listeners == 0) if (_listeners == 0)
return; return;
/* We use a std::unique_lock rather than a std::lock_guard because we may
need to unlock early. */
std::unique_lock<std::recursive_mutex> lock(_listeners->_rmutex);
vector<SGPropertyChangeListener*>::iterator it = vector<SGPropertyChangeListener*>::iterator it =
find(_listeners->_items.begin(), _listeners->_items.end(), listener); find(_listeners->_items.begin(), _listeners->_items.end(), listener);
if (it != _listeners->_items.end()) { if (it != _listeners->_items.end()) {
assert(_listeners->_num_iterators >= 0);
if (_listeners->_num_iterators) { if (_listeners->_num_iterators) {
/* _listeners._items is currently being iterated further up the stack in /* _listeners._items is currently being iterated further up the stack in
this thread by one or more nested invocations of forEachListener(), so this thread by one or more nested invocations of forEachListener(), so
@@ -2450,6 +2463,7 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
_listeners->_items.erase(it); _listeners->_items.erase(it);
listener->unregister_property(this); listener->unregister_property(this);
if (_listeners->_items.empty()) { if (_listeners->_items.empty()) {
lock.unlock();
delete _listeners; delete _listeners;
_listeners = 0; _listeners = 0;
} }
@@ -2511,9 +2525,11 @@ static void forEachListener(
) )
{ {
if (!_listeners) return; if (!_listeners) return;
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
assert(_listeners->_num_iterators >= 0);
_listeners->_num_iterators += 1; _listeners->_num_iterators += 1;
/* We need to use an index here when iterating _listeners->_items, not an /* We need to use an index here when iterating _listeners->_items, not an
iterator. This is because a listener may add new listeners, causing the iterator. This is because a listener may add new listeners, causing the
vector to be reallocated, which would invalidate any iterator. */ vector to be reallocated, which would invalidate any iterator. */
@@ -2528,10 +2544,12 @@ static void forEachListener(
} }
} }
} }
_listeners->_num_iterators -= 1; _listeners->_num_iterators -= 1;
assert(_listeners->_num_iterators >= 0);
if (_listeners->_num_iterators == 0) { if (_listeners->_num_iterators == 0) {
/* Remove any items that have been set to nullptr. */ /* Remove any items that have been set to nullptr. */
_listeners->_items.erase( _listeners->_items.erase(
std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr), std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr),
@@ -2547,6 +2565,7 @@ static void forEachListener(
int SGPropertyNode::nListeners() const int SGPropertyNode::nListeners() const
{ {
if (!_listeners) return 0; if (!_listeners) return 0;
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
int n = 0; int n = 0;
for (auto listener: _listeners->_items) { for (auto listener: _listeners->_items) {
if (listener) n += 1; if (listener) n += 1;
+12 -6
View File
@@ -212,7 +212,8 @@ struct ReaderWriterSTG::_ModelBin {
STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end()); quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end());
osg::ref_ptr<osg::Group> group = quadtree.getRoot(); osg::ref_ptr<osg::Group> group = quadtree.getRoot();
group->setName("STG-group-A"); string group_name = string("STG-group-A ").append(_bucket.gen_index_str());
group->setName(group_name);
group->setDataVariance(osg::Object::STATIC); group->setDataVariance(osg::Object::STATIC);
simgear::AirportSignBuilder signBuilder(_options->getMaterialLib(), _bucket.get_center()); simgear::AirportSignBuilder signBuilder(_options->getMaterialLib(), _bucket.get_center());
@@ -586,10 +587,12 @@ struct ReaderWriterSTG::_ModelBin {
{ {
osg::ref_ptr<SGReaderWriterOptions> options; osg::ref_ptr<SGReaderWriterOptions> options;
options = SGReaderWriterOptions::copyOrCreate(opt); options = SGReaderWriterOptions::copyOrCreate(opt);
float pagedLODExpiry = atoi(options->getPluginStringData("SimGear::PAGED_LOD_EXPIRY").c_str());
osg::ref_ptr<osg::Group> terrainGroup = new osg::Group; osg::ref_ptr<osg::Group> terrainGroup = new osg::Group;
terrainGroup->setDataVariance(osg::Object::STATIC); terrainGroup->setDataVariance(osg::Object::STATIC);
terrainGroup->setName("terrain"); std::string terrain_name = string("terrain ").append(bucket.gen_index_str());
terrainGroup->setName(terrain_name);
if (_foundBase) { if (_foundBase) {
for (auto stgObject : _objectList) { for (auto stgObject : _objectList) {
@@ -637,11 +640,13 @@ struct ReaderWriterSTG::_ModelBin {
} else { } else {
osg::PagedLOD* pagedLOD = new osg::PagedLOD; osg::PagedLOD* pagedLOD = new osg::PagedLOD;
pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER); pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
pagedLOD->setName("pagedObjectLOD"); std::string name = string("pagedObjectLOD ").append(bucket.gen_index_str());
pagedLOD->setName(name);
// This should be visible in any case. // This should be visible in any case.
// If this is replaced by some lower level of detail, the parent LOD node handles this. // If this is replaced by some lower level of detail, the parent LOD node handles this.
pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max()); pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max());
pagedLOD->setMinimumExpiryTime(0, pagedLODExpiry);
// we just need to know about the read file callback that itself holds the data // we just need to know about the read file callback that itself holds the data
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback; osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
@@ -658,10 +663,11 @@ struct ReaderWriterSTG::_ModelBin {
// Objects may end up displayed up to 2x the object range. // Objects may end up displayed up to 2x the object range.
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough); pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), pagedLODExpiry);
pagedLOD->setRadius(SG_TILE_RADIUS); pagedLOD->setRadius(SG_TILE_RADIUS);
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() ); SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Range: " << (2.0 * _object_range_rough)); SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Range: " << (2.0 * _object_range_rough));
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Radius: " << SG_TILE_RADIUS); SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Radius: " << SG_TILE_RADIUS);
return pagedLOD; return pagedLOD;
} }
} }