Merge pull request #9424 from SeppoTakalo/ONME-4125

Allows multiple network status listeners
pull/9679/head
Martin Kojtal 2019-02-11 10:41:35 +01:00 committed by GitHub
commit 10bb66a053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 232 additions and 15 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
@ -35,4 +36,5 @@ set(unittest-test-sources
stubs/NetworkStack_stub.cpp
stubs/NetworkInterfaceDefaults_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,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);
}

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
@ -27,4 +28,5 @@ set(unittest-test-sources
features/netsocket/NetworkInterface/test_NetworkInterface.cpp
stubs/NetworkInterfaceDefaults_stub.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

@ -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;
*
@ -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))
@ -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.
*
@ -500,8 +525,8 @@ 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.
*
@ -509,7 +534,8 @@ typedef struct ns_list_link {
* 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.
*
@ -517,8 +543,8 @@ typedef struct ns_list_link {
* 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
*

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
@ -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

View File

@ -43,7 +43,7 @@ class EMACInterface;
class NetworkInterface: public DNS {
public:
virtual ~NetworkInterface() {};
virtual ~NetworkInterface();
/** Return the default network interface.
*
@ -251,12 +251,38 @@ 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);
/** 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).

View File

@ -94,6 +94,7 @@ public:
*/
Callback(const Callback<R()> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}
@ -718,6 +719,7 @@ public:
*/
Callback(const Callback<R(A0)> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}
@ -1343,6 +1345,7 @@ public:
*/
Callback(const Callback<R(A0, A1)> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}
@ -1969,6 +1972,7 @@ public:
*/
Callback(const Callback<R(A0, A1, A2)> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}
@ -2596,6 +2600,7 @@ public:
*/
Callback(const Callback<R(A0, A1, A2, A3)> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}
@ -3224,6 +3229,7 @@ public:
*/
Callback(const Callback<R(A0, A1, A2, A3, A4)> &func)
{
memset(this, 0, sizeof(Callback));
if (func._ops) {
func._ops->move(this, &func);
}

View File

@ -89,10 +89,11 @@ multicast
multicasts
singleshot
multishot
_doxy_
sa
tparam
retarget
TCPSocket
UDPSocket
Socket
Socket
unregister
_doxy_