我在C++中有以下偵聽器,它接收一個Python對象來傳播回調。Boost Python,將C++回調傳播到Python導致分段錯誤
class PyClient {
private:
std::vector<DipSubscription *> subs;
subsFactory *sub;
class GeneralDataListener: public SubscriptionListener {
private:
PyClient * client;
public:
GeneralDataListener(PyClient *c):client(c){
client->pyListener.attr("log_message")("Handler created");
}
void handleMessage(Subscription *sub, Data &message) {
// Lock the execution of this method
PyGILState_STATE state = PyGILState_Ensure();
client->pyListener.attr("log_message")("Data received for topic");
...
// This method ends modifying the value of the Python object
topicEntity.attr("save_value")(valueKey, extractDipValue(valueKey.c_str(), message))
// Release the lock
PyGILState_Release(state);
}
void connected(Subscription *sub) {
client->pyListener.attr("connected")(sub->getTopicName());
}
void disconnected(Subscription *sub, char* reason) {
std::string s_reason(reason);
client->pyListener.attr("disconnected")(sub->getTopicName(), s_reason);
}
void handleException(Subscription *sub, Exception &ex) {
client->pyListener.attr("handle_exception")(sub->getTopicName())(ex.what());
}
};
GeneralDataListener *handler;
public:
python::object pyListener;
PyClient(python::object pyList): pyListener(pyList) {
std::ostringstream iss;
iss << "Listener" << getpid();
sub = Sub::create(iss.str().c_str());
createSubscriptions();
}
~PyClient() {
for (unsigned int i = 0; i < subs.size(); i++) {
if (subs[i] == NULL) {
continue;
}
sub->destroySubscription(subs[i]);
}
}
};
BOOST_PYTHON_MODULE(pytest)
{
// There is no need to expose more methods as will be used as callbacks
Py_Initialize();
PyEval_InitThreads();
python::class_<PyClient>("PyClient", python::init<python::object>())
.def("pokeHandler", &PyClient::pokeHandler);
};
然後,我有我的Python程序,它是這樣的:
import sys
import time
import pytest
class Entity(object):
def __init__(self, entity, mapping):
self.entity = entity
self.mapping = mapping
self.values = {}
for field in mapping:
self.values[field] = ""
self.updated = False
def save_value(self, field, value):
self.values[field] = value
self.updated = True
class PyListener(object):
def __init__(self):
self.listeners = 0
self.mapping = ["value"]
self.path_entity = {}
self.path_entity["path/to/node"] = Entity('Name', self.mapping)
def connected(self, topic):
print "%s topic connected" % topic
def disconnected(self, topic, reason):
print "%s topic disconnected, reason: %s" % (topic, reason)
def handle_message(self, topic):
print "Handling message from topic %s" % topic
def handle_exception(self, topic, exception):
print "Exception %s in topic %s" % (exception, topic)
def log_message(self, message):
print message
def sample(self):
for path, entity in self.path_entity.iteritems():
if not entity.updated:
return False
sample = " ".join([entity.values[field] for field in dip_entity.mapping])
print "%d %s %d %s" % (0, entity.entity, 4324, sample)
entity.updated = False
return True
if __name__ == "__main__":
sys.settrace(trace)
py_listener = PyListener()
sub = pytest.PyClient(py_listener)
while True:
if py_listener.sample():
break
所以,最後,我的問題似乎是,當我開始運行,而真正的Python程序腳本如果實體更新,則會停滯不前,而隨機地,當C++偵聽器嘗試調用回調時,會出現分段錯誤。
同樣,如果我只是在python腳本中嘗試time.sleep並且每次調用一次樣本。我知道如果我從C++代碼中調用樣本,它將被解決,但是這個腳本將由其他Python模塊運行,該模塊將在給定特定延遲的情況下調用樣本方法。因此,預期的功能將是C++更新實體和Python腳本來閱讀它們。
我調試用gdb的錯誤,但堆棧跟蹤我越來越沒有太多解釋:
#0 0x00007ffff7a83717 in PyFrame_New() from /lib64/libpython2.7.so.1.0
#1 0x00007ffff7af58dc in PyEval_EvalFrameEx() from /lib64/libpython2.7.so.1.0
#2 0x00007ffff7af718d in PyEval_EvalCodeEx() from /lib64/libpython2.7.so.1.0
#3 0x00007ffff7af7292 in PyEval_EvalCode() from /lib64/libpython2.7.so.1.0
#4 0x00007ffff7b106cf in run_mod() from /lib64/libpython2.7.so.1.0
#5 0x00007ffff7b1188e in PyRun_FileExFlags() from /lib64/libpython2.7.so.1.0
#6 0x00007ffff7b12b19 in PyRun_SimpleFileExFlags() from /lib64/libpython2.7.so.1.0
#7 0x00007ffff7b23b1f in Py_Main() from /lib64/libpython2.7.so.1.0
#8 0x00007ffff6d50af5 in __libc_start_main() from /lib64/libc.so.6
#9 0x0000000000400721 in _start()
如果裏面的Python sys.trace調試的最後一行之前分割故障總是在示例方法中,但可能會有所不同。
我不知道我該如何解決這個溝通問題,所以任何正確的方向建議都將不勝感激。
編輯 將PyDipClient引用修改爲PyClient。
發生了什麼是我從Python主要方法啓動程序,如果然後C++偵聽器試圖回調它與分段錯誤錯誤崩潰的Python偵聽器,我相信創建的唯一線程是當我創建一個訂閱,但這是來自庫中的代碼,我不知道如何正確工作。
如果我刪除所有對Python偵聽器的回調,並強制Python的方法(如調用pokehandler),那麼所有的工作都是完美的。
有太多缺失的部分來診斷問題(例如'PyDipClient',線程等)。 [mcve](http://stackoverflow.com/help/mcve)在幫助其他人幫助診斷問題方面會有很大的幫助。 –
我更新了一些問題,PyDipClient是PyClient(重寫時忘記它)。 – KBorja
如果你可以提供一個MCVE(這通常與僅僅發佈一個已有的代碼非常不同),那麼除了推測它是一個GIL管理問題之外,我可以提供進一步的幫助。我強烈鼓勵任何人使用庫,其中內部線程調用回用戶代碼時要謹慎,並且非常熟悉線程行爲。 –