Compare commits

...
Author SHA1 Message Date
Automatic Release Builder def2af2bdd new version: 2020.3.5 2020-12-17 13:48:00 +00:00
Fernando García Liñán 6f6d705f22 Add support for anisotropic filtering in Canvas 2020-12-17 13:00:01 +00:00
Richard Harrison e9c33104d3 DDS-TC add option to exclude all Canvas orignated images. 2020-12-17 12:59:26 +00:00
Richard Harrison 985897f8ba Emesary : change to use scoped lock
- scoped locks using lock_guard are better
- because of this the exception handler can also be removed;
  although this was originally intended to manage the locks
  it never did work properly with C++ exceptions.
2020-12-17 12:57:53 +00:00
Richard Harrison 4251b28e88 Emesary: add missing property initialisation to constructor 2020-12-17 12:56:49 +00:00
James Turner 76540a211c SGGeod: add static constructor of an invalid Geod
Use this to allow explicitly initializing a value which isValid
will return false for.
2020-12-17 12:55:30 +00:00
James Turner 904fc5a7dd Fix Nasal GC errors on tests/reset
Ensure the Context temps are cleared, and when recycling an naCode,
ensure old values are cleared explicily.

Sentry-Id: FLIGHTGEAR-Y
2020-12-17 12:55:25 +00:00
Stuart Buchanan 4aebc159d5 Use ref_ptr for ReaderWriterSPT 2020-12-07 17:18:32 +00:00
Richard Harrison f89227fc1a Remove mutex lock on realizeTechniques
This was causing a deadlock - and I can't quite remember what problem it was intended to solve so it is best to remove it.
2020-12-07 17:17:44 +00:00
James Turner f50f383cc0 Reporting of std::bad_alloc in Subsystem::update
Trying to trace down our bad-alloc exception, starting with the simplest
place for now.
2020-12-07 17:16:58 +00:00
James Turner 706ab387de Add reporting callback option to SimGear
Allows us to trigger an error logging callback explicitly, which can
be used to drive Sentry.io on the FlightGear side.
2020-12-07 17:16:47 +00:00
James Turner cddfdb7d1d TerraSync: stronger fix for handling 0-byte files
Change logic so we create an empty file for such cases, i.e exactly
matching the repository. This simplifies logic in downstream code,
compared with not creating a local file.

Add a test-case to cover this

Modify TerraSync to detect a failure of Airports_archive downloading,
and fall back to file-by-file updating.
2020-12-07 17:15:51 +00:00
James Turner 3c64578848 HTTPRepository: don’t crash on empty files
Fix some additional crash cases around 0-length files
2020-12-07 17:15:38 +00:00
James Turner e1fe9b45e0 Unzip: adjust error reporting mechanism
Don’t use local exception throw+catch to report failures in extracting
a zip archive, since this generates noise in Sentry.
2020-12-07 17:15:30 +00:00
James Turner 8fdc1d306f State-machines: don’t require name for transitions
Allow anonymous transitions, since the name is purely informational
(unlike for states).

