Skip to content

Commit 10bb66a

Browse files
authored
Merge pull request #9424 from SeppoTakalo/ONME-4125
Allows multiple network status listeners
2 parents 75d5bd3 + 43a53df commit 10bb66a

File tree

10 files changed

+232
-15
lines changed

10 files changed

+232
-15
lines changed

UNITTESTS/features/netsocket/EthernetInterface/unittest.cmake

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ set(unittest-sources
1717
../features/frameworks/nanostack-libservice/source/libip6string/ip6tos.c
1818
../features/frameworks/nanostack-libservice/source/libip4string/stoip4.c
1919
../features/frameworks/nanostack-libservice/source/libip6string/stoip6.c
20-
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
20+
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
21+
../features/frameworks/nanostack-libservice/source/libList/ns_list.c
2122
)
2223

2324
# Test files
@@ -35,4 +36,5 @@ set(unittest-test-sources
3536
stubs/NetworkStack_stub.cpp
3637
stubs/NetworkInterfaceDefaults_stub.cpp
3738
stubs/SocketStats_Stub.cpp
39+
stubs/mbed_error.c
3840
)

UNITTESTS/features/netsocket/NetworkInterface/test_NetworkInterface.cpp

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "NetworkStack_stub.h"
2121

2222
class stubNetworkInterface : public NetworkInterface {
23+
public:
2324
virtual nsapi_error_t connect()
2425
{
2526
return NSAPI_ERROR_OK;
@@ -32,13 +33,22 @@ class stubNetworkInterface : public NetworkInterface {
3233
{
3334
return &stack;
3435
};
35-
public:
36+
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> cb)
37+
{
38+
status_cb = cb;
39+
}
40+
void event(nsapi_event_t e, intptr_t i)
41+
{
42+
status_cb(e, i);
43+
}
44+
private:
45+
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb;
3646
NetworkStackstub stack;
3747
};
3848

3949
class TestNetworkInterface : public testing::Test {
4050
protected:
41-
NetworkInterface *iface;
51+
stubNetworkInterface *iface;
4252

4353
virtual void SetUp()
4454
{
@@ -131,4 +141,79 @@ TEST_F(TestNetworkInterface, set_blocking)
131141
EXPECT_EQ(iface->set_blocking(true), NSAPI_ERROR_UNSUPPORTED);
132142
}
133143

134-
// No way to test attach as it doesn't do or return anything.
144+
void my_iface_callback(nsapi_event_t e, intptr_t i)
145+
{
146+
(void)e;
147+
(void)i;
148+
callback_is_called = true;
149+
}
150+
static bool second_callback_called;
151+
void my_iface_callback2(nsapi_event_t e, intptr_t i)
152+
{
153+
(void)e;
154+
(void)i;
155+
second_callback_called = true;
156+
}
157+
158+
TEST_F(TestNetworkInterface, add_event_listener)
159+
{
160+
callback_is_called = false;
161+
second_callback_called = false;
162+
iface->add_event_listener(my_iface_callback);
163+
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
164+
EXPECT_EQ(callback_is_called, true);
165+
166+
iface->remove_event_listener(my_iface_callback);
167+
}
168+
169+
TEST_F(TestNetworkInterface, remove_event_listener)
170+
{
171+
// Add two callback and check that both are called
172+
callback_is_called = false;
173+
second_callback_called = false;
174+
iface->add_event_listener(my_iface_callback);
175+
iface->add_event_listener(my_iface_callback2);
176+
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
177+
EXPECT_EQ(callback_is_called, true);
178+
EXPECT_EQ(second_callback_called, true);
179+
180+
// Remove one of the callbacks
181+
iface->remove_event_listener(my_iface_callback2);
182+
callback_is_called = false;
183+
second_callback_called = false;
184+
185+
// expect only the one is called which remains in the list
186+
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
187+
EXPECT_EQ(callback_is_called, true);
188+
EXPECT_EQ(second_callback_called, false);
189+
190+
// Remove also the last callback, and expect nothing is called
191+
iface->remove_event_listener(my_iface_callback);
192+
callback_is_called = false;
193+
second_callback_called = false;
194+
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
195+
EXPECT_EQ(callback_is_called, false);
196+
EXPECT_EQ(second_callback_called, false);
197+
}
198+
199+
TEST_F(TestNetworkInterface, correct_event_listener_per_interface)
200+
{
201+
stubNetworkInterface *iface2 = new stubNetworkInterface();
202+
iface->add_event_listener(my_iface_callback);
203+
iface2->add_event_listener(my_iface_callback2);
204+
205+
callback_is_called = false;
206+
second_callback_called = false;
207+
iface->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
208+
EXPECT_EQ(callback_is_called, true);
209+
EXPECT_EQ(second_callback_called, false);
210+
211+
callback_is_called = false;
212+
second_callback_called = false;
213+
iface2->event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, 0);
214+
EXPECT_EQ(callback_is_called, false);
215+
EXPECT_EQ(second_callback_called, true);
216+
217+
iface->remove_event_listener(my_iface_callback);
218+
iface2->remove_event_listener(my_iface_callback2);
219+
}

UNITTESTS/features/netsocket/NetworkInterface/unittest.cmake

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ set(unittest-sources
1212
../features/frameworks/nanostack-libservice/source/libip6string/ip6tos.c
1313
../features/frameworks/nanostack-libservice/source/libip4string/stoip4.c
1414
../features/frameworks/nanostack-libservice/source/libip6string/stoip6.c
15-
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
15+
../features/frameworks/nanostack-libservice/source/libBits/common_functions.c
16+
../features/frameworks/nanostack-libservice/source/libList/ns_list.c
1617
)
1718

1819
# Test files
@@ -27,4 +28,5 @@ set(unittest-test-sources
2728
features/netsocket/NetworkInterface/test_NetworkInterface.cpp
2829
stubs/NetworkInterfaceDefaults_stub.cpp
2930
stubs/SocketStats_Stub.cpp
31+
stubs/mbed_error.c
3032
)

UNITTESTS/stubs/NetworkInterface_stub.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,6 @@ nsapi_error_t NetworkInterface::gethostbyname_async_cancel(int id)
8787
return NSAPI_ERROR_UNSUPPORTED;
8888
}
8989

