Skip to content

Commit b4f2def

Browse files
authored
Merge pull request #60 from robohouse-delft/interrupt-subscription
Add thread-safe way to interrupt waitForSubscriptionEvent().
2 parents 2abd561 + dbfce71 commit b4f2def

File tree

6 files changed

+102
-4
lines changed

6 files changed

+102
-4
lines changed

include/abb_librws/rws_client.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,21 @@ class RWSClient : public POCOClient
574574
* \return RWSResult containing the result.
575575
*/
576576
RWSResult endSubscription();
577+
578+
/**
579+
* \brief Force close the active subscription connection.
580+
*
581+
* This will cause waitForSubscriptionEvent() to return or throw.
582+
* It does not delete the subscription from the controller.
583+
*
584+
* The preferred way to close the subscription is to request the robot controller to end it via
585+
* endSubscription(). This function can be used to force the connection to close immediately in
586+
* case the robot controller is not responding.
587+
*
588+
* This function blocks until an active waitForSubscriptionEvent() has finished.
589+
*
590+
*/
591+
void forceCloseSubscription();
577592

578593
/**
579594
* \brief A method for logging out the currently active RWS session.

include/abb_librws/rws_interface.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,20 @@ class RWSInterface
512512
*/
513513
bool endSubscription();
514514

515+
/**
516+
* \brief Froce close the active subscription connection.
517+
*
518+
* This will cause waitForSubscriptionEvent() to return or throw.
519+
* It does not delete the subscription from the controller.
520+
*
521+
* The preferred way to close the subscription is to request the robot controller to end it via
522+
* endSubscription(). This function can be used to force the connection to close immediately in
523+
* case the robot controller is not responding.
524+
*
525+
* This function blocks until an active waitForSubscriptionEvent() has finished.
526+
*/
527+
void forceCloseSubscription();
528+
515529
/**
516530
* \brief A method for registering a user as local.
517531
*

include/abb_librws/rws_poco_client.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,19 @@ class POCOClient
345345
*/
346346
POCOResult webSocketRecieveFrame();
347347

348+
/**
349+
* \brief Forcibly shut down the websocket connection.
350+
*
351+
* The connection is shut down immediately.
352+
* Subsequently, the function will block until a current call to webSocketRecieveFrame() has finished,
353+
* before cleaning up the local state.
354+
*
355+
* Note that since mutexes do not guarantee the order of acquisition for multiple contenders,
356+
* it is undefined how many calls to webSocketRecieveFrame() will still attempt to use the shut down
357+
* connection before the local state is cleaned. Those invocation will throw a runtime error.
358+
*/
359+
void webSocketShutdown();
360+
348361
/**
349362
* \brief A method for retrieving a substring in a string.
350363
*
@@ -420,10 +433,28 @@ class POCOClient
420433
*/
421434
Poco::Mutex http_mutex_;
422435

436+
/**
437+
* \brief A mutex for protecting the client's WebSocket pointer.
438+
*
439+
* This mutex must be held while setting or invalidating the p_websocket_ member.
440+
* Note that the websocket_use_mutex_ must also be held while invalidating the pointer,
441+
* since someone may be using it otherwise.
442+
*
443+
* If acquiring both websocket_connect_mutex_ and websocket_use_mutex_,
444+
* the connect mutex must be acquired first.
445+
*/
446+
Poco::Mutex websocket_connect_mutex_;
447+
423448
/**
424449
* \brief A mutex for protecting the client's WebSocket resources.
450+
*
451+
* This mutex must be held while using the p_websocket_ member,
452+
* and while invalidating the pointer.
453+
*
454+
* If acquiring both websocket_connect_mutex_ and websocket_use_mutex_,
455+
* the connect mutex must be acquired first.
425456
*/
426-
Poco::Mutex websocket_mutex_;
457+
Poco::Mutex websocket_use_mutex_;
427458

428459
/**
429460
* \brief HTTP credentials for the remote server's access authentication process.

src/rws_client.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ RWSClient::RWSResult RWSClient::endSubscription()
490490
return result;
491491
}
492492

493+
void RWSClient::forceCloseSubscription()
494+
{
495+
webSocketShutdown();
496+
}
497+
493498
RWSClient::RWSResult RWSClient::logout()
494499
{
495500
uri_ = Resources::LOGOUT;

src/rws_interface.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,11 @@ bool RWSInterface::endSubscription()
374374
return rws_client_.endSubscription().success;
375375
}
376376

377+
void RWSInterface::forceCloseSubscription()
378+
{
379+
rws_client_.webSocketShutdown();
380+
}
381+
377382
bool RWSInterface::registerLocalUser(std::string username,
378383
std::string application,
379384
std::string location)

src/rws_poco_client.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,16 @@ POCOClient::POCOResult POCOClient::webSocketConnect(const std::string uri,
334334
try
335335
{
336336
result.addHTTPRequestInfo(request);
337-
p_websocket_ = new WebSocket(http_client_session_, request, response);
338-
p_websocket_->setReceiveTimeout(Poco::Timespan(timeout));
337+
{
338+
// We must have at least websocket_connect_mutext_.
339+
// If a connection already exists, we must also have websocket_use_mutex_.
340+
// If not, nobody should have the mutex anyway, so we should get it immediately.
341+
ScopedLock<Mutex> connect_lock(websocket_connect_mutex_);
342+
ScopedLock<Mutex> use_lock(websocket_use_mutex_);
343+
p_websocket_ = new WebSocket(http_client_session_, request, response);
344+
345+
p_websocket_->setReceiveTimeout(Poco::Timespan(timeout));
346+
}
339347

340348
result.addHTTPResponseInfo(response);
341349
result.status = POCOResult::OK;
@@ -372,7 +380,7 @@ POCOClient::POCOResult POCOClient::webSocketConnect(const std::string uri,
372380
POCOClient::POCOResult POCOClient::webSocketRecieveFrame()
373381
{
374382
// Lock the object's mutex. It is released when the method goes out of scope.
375-
ScopedLock<Mutex> lock(websocket_mutex_);
383+
ScopedLock<Mutex> lock(websocket_use_mutex_);
376384

377385
// Result of the communication.
378386
POCOResult result;
@@ -451,6 +459,26 @@ POCOClient::POCOResult POCOClient::webSocketRecieveFrame()
451459
return result;
452460
}
453461

462+
void POCOClient::webSocketShutdown()
463+
{
464+
// Make sure nobody is connecting while we're closing.
465+
ScopedLock<Mutex> connect_lock(websocket_connect_mutex_);
466+
467+
// Make sure there is actually a connection to close.
468+
if (!webSocketExist())
469+
{
470+
return;
471+
}
472+
473+
// Shut down the socket. This should make webSocketReceiveFrame() return as soon as possible.
474+
p_websocket_->shutdown();
475+
476+
// Also acquire the websocket lock before invalidating the pointer,
477+
// or we will break running calls to webSocketRecieveFrame().
478+
ScopedLock<Mutex> use_lock(websocket_use_mutex_);
479+
p_websocket_ = Poco::SharedPtr<Poco::Net::WebSocket>();
480+
}
481+
454482
/************************************************************
455483
* Auxiliary methods
456484
*/

0 commit comments

Comments
 (0)