Allow multiple network status listeners

Allow more than one callback to be register to NetworkInterfaces.
This introduces new APIs:
void NetworkInterface::add_event_listener(...);
void NetworkInterface::remove_event_listener(...);

Which internally calls interfaces attach() functions.
pull/9424/head
Seppo Takalo 2019-01-17 17:55:28 +02:00
parent a4ed473afc
commit 9c98d1572b
7 changed files with 182 additions and 9 deletions

View File

@ -17,7 +17,8 @@ set(unittest-sources
../features/frameworks/nanostack-libservice/source/libip6string/ip6tos.c
../features/frameworks/nanostack-libservice/source/libip4string/stoip4.c
../features/frameworks/nanostack-libservice/source/libip6string/stoip6.c
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
../features/frameworks/nanostack-libservice/source/libList/ns_list.c
)
# Test files
@ -34,4 +35,5 @@ set(unittest-test-sources
stubs/ip4tos_stub.c
stubs/NetworkStack_stub.cpp
stubs/SocketStats_Stub.cpp
stubs/mbed_error.c
)

View File

@ -20,6 +20,7 @@
#include "NetworkStack_stub.h"
class stubNetworkInterface : public NetworkInterface {
public:
virtual nsapi_error_t connect()
{
return NSAPI_ERROR_OK;
@ -32,13 +33,22 @@ class stubNetworkInterface : public NetworkInterface {
{
return &stack;
};
public:
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> cb)
{
status_cb = cb;
}
void event(nsapi_event_t e, intptr_t i)
{
status_cb(e, i);
}
private:
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb;
NetworkStackstub stack;
};
class TestNetworkInterface : public testing::Test {
protected:
NetworkInterface *iface;
stubNetworkInterface *iface;
virtual void SetUp()
{
@ -131,4 +141,70 @@ TEST_F(TestNetworkInterface, set_blocking)
EXPECT_EQ(iface->set_blocking(true), NSAPI_ERROR_UNSUPPORTED);
}
// No way to test attach as it doesn't do or return anything.
void my_iface_callback(nsapi_event_t e, intptr_t i)
{
(void)e;
(void)i;
callback_is_called = true;
}
static bool second_callback_called;
void my_iface_callback2(nsapi_event_t e, intptr_t i)
{
(void)e;
(void)i;
second_callback_called = true;
}
TEST_F(TestNetworkInterface, add_event_listener)
{
callback_is_called = false;
second_callback_called = false;
iface->add_event_listener(my_iface_callback);
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, true);
iface->remove_event_listener(my_iface_callback);
}
TEST_F(TestNetworkInterface, remove_event_listener)
{
callback_is_called = false;
second_callback_called = false;
iface->add_event_listener(my_iface_callback);
iface->add_event_listener(my_iface_callback2);
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, true);
EXPECT_EQ(second_callback_called, true);
iface->remove_event_listener(my_iface_callback2);
callback_is_called = false;
second_callback_called = false;
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, true);
EXPECT_EQ(second_callback_called, false);
iface->remove_event_listener(my_iface_callback);
}
TEST_F(TestNetworkInterface, correct_event_listener_per_interface)
{
stubNetworkInterface *iface2 = new stubNetworkInterface();
iface->add_event_listener(my_iface_callback);
iface2->add_event_listener(my_iface_callback2);
callback_is_called = false;
second_callback_called = false;
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, true);
EXPECT_EQ(second_callback_called, false);
callback_is_called = false;
second_callback_called = false;
iface2->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, false);
EXPECT_EQ(second_callback_called, true);
iface->remove_event_listener(my_iface_callback);
iface2->remove_event_listener(my_iface_callback2);
}

View File

@ -12,7 +12,8 @@ set(unittest-sources
../features/frameworks/nanostack-libservice/source/libip6string/ip6tos.c
../features/frameworks/nanostack-libservice/source/libip4string/stoip4.c
../features/frameworks/nanostack-libservice/source/libip6string/stoip6.c
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
../features/frameworks/nanostack-libservice/source/libList/ns_list.c
)
# Test files
@ -26,4 +27,5 @@ set(unittest-test-sources
stubs/EventFlags_stub.cpp
features/netsocket/NetworkInterface/test_NetworkInterface.cpp
stubs/SocketStats_Stub.cpp
stubs/mbed_error.c
)

View File

@ -87,3 +87,6 @@ nsapi_error_t NetworkInterface::gethostbyname_async_cancel(int id)
return NSAPI_ERROR_UNSUPPORTED;
}
NetworkInterface::~NetworkInterface()
{
}

View File

@ -0,0 +1,5 @@
int mbed_error(int error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number)
{
return 0;
}

View File

@ -16,7 +16,10 @@
#include "netsocket/NetworkInterface.h"
#include "netsocket/NetworkStack.h"
#include "platform/Callback.h"
#include "platform/mbed_error.h"
#include <string.h>
#include "ns_list.h"
// Default network-interface state
@ -75,8 +78,64 @@ nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)
return get_stack()->add_dns_server(address);
}
void NetworkInterface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
typedef struct iface_eventlist_entry {
NetworkInterface *iface;
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb;
ns_list_link_t link;
} iface_eventlist_entry_t;
typedef NS_LIST_HEAD(iface_eventlist_entry_t, link) iface_eventlist_t;
static iface_eventlist_t *get_interface_event_list_head()
{
static iface_eventlist_t NS_LIST_NAME_INIT(event_list);
return &event_list;
}
static void call_all_event_listeners(NetworkInterface *iface, nsapi_event_t event, intptr_t val)
{
iface_eventlist_t *event_list = get_interface_event_list_head();
ns_list_foreach(iface_eventlist_entry_t, entry, event_list) {
if (entry->iface == iface) {
entry->status_cb(event, val);
}
}
}
void NetworkInterface::add_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
{
iface_eventlist_t *event_list = get_interface_event_list_head();
iface_eventlist_entry_t *entry = new (std::nothrow) iface_eventlist_entry_t;
if (!entry) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_NETWORK_STACK, MBED_ERROR_CODE_ENOMEM), "Failed to allocate entry");
return;
}
entry->iface = this;
entry->status_cb = status_cb;
ns_list_add_to_end(event_list, entry);
attach(mbed::callback(&call_all_event_listeners, this));
}
void NetworkInterface::remove_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
{
iface_eventlist_t *event_list = get_interface_event_list_head();
ns_list_foreach_safe(iface_eventlist_entry_t, entry, event_list) {
if (entry->status_cb == status_cb && entry->iface == this) {
ns_list_remove(event_list, entry);
delete entry;
return;
}
}
}
NetworkInterface::~NetworkInterface()
{
iface_eventlist_t *event_list = get_interface_event_list_head();
ns_list_foreach_safe(iface_eventlist_entry_t, entry, event_list) {
if (entry->iface == this) {
ns_list_remove(event_list, entry);
}
}
}
nsapi_connection_status_t NetworkInterface::get_connection_status() const

View File

@ -43,7 +43,7 @@ class EMACInterface;
class NetworkInterface: public DNS {
public:
virtual ~NetworkInterface() {};
virtual ~NetworkInterface();
/** Return the default network interface.
*
@ -241,11 +241,37 @@ public:
*
* The specified status callback function will be called on status changes
* on the network. The parameters on the callback are the event type and
* event-type dependent reason parameter.
* event-type dependent reason parameter. Only one callback can be registered at a time.
*
* To unregister a callback call with status_cb parameter as a zero.
*
* *NOTE:* Any callbacks registered with this function will be overwritten if
* add_event_listener() API is used.
*
* @param status_cb The callback for status changes.
*/
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb) = 0;
/** Add event listener for interface.
*
* This API allows multiple callback to be registered for a single interface.
* When first called, internal list of event handlers are created and registered to
* interface through attach() API.
*
* Application may only use attach() or add_event_listener() interface. Mixing usage
* of both leads to undefined behavior.
*
* @param status_cb The callback for status changes.
*/
void add_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
/** Remove event listener from interface.
*
* Remove previously added callback from the handler list.
*
* @param status_cb The callback to unregister.
*/
void remove_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
/** Get the connection status.
*