Skip to content

Commit 5bced2a

Browse files
committed
Reduce errors binding socket and improve feedback for Event Broadcaster
1 parent fc01f00 commit 5bced2a

4 files changed

Lines changed: 120 additions & 17 deletions

File tree

Source/Plugins/EventBroadcaster/EventBroadcaster.cpp

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,29 @@ std::shared_ptr<void> EventBroadcaster::getZMQContext() {
2222
return ctx;
2323
}
2424

25+
int EventBroadcaster::unbindZMQSocket()
26+
{
27+
#ifdef ZEROMQ
28+
void* socket = zmqSocket.get();
29+
if (socket != nullptr && listeningPort != 0)
30+
{
31+
return zmq_unbind(socket, getEndpoint(listeningPort).toRawUTF8());
32+
}
33+
#endif
34+
return 0;
35+
}
36+
37+
int EventBroadcaster::rebindZMQSocket()
38+
{
39+
#ifdef ZEROMQ
40+
void* socket = zmqSocket.get();
41+
if (socket != nullptr && listeningPort != 0)
42+
{
43+
return zmq_bind(socket, getEndpoint(listeningPort).toRawUTF8());
44+
}
45+
#endif
46+
return 0;
47+
}
2548

2649
void EventBroadcaster::closeZMQSocket(void* socket)
2750
{
@@ -30,16 +53,35 @@ void EventBroadcaster::closeZMQSocket(void* socket)
3053
#endif
3154
}
3255

56+
String EventBroadcaster::getEndpoint(int port)
57+
{
58+
return String("tcp://*:") + String(port);
59+
}
60+
61+
void EventBroadcaster::reportActualListeningPort(int port)
62+
{
63+
listeningPort = port;
64+
auto editor = static_cast<EventBroadcasterEditor*>(getEditor());
65+
if (editor)
66+
{
67+
editor->setDisplayedPort(port);
68+
}
69+
}
3370

3471
EventBroadcaster::EventBroadcaster()
3572
: GenericProcessor ("Event Broadcaster")
3673
, zmqContext (getZMQContext())
37-
, zmqSocket (nullptr, &closeZMQSocket)
74+
, zmqSocket (nullptr)
3875
, listeningPort (0)
3976
{
4077
setProcessorType (PROCESSOR_TYPE_SINK);
4178

42-
setListeningPort(5557);
79+
int portToTry = 5557;
80+
while (setListeningPort(portToTry) == EADDRINUSE)
81+
{
82+
// try the next port, looking for one not in use
83+
portToTry++;
84+
}
4385
}
4486

4587

@@ -56,27 +98,53 @@ int EventBroadcaster::getListeningPort() const
5698
}
5799

58100

59-
void EventBroadcaster::setListeningPort(int port, bool forceRestart)
101+
int EventBroadcaster::setListeningPort(int port, bool forceRestart)
60102
{
61103
if ((listeningPort != port) || forceRestart)
62104
{
63105
#ifdef ZEROMQ
64-
zmqSocket.reset(zmq_socket(zmqContext.get(), ZMQ_PUB));
65-
if (!zmqSocket)
106+
// unbind current socket (if any) to free up port
107+
unbindZMQSocket();
108+
zmqSocketPtr newSocket(zmq_socket(zmqContext.get(), ZMQ_PUB));
109+
auto editor = static_cast<EventBroadcasterEditor*>(getEditor());
110+
int status = 0;
111+
112+
if (!newSocket.get())
66113
{
67-
std::cout << "Failed to create socket: " << zmq_strerror(zmq_errno()) << std::endl;
68-
return;
114+
status = zmq_errno();
115+
std::cout << "Failed to create socket: " << zmq_strerror(status) << std::endl;
116+
}
117+
else
118+
{
119+
if (0 != zmq_bind(newSocket.get(), getEndpoint(port).toRawUTF8()))
120+
{
121+
status = zmq_errno();
122+
std::cout << "Failed to open socket: " << zmq_strerror(status) << std::endl;
123+
}
124+
else
125+
{
126+
// success
127+
zmqSocket.swap(newSocket);
128+
reportActualListeningPort(port);
129+
return status;
130+
}
69131
}
70132

71-
String url = String("tcp://*:") + String(port);
72-
if (0 != zmq_bind(zmqSocket.get(), url.toRawUTF8()))
133+
// failure, try to rebind current socket to previous port
134+
if (0 == rebindZMQSocket())
73135
{
74-
std::cout << "Failed to open socket: " << zmq_strerror(zmq_errno()) << std::endl;
75-
return;
136+
reportActualListeningPort(listeningPort);
76137
}
77-
#endif
138+
else
139+
{
140+
reportActualListeningPort(0);
141+
}
142+
return status;
78143

79-
listeningPort = port;
144+
#else
145+
reportActualListeningPort(port);
146+
return 0;
147+
#endif
80148
}
81149
}
82150