Sentry-Id: FLIGHTGEAR-9H
2020-12-07 17:15:17 +00:00
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
32 changed files with 500 additions and 232 deletions
+1 -1
View File
@@ -1 +1 @@
2020.3.2 2020.3.5
+4
View File
@@ -691,6 +691,10 @@ namespace canvas
{ {
_sampling_dirty = true; _sampling_dirty = true;
} }
else if( name == "anisotropy" )
{
_texture.setMaxAnisotropy( node->getFloatValue() );
}
else if( name == "additive-blend" ) else if( name == "additive-blend" )
{ {
_texture.useAdditiveBlend( node->getBoolValue() ); _texture.useAdditiveBlend( node->getBoolValue() );
+6
View File
@@ -202,6 +202,12 @@ namespace canvas
updateSampling(); updateSampling();
} }
//----------------------------------------------------------------------------
void ODGauge::setMaxAnisotropy(float anis)
{
texture->setMaxAnisotropy(anis);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ODGauge::setRender(bool render) void ODGauge::setRender(bool render)
{ {
+2
View File
@@ -109,6 +109,8 @@ namespace canvas
int coverage_samples = 0, int coverage_samples = 0,
int color_samples = 0 ); int color_samples = 0 );
void setMaxAnisotropy(float anis);
/** /**
* Enable/Disable updating the texture (If disabled the contents of the * Enable/Disable updating the texture (If disabled the contents of the
* texture remains with the outcome of the last rendering pass) * texture remains with the outcome of the last rendering pass)
+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
+6 -2
View File
@@ -3,8 +3,12 @@ include (SimGearComponent)
set(HEADERS debug_types.h set(HEADERS debug_types.h
logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
LogCallback.hxx LogEntry.hxx) LogCallback.hxx LogEntry.hxx
ErrorReportingCallback.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx set(SOURCES logstream.cxx BufferedLogCallback.cxx
LogCallback.cxx LogEntry.cxx) LogCallback.cxx LogEntry.cxx
ErrorReportingCallback.cxx
)
simgear_component(debug debug "${SOURCES}" "${HEADERS}") simgear_component(debug debug "${SOURCES}" "${HEADERS}")
+47
View File
@@ -0,0 +1,47 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library 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 <simgear_config.h>
#include "ErrorReportingCallback.hxx"
using std::string;
namespace simgear {
static ErrorReportCallback static_callback;
void setErrorReportCallback(ErrorReportCallback cb)
{
static_callback = cb;
}
void reportError(const std::string& msg, const std::string& more)
{
if (!static_callback)
return;
static_callback(msg, more, false);
}
void reportFatalError(const std::string& msg, const std::string& more)
{
if (!static_callback)
return;
static_callback(msg, more, true);
}
} // namespace simgear
+31
View File
@@ -0,0 +1,31 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library 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.
//
#pragma once
#include <functional>
#include <string>
namespace simgear {
void reportError(const std::string& msg, const std::string& more = {});
void reportFatalError(const std::string& msg, const std::string& more = {});
using ErrorReportCallback = std::function<void(const std::string& msg, const std::string& more, bool isFatal)>;
void setErrorReportCallback(ErrorReportCallback cb);
} // namespace simgear
+47 -61
View File
@@ -45,14 +45,6 @@ namespace simgear
std::atomic<int> receiveDepth; std::atomic<int> receiveDepth;
std::atomic<int> sentMessageCount; std::atomic<int> sentMessageCount;
void UnlockList()
{
_lock.unlock();
}
void LockList()
{
_lock.lock();
}
public: public:
Transmitter() : receiveDepth(0), sentMessageCount(0) Transmitter() : receiveDepth(0), sentMessageCount(0)
{ {
@@ -69,20 +61,17 @@ namespace simgear
// most recently registered recipients should process the messages/events first. // most recently registered recipients should process the messages/events first.
virtual void Register(IReceiver& r) virtual void Register(IReceiver& r)
{ {
LockList(); std::lock_guard<std::mutex> scopeLock(_lock);
recipient_list.push_back(&r); recipient_list.push_back(&r);
r.OnRegisteredAtTransmitter(this); r.OnRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
deleted_recipients.remove(&r); deleted_recipients.remove(&r);
UnlockList();
} }
// Removes an object from receving message from this transmitter // Removes an object from receving message from this transmitter
virtual void DeRegister(IReceiver& R) virtual void DeRegister(IReceiver& R)
{ {
LockList(); std::lock_guard<std::mutex> scopeLock(_lock);
//printf("Remove %x\n", &R);
if (recipient_list.size()) if (recipient_list.size())
{ {
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
@@ -93,7 +82,6 @@ namespace simgear
deleted_recipients.push_back(&R); deleted_recipients.push_back(&R);
} }
} }
UnlockList();
} }
// Notify all registered recipients. Stop when receipt status of abort or finished are received. // Notify all registered recipients. Stop when receipt status of abort or finished are received.
@@ -107,69 +95,68 @@ namespace simgear
ReceiptStatus return_status = ReceiptStatusNotProcessed; ReceiptStatus return_status = ReceiptStatusNotProcessed;
sentMessageCount++; sentMessageCount++;
try
std::vector<IReceiver*> temp;
{ {
LockList(); std::lock_guard<std::mutex> scopeLock(_lock);
if (receiveDepth == 0) if (receiveDepth == 0)
deleted_recipients.clear(); deleted_recipients.clear();
receiveDepth++; receiveDepth++;
std::vector<IReceiver*> temp(recipient_list.size());
int idx = 0; int idx = 0;
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
{ {
temp[idx++] = *i; temp.push_back(*i);
} }
UnlockList(); }
int tempSize = temp.size(); int tempSize = temp.size();
for (int index = 0; index < tempSize; index++) for (int index = 0; index < tempSize; index++)
{
IReceiver* R = temp[index];
{ {
IReceiver* R = temp[index]; std::lock_guard<std::mutex> scopeLock(_lock);
LockList();
if (deleted_recipients.size()) if (deleted_recipients.size())
{ {
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
{ {
UnlockList();
continue; continue;
} }
} }
UnlockList();
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
} }
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
} }
catch (...)
{
throw;
// return_status = ReceiptStatusAbort;
}
receiveDepth--; receiveDepth--;
return return_status; return return_status;
} }
@@ -177,9 +164,8 @@ namespace simgear
// number of currently registered recipients // number of currently registered recipients
virtual int Count() virtual int Count()
{ {
LockList(); std::lock_guard<std::mutex> scopeLock(_lock);
return recipient_list.size(); return recipient_list.size();
UnlockList();
} }
// number of sent messages. // number of sent messages.
+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;
+86 -59
View File
@@ -437,32 +437,37 @@ public:
ArchiveExtractTask(const ArchiveExtractTask &) = delete; ArchiveExtractTask(const ArchiveExtractTask &) = delete;
HTTPRepoPrivate::ProcessResult run(HTTPRepoPrivate *repo) { HTTPRepoPrivate::ProcessResult run(HTTPRepoPrivate* repo)
size_t rd = file.read((char *)buffer, bufferSize); {
extractor.extractBytes(buffer, rd); if (!buffer) {
return HTTPRepoPrivate::ProcessFailed;
if (file.eof()) {
extractor.flush();
file.close();
if (!extractor.isAtEndOfArchive()) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << relativePath);
repo->failedToUpdateChild(relativePath,
HTTPRepository::REPO_ERROR_IO);
return HTTPRepoPrivate::ProcessFailed;
} }
if (extractor.hasError()) { size_t rd = file.read((char*)buffer, bufferSize);
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << relativePath); extractor.extractBytes(buffer, rd);
repo->failedToUpdateChild(relativePath,
HTTPRepository::REPO_ERROR_IO); if (file.eof()) {
return HTTPRepoPrivate::ProcessFailed; extractor.flush();
file.close();
if (!extractor.isAtEndOfArchive()) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << relativePath);
repo->failedToUpdateChild(relativePath,
HTTPRepository::REPO_ERROR_IO);
return HTTPRepoPrivate::ProcessFailed;
}
if (extractor.hasError()) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << relativePath);
repo->failedToUpdateChild(relativePath,
HTTPRepository::REPO_ERROR_IO);
return HTTPRepoPrivate::ProcessFailed;
}
return HTTPRepoPrivate::ProcessDone;
} }
return HTTPRepoPrivate::ProcessDone; return HTTPRepoPrivate::ProcessContinue;
}
return HTTPRepoPrivate::ProcessContinue;
} }
~ArchiveExtractTask() { free(buffer); } ~ArchiveExtractTask() { free(buffer); }
@@ -976,49 +981,71 @@ HTTPRepository::failure() const
} }
protected: protected:
void gotBodyData(const char *s, int n) override { void gotBodyData(const char* s, int n) override
if (!file.get()) { {
file.reset(new SGBinaryFile(pathInRepo)); if (!file.get()) {
if (!file->open(SG_IO_OUT)) { const bool ok = createOutputFile();
SG_LOG(SG_TERRASYNC, SG_WARN, if (!ok) {
"unable to create file " << pathInRepo); _directory->repository()->http->cancelRequest(
_directory->repository()->http->cancelRequest( this, "Unable to create output file:" + pathInRepo.utf8Str());
this, "Unable to create output file:" + pathInRepo.utf8Str()); }
} }
sha1_init(&hashContext); sha1_write(&hashContext, s, n);
file->write(s, n);
} }
sha1_write(&hashContext, s, n); bool createOutputFile()
file->write(s, n); {
} file.reset(new SGBinaryFile(pathInRepo));
if (!file->open(SG_IO_OUT)) {
SG_LOG(SG_TERRASYNC, SG_WARN,
"unable to create file " << pathInRepo);
return false;
}
void onDone() override { sha1_init(&hashContext);
file->close(); return true;
if (responseCode() == 200) {
std::string hash =
strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
_directory->didUpdateFile(fileName, hash, contentSize());
} else if (responseCode() == 404) {
SG_LOG(SG_TERRASYNC, SG_WARN,
"terrasync file not found on server: "
<< fileName << " for " << _directory->absolutePath());
_directory->didFailToUpdateFile(
fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
} else {
SG_LOG(SG_TERRASYNC, SG_WARN,
"terrasync file download error on server: "
<< fileName << " for " << _directory->absolutePath()
<< "\n\tserver responded: " << responseCode() << "/"
<< responseReason());
_directory->didFailToUpdateFile(fileName,
HTTPRepository::REPO_ERROR_HTTP);
// should we every retry here?
} }
_directory->repository()->finishedRequest( void onDone() override
this, HTTPRepoPrivate::RequestFinish::Done); {
} const bool is200Response = (responseCode() == 200);
if (!file && is200Response) {
// if the server defines a zero-byte file, we will never call
// gotBodyData, so create the file here
// this ensures all the logic below works as expected
createOutputFile();
}
if (file) {
file->close();
}
if (is200Response) {
std::string hash =
strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
_directory->didUpdateFile(fileName, hash, contentSize());
} else if (responseCode() == 404) {
SG_LOG(SG_TERRASYNC, SG_WARN,
"terrasync file not found on server: "
<< fileName << " for " << _directory->absolutePath());
_directory->didFailToUpdateFile(
fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
} else {
SG_LOG(SG_TERRASYNC, SG_WARN,
"terrasync file download error on server: "
<< fileName << " for " << _directory->absolutePath()
<< "\n\tserver responded: " << responseCode() << "/"
<< responseReason());
_directory->didFailToUpdateFile(fileName,
HTTPRepository::REPO_ERROR_HTTP);
// should we every retry here?
}
_directory->repository()->finishedRequest(
this, HTTPRepoPrivate::RequestFinish::Done);
}
void onFail() override { void onFail() override {
HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET; HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET;
+7
View File
@@ -31,6 +31,10 @@ using TestApi = simgear::HTTP::TestApi;
std::string dataForFile(const std::string& parentName, const std::string& name, int revision) std::string dataForFile(const std::string& parentName, const std::string& name, int revision)
{ {
if (name == "zeroByteFile") {
return {};
}
std::ostringstream os; std::ostringstream os;
// random content but which definitely depends on our tree location // random content but which definitely depends on our tree location
// and revision. // and revision.
@@ -446,6 +450,7 @@ void testBasicClone(HTTP::Client* cl)
verifyFileState(p, "fileA"); verifyFileState(p, "fileA");
verifyFileState(p, "dirA/subdirA/fileAAA"); verifyFileState(p, "dirA/subdirA/fileAAA");
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA"); verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
verifyFileState(p, "dirA/subdirA/zeroByteFile");
global_repo->findEntry("fileA")->revision++; global_repo->findEntry("fileA")->revision++;
global_repo->findEntry("dirB/subdirA/fileBAA")->revision++; global_repo->findEntry("dirB/subdirA/fileBAA")->revision++;
@@ -911,6 +916,8 @@ int main(int argc, char* argv[])
global_repo->defineFile("dirA/fileAC"); global_repo->defineFile("dirA/fileAC");
global_repo->defineFile("dirA/subdirA/fileAAA"); global_repo->defineFile("dirA/subdirA/fileAAA");
global_repo->defineFile("dirA/subdirA/fileAAB"); global_repo->defineFile("dirA/subdirA/fileAAB");
global_repo->defineFile("dirA/subdirA/zeroByteFile");
global_repo->defineFile("dirB/subdirA/fileBAA"); global_repo->defineFile("dirB/subdirA/fileBAA");
global_repo->defineFile("dirB/subdirA/fileBAB"); global_repo->defineFile("dirB/subdirA/fileBAB");
global_repo->defineFile("dirB/subdirA/fileBAC"); global_repo->defineFile("dirB/subdirA/fileBAC");
+26 -24
View File
@@ -511,32 +511,34 @@ public:
const size_t BUFFER_SIZE = 1024 * 1024; const size_t BUFFER_SIZE = 1024 * 1024;
void* buf = malloc(BUFFER_SIZE); void* buf = malloc(BUFFER_SIZE);
try { int result = unzGoToFirstFile(zip);
int result = unzGoToFirstFile(zip); if (result != UNZ_OK) {
if (result != UNZ_OK) { SG_LOG(SG_IO, SG_ALERT, outer->rootPath() << "failed to go to first file in archive:" << result);
throw sg_exception("failed to go to first file in archive");
}
while (true) {
extractCurrentFile(zip, (char*)buf, BUFFER_SIZE);
if (state == FILTER_STOPPED) {
break;
}
result = unzGoToNextFile(zip);
if (result == UNZ_END_OF_LIST_OF_FILE) {
break;
}
else if (result != UNZ_OK) {
throw sg_io_exception("failed to go to next file in the archive");
}
}
state = END_OF_ARCHIVE;
}
catch (sg_exception&) {
state = BAD_ARCHIVE; state = BAD_ARCHIVE;
free(buf);
unzClose(zip);
return;
} }
while (true) {
extractCurrentFile(zip, (char*)buf, BUFFER_SIZE);
if (state == FILTER_STOPPED) {
state = END_OF_ARCHIVE;
break;
}
result = unzGoToNextFile(zip);
if (result == UNZ_END_OF_LIST_OF_FILE) {
state = END_OF_ARCHIVE;
break;
} else if (result != UNZ_OK) {
SG_LOG(SG_IO, SG_ALERT, outer->rootPath() << "failed to go to next file in archive:" << result);
state = BAD_ARCHIVE;
break;
}
}
free(buf); free(buf);
unzClose(zip); unzClose(zip);
} }
+5
View File
@@ -70,6 +70,11 @@ public:
Stop Stop
}; };
SGPath rootPath() const
{
return _rootPath;
}
protected: protected:
+7
View File
@@ -29,6 +29,13 @@ public:
/// Default constructor, initializes the instance to lat = lon = elev = 0 /// Default constructor, initializes the instance to lat = lon = elev = 0
SGGeod(void); SGGeod(void);
/**
return an SGGeod for which isValid() returns false.
This is necessaerby becuase for historical reasons, ther defaulrt constructor above initialsies to zero,zero,zero
which *is*
*/
static SGGeod invalid();
/// Factory from angular values in radians and elevation is 0 /// Factory from angular values in radians and elevation is 0
static SGGeod fromRad(double lon, double lat); static SGGeod fromRad(double lon, double lat);
/// Factory from angular values in degrees and elevation is 0 /// Factory from angular values in degrees and elevation is 0
+5
View File
@@ -663,3 +663,8 @@ SGGeodesy::radialIntersection(const SGGeod& a, double aRadial,
result = SGGeod::fromGeoc(r); result = SGGeod::fromGeoc(r);
return true; return true;
} }
SGGeod SGGeod::invalid()
{
return SGGeod::fromDeg(-999.9, -999.0);
}
+1 -1
View File
@@ -248,7 +248,7 @@ void naFreeContext(naContext c)
// than I have right now. So instead I'm clearing the stack tops here, so // than I have right now. So instead I'm clearing the stack tops here, so
// a freed context looks the same as a new one returned by initContext. // a freed context looks the same as a new one returned by initContext.
c->fTop = c->opTop = c->markTop = 0; c->fTop = c->opTop = c->markTop = c->ntemps = 0;
c->nextFree = globals->freeContexts; c->nextFree = globals->freeContexts;
globals->freeContexts = c; globals->freeContexts = c;
@@ -63,7 +63,7 @@ namespace nasal
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver { class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
public: public:
NasalMainLoopRecipient() : receiveCount(0) { NasalMainLoopRecipient() : receiveCount(0), Active(false), CanWait(false) {
simgear::Emesary::GlobalTransmitter::instance()->Register(*this); simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
} }
virtual ~NasalMainLoopRecipient() { virtual ~NasalMainLoopRecipient() {
+8 -1
View File
@@ -100,7 +100,14 @@ naRef naNewHash(struct Context* c)
naRef naNewCode(struct Context* c) naRef naNewCode(struct Context* c)
{ {
return naNew(c, T_CODE); naRef r = naNew(c, T_CODE);
// naNew can return a previously used naCode. naCodeGen will init
// all these members but a GC can occur inside naCodeGen, so we see
// partially initalized state here. To avoid this, clear out the values
// which mark() cares about.
PTR(r).code->srcFile = naNil();
PTR(r).code->nConstants = 0;
return r;
} }
naRef naNewCCode(struct Context* c, naCFunction fptr) naRef naNewCCode(struct Context* c, naCFunction fptr)
+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;
-2
View File
@@ -1481,10 +1481,8 @@ void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
// Walk the techniques property tree, building techniques and // Walk the techniques property tree, building techniques and
// passes. // passes.
static std::mutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options) bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{ {
std::lock_guard<std::mutex> g(realizeTechniques_lock);
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
mergeSchemesFallbacks(this, options); mergeSchemesFallbacks(this, options);
+4
View File
@@ -393,6 +393,10 @@ ModelRegistry::readImage(const string& fileName,
isEffect = true; isEffect = true;
// can_compress = false; // can_compress = false;
} }
else if (sgoptC && !transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_CANVAS) {
SG_LOG(SG_IO, SG_INFO, "From Canvas " + absFileName + " will generate mipmap only");
can_compress = false;
}
if (can_compress) if (can_compress)
{ {
std::string pot_message; std::string pot_message;
+1 -1
View File
@@ -294,7 +294,7 @@ ReaderWriterSPT::createTree(const BucketBox& bucketBox, const LocalOptions& opti
osg::ref_ptr<osg::Node> osg::ref_ptr<osg::Node>
ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const
{ {
osg::PagedLOD* pagedLOD = new osg::PagedLOD; osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD;
pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER); pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
SGSpheref sphere = bucketBox.getBoundingSphere(); SGSpheref sphere = bucketBox.getBoundingSphere();
+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;
} }
} }
+9
View File
@@ -572,6 +572,15 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
notFound(slot.currentItem); notFound(slot.currentItem);
} else if (res != HTTPRepository::REPO_NO_ERROR) { } else if (res != HTTPRepository::REPO_NO_ERROR) {
fail(slot.currentItem); fail(slot.currentItem);
// in case the Airports_archive download fails, create the
// directory, so that next sync, we do a manual sync
if ((slot.currentItem._type == SyncItem::AirportData) && slot.isNewDirectory) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Failed to download Airports_archive, will download discrete files next time");
simgear::Dir d(_local_dir + "/Airports");
d.create(0755);
_completedTiles.erase(slot.currentItem._dir);
}
} else { } else {
updated(slot.currentItem, slot.isNewDirectory); updated(slot.currentItem, slot.isNewDirectory);
SG_LOG(SG_TERRASYNC, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " finished (" SG_LOG(SG_TERRASYNC, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " finished ("
@@ -46,6 +46,7 @@ public:
ORIGIN_EFFECTS, ORIGIN_EFFECTS,
ORIGIN_EFFECTS_NORMALIZED, ORIGIN_EFFECTS_NORMALIZED,
ORIGIN_SPLASH_SCREEN, ORIGIN_SPLASH_SCREEN,
ORIGIN_CANVAS,
}; };
//SGReaderWriterOptions* cloneOptions(const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) const { return static_cast<SGReaderWriterOptions*>(clone(copyop)); } //SGReaderWriterOptions* cloneOptions(const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) const { return static_cast<SGReaderWriterOptions*>(clone(copyop)); }
+3 -3
View File
@@ -441,7 +441,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
std::string nm = stateDesc->getStringValue("name"); std::string nm = stateDesc->getStringValue("name");
if (nm.empty()) { if (nm.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for state in branch " << path); SG_LOG(SG_GENERAL, SG_DEV_ALERT, "No name found for state in branch " << path);
throw sg_exception("No name element in state"); throw sg_exception("No name element in state");
} }
@@ -464,8 +464,8 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
std::string target_id = tDesc->getStringValue("target"); std::string target_id = tDesc->getStringValue("target");
if (nm.empty()) { if (nm.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for transition in branch " << path); SG_LOG(SG_GENERAL, SG_DEV_WARN, "No name found for transition in branch " << path);
throw sg_exception("No name element in transition"); nm = "transition-to-" + target_id;
} }
if (target_id.empty()) { if (target_id.empty()) {
+13 -2
View File
@@ -30,9 +30,10 @@
#include "subsystem_mgr.hxx" #include "subsystem_mgr.hxx"
#include "commands.hxx" #include "commands.hxx"
#include <simgear/props/props.hxx>
#include <simgear/math/SGMath.hxx>
#include "SGSmplstat.hxx" #include "SGSmplstat.hxx"
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/props/props.hxx>
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4; const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
const char SUBSYSTEM_NAME_SEPARATOR = '.'; const char SUBSYSTEM_NAME_SEPARATOR = '.';
@@ -828,8 +829,18 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) { if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount << SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
", suspending)"); ", suspending)");
simgear::reportError("suspending subsystem after too many errors:" + name);
subsystem->suspend(); subsystem->suspend();
} }
} catch (std::bad_alloc& ba) {
// attempting to track down source of these on Sentry.io
SG_LOG(SG_GENERAL, SG_ALERT, "caught bad_alloc processing subsystem:" << name);
simgear::reportError("caught bad_alloc processing subsystem:" + name);
if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount << ", suspending)");
subsystem->suspend();
}
} }
} }