Skip to content
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

Make openbabel objects thread-safe #2273

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/openbabel/base.h
Expand Up @@ -325,7 +325,7 @@ class OBConversion; //used only as pointer
//! \since version 2.2
std::vector<OBGenericData*> GetAllData(const unsigned int type);
//! \return all data, suitable for iterating
std::vector<OBGenericData*> &GetData() { return(_vdata); }
std::vector<OBGenericData*> &GetData() { return(_vdata); }
//! \return all data with a specific origin, suitable for iterating
std::vector<OBGenericData*> GetData(DataOrigin source);
//! \return An iterator pointing to the beginning of the data
Expand Down
44 changes: 7 additions & 37 deletions include/openbabel/data.h
Expand Up @@ -28,6 +28,7 @@ GNU General Public License for more details.
#include <vector>
#include <string>
#include <cstring>
#include <openbabel/data_utilities.h>

namespace OpenBabel
{
Expand All @@ -54,6 +55,7 @@ namespace OpenBabel
std::string _dir; //!< Data directory for file if _envvar fails
std::string _subdir; //!< Subdirectory (if using environment variable)
std::string _envvar; //!< Environment variable to check first
mutable OBGlobalMutex _db_mutex; //!< Mutex for concurrency

public:
//! Constructor
Expand Down Expand Up @@ -187,37 +189,19 @@ namespace OpenBabel
{
int _linecount;
unsigned int _ncols,_nrows;
int _from,_to;
std::vector<std::string> _colnames;
std::vector<std::vector<std::string> > _table;

public:
friend class OBTranslator;

public:
OBTypeTable(void);
~OBTypeTable() {}

void ParseLine(const char*);

//! \return the number of atom types in the translation table
size_t GetSize() { return _table.size(); }

//! Set the initial atom type to be translated
bool SetFromType(const char*);
//! Set the destination atom type for translation
bool SetToType(const char*);
//! Translate atom types
bool Translate(char *to, const char *from); // to, from
//! Translate atom types
//! \return whether the translation was successful
bool Translate(std::string &to, const std::string &from); // to, from
//! Translate atom types
//! \return the translated atom type, or an empty string if not possible
std::string Translate(const std::string &from);

//! \return the initial atom type to be translated
std::string GetFromType();
//! \return the destination atom type for translation
std::string GetToType();
};

//! Global OBTypeTable for translating between different atom types
Expand All @@ -231,14 +215,16 @@ namespace OpenBabel
**/
class OBAPI OBResidueData : public OBGlobalDataBase
{
int _resnum;
std::vector<std::string> _resname;
std::vector<std::vector<std::string> > _resatoms;
std::vector<std::vector<std::pair<std::string,int> > > _resbonds;

//variables used only temporarily for parsing resdata.txt
std::vector<std::string> _vatmtmp;
std::vector<std::pair<std::string,int> > _vtmp;

friend class OBResidueObserver;

public:

OBResidueData();
Expand All @@ -247,22 +233,6 @@ namespace OpenBabel
//! \return the number of residues in the table
size_t GetSize() { return _resname.size(); }

//! Sets the table to access the residue information for a specified
//! residue name
//! \return whether this residue name is in the table
bool SetResName(const std::string &);
//! \return the bond order for the bond specified in the current residue
//! \deprecated Easier to use the two-argument form
int LookupBO(const std::string &);
//! \return the bond order for the bond specified between the two specified
//! atom labels
int LookupBO(const std::string &, const std::string&);
//! Look up the atom type and hybridization for the atom label specified
//! in the first argument for the current residue
//! \return whether the atom label specified is found in the current residue
bool LookupType(const std::string &,std::string&,int&);
//! Assign bond orders, atom types and residues for the supplied OBMol
//! based on the residue information assigned to atoms
bool AssignBonds(OBMol &);
};

Expand Down
85 changes: 85 additions & 0 deletions include/openbabel/data_utilities.h
Expand Up @@ -23,6 +23,7 @@ GNU General Public License for more details.
#include <openbabel/babelconfig.h>

#include <vector>
#include <mutex>

namespace OpenBabel {
class OBMol;
Expand Down Expand Up @@ -68,6 +69,90 @@ namespace OpenBabel {
std::vector<double> &Scomponents,
double *ZPVE);

class OBAPI OBTranslator
{
int _from, _to;

public:
//! Constructor
OBTranslator();
OBTranslator(const char*, const char*);
//! Destructor
~OBTranslator() {};

//! Set the initial atom type to be translated
bool SetFromType(const char*);
//! Set the destination atom type for translation
bool SetToType(const char*);
//! Translate atom types
bool Translate(char *to, const char *from) const; // to, from
//! Translate atom types
//! \return whether the translation was successful
bool Translate(std::string &to, const std::string &from) const; // to, from
//! Translate atom types
//! \return the translated atom type, or an empty string if not possible
std::string Translate(const std::string &from) const;

//! \return the initial atom type to be translated
std::string GetFromType() const;
//! \return the destination atom type for translation
std::string GetToType() const;
};

class OBAPI OBResidueObserver
{
int _resnum;

public:
//! Sets the table to access the residue information for a specified
//! residue name
//! \return whether this residue name is in the table
bool SetResName(const std::string &);
//! \return the bond order for the bond specified in the current residue
//! \deprecated Easier to use the two-argument form
int LookupBO(const std::string &);
//! \return the bond order for the bond specified between the two specified
//! atom labels
int LookupBO(const std::string &, const std::string&);
//! Look up the atom type and hybridization for the atom label specified
//! in the first argument for the current residue
//! \return whether the atom label specified is found in the current residue
bool LookupType(const std::string &,std::string&,int&);
//! Assign bond orders, atom types and residues for the supplied OBMol
//! based on the residue information assigned to atoms
};

class OBGlobalMutex: public std::mutex
{
public:

#ifndef HAS_NOEXCEPT
#if defined(__clang__)
#if __has_feature(cxx_noexcept)
#define HAS_NOEXCEPT
#endif
#else
#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 || \
defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026
#define HAS_NOEXCEPT
#endif
#endif
#endif

#ifdef HAS_NOEXCEPT
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif

constexpr OBGlobalMutex() NOEXCEPT = default;

OBGlobalMutex(const OBGlobalMutex &) { OBGlobalMutex(); } // Copy constructor

~OBGlobalMutex() = default;

OBGlobalMutex& operator=(const OBGlobalMutex &) { return *this; } // Copy assignment operator
};
}

#endif //DATA_UTILITIES_H
Expand Down
8 changes: 7 additions & 1 deletion include/openbabel/groupcontrib.h
Expand Up @@ -52,7 +52,12 @@ class OBDESC OBGroupContrib : public OBDescriptor

//! constructor. Each instance provides an ID and a datafile.
OBGroupContrib(const char* ID, const char* filename, const char* descr)
: OBDescriptor(ID, false), _filename(filename), _descr(descr), _debug(false){}
: OBDescriptor(ID, false), _filename(filename), _descr(descr), _debug(false) {
_alldescr = _descr;
_alldescr += "\n Datafile: ";
_alldescr += _filename;
_alldescr += "\nOBGroupContrib is definable";
}

virtual const char* Description();

Expand All @@ -69,6 +74,7 @@ class OBDESC OBGroupContrib : public OBDescriptor

const char* _filename;
const char* _descr;
std::string _alldescr;
std::vector<std::pair<OBSmartsPattern*, double> > _contribsHeavy; //! heavy atom contributions
std::vector<std::pair<OBSmartsPattern*, double> > _contribsHydrogen; //! hydrogen contributions
bool _debug;
Expand Down
2 changes: 2 additions & 0 deletions include/openbabel/locale.h
Expand Up @@ -20,6 +20,7 @@ GNU General Public License for more details.
#define OB_LOCALE_H

#include <locale>
#include <mutex>
#include <openbabel/babelconfig.h>

#ifndef OBERROR
Expand All @@ -43,6 +44,7 @@ namespace OpenBabel

protected:
OBLocalePrivate* d;
static std::mutex LocaleMutex;
};