Source/Plugins/EventBroadcaster/EventBroadcaster.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class EventBroadcaster : public GenericProcessor
3333
AudioProcessorEditor* createEditor() override;
3434

3535
int getListeningPort() const;
36-
void setListeningPort (int port, bool forceRestart = false);
36+
// returns 0 on success, else the errno value for the error that occurred.
37+
int setListeningPort (int port, bool forceRestart = false);
3738

3839
void process (AudioSampleBuffer& continuousBuffer) override;
3940
void handleEvent (const EventChannel* channelInfo, const MidiMessage& event, int samplePosition = 0) override;
@@ -46,10 +47,23 @@ class EventBroadcaster : public GenericProcessor
4647
private:
4748
void sendEvent(const MidiMessage& event, float eventSampleRate) const;
4849
static std::shared_ptr<void> getZMQContext();
50+
int unbindZMQSocket();
51+
int rebindZMQSocket();
4952
static void closeZMQSocket (void* socket);
53+
static String getEndpoint(int port);
54+
// called from getListeningPort() depending on success/failure of ZMQ operations
55+
void reportActualListeningPort(int port);
56+
57+
// encapuslate closing sockets when their pointers go out of scope
58+
struct zmqSocketPtr : public std::unique_ptr<void, decltype(&closeZMQSocket)>
59+
{
60+
zmqSocketPtr(void* ptr)
61+
: std::unique_ptr<void, decltype(&closeZMQSocket)>(ptr, &closeZMQSocket)
62+
{}
63+
};
5064

5165
const std::shared_ptr<void> zmqContext;
52-
std::unique_ptr<void, decltype (&closeZMQSocket)> zmqSocket;
66+
zmqSocketPtr zmqSocket;
5367
int listeningPort;
5468

5569
};

Source/Plugins/EventBroadcaster/EventBroadcasterEditor.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ void EventBroadcasterEditor::buttonEvent(Button* button)
4646
if (button == restartConnection)
4747
{
4848
EventBroadcaster* p = (EventBroadcaster*)getProcessor();
49-
p->setListeningPort(p->getListeningPort(), true);
49+
int status = p->setListeningPort(p->getListeningPort(), true);
50+
51+
#ifdef ZEROMQ
52+
if (status != 0)
53+
{
54+
CoreServices::sendStatusMessage(String("Restart failed: ") + zmq_strerror(status));
55+
}
56+
#endif
5057
}
5158
}
5259

@@ -58,6 +65,18 @@ void EventBroadcasterEditor::labelTextChanged(juce::Label* label)
5865
Value val = label->getTextValue();
5966

6067
EventBroadcaster* p = (EventBroadcaster*)getProcessor();
61-
p->setListeningPort(val.getValue());
68+
int status = p->setListeningPort(val.getValue());
69+
70+
#ifdef ZEROMQ
71+
if (status != 0)
72+
{
73+
CoreServices::sendStatusMessage(String("Port change failed: ") + zmq_strerror(status));
74+
}
75+
#endif
6276
}
6377
}
78+
79+
void EventBroadcasterEditor::setDisplayedPort(int port)
80+
{
81+
portLabel->setText(String(port), dontSendNotification);
82+
}

Source/Plugins/EventBroadcaster/EventBroadcasterEditor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class EventBroadcasterEditor : public GenericEditor, public Label::Listener
3030
void buttonEvent(Button* button) override;
3131
void labelTextChanged(juce::Label* label) override;
3232

33+
void setDisplayedPort(int port);
34+
3335
private:
3436
ScopedPointer<UtilityButton> restartConnection;
3537
ScopedPointer<Label> urlLabel;

0 commit comments

Comments
 (0)