mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #9424 from SeppoTakalo/ONME-4125
Allows multiple network status listenerspull/9679/head
commit
10bb66a053
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -87,3 +87,6 @@ nsapi_error_t NetworkInterface::gethostbyname_async_cancel(int id)
|
|||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
NetworkInterface::~NetworkInterface()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -89,10 +89,11 @@ multicast
|
|||
multicasts
|
||||
singleshot
|
||||
multishot
|
||||
_doxy_
|
||||
sa
|
||||
tparam
|
||||
retarget
|
||||
TCPSocket
|
||||
UDPSocket
|
||||
Socket
|
||||
Socket
|
||||
unregister
|
||||
_doxy_
|
Loading…
Reference in New Issue