-
Notifications
You must be signed in to change notification settings - Fork 75
Bug/220 spice makes bsk sims not thread safe #994
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,10 @@ | |
#include "architecture/utilities/macroDefinitions.h" | ||
#include "architecture/utilities/rigidBodyKinematics.h" | ||
|
||
// Initialize static members | ||
std::mutex SpiceInterface::kernel_manipulation_mutex; | ||
std::map<std::string, int> SpiceInterface::kernel_reference_counter; | ||
|
||
/*! This constructor initializes the variables that spice uses. Most of them are | ||
not intended to be changed, but a couple are user configurable. | ||
*/ | ||
|
@@ -74,15 +78,34 @@ SpiceInterface::~SpiceInterface() | |
delete this->transRefStateOutMsgs.at(c); | ||
} | ||
delete [] this->spiceBuffer; | ||
// if(this->SPICELoaded) | ||
// { | ||
// this->clearKeeper(); | ||
// } | ||
|
||
// Properly unload kernels if they were loaded | ||
if(this->SPICELoaded) | ||
{ | ||
// Create vector instead of variable-length array for Windows compatibility | ||
std::vector<char> kernelNameVec(this->charBufferSize); | ||
char* kernelName = kernelNameVec.data(); | ||
|
||
// Unload the SPICE kernels in reverse order of loading | ||
strcpy(kernelName, "de430.bsp"); | ||
unloadSpiceKernel(kernelName, this->SPICEDataPath.c_str()); | ||
|
||
strcpy(kernelName, "de-403-masses.tpc"); | ||
unloadSpiceKernel(kernelName, this->SPICEDataPath.c_str()); | ||
|
||
strcpy(kernelName, "pck00010.tpc"); | ||
unloadSpiceKernel(kernelName, this->SPICEDataPath.c_str()); | ||
|
||
strcpy(kernelName, "naif0012.tls"); | ||
unloadSpiceKernel(kernelName, this->SPICEDataPath.c_str()); | ||
} | ||
|
||
return; | ||
} | ||
|
||
void SpiceInterface::clearKeeper() | ||
{ | ||
std::lock_guard<std::mutex> lock(kernel_manipulation_mutex); | ||
kclear_c(); | ||
} | ||
|
||
|
@@ -101,18 +124,32 @@ void SpiceInterface::Reset(uint64_t CurrenSimNanos) | |
//!- Load the SPICE kernels if they haven't already been loaded | ||
if(!this->SPICELoaded) | ||
{ | ||
if(loadSpiceKernel((char *)"naif0012.tls", this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", "naif0012.tls"); | ||
// Create vector instead of variable-length array for Windows compatibility | ||
std::vector<char> kernelNameVec(this->charBufferSize); | ||
char* kernelName = kernelNameVec.data(); | ||
|
||
// Load the required SPICE kernels - they will only be loaded once per kernel | ||
// across all threads due to our reference counting mechanism | ||
strcpy(kernelName, "naif0012.tls"); | ||
if(loadSpiceKernel(kernelName, this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", kernelName); | ||
} | ||
if(loadSpiceKernel((char *)"pck00010.tpc", this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", "pck00010.tpc"); | ||
|
||
strcpy(kernelName, "pck00010.tpc"); | ||
if(loadSpiceKernel(kernelName, this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", kernelName); | ||
} | ||
if(loadSpiceKernel((char *)"de-403-masses.tpc", this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", "de-403-masses.tpc"); | ||
|
||
strcpy(kernelName, "de-403-masses.tpc"); | ||
if(loadSpiceKernel(kernelName, this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", kernelName); | ||
} | ||
if(loadSpiceKernel((char *)"de430.bsp", this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", "de430.tpc"); | ||
|
||
Comment on lines
+128
to
+147
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might want to refactor this into:
actually a similar refactor is also possible above for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, this is a cleaner solution. |
||
strcpy(kernelName, "de430.bsp"); | ||
if(loadSpiceKernel(kernelName, this->SPICEDataPath.c_str())) { | ||
bskLogger.bskLog(BSK_ERROR, "Unable to load %s", kernelName); | ||
} | ||
|
||
this->SPICELoaded = true; | ||
} | ||
|
||
|
@@ -436,25 +473,44 @@ void SpiceInterface::pullSpiceData(std::vector<SpicePlanetStateMsgPayload> *spic | |
*/ | ||
int SpiceInterface::loadSpiceKernel(char *kernelName, const char *dataPath) | ||
{ | ||
char *fileName = new char[this->charBufferSize]; | ||
SpiceChar *name = new SpiceChar[this->charBufferSize]; | ||
// Create vectors instead of raw pointers for Windows compatibility | ||
std::vector<char> fileNameVec(this->charBufferSize); | ||
char* fileName = fileNameVec.data(); | ||
|
||
std::vector<char> nameVec(this->charBufferSize); | ||
char* name = nameVec.data(); | ||
|
||
//! - The required calls come from the SPICE documentation. | ||
//! - The most critical call is furnsh_c | ||
strcpy(name, "REPORT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
// Create the full filepath | ||
strcpy(fileName, dataPath); | ||
strcat(fileName, kernelName); | ||
furnsh_c(fileName); | ||
|
||
//! - Check to see if we had trouble loading a kernel and alert user if so | ||
strcpy(name, "DEFAULT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
delete[] fileName; | ||
delete[] name; | ||
if(failed_c()) { | ||
return 1; | ||
std::string filepath(fileName); | ||
Comment on lines
+476
to
+486
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you refactor the inputs into
|
||
|
||
// Acquire the mutex to protect kernel operations | ||
std::lock_guard<std::mutex> lock(kernel_manipulation_mutex); | ||
|
||
// Initialize the reference counter for this kernel if it doesn't exist | ||
kernel_reference_counter.try_emplace(filepath, 0); | ||
|
||
// Only load the kernel if it hasn't been loaded yet | ||
if (kernel_reference_counter.at(filepath) <= 0) { | ||
// The required calls come from the SPICE documentation. | ||
// The most critical call is furnsh_c | ||
strcpy(name, "REPORT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
furnsh_c(fileName); | ||
|
||
// Check to see if we had trouble loading a kernel | ||
strcpy(name, "DEFAULT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
|
||
if(failed_c()) { | ||
return 1; | ||
} | ||
Comment on lines
+495
to
+508
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice job fetching the error from SPICE. |
||
} | ||
|
||
// Increment the reference counter for this kernel | ||
kernel_reference_counter[filepath]++; | ||
|
||
return 0; | ||
} | ||
|
||
|
@@ -468,27 +524,47 @@ int SpiceInterface::loadSpiceKernel(char *kernelName, const char *dataPath) | |
*/ | ||
int SpiceInterface::unloadSpiceKernel(char *kernelName, const char *dataPath) | ||
{ | ||
char *fileName = new char[this->charBufferSize]; | ||
SpiceChar *name = new SpiceChar[this->charBufferSize]; | ||
// Create vectors instead of raw pointers for Windows compatibility | ||
std::vector<char> fileNameVec(this->charBufferSize); | ||
char* fileName = fileNameVec.data(); | ||
|
||
//! - The required calls come from the SPICE documentation. | ||
//! - The most critical call is furnsh_c | ||
strcpy(name, "REPORT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
std::vector<char> nameVec(this->charBufferSize); | ||
char* name = nameVec.data(); | ||
|
||
// Create the full filepath | ||
strcpy(fileName, dataPath); | ||
strcat(fileName, kernelName); | ||
unload_c(fileName); | ||
delete[] fileName; | ||
delete[] name; | ||
if(failed_c()) { | ||
return 1; | ||
std::string filepath(fileName); | ||
Comment on lines
+528
to
+537
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comment as above. |
||
|
||
// Acquire the mutex to protect kernel operations | ||
std::lock_guard<std::mutex> lock(kernel_manipulation_mutex); | ||
|
||
// Initialize the reference counter for this kernel if it doesn't exist | ||
kernel_reference_counter.try_emplace(filepath, 0); | ||
|
||
// Decrement the reference counter | ||
kernel_reference_counter[filepath]--; | ||
|
||
// Only unload if no more references to this kernel | ||
if (kernel_reference_counter.at(filepath) <= 0) { | ||
// The required calls come from the SPICE documentation. | ||
strcpy(name, "REPORT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
unload_c(fileName); | ||
|
||
strcpy(name, "DEFAULT"); | ||
erract_c("SET", this->charBufferSize, name); | ||
|
||
if(failed_c()) { | ||
return 1; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
std::string SpiceInterface::getCurrentTimeString() | ||
{ | ||
char *spiceOutputBuffer; | ||
int64_t allowedOutputLength; | ||
|
||
allowedOutputLength = (int64_t)this->timeOutPicture.size() - 5; | ||
|
@@ -499,10 +575,12 @@ std::string SpiceInterface::getCurrentTimeString() | |
return(""); | ||
} | ||
|
||
spiceOutputBuffer = new char[allowedOutputLength]; | ||
// Use vector instead of raw pointer for Windows compatibility | ||
std::vector<char> spiceOutputBufferVec(allowedOutputLength); | ||
char* spiceOutputBuffer = spiceOutputBufferVec.data(); | ||
Comment on lines
+579
to
+580
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand you're using
is a bit more expressive and safe. Just a thought. Maybe actually we should make
and thus we get rid of nasty dynamically allocated memory. |
||
|
||
timout_c(this->J2000Current, this->timeOutPicture.c_str(), (SpiceInt) allowedOutputLength, | ||
spiceOutputBuffer); | ||
std::string returnTimeString = spiceOutputBuffer; | ||
delete[] spiceOutputBuffer; | ||
return(returnTimeString); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
|
||
#include <vector> | ||
#include <map> | ||
#include <mutex> | ||
#include "architecture/_GeneralModuleFiles/sys_model.h" | ||
#include "architecture/utilities/linearAlgebra.h" | ||
#include "architecture/utilities/bskLogging.h" | ||
|
@@ -40,7 +41,7 @@ class SpiceInterface: public SysModel { | |
public: | ||
SpiceInterface(); | ||
~SpiceInterface(); | ||
|
||
void UpdateState(uint64_t CurrentSimNanos); | ||
int loadSpiceKernel(char *kernelName, const char *dataPath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should change these methods into:
or at least This will also save you a lot of headache on the implementation (see later comment). |
||
int unloadSpiceKernel(char *kernelName, const char *dataPath); | ||
|
@@ -72,7 +73,7 @@ class SpiceInterface: public SysModel { | |
std::string UTCCalInit; //!< -- UTC time string for init time | ||
|
||
std::vector<std::string>planetFrames; //!< -- Optional vector of planet frame names. Default values are IAU_ + planet name | ||
|
||
bool timeDataInit; //!< -- Flag indicating whether time has been init | ||
double J2000ETInit; //!< s Seconds elapsed since J2000 at init | ||
double J2000Current; //!< s Current J2000 elapsed time | ||
|
@@ -90,6 +91,8 @@ class SpiceInterface: public SysModel { | |
std::vector<SpicePlanetStateMsgPayload> planetData; | ||
std::vector<SpicePlanetStateMsgPayload> scData; | ||
|
||
static std::mutex kernel_manipulation_mutex; //!< -- Mutex to protect SPICE kernel loading/unloading operations | ||
static std::map<std::string, int> kernel_reference_counter; //!< -- Reference counter for SPICE kernels | ||
Comment on lines
+94
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should probably make these variables camelCase. (sorry I originally suggested snake_case!).
Comment on lines
+94
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, variables should be lower camel case. |
||
}; | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change the signature of
unloadSpiceKernel
(see previous comment), you'll be able to call: