Skip to content

Commit b883f42

Browse files
committed
Wrap the interruption to a custom exception when a blocking API is interrupted (#99)
### Motivation Currently, when a blocking API is interrupted by a signal, `SystemError` will be thrown. However, in this case, `PyErr_SetInterrupt` will be called and next time a blocking API is called, `std::system_error` will be somehow thrown. The failure of https://lists.apache.org/thread/cmzykd9qz9x1d0s35nc5912o3slwpxpv is caused by this issue. The `SystemError` is not called, then `client.close()` will be skipped, which leads to the `bad_weak_ptr` error. P.S. Currently we have to call `client.close()` on a `Client` instance, otherwise, the `bad_weak_ptr` will be thrown. However, even if we caught the `SystemError` like: ```python try: msg = consumer.receive() # ... except SystemError: break ``` we would still see the following error: ``` terminate called after throwing an instance of 'std::system_error' what(): Operation not permitted Aborted ``` ### Modifications - Wrap `ResultInterrupted` into the `pulsar.Interrupted` exception. - Refactor the `waitForAsyncValue` and `waitForAsyncResult` functions and raise `pulsar.Interrupted` when `PyErr_CheckSignals` detects a signal. - Add `InterruptedTest` to cover this case. - Remove `future.h` since we now use `std::future` instead of the manually implemented `Future`. - Fix the `examples/consumer.py` to support stopping by Ctrl+C. (cherry picked from commit ec05f50)
1 parent 9ed92ec commit b883f42

File tree

11 files changed

+121
-331
lines changed

11 files changed

+121
-331
lines changed

examples/consumer.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@
2929
})
3030

3131
while True:
32-
msg = consumer.receive()
33-
print("Received message '{0}' id='{1}'".format(msg.data().decode('utf-8'), msg.message_id()))
34-
consumer.acknowledge(msg)
32+
try:
33+
msg = consumer.receive()
34+
print("Received message '{0}' id='{1}'".format(msg.data().decode('utf-8'), msg.message_id()))
35+
consumer.acknowledge(msg)
36+
except pulsar.Interrupted:
37+
print("Stop receiving messages")
38+
break
3539

3640
client.close()

pulsar/exceptions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
ProducerBlockedQuotaExceededException, ProducerQueueIsFull, MessageTooBig, TopicNotFound, SubscriptionNotFound, \
2626
ConsumerNotFound, UnsupportedVersionError, TopicTerminated, CryptoError, IncompatibleSchema, ConsumerAssignError, \
2727
CumulativeAcknowledgementNotAllowedError, TransactionCoordinatorNotFoundError, InvalidTxnStatusError, \
28-
NotAllowedError, TransactionConflict, TransactionNotFound, ProducerFenced, MemoryBufferIsFull
28+
NotAllowedError, TransactionConflict, TransactionNotFound, ProducerFenced, MemoryBufferIsFull, Interrupted

src/client.cc

+13-48
Original file line numberDiff line numberDiff line change
@@ -24,73 +24,38 @@
2424
namespace py = pybind11;
2525

2626
Producer Client_createProducer(Client& client, const std::string& topic, const ProducerConfiguration& conf) {
27-
Producer producer;
28-
29-
waitForAsyncValue(std::function<void(CreateProducerCallback)>([&](CreateProducerCallback callback) {
30-
client.createProducerAsync(topic, conf, callback);
31-
}),
32-
producer);
33-
34-
return producer;
27+
return waitForAsyncValue<Producer>(
28+
[&](CreateProducerCallback callback) { client.createProducerAsync(topic, conf, callback); });
3529
}
3630

