Skip to content

Allows multiple network status listeners #9424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,4 +35,5 @@ set(unittest-test-sources
stubs/ip4tos_stub.c
stubs/NetworkStack_stub.cpp
stubs/SocketStats_Stub.cpp
stubs/mbed_error.c
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "NetworkStack_stub.h"

class stubNetworkInterface : public NetworkInterface {
public:
virtual nsapi_error_t connect()
{
return NSAPI_ERROR_OK;
Expand All @@ -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()
{
Expand Down Expand Up @@ -131,4 +141,79 @@ 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)
{
// Add two callback and check that both are called
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);

// Remove one of the callbacks
iface->remove_event_listener(my_iface_callback2);
callback_is_called = false;
second_callback_called = false;

// expect only the one is called which remains in the list
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, true);
EXPECT_EQ(second_callback_called, false);

// Remove also the last callback, and expect nothing is called
iface->remove_event_listener(my_iface_callback);
callback_is_called = false;
second_callback_called = false;
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
EXPECT_EQ(callback_is_called, false);
EXPECT_EQ(second_callback_called, false);
}

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);
}
4 changes: 3 additions & 1 deletion UNITTESTS/features/netsocket/NetworkInterface/unittest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
)
3 changes: 3 additions & 0 deletions UNITTESTS/stubs/NetworkInterface_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ nsapi_error_t NetworkInterface::gethostbyname_async_cancel(int id)
return NSAPI_ERROR_UNSUPPORTED;
}

NetworkInterface::~NetworkInterface()
{
}
5 changes: 5 additions & 0 deletions UNITTESTS/stubs/mbed_error.c
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ typedef struct ns_list {
* always assign returned entry pointers to a properly typed pointer variable.
* This assignment will be then type-checked where the compiler supports it, and
* will dereference correctly on compilers that don't support this extension.
*
* If you need to support C++03 compilers that cannot return properly-typed
* pointers, such as IAR 7, you need to use NS_LIST_TYPECOERCE to force the type.
* ~~~
* NS_LIST_HEAD(example_entry_t, link) my_list;
*
Expand Down Expand Up @@ -199,6 +202,27 @@ union \
#define NS_LIST_TYPECAST_(list, val) (0 ? (list)->type : (val))
#endif

/** \brief Macro to force correct type if necessary.
*
* In C, doesn't matter if NS_LIST_TYPECAST_ works or not, as it's legal
* to assign void * to a pointer. In C++, we can't do that, so need
* a back-up plan for C++03. This forces the type, so breaks type-safety -
* only activate when needed, meaning we still get typechecks on other
* toolchains.
*
* If a straight assignment of a ns_list function to a pointer fails
* on a C++03 compiler, use the following construct. This will not be
* required with C++11 compilers.
* ~~~
* type *elem = NS_LIST_TYPECOERCE(type *, ns_list_get_first(list));
* ~~~
*/
#if defined(NS_LIST_PTR_TYPE_) || !defined(__cplusplus)
#define NS_LIST_TYPECOERCE(type, val) (val)
#else
#define NS_LIST_TYPECOERCE(type, val) (type) (val)
#endif

/** \brief Internal macro to check types of input entry pointer. */
#define NS_LIST_TYPECHECK_(list, entry) \
(NS_PTR_MATCH_((list)->type, (entry), "incorrect entry type for list"), (entry))
Expand Down Expand Up @@ -480,7 +504,8 @@ typedef struct ns_list_link {
* \param list `(const list_t *)` Pointer to list - evaluated multiple times.
*/
#define ns_list_foreach(type, e, list) \
for (type *e = ns_list_get_first(list); e; e = ns_list_get_next(list, e))
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_first(list)); \
e; e = NS_LIST_TYPECOERCE(type *, ns_list_get_next(list, e)))

/** \brief Iterate forwards over a list, where user may delete.
*
Expand All @@ -500,25 +525,26 @@ typedef struct ns_list_link {
* \param list `(list_t *)` Pointer to list - evaluated multiple times.
*/
#define ns_list_foreach_safe(type, e, list) \
for (type *e = ns_list_get_first(list), *_next##e; \
e && (_next##e = ns_list_get_next(list, e), true); e = _next##e)
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_first(list)), *_next##e; \
e && (_next##e = NS_LIST_TYPECOERCE(type *, ns_list_get_next(list, e)), true); e = _next##e)

/** \brief Iterate backwards over a list.
*
* As ns_list_foreach(), but going backwards - see its documentation.
* Iterating forwards is *slightly* more efficient.
*/
#define ns_list_foreach_reverse(type, e, list) \
for (type *e = ns_list_get_last(list); e; e = ns_list_get_previous(list, e))
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_last(list)); \
e; e = NS_LIST_TYPECOERCE(type *, ns_list_get_previous(list, e)))

/** \brief Iterate backwards over a list, where user may delete.
*
* As ns_list_foreach_safe(), but going backwards - see its documentation.
* Iterating forwards is *slightly* more efficient.
*/
#define ns_list_foreach_reverse_safe(type, e, list) \
for (type *e = ns_list_get_last(list), *_next##e; \
e && (_next##e = ns_list_get_previous(list, e), true); e = _next##e)
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_last(list)), *_next##e; \
e && (_next##e = NS_LIST_TYPECOERCE(type *, ns_list_get_previous(list, e)), true); e = _next##e)

/** \hideinitializer \brief Count entries on a list
*
Expand Down
61 changes: 61 additions & 0 deletions features/netsocket/NetworkInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -77,6 +80,64 @@ nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)

void NetworkInterface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
{
// Dummy, that needs to be overwritten when inherited, but cannot be removed
// because suplied previously and can be referred from binaries.
}

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 iface_eventlist_entry_t;
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
Expand Down
30 changes: 28 additions & 2 deletions features/netsocket/NetworkInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class EMACInterface;
class NetworkInterface: public DNS {
public:

virtual ~NetworkInterface() {};
virtual ~NetworkInterface();

/** Return the default network interface.
*
Expand Down Expand Up @@ -241,12 +241,38 @@ class NetworkInterface: public DNS {
*
* 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);

/** 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.
*
* @return The connection status (@see nsapi_types.h).
Expand Down
Loading