//global definitions
Expand Down
4 changes: 4 additions & 0 deletions include/openbabel/mol.h
Expand Up @@ -44,6 +44,7 @@ GNU General Public License for more details.
#include <vector>
#include <string>
#include <map>
#include <mutex>

#include <openbabel/base.h>

Expand Down Expand Up @@ -124,6 +125,7 @@ enum HydrogenType { AllHydrogen, PolarHydrogen, NonPolarHydrogen };
bool _autoPartialCharge;//!< Assign partial charges automatically
bool _autoFormalCharge;//!< Assign formal charges automatically
std::string _title; //!< Molecule title
std::string _sttl; //!< Molecule title (Single line)
std::vector<OBAtom*> _vatom; //!< vector of atoms
std::vector<OBAtom*> _atomIds; //!< vector of atoms indexed by id
std::vector<OBBond*> _vbond; //!< vector of bonds
Expand All @@ -140,6 +142,8 @@ enum HydrogenType { AllHydrogen, PolarHydrogen, NonPolarHydrogen };
std::vector<OBInternalCoord*> _internals; //!< Internal Coordinates (if applicable)
unsigned short int _mod; //!< Number of nested calls to BeginModify()

std::mutex _molMutex; //!< Mutex for concurrency

public:

//! \name Initialization and data (re)size methods
Expand Down
52 changes: 38 additions & 14 deletions include/openbabel/oberror.h
Expand Up @@ -27,6 +27,9 @@ General Public License for more details.
#include <string>
#include <vector>
#include <deque>
#include <mutex>

#include <openbabel/data_utilities.h>

#ifndef OBERROR
#define OBERROR
Expand Down Expand Up @@ -120,28 +123,46 @@ namespace OpenBabel
obMessageLevel level = obDebug, errorQualifier qualifier = always);

//! \return all messages matching a specified level
std::vector<std::string> GetMessagesOfLevel(const obMessageLevel);
std::vector<std::string> GetMessagesOfLevel(const obMessageLevel) const;

//! Start logging messages (default)
void StartLogging() { _logging = true; }
void StartLogging() {
std::lock_guard<std::mutex> lock(_handlerMutex);
_logging = true;
}
//! Stop logging messages completely
void StopLogging() { _logging = false; }
void StopLogging() {
std::lock_guard<std::mutex> lock(_handlerMutex);
_logging = false;
}

//! Set the maximum number of entries (or 0 for no limit)
void SetMaxLogEntries(unsigned int max) { _maxEntries = max; }
void SetMaxLogEntries(unsigned int max) {
std::lock_guard<std::mutex> lock(_handlerMutex);
_maxEntries = max;
}
//! \return the current maximum number of entries (default = 0 for no limit)
unsigned int GetMaxLogEntries() { return _maxEntries; }
unsigned int GetMaxLogEntries() const { return _maxEntries; }

//! Clear the current message log entirely
void ClearLog() { _messageList.clear(); }
void ClearLog() {
std::lock_guard<std::mutex> lock(_handlerMutex);
_messageList.clear();
}

//! \brief Set the level of messages to output
//! (i.e., messages with at least this priority will be output)
void SetOutputLevel(const obMessageLevel level) { _outputLevel = level; }
void SetOutputLevel(const obMessageLevel level) {
std::lock_guard<std::mutex> lock(_handlerMutex);
_outputLevel = level;
}
//! \return the current output level
obMessageLevel GetOutputLevel() { return _outputLevel; }
obMessageLevel GetOutputLevel() const { return _outputLevel; }

void SetOutputStream(std::ostream *os) { _outputStream = os; }
void SetOutputStream(std::ostream *os) {
std::lock_guard<std::mutex> lock(_handlerMutex);
_outputStream = os;
}
std::ostream* GetOutputStream() { return _outputStream; }

//! Start "wrapping" messages to cerr into ThrowError calls
Expand All @@ -150,15 +171,15 @@ namespace OpenBabel
bool StopErrorWrap();

//! \return Count of messages received at the obError level
unsigned int GetErrorMessageCount() { return _messageCount[obError];}
unsigned int GetErrorMessageCount() const { return _messageCount[obError];}
//! \return Count of messages received at the obWarning level
unsigned int GetWarningMessageCount() { return _messageCount[obWarning];}
unsigned int GetWarningMessageCount() const { return _messageCount[obWarning];}
//! \return Count of messages received at the obInfo level
unsigned int GetInfoMessageCount() { return _messageCount[obInfo];}
unsigned int GetInfoMessageCount() const { return _messageCount[obInfo];}
//! \return Count of messages received at the obAuditMsg level
unsigned int GetAuditMessageCount() { return _messageCount[obAuditMsg];}
unsigned int GetAuditMessageCount() const { return _messageCount[obAuditMsg];}
//! \return Count of messages received at the obDebug level
unsigned int GetDebugMessageCount() { return _messageCount[obDebug];}
unsigned int GetDebugMessageCount() const { return _messageCount[obDebug];}
//! \return Summary of messages received at all levels
std::string GetMessageSummary();

Expand All @@ -181,6 +202,9 @@ namespace OpenBabel
std::streambuf *_inWrapStreamBuf;
//! The filtered obLogBuf stream buffer to wrap error messages
std::streambuf *_filterStreamBuf;

//! For concurrency
mutable OBGlobalMutex _handlerMutex;
};

//! Global OBMessageHandler error handler
Expand Down
8 changes: 7 additions & 1 deletion include/openbabel/optransform.h
Expand Up @@ -33,7 +33,12 @@ class OpTransform : public OBOp
public:
//! constructor. Each instance provides an ID, a datafile and a description.
OpTransform(const char* ID, const char* filename, const char* descr)
: OBOp(ID, false), _filename(filename), _descr(descr), _dataLoaded(false){}
: OBOp(ID, false), _filename(filename), _descr(descr), _dataLoaded(false) {
_alldescr = _descr;
_alldescr += "\n Datafile: ";
_alldescr += _filename;
_alldescr += "\nOpTransform is definable";
}

~OpTransform(){}

Expand Down Expand Up @@ -61,6 +66,7 @@ class OpTransform : public OBOp
const char* _filename;
const char* _descr;
std::vector<std::string> _textlines;
std::string _alldescr;

bool _dataLoaded;
std::vector<OBChemTsfm> _transforms;
Expand Down