90+
NetworkInterface::~NetworkInterface()
91+
{
92+
}

UNITTESTS/stubs/mbed_error.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
int mbed_error(int error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number)
3+
{
4+
return 0;
5+
}

features/frameworks/nanostack-libservice/mbed-client-libservice/ns_list.h

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ typedef struct ns_list {
9696
* always assign returned entry pointers to a properly typed pointer variable.
9797
* This assignment will be then type-checked where the compiler supports it, and
9898
* will dereference correctly on compilers that don't support this extension.
99+
*
100+
* If you need to support C++03 compilers that cannot return properly-typed
101+
* pointers, such as IAR 7, you need to use NS_LIST_TYPECOERCE to force the type.
99102
* ~~~
100103
* NS_LIST_HEAD(example_entry_t, link) my_list;
101104
*
@@ -199,6 +202,27 @@ union \
199202
#define NS_LIST_TYPECAST_(list, val) (0 ? (list)->type : (val))
200203
#endif
201204

205+
/** \brief Macro to force correct type if necessary.
206+
*
207+
* In C, doesn't matter if NS_LIST_TYPECAST_ works or not, as it's legal
208+
* to assign void * to a pointer. In C++, we can't do that, so need
209+
* a back-up plan for C++03. This forces the type, so breaks type-safety -
210+
* only activate when needed, meaning we still get typechecks on other
211+
* toolchains.
212+
*
213+
* If a straight assignment of a ns_list function to a pointer fails
214+
* on a C++03 compiler, use the following construct. This will not be
215+
* required with C++11 compilers.
216+
* ~~~
217+
* type *elem = NS_LIST_TYPECOERCE(type *, ns_list_get_first(list));
218+
* ~~~
219+
*/
220+
#if defined(NS_LIST_PTR_TYPE_) || !defined(__cplusplus)
221+
#define NS_LIST_TYPECOERCE(type, val) (val)
222+
#else
223+
#define NS_LIST_TYPECOERCE(type, val) (type) (val)
224+
#endif
225+
202226
/** \brief Internal macro to check types of input entry pointer. */
203227
#define NS_LIST_TYPECHECK_(list, entry) \
204228
(NS_PTR_MATCH_((list)->type, (entry), "incorrect entry type for list"), (entry))
@@ -480,7 +504,8 @@ typedef struct ns_list_link {
480504
* \param list `(const list_t *)` Pointer to list - evaluated multiple times.
481505
*/
482506
#define ns_list_foreach(type, e, list) \
483-
for (type *e = ns_list_get_first(list); e; e = ns_list_get_next(list, e))
507+
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_first(list)); \
508+
e; e = NS_LIST_TYPECOERCE(type *, ns_list_get_next(list, e)))
484509

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

506531
/** \brief Iterate backwards over a list.
507532
*
508533
* As ns_list_foreach(), but going backwards - see its documentation.
509534
* Iterating forwards is *slightly* more efficient.
510535
*/
511536
#define ns_list_foreach_reverse(type, e, list) \
512-
for (type *e = ns_list_get_last(list); e; e = ns_list_get_previous(list, e))
537+
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_last(list)); \
538+
e; e = NS_LIST_TYPECOERCE(type *, ns_list_get_previous(list, e)))
513539

514540
/** \brief Iterate backwards over a list, where user may delete.
515541
*
516542
* As ns_list_foreach_safe(), but going backwards - see its documentation.
517543
* Iterating forwards is *slightly* more efficient.
518544
*/
519545
#define ns_list_foreach_reverse_safe(type, e, list) \
520-
for (type *e = ns_list_get_last(list), *_next##e; \
521-
e && (_next##e = ns_list_get_previous(list, e), true); e = _next##e)
546+
for (type *e = NS_LIST_TYPECOERCE(type *, ns_list_get_last(list)), *_next##e; \
547+
e && (_next##e = NS_LIST_TYPECOERCE(type *, ns_list_get_previous(list, e)), true); e = _next##e)
522548

523549
/** \hideinitializer \brief Count entries on a list
524550
*

features/netsocket/NetworkInterface.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
#include "netsocket/NetworkInterface.h"
1818
#include "netsocket/NetworkStack.h"
19+
#include "platform/Callback.h"
20+
#include "platform/mbed_error.h"
1921
#include <string.h>
22+
#include "ns_list.h"
2023

2124

2225
// Default network-interface state
@@ -77,6 +80,64 @@ nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)
7780

7881
void NetworkInterface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
7982
{
83+
// Dummy, that needs to be overwritten when inherited, but cannot be removed
84+
// because suplied previously and can be referred from binaries.
85+
}
86+
87+
typedef struct iface_eventlist_entry {
88+
NetworkInterface *iface;
89+
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb;
90+
ns_list_link_t link;
91+
} iface_eventlist_entry_t;
92+
93+
typedef NS_LIST_HEAD(iface_eventlist_entry_t, link) iface_eventlist_t;
94+
95+
static iface_eventlist_t *get_interface_event_list_head()
96+
{
97+
static iface_eventlist_t NS_LIST_NAME_INIT(event_list);
98+
return &event_list;
99+
}
100+
101+
static void call_all_event_listeners(NetworkInterface *iface, nsapi_event_t event, intptr_t val)
102+
{
103+
iface_eventlist_t *event_list = get_interface_event_list_head();
104+
ns_list_foreach(iface_eventlist_entry_t, entry, event_list) {
105+
if (entry->iface == iface) {
106+
entry->status_cb(event, val);
107+
}
108+
}
109+
}
110+
111+
void NetworkInterface::add_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
112+
{
113+
iface_eventlist_t *event_list = get_interface_event_list_head();
114+
iface_eventlist_entry_t *entry = new iface_eventlist_entry_t;
115+
entry->iface = this;
116+
entry->status_cb = status_cb;
117+
ns_list_add_to_end(event_list, entry);
118+
attach(mbed::callback(&call_all_event_listeners, this));
119+
}
120+
121+
void NetworkInterface::remove_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
122+
{
123+
iface_eventlist_t *event_list = get_interface_event_list_head();
124+
ns_list_foreach_safe(iface_eventlist_entry_t, entry, event_list) {
125+
if (entry->status_cb == status_cb && entry->iface == this) {
126+
ns_list_remove(event_list, entry);
127+
delete entry;
128+
return;
129+
}
130+
}
131+
}
132+
133+
NetworkInterface::~NetworkInterface()
134+
{
135+
iface_eventlist_t *event_list = get_interface_event_list_head();
136+
ns_list_foreach_safe(iface_eventlist_entry_t, entry, event_list) {
137+
if (entry->iface == this) {
138+
ns_list_remove(event_list, entry);
139+
}
140+
}
80141
}
81142

82143
nsapi_connection_status_t NetworkInterface::get_connection_status() const

features/netsocket/NetworkInterface.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class EMACInterface;
4343
class NetworkInterface: public DNS {
4444
public:
4545

46-
virtual ~NetworkInterface() {};
46+
virtual ~NetworkInterface();
4747

4848
/** Return the default network interface.
4949
*
@@ -251,12 +251,38 @@ class NetworkInterface: public DNS {
251251
*
252252
* The specified status callback function will be called on status changes
253253
* on the network. The parameters on the callback are the event type and
254-
* event-type dependent reason parameter.
254+
* event-type dependent reason parameter. Only one callback can be registered at a time.
255+
*
256+
* To unregister a callback call with status_cb parameter as a zero.
257+
*
258+
* *NOTE:* Any callbacks registered with this function will be overwritten if
259+
* add_event_listener() API is used.
255260
*
256261
* @param status_cb The callback for status changes.
257262
*/
258263
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
259264

265+
/** Add event listener for interface.
266+
*
267+
* This API allows multiple callback to be registered for a single interface.
268+
* When first called, internal list of event handlers are created and registered to
269+
* interface through attach() API.
270+
*
271+
* Application may only use attach() or add_event_listener() interface. Mixing usage
272+
* of both leads to undefined behavior.
273+
*
274+
* @param status_cb The callback for status changes.
275+
*/
276+
void add_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
277+
278+
/** Remove event listener from interface.
279+
*
280+
* Remove previously added callback from the handler list.
281+
*
282+
* @param status_cb The callback to unregister.
283+
*/
284+
void remove_event_listener(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
285+
260286
/** Get the connection status.
261287
*
262288
* @return The connection status (@see nsapi_types.h).

0 commit comments

Comments
 (0)