3731
Consumer Client_subscribe(Client& client, const std::string& topic, const std::string& subscriptionName,
3832
const ConsumerConfiguration& conf) {
39-
Consumer consumer;
40-
41-
waitForAsyncValue(std::function<void(SubscribeCallback)>([&](SubscribeCallback callback) {
42-
client.subscribeAsync(topic, subscriptionName, conf, callback);
43-
}),
44-
consumer);
45-
46-
return consumer;
33+
return waitForAsyncValue<Consumer>(
34+
[&](SubscribeCallback callback) { client.subscribeAsync(topic, subscriptionName, conf, callback); });
4735
}
4836

4937
Consumer Client_subscribe_topics(Client& client, const std::vector<std::string>& topics,
5038
const std::string& subscriptionName, const ConsumerConfiguration& conf) {
51-
Consumer consumer;
52-
53-
waitForAsyncValue(std::function<void(SubscribeCallback)>([&](SubscribeCallback callback) {
54-
client.subscribeAsync(topics, subscriptionName, conf, callback);
55-
}),
56-
consumer);
57-
58-
return consumer;
39+
return waitForAsyncValue<Consumer>(
40+
[&](SubscribeCallback callback) { client.subscribeAsync(topics, subscriptionName, conf, callback); });
5941
}
6042

6143
Consumer Client_subscribe_pattern(Client& client, const std::string& topic_pattern,
6244
const std::string& subscriptionName, const ConsumerConfiguration& conf) {
63-
Consumer consumer;
64-
65-
waitForAsyncValue(std::function<void(SubscribeCallback)>([&](SubscribeCallback callback) {
66-
client.subscribeWithRegexAsync(topic_pattern, subscriptionName, conf, callback);
67-
}),
68-
consumer);
69-
70-
return consumer;
45+
return waitForAsyncValue<Consumer>([&](SubscribeCallback callback) {
46+
client.subscribeWithRegexAsync(topic_pattern, subscriptionName, conf, callback);
47+
});
7148
}
7249

7350
Reader Client_createReader(Client& client, const std::string& topic, const MessageId& startMessageId,
7451
const ReaderConfiguration& conf) {
75-
Reader reader;
76-
77-
waitForAsyncValue(std::function<void(ReaderCallback)>([&](ReaderCallback callback) {
78-
client.createReaderAsync(topic, startMessageId, conf, callback);
79-
}),
80-
reader);
81-
82-
return reader;
52+
return waitForAsyncValue<Reader>(
53+
[&](ReaderCallback callback) { client.createReaderAsync(topic, startMessageId, conf, callback); });
8354
}
8455

8556
std::vector<std::string> Client_getTopicPartitions(Client& client, const std::string& topic) {
86-
std::vector<std::string> partitions;
87-
88-
waitForAsyncValue(std::function<void(GetPartitionsCallback)>([&](GetPartitionsCallback callback) {
89-
client.getPartitionsForTopicAsync(topic, callback);
90-
}),
91-
partitions);
92-
93-
return partitions;
57+
return waitForAsyncValue<std::vector<std::string>>(
58+
[&](GetPartitionsCallback callback) { client.getPartitionsForTopicAsync(topic, callback); });
9459
}
9560

9661
void Client_close(Client& client) {

src/consumer.cc

+6-17
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,7 @@ void Consumer_unsubscribe(Consumer& consumer) {
2929
}
3030

3131
Message Consumer_receive(Consumer& consumer) {
32-
Message msg;
33-
34-
waitForAsyncValue(std::function<void(ReceiveCallback)>(
35-
[&consumer](ReceiveCallback callback) { consumer.receiveAsync(callback); }),
36-
msg);
37-
38-
return msg;
32+
return waitForAsyncValue<Message>([&](ReceiveCallback callback) { consumer.receiveAsync(callback); });
3933
}
4034

4135
Message Consumer_receive_timeout(Consumer& consumer, int timeoutMs) {
@@ -59,32 +53,27 @@ Messages Consumer_batch_receive(Consumer& consumer) {
5953
void Consumer_acknowledge(Consumer& consumer, const Message& msg) { consumer.acknowledgeAsync(msg, nullptr); }
6054

6155
void Consumer_acknowledge_message_id(Consumer& consumer, const MessageId& msgId) {
62-
Py_BEGIN_ALLOW_THREADS
63-
consumer.acknowledgeAsync(msgId, nullptr);
56+
Py_BEGIN_ALLOW_THREADS consumer.acknowledgeAsync(msgId, nullptr);
6457
Py_END_ALLOW_THREADS
6558
}
6659

6760
void Consumer_negative_acknowledge(Consumer& consumer, const Message& msg) {
68-
Py_BEGIN_ALLOW_THREADS
69-
consumer.negativeAcknowledge(msg);
61+
Py_BEGIN_ALLOW_THREADS consumer.negativeAcknowledge(msg);
7062
Py_END_ALLOW_THREADS
7163
}
7264

7365
void Consumer_negative_acknowledge_message_id(Consumer& consumer, const MessageId& msgId) {
74-
Py_BEGIN_ALLOW_THREADS
75-
consumer.negativeAcknowledge(msgId);
66+
Py_BEGIN_ALLOW_THREADS consumer.negativeAcknowledge(msgId);
7667
Py_END_ALLOW_THREADS
7768
}
7869

7970
void Consumer_acknowledge_cumulative(Consumer& consumer, const Message& msg) {
80-
Py_BEGIN_ALLOW_THREADS
81-
consumer.acknowledgeCumulativeAsync(msg, nullptr);
71+
Py_BEGIN_ALLOW_THREADS consumer.acknowledgeCumulativeAsync(msg, nullptr);
8272
Py_END_ALLOW_THREADS
8373
}
8474

8575
void Consumer_acknowledge_cumulative_message_id(Consumer& consumer, const MessageId& msgId) {
86-
Py_BEGIN_ALLOW_THREADS
87-
consumer.acknowledgeCumulativeAsync(msgId, nullptr);
76+
Py_BEGIN_ALLOW_THREADS consumer.acknowledgeCumulativeAsync(msgId, nullptr);
8877
Py_END_ALLOW_THREADS
8978
}
9079

src/future.h

-181
This file was deleted.

src/producer.cc

+4-10
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,15 @@
2525
namespace py = pybind11;
2626

2727
MessageId Producer_send(Producer& producer, const Message& message) {
28-
MessageId messageId;
29-
30-
waitForAsyncValue(std::function<void(SendCallback)>(
31-
[&](SendCallback callback) { producer.sendAsync(message, callback); }),
32-
messageId);
33-
34-
return messageId;
28+
return waitForAsyncValue<MessageId>(
29+
[&](SendCallback callback) { producer.sendAsync(message, callback); });
3530
}
3631

3732
void Producer_sendAsync(Producer& producer, const Message& msg, SendCallback callback) {
38-
Py_BEGIN_ALLOW_THREADS
39-
producer.sendAsync(msg, callback);
33+
Py_BEGIN_ALLOW_THREADS producer.sendAsync(msg, callback);
4034
Py_END_ALLOW_THREADS
4135

42-
if (PyErr_CheckSignals() == -1) {
36+
if (PyErr_CheckSignals() == -1) {
4337
PyErr_SetInterrupt();
4438
}
4539
}

src/reader.cc

+2-8
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,8 @@ Message Reader_readNextTimeout(Reader& reader, int timeoutMs) {
6262
}
6363

6464
bool Reader_hasMessageAvailable(Reader& reader) {
65-
bool available = false;
66-
67-
waitForAsyncValue(
68-
std::function<void(HasMessageAvailableCallback)>(
69-
[&](HasMessageAvailableCallback callback) { reader.hasMessageAvailableAsync(callback); }),
70-
available);
71-
72-
return available;
65+
return waitForAsyncValue<bool>(
66+
[&](HasMessageAvailableCallback callback) { reader.hasMessageAvailableAsync(callback); });
7367
}
7468

7569
void Reader_close(Reader& reader) {

0 commit comments

Comments
 (0)