Skip to contentSkip to conversation

watchlist: added watchlist functionality

PATCH In review James Prestwood 3 participants 6 messages Oct 31, 2018 Kbuild

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/3 watchlist: added watchlist functionality

These two files (watchlist.[ch]) were copied from IWD, and public API's changed to l_ prefix.

---
 Makefile.am     |   6 +-
 ell/ell.h       |   1 +
 ell/watchlist.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++
 ell/watchlist.h | 129 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 283 insertions(+), 2 deletions(-)
 create mode 100644 ell/watchlist.c
 create mode 100644 ell/watchlist.h
Makefile.am +4 -2
index 0a8d0f6..31612a7 100644
@@ -48,7 +48,8 @@ pkginclude_HEADERS = ell/ell.h \
 			ell/file.h \
 			ell/dir.h \
 			ell/net.h \
-			ell/dhcp.h
+			ell/dhcp.h \
+			ell/watchlist.h
 
 lib_LTLIBRARIES = ell/libell.la
 
@@ -109,7 +110,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
 			ell/dhcp-private.h \
 			ell/dhcp.c \
 			ell/dhcp-transport.c \
-			ell/dhcp-lease.c
+			ell/dhcp-lease.c \
+			ell/watchlist.c
 
 ell_libell_la_LDFLAGS = -no-undefined \
 			-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
ell/ell.h +1 -0
index ba1928d..0d8e74e 100644
@@ -56,3 +56,4 @@
 #include <ell/dbus-service.h>
 #include <ell/dbus-client.h>
 #include <ell/dhcp.h>
+#include <ell/watchlist.h>
ell/watchlist.c new file +149 -0

149 lines not on this page · raw message

ell/watchlist.h new file +129 -0

130 lines not on this page · raw message

Signature · 1 line
2.17.1
#1 lore raw

PATCH v2 2/3 genl: rework l_genl_family to avoid extra lookups

Rather than doing a full lookup in the kernel when creating a new family object, we can use a previous lookup that matches our family name. Several API changes were introduced in order for this to work cleanly.

l_genl_family_new was removed, and instead l_genl_get_family should be used. This is an async function that returns the l_genl_family object in the callback. This better matches how the lookup actually works. Because of this l_genl_set_watches was also removed (since the "appeared" callback is now provided directly to l_genl_get_family, "vanished" callback is explained further below). It is up to the caller to manage the l_genl_family object memory (genl does not keep a list of created family objects).

l_genl_family_set_watches was removed in place of l_genl_add_destroy_watch. This is a watch on the genl object rather than on each family object. This watchlist will get notified if the genl object disappears.

l_genl_family_[ref|unref] were both removed as they no longer made sense. l_genl_family objects should now be cleaned up with l_genl_family_destroy.

The main intent with this patch is for each module that requires genl to "get" its own family object and send messages on that object rather than a shared global object. This, in most cases, will remove the need for command ID message tracking since messages on a family object can be canceled without affecting other family objects under the same name using l_genl_family_cancel_all.

Internally, the l_genl_family object is now a thin struct containing only a pointer back to the parent and a group ID. The parent object (genl_family_internal) now holds all the lookup information for a given name. If a new family is requested and a parent object already exists then we do not need to lookup the family in the kernel then the new l_genl_family object is returned in the callback. All l_genl_family objects are tagged with a unique group ID (gid), and any messages sent on that family are also tagged with the same gid. This allows cancelling all messages by gid, which leaves other l_genl_family messages untouched, even if they are under the same family name.

---
 ell/genl.c | 505 +++++++++++++++++++++++++++++++++++------------------
 ell/genl.h |  26 +--
 2 files changed, 353 insertions(+), 178 deletions(-)
ell/genl.c +338 -167
index 3a0281a..b38d044 100644
@@ -2,7 +2,7 @@
  *
  *  Embedded Linux library
  *
- *  Copyright (C) 2011-2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011-2018  Intel Corporation. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -36,6 +36,8 @@
 #include "genl.h"
 #include "genl-private.h"
 #include "private.h"
+#include "watchlist.h"
+#include "idle.h"
 
 #define MAX_NESTING_LEVEL 4
 
@@ -63,12 +65,14 @@ struct l_genl {
 	struct l_queue *notify_list;
 	unsigned int next_request_id;
 	unsigned int next_notify_id;
+	unsigned int next_gid;
 	struct l_queue *family_list;
-	struct l_genl_family *nlctrl;
+	struct genl_family_internal *nlctrl;
 	struct genl_unicast_notify *unicast_notify;
 	l_genl_debug_func_t debug_callback;
 	l_genl_destroy_func_t debug_destroy;
 	void *debug_data;
+	struct l_watchlist destroy_watches;
 };
 
 struct l_genl_msg {
@@ -85,6 +89,7 @@ struct l_genl_msg {
 
 struct genl_request {
 	unsigned int id;
+	unsigned int gid;
 	uint16_t type;
 	uint16_t flags;
 	uint32_t seq;
@@ -114,8 +119,8 @@ struct genl_mcast {
 	unsigned int users;
 };
 
-struct l_genl_family {
-	int ref_count;
+struct genl_family_internal {
+	unsigned int ref_count;
 	struct l_genl *genl;
 	char name[GENL_NAMSIZ];
 	uint16_t id;
@@ -124,11 +129,21 @@ struct l_genl_family {
 	uint32_t maxattr;
 	struct l_queue *op_list;
 	struct l_queue *mcast_list;
-	l_genl_watch_func_t watch_appeared;
-	l_genl_watch_func_t watch_vanished;
-	l_genl_destroy_func_t watch_destroy;
-	void *watch_data;
 	unsigned int nlctrl_cmd;
+	struct l_queue *pending_lookups;
+	bool live;
+};
+
+struct l_genl_family {
+	struct genl_family_internal *internal;
+	unsigned int gid;
+};
+
+struct cb_data {
+	l_genl_get_family_func_t cb;
+	l_genl_destroy_func_t destroy;
+	struct l_genl_family *family;
+	void *user_data;
 };
 
 static void destroy_request(void *data)
@@ -153,20 +168,42 @@ static void destroy_notify(void *data)
 	l_free(notify);
 }
 
-static struct l_genl_family *family_alloc(struct l_genl *genl,
-							const char *name)
+static struct genl_family_internal *family_internal_ref(
+					struct genl_family_internal *internal)
 {
-	struct l_genl_family *family;
+	if (unlikely(!internal))
+		return NULL;
 
-	family = l_new(struct l_genl_family, 1);
+	__sync_fetch_and_add(&internal->ref_count, 1);
 
-	family->genl = genl;
-	strncpy(family->name, name, GENL_NAMSIZ);
+	return internal;
+}
+
+static struct l_genl_family *family_alloc(
+					struct genl_family_internal *internal)
+{
+	struct l_genl_family *family = l_new(struct l_genl_family, 1);
 
-	family->op_list = l_queue_new();
-	family->mcast_list = l_queue_new();
+	family->internal = family_internal_ref(internal);
+	family->gid = ++internal->genl->next_gid;
 
-	return l_genl_family_ref(family);
+	return family;
+}
+
+static struct genl_family_internal *family_alloc_internal(struct l_genl *genl,
+							const char *name)
+{
+	struct genl_family_internal *internal = l_new(
+						struct genl_family_internal, 1);
+
+	internal->genl = l_genl_ref(genl);
+	strncpy(internal->name, name, GENL_NAMSIZ);
+
+	internal->op_list = l_queue_new();
+	internal->mcast_list = l_queue_new();
+	internal->pending_lookups = l_queue_new();
+
+	return internal;
 }
 
 static void op_free(void *data)
@@ -191,16 +228,56 @@ static void mcast_free(void *data, void *user_data)
 	l_free(mcast);
 }
 
-static void family_free(void *data)
+static void destroy_lookups(void *user_data)
 {
-	struct l_genl_family *family = data;
+	struct cb_data *cbd = user_data;
 
-	family->genl = NULL;
+	if (cbd->destroy)
+		cbd->destroy(cbd->user_data);
 
-	l_genl_family_unref(family);
+	l_free(cbd->family);
+	l_free(cbd);
 }
 
-static void family_add_op(struct l_genl_family *family, uint32_t id,
+static void internal_free(void *data)
+{
+	struct genl_family_internal *internal = data;
+	struct l_genl *genl = internal->genl;
+
+	if (internal->pending_lookups) {
+		l_queue_destroy(internal->pending_lookups, destroy_lookups);
+		internal->pending_lookups = NULL;
+	}
+
+	l_queue_destroy(internal->op_list, op_free);
+
+	l_queue_foreach(internal->mcast_list, mcast_free, internal->genl);
+	l_queue_destroy(internal->mcast_list, NULL);
+	internal->mcast_list = NULL;
+
+	l_genl_unref(genl);
+
+	l_free(internal);
+}
+
+static void family_internal_unref(struct genl_family_internal *internal)
+{
+	struct l_genl *genl;
+
+	if (unlikely(!internal))
+		return;
+
+	if (__sync_sub_and_fetch(&internal->ref_count, 1))
+		return;
+
+	genl = internal->genl;
+	if (genl)
+		l_queue_remove(genl->family_list, internal);
+
+	internal_free(internal);
+}
+
+static void family_add_op(struct genl_family_internal *internal, uint32_t id,
 							uint32_t flags)
 {
 	struct genl_op *op;
@@ -210,7 +287,7 @@ static void family_add_op(struct l_genl_family *family, uint32_t id,
 	op->id = id;
 	op->flags = flags;
 
-	l_queue_push_tail(family->op_list, op);
+	l_queue_push_tail(internal->op_list, op);
 }
 
 static bool match_mcast_name(const void *a, const void *b)
@@ -221,16 +298,16 @@ static bool match_mcast_name(const void *a, const void *b)
 	return !strcmp(mcast->name, name);
 }
 
-static void family_add_mcast(struct l_genl_family *family, const char *name,
+static void family_add_mcast(struct genl_family_internal *internal, const char *name,
 								uint32_t id)
 {
-	struct l_genl *genl = family->genl;
+	struct l_genl *genl = internal->genl;
 	struct genl_mcast *mcast;
 
 	if (!genl)
 		return;
 
-	mcast = l_queue_find(family->mcast_list, match_mcast_name,
+	mcast = l_queue_find(internal->mcast_list, match_mcast_name,
 							(char *) name);
 	if (mcast)
 		return;
@@ -241,7 +318,26 @@ static void family_add_mcast(struct l_genl_family *family, const char *name,
 	mcast->id = id;
 	mcast->users = 0;
 
-	l_queue_push_tail(family->mcast_list, mcast);
+	l_queue_push_tail(internal->mcast_list, mcast);
+}
+
+/* special initializer for the nlctrl object */
+static struct genl_family_internal *family_alloc_ctrl(struct l_genl *genl)
+{
+	struct genl_family_internal *nlctrl;
+
+	nlctrl = l_new(struct genl_family_internal, 1);
+
+	nlctrl->genl = genl;
+	strncpy(nlctrl->name, "nlctrl", GENL_NAMSIZ);
+
+	nlctrl->op_list = l_queue_new();
+	nlctrl->mcast_list = l_queue_new();
+	nlctrl->id = GENL_ID_CTRL;
+
+	family_add_mcast(nlctrl, "notify", GENL_ID_CTRL);
+
+	return nlctrl;
 }
 
 static struct l_genl_msg *msg_alloc(uint8_t cmd, uint8_t version, uint32_t size)
@@ -473,6 +569,10 @@ static void process_multicast(struct l_genl *genl, uint32_t group,
 
 static void read_watch_destroy(void *user_data)
 {
+	struct l_genl *genl = user_data;
+
+	L_WATCHLIST_NOTIFY_NO_ARGS(&genl->destroy_watches,
+					l_genl_destroy_func_t);
 }
 
 static bool received_data(struct l_io *io, void *user_data)
@@ -549,13 +649,7 @@ LIB_EXPORT struct l_genl *l_genl_new(int fd)
 	genl->fd = fd;
 	genl->close_on_unref = false;
 
-	genl->nlctrl = family_alloc(genl, "nlctrl");
-
-	genl->nlctrl->id = GENL_ID_CTRL;
-
-	family_add_mcast(genl->nlctrl, "notify", GENL_ID_CTRL);
-
-	l_queue_push_tail(genl->family_list, genl->nlctrl);
+	genl->nlctrl = family_alloc_ctrl(genl);
 
 	genl->io = l_io_new(genl->fd);
 
@@ -563,6 +657,7 @@ LIB_EXPORT struct l_genl *l_genl_new(int fd)
 	genl->pending_list = l_queue_new();
 	genl->notify_list = l_queue_new();
 	genl->family_list = l_queue_new();
+	l_watchlist_init(&genl->destroy_watches, NULL);
 
 	l_io_set_read_handler(genl->io, received_data, genl,
 						read_watch_destroy);
@@ -611,6 +706,20 @@ LIB_EXPORT struct l_genl *l_genl_new_default(void)
 	return genl;
 }
 
+LIB_EXPORT unsigned int l_genl_add_destroy_watch(struct l_genl *genl,
+					l_genl_destroy_func_t destroy,
+					void *user_data)
+{
+	return l_watchlist_add(&genl->destroy_watches, (void *)destroy,
+				user_data, NULL);
+}
+
+LIB_EXPORT void l_genl_remove_destroy_watch(struct l_genl *genl,
+						unsigned int id)
+{
+	l_watchlist_remove(&genl->destroy_watches, id);
+}
+
 LIB_EXPORT struct l_genl *l_genl_ref(struct l_genl *genl)
 {
 	if (unlikely(!genl))
@@ -632,6 +741,7 @@ LIB_EXPORT void l_genl_unref(struct l_genl *genl)
 	l_queue_destroy(genl->notify_list, destroy_notify);
 	l_queue_destroy(genl->pending_list, destroy_request);
 	l_queue_destroy(genl->request_queue, destroy_request);
+	genl->request_queue = NULL;
 
 	l_io_set_write_handler(genl->io, NULL, NULL, NULL);
 	l_io_set_read_handler(genl->io, NULL, NULL, NULL);
@@ -639,9 +749,10 @@ LIB_EXPORT void l_genl_unref(struct l_genl *genl)
 	l_io_destroy(genl->io);
 	genl->io = NULL;
 
-	l_genl_family_unref(genl->nlctrl);
+	internal_free(genl->nlctrl);
 
-	l_queue_destroy(genl->family_list, family_free);
+	l_queue_destroy(genl->family_list, internal_free);
+	l_watchlist_destroy(&genl->destroy_watches);
 
 	if (genl->close_on_unref)
 		close(genl->fd);
@@ -984,7 +1095,7 @@ LIB_EXPORT bool l_genl_attr_recurse(struct l_genl_attr *attr,
 	return true;
 }
 
-static void family_ops(struct l_genl_family *family, struct l_genl_attr *attr)
+static void family_ops(struct genl_family_internal *internal, struct l_genl_attr *attr)
 {
 	uint16_t type, len;
 	const void *data;
@@ -1007,11 +1118,11 @@ static void family_ops(struct l_genl_family *family, struct l_genl_attr *attr)
 		}
 
 		if (id > 0)
-			family_add_op(family, id, flags);
+			family_add_op(internal, id, flags);
 	}
 }
 
-static void family_mcast_groups(struct l_genl_family *family,
+static void family_mcast_groups(struct genl_family_internal *internal,
 						struct l_genl_attr *attr)
 {
 	uint16_t type, len;
@@ -1036,27 +1147,37 @@ static void family_mcast_groups(struct l_genl_family *family,
 		}
 
 		if (name && id > 0)
-			family_add_mcast(family, name, id);
+			family_add_mcast(internal, name, id);
 	}
 }
 
+static void family_notify_appeared(void *data)
+{
+	struct cb_data *cbd = data;
+
+	if (cbd->cb)
+		cbd->cb(cbd->family, cbd->user_data);
+
+	l_free(cbd);
+}
+
 static void get_family_callback(struct l_genl_msg *msg, void *user_data)
 {
-	struct l_genl_family *family = user_data;
+	struct genl_family_internal *internal = user_data;
 	struct l_genl_attr attr, nested;
 	uint16_t type, len;
 	const void *data;
 	int error;
 
-	family->nlctrl_cmd = 0;
+	internal->nlctrl_cmd = 0;
 
-	if (family->id > 0)
+	if (internal->id > 0)
 		return;
 
 	error = l_genl_msg_get_error(msg);
 	if (error < 0) {
-		if (family->watch_vanished)
-			family->watch_vanished(family->watch_data);
+		l_queue_remove(internal->genl->family_list, internal);
+		internal_free(internal);
 		return;
 	}
 
@@ -1066,127 +1187,164 @@ static void get_family_callback(struct l_genl_msg *msg, void *user_data)
 	while (l_genl_attr_next(&attr, &type, &len, &data)) {
 		switch (type) {
 		case CTRL_ATTR_FAMILY_ID:
-			family->id = *((uint16_t *) data);
+			internal->id = *((uint16_t *) data);
 			break;
 		case CTRL_ATTR_FAMILY_NAME:
-			strncpy(family->name, data, GENL_NAMSIZ);
+			strncpy(internal->name, data, GENL_NAMSIZ);
 			break;
 		case CTRL_ATTR_VERSION:
-			family->version = *((uint32_t *) data);
+			internal->version = *((uint32_t *) data);
 			break;
 		case CTRL_ATTR_HDRSIZE:
-			family->hdrsize = *((uint32_t *) data);
+			internal->hdrsize = *((uint32_t *) data);
 			break;
 		case CTRL_ATTR_MAXATTR:
-			family->maxattr = *((uint32_t *) data);
+			internal->maxattr = *((uint32_t *) data);
 			break;
 		case CTRL_ATTR_OPS:
 			if (l_genl_attr_recurse(&attr, &nested))
-				family_ops(family, &nested);
+				family_ops(internal, &nested);
 			break;
 		case CTRL_ATTR_MCAST_GROUPS:
 			if (l_genl_attr_recurse(&attr, &nested))
-				family_mcast_groups(family, &nested);
+				family_mcast_groups(internal, &nested);
 			break;
 		}
 	}
 
-	if (family->watch_appeared)
-		family->watch_appeared(family->watch_data);
+	internal->live = true;
+
+	l_queue_destroy(internal->pending_lookups, family_notify_appeared);
+	internal->pending_lookups = NULL;
 }
 
-LIB_EXPORT struct l_genl_family *l_genl_family_new(struct l_genl *genl,
-							const char *name)
+static bool match_family_name(const void *a, const void *b)
 {
-	struct l_genl_family *family;
-	struct l_genl_msg *msg;
+	const struct genl_family_internal *internal = a;
 
-	if (unlikely(!genl) || unlikely(!name) ||
-			unlikely(strlen(name) >= GENL_NAMSIZ))
-		return NULL;
+	return !strncmp(internal->name, (const char *)b, GENL_NAMSIZ);
+}
 
-	family = family_alloc(genl, name);
-	if (!family)
-		return NULL;
+static unsigned int send_common(struct genl_family_internal *internal, uint16_t flags,
+				struct l_genl_msg *msg, unsigned int gid,
+				l_genl_msg_func_t callback, void *user_data,
+				l_genl_destroy_func_t destroy)
+{
+	struct l_genl *genl;
+	struct genl_request *request;
 
-	msg = l_genl_msg_new_sized(CTRL_CMD_GETFAMILY, NLA_HDRLEN + GENL_NAMSIZ);
+	if (!internal || !msg)
+		return 0;
 
-	l_genl_msg_append_attr(msg, CTRL_ATTR_FAMILY_NAME,
-						GENL_NAMSIZ, family->name);
+	genl = internal->genl;
+	if (!genl)
+		return 0;
 
-	family->nlctrl_cmd = l_genl_family_send(genl->nlctrl, msg,
-					get_family_callback, family, NULL);
+	request = l_new(struct genl_request, 1);
 
-	if (!family->nlctrl_cmd) {
-		family_free(family);
-		return NULL;
-	}
+	request->type = internal->id;
+	request->flags = NLM_F_REQUEST | flags;
 
-	l_queue_push_tail(genl->family_list, family);
+	request->msg = msg;
+	request->gid = gid;
 
-	return family;
+	request->callback = callback;
+	request->destroy = destroy;
+	request->user_data = user_data;
+
+	if (genl->next_request_id < 1)
+		genl->next_request_id = 1;
+
+	request->id = genl->next_request_id++;
+
+	l_queue_push_tail(genl->request_queue, request);
+
+	wakeup_writer(genl);
+
+	return request->id;
 }
 
-LIB_EXPORT struct l_genl_family *l_genl_family_ref(
-						struct l_genl_family *family)
+static struct cb_data *cb_data_new(struct l_genl_family *family,
+				l_genl_get_family_func_t cb, void *user_data,
+				l_genl_destroy_func_t destroy)
 {
-	if (unlikely(!family))
-		return NULL;
+	struct cb_data *cbd = l_new(struct cb_data, 1);
 
-	__sync_fetch_and_add(&family->ref_count, 1);
+	cbd->family = family;
+	cbd->cb = cb;
+	cbd->destroy = destroy;
+	cbd->user_data = user_data;
 
-	return family;
+	return cbd;
+}
+static void idle_get_family(void *user_data)
+{
+	struct cb_data *cbd = user_data;
+
+	if (cbd->cb)
+		cbd->cb(cbd->family, cbd->user_data);
 }
 
-LIB_EXPORT void l_genl_family_unref(struct l_genl_family *family)
+LIB_EXPORT bool l_genl_get_family(struct l_genl *genl, const char *name,
+					l_genl_get_family_func_t callback,
+					void *user_data,
+					l_genl_destroy_func_t destroy)
 {
-	struct l_genl *genl;
+	struct genl_family_internal *internal;
+	struct l_genl_family *family;
+	struct l_genl_msg *msg;
+	struct cb_data *cbd;
 
-	if (unlikely(!family))
-		return;
+	if (unlikely(!genl) || unlikely(!name) ||
+			unlikely(strlen(name) >= GENL_NAMSIZ))
+		return false;
 
-	if (__sync_sub_and_fetch(&family->ref_count, 1))
-		return;
+	internal = l_queue_find(genl->family_list, match_family_name, name);
+	if (internal) {
+		family = family_alloc(internal);
 
-	if (family->nlctrl_cmd > 0)
-		l_genl_family_cancel(family, family->nlctrl_cmd);
+		cbd = cb_data_new(family, callback, user_data, destroy);
 
-	genl = family->genl;
-	if (genl)
-		l_queue_remove(genl->family_list, family);
+		/*
+		 * If family is looked up, just call the callback in the next
+		 * event loop. Otherwise we must have a currently pending lookup
+		 * so add the callback data to the pending queue
+		 */
+		if (internal->live)
+			return l_idle_oneshot(idle_get_family, cbd, l_free);
+		else
+			return l_queue_push_tail(internal->pending_lookups,
+							cbd);
+	}
+
+	internal = family_alloc_internal(genl, name);
+	if (!internal)
+		return false;
 
-	l_queue_destroy(family->op_list, op_free);
+	family = family_alloc(internal);
 
-	l_queue_foreach(family->mcast_list, mcast_free, genl);
+	cbd = cb_data_new(family, callback, user_data, destroy);
 
-	l_queue_destroy(family->mcast_list, NULL);
-	family->mcast_list = NULL;
+	l_queue_push_tail(internal->pending_lookups, cbd);
 
-	if (family->id > 0 && family->watch_vanished)
-		family->watch_vanished(family->watch_data);
+	/* family has not yet been looked up */
+	msg = l_genl_msg_new_sized(CTRL_CMD_GETFAMILY,
+						NLA_HDRLEN + GENL_NAMSIZ);
 
-	if (family->watch_destroy)
-		family->watch_destroy(family->watch_data);
+	l_genl_msg_append_attr(msg, CTRL_ATTR_FAMILY_NAME,
+						GENL_NAMSIZ, internal->name);
 
-	l_free(family);
-}
+	internal->nlctrl_cmd = send_common(genl->nlctrl, NLM_F_ACK, msg, 0,
+						get_family_callback, internal,
+						NULL);
 
-LIB_EXPORT bool l_genl_family_set_watches(struct l_genl_family *family,
-						l_genl_watch_func_t appeared,
-						l_genl_watch_func_t vanished,
-						void *user_data,
-						l_genl_destroy_func_t destroy)
-{
-	if (unlikely(!family))
+	if (!internal->nlctrl_cmd) {
+		l_genl_family_destroy(family);
+		internal_free(internal);
 		return false;
+	}
 
-	if (family->watch_destroy)
-		family->watch_destroy(family->watch_data);
-
-	family->watch_appeared = appeared;
-	family->watch_vanished = vanished;
-	family->watch_destroy = destroy;
-	family->watch_data = user_data;
+	l_queue_push_tail(genl->family_list, internal);
 
 	return true;
 }
@@ -1196,7 +1354,7 @@ LIB_EXPORT uint32_t l_genl_family_get_version(struct l_genl_family *family)
 	if (unlikely(!family))
 		return 0;
 
-	return family->version;
+	return family->internal->version;
 }
 
 LIB_EXPORT struct l_genl *l_genl_family_get_genl(struct l_genl_family *family)
@@ -1204,7 +1362,7 @@ LIB_EXPORT struct l_genl *l_genl_family_get_genl(struct l_genl_family *family)
 	if (unlikely(!family))
 		return 0;
 
-	return family->genl;
+	return family->internal->genl;
 }
 
 static bool match_op_id(const void *a, const void *b)
@@ -1223,7 +1381,8 @@ LIB_EXPORT bool l_genl_family_can_send(struct l_genl_family *family,
 	if (unlikely(!family))
 		return false;
 
-	op = l_queue_find(family->op_list, match_op_id, L_UINT_TO_PTR(cmd));
+	op = l_queue_find(family->internal->op_list, match_op_id,
+				L_UINT_TO_PTR(cmd));
 	if (!op)
 		return false;
 
@@ -1241,7 +1400,8 @@ LIB_EXPORT bool l_genl_family_can_dump(struct l_genl_family *family,
 	if (!family)
 		return false;
 
-	op = l_queue_find(family->op_list, match_op_id, L_UINT_TO_PTR(cmd));
+	op = l_queue_find(family->internal->op_list, match_op_id,
+				L_UINT_TO_PTR(cmd));
 	if (!op)
 		return false;
 
@@ -1251,51 +1411,14 @@ LIB_EXPORT bool l_genl_family_can_dump(struct l_genl_family *family,
 	return false;
 }
 
-static unsigned int send_common(struct l_genl_family *family, uint16_t flags,
-				struct l_genl_msg *msg, l_genl_msg_func_t callback,
-				void *user_data, l_genl_destroy_func_t destroy)
-{
-	struct l_genl *genl;
-	struct genl_request *request;
-
-	if (!family || !msg)
-		return 0;
-
-	genl = family->genl;
-	if (!genl)
-		return 0;
-
-	request = l_new(struct genl_request, 1);
-
-	request->type = family->id;
-	request->flags = NLM_F_REQUEST | flags;
-
-	request->msg = msg;
-
-	request->callback = callback;
-	request->destroy = destroy;
-	request->user_data = user_data;
-
-	if (genl->next_request_id < 1)
-		genl->next_request_id = 1;
-
-	request->id = genl->next_request_id++;
-
-	l_queue_push_tail(genl->request_queue, request);
-
-	wakeup_writer(genl);
-
-	return request->id;
-}
-
 LIB_EXPORT unsigned int l_genl_family_send(struct l_genl_family *family,
 						struct l_genl_msg *msg,
 						l_genl_msg_func_t callback,
 						void *user_data,
 						l_genl_destroy_func_t destroy)
 {
-	return send_common(family, NLM_F_ACK, msg, callback,
-						user_data, destroy);
+	return send_common(family->internal, NLM_F_ACK, msg, family->gid,
+						callback, user_data, destroy);
 }
 
 LIB_EXPORT unsigned int l_genl_family_dump(struct l_genl_family *family,
@@ -1304,8 +1427,9 @@ LIB_EXPORT unsigned int l_genl_family_dump(struct l_genl_family *family,
 						void *user_data,
 						l_genl_destroy_func_t destroy)
 {
-	return send_common(family, NLM_F_ACK | NLM_F_DUMP, msg, callback,
-							user_data, destroy);
+	return send_common(family->internal, NLM_F_ACK | NLM_F_DUMP, msg,
+						family->gid, callback,
+						user_data, destroy);
 }
 
 static bool match_request_id(const void *a, const void *b)
@@ -1325,7 +1449,7 @@ LIB_EXPORT bool l_genl_family_cancel(struct l_genl_family *family,
 	if (unlikely(!family) || unlikely(!id))
 		return false;
 
-	genl = family->genl;
+	genl = family->internal->genl;
 	if (!genl)
 		return false;
 
@@ -1345,6 +1469,53 @@ done:
 	return true;
 }
 
+static bool remove_by_gid(void *data, void *user_data)
+{
+	struct genl_request *request = data;
+	unsigned int gid = L_PTR_TO_UINT(user_data);
+
+	if (request->gid != gid)
+		return false;
+
+	destroy_request(request);
+
+	return true;
+}
+
+LIB_EXPORT bool l_genl_family_cancel_all(struct l_genl_family *family)
+{
+	struct l_genl *genl;
+	unsigned int removed;
+
+	if (unlikely(!family))
+		return false;
+
+	genl = family->internal->genl;
+	if (!genl)
+		return false;
+
+	removed = l_queue_foreach_remove(genl->request_queue, remove_by_gid,
+						L_UINT_TO_PTR(family->gid));
+	if (removed)
+		return true;
+
+	removed = l_queue_foreach_remove(genl->pending_list, remove_by_gid,
+						L_UINT_TO_PTR(family->gid));
+	if (!removed)
+		return false;
+
+	return true;
+}
+
+LIB_EXPORT void l_genl_family_destroy(struct l_genl_family *family)
+{
+	l_genl_family_cancel_all(family);
+
+	family_internal_unref(family->internal);
+
+	l_free(family);
+}
+
 static void add_membership(struct l_genl *genl, struct genl_mcast *mcast)
 {
 	int group = mcast->id;
@@ -1367,7 +1538,7 @@ LIB_EXPORT bool l_genl_family_has_group(struct l_genl_family *family,
 	if (unlikely(!family))
 		return false;
 
-	mcast = l_queue_find(family->mcast_list, match_mcast_name,
+	mcast = l_queue_find(family->internal->mcast_list, match_mcast_name,
 							(char *) group);
 	if (!mcast)
 		return false;
@@ -1388,18 +1559,18 @@ LIB_EXPORT unsigned int l_genl_family_register(struct l_genl_family *family,
 	if (unlikely(!family) || unlikely(!group))
 		return 0;
 
-	genl = family->genl;
+	genl = family->internal->genl;
 	if (!genl)
 		return 0;
 
-	mcast = l_queue_find(family->mcast_list, match_mcast_name,
+	mcast = l_queue_find(family->internal->mcast_list, match_mcast_name,
 							(char *) group);
 	if (!mcast)
 		return 0;
 
 	notify = l_new(struct genl_mcast_notify, 1);
 
-	notify->type = family->id;
+	notify->type = family->internal->id;
 	notify->group = mcast->id;
 
 	notify->callback = callback;
@@ -1435,7 +1606,7 @@ LIB_EXPORT bool l_genl_family_unregister(struct l_genl_family *family,
 	if (!family || !id)
 		return false;
 
-	genl = family->genl;
+	genl = family->internal->genl;
 	if (!genl)
 		return false;
 
ell/genl.h +15 -11
index ff5687c..1a92e3a 100644
@@ -2,7 +2,7 @@
  *
  *  Embedded Linux library
  *
- *  Copyright (C) 2011-2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011-2018  Intel Corporation. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -33,12 +33,16 @@ extern "C" {
 
 typedef void (*l_genl_destroy_func_t)(void *user_data);
 
-
 struct l_genl;
 
 struct l_genl *l_genl_new(int fd);
 struct l_genl *l_genl_new_default(void);
 
+unsigned int l_genl_add_destroy_watch(struct l_genl *genl,
+					l_genl_destroy_func_t destroy,
+					void *user_data);
+void l_genl_remove_destroy_watch(struct l_genl *genl, unsigned int id);
+
 struct l_genl *l_genl_ref(struct l_genl *genl);
 void l_genl_unref(struct l_genl *genl);
 
@@ -49,7 +53,6 @@ bool l_genl_set_debug(struct l_genl *genl, l_genl_debug_func_t callback,
 
 bool l_genl_set_close_on_unref(struct l_genl *genl, bool do_close);
 
-
 struct l_genl_msg;
 
 struct l_genl_attr {
@@ -85,17 +88,17 @@ bool l_genl_attr_recurse(struct l_genl_attr *attr, struct l_genl_attr *nested);
 
 struct l_genl_family;
 
-struct l_genl_family *l_genl_family_new(struct l_genl *genl, const char *name);
+typedef void (*l_genl_get_family_func_t)(struct l_genl_family *family,
+						void *user_data);
 
-struct l_genl_family *l_genl_family_ref(struct l_genl_family *family);
-void l_genl_family_unref(struct l_genl_family *family);
+bool l_genl_get_family(struct l_genl *genl, const char *name,
+					l_genl_get_family_func_t callback,
+					void *user_data,
+					l_genl_destroy_func_t destroy);
 
-typedef void (*l_genl_watch_func_t)(void *user_data);
+void l_genl_family_destroy(struct l_genl_family *family);
 
-bool l_genl_family_set_watches(struct l_genl_family *family,
-				l_genl_watch_func_t appeared,
-				l_genl_watch_func_t vanished,
-				void *user_data, l_genl_destroy_func_t destroy);
+typedef void (*l_genl_watch_func_t)(void *user_data);
 
 typedef void (*l_genl_msg_func_t)(struct l_genl_msg *msg, void *user_data);
 
@@ -116,6 +119,7 @@ unsigned int l_genl_family_dump(struct l_genl_family *family,
 				struct l_genl_msg *msg, l_genl_msg_func_t callback,
 				void *user_data, l_genl_destroy_func_t destroy);
 bool l_genl_family_cancel(struct l_genl_family *family, unsigned int id);
+bool l_genl_family_cancel_all(struct l_genl_family *family);
 
 bool l_genl_family_has_group(struct l_genl_family *family, const char *group);
 unsigned int l_genl_family_register(struct l_genl_family *family,
Signature · 1 line
2.17.1
#2 lore raw

PATCH v2 3/3 unit: test-genl: Reworked genl tests after restructuring

In reply to James Prestwood
---
 unit/test-genl.c | 351 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 270 insertions(+), 81 deletions(-)
unit/test-genl.c +270 -81
index a381e25..c05c55c 100644
@@ -2,7 +2,7 @@
  *
  *  Embedded Linux library
  *
- *  Copyright (C) 2011-2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011-2018  Intel Corporation. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -32,10 +32,13 @@
 struct test_data
 {
 	struct l_genl_family *appeared_family;
-	struct l_genl_family *vanished_family;
 
-	unsigned int group_id;
-	bool vanished_called;
+	bool destroyed_called;
+	bool appeared_called;
+	bool genl_destroyed;
+	int message_destroy;
+
+	int idle_count;
 };
 
 static void do_debug(const char *str, void *user_data)
@@ -45,138 +48,324 @@ static void do_debug(const char *str, void *user_data)
 	l_info("%s%s", prefix, str);
 }
 
-static void notify_callback(struct l_genl_msg *msg, void *user_data)
-{
-}
-
-static void family_appeared(void *user_data)
+static void family_appeared(struct l_genl_family *family, void *user_data)
 {
 	struct test_data *data = user_data;
 
-	data->group_id = l_genl_family_register(data->appeared_family,
-						"notify",
-						notify_callback,
-						NULL,
-						NULL);
+	data->appeared_called = true;
+	data->appeared_family = family;
 }
 
-static void family_vanished(void *user_data)
+static void family_destroyed(void *user_data)
 {
-	struct test_data *data = user_data;
+	struct test_data *td = user_data;
 
-	data->vanished_called = true;
+	td->destroyed_called = true;
 }
 
-static bool prep_family_appeared(struct l_genl *genl,
-					struct test_data *data)
+static void idle_callback(struct l_idle *idle, void *user_data)
 {
+	struct test_data *data = user_data;
+
 	/*
-	 * Set a family_appeared watch for the "nlctrl" family.
+	 * Exit the event loop if the desired results have been
+	 * obtained, but limit the number of iterations to prevent the
+	 * loop from running indefinitely if the conditions for
+	 * success are never reached.
 	 *
-	 * The "nlctrl" generic netlink family always exists in the
-	 * kernel so it is a suitable family to use for testing
-	 * the family_appeared watch and related family registration
-	 * operations.
+	 * Allow the main loop to iterate at least four times to allow
+	 * the generic netlink watches and family registration to be
+	 * called and completed, respectively.
 	 */
-	data->appeared_family = l_genl_family_new(genl, "nlctrl");
-
-	return l_genl_family_set_watches(data->appeared_family,
-						family_appeared, NULL,
-						data, NULL);
+	if (++data->idle_count > 3)
+		l_main_quit();
 }
 
-static bool prep_family_vanished(struct l_genl *genl,
-					struct test_data *data)
+static void test_basic(const void *user_data)
 {
-	/*
-	 * Use a bogus family name to trigger the vanished watch to
-	 * be called during the ELL event loop run.
-	 */
-	static const char BOGUS_GENL_NAME[] = "bogusgenlfamily";
+	struct l_genl *genl;
+	struct l_idle *idle;
+	struct test_data td1 = { 0 };
+	struct test_data td2 = { 0 };
 
-	data->vanished_family = l_genl_family_new(genl, BOGUS_GENL_NAME);
-	return l_genl_family_set_watches(data->vanished_family,
-						NULL, family_vanished,
-						data, NULL);
-}
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
 
-static bool name_too_long(struct l_genl *genl, struct test_data *data)
-{
-	static const char LONG_GENL_NAME[] = "long_genl_family_name";
+	l_log_set_stderr();
+
+	genl = l_genl_new_default();
+
+	l_genl_set_debug(genl, do_debug, "[GENL] ", NULL);
 
-	assert(!l_genl_family_new(genl, LONG_GENL_NAME));
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared,
+					&td1, family_destroyed));
+	assert(l_genl_get_family(genl, "bogusgenlfamily", family_appeared,
+					&td2, family_destroyed));
+	assert(!l_genl_get_family(genl, "long_genl_family_name",
+					family_appeared, NULL,
+					family_destroyed));
+
+	idle = l_idle_create(idle_callback, &td1, NULL);
+
+	l_main_run();
+
+	l_idle_remove(idle);
+
+	assert(td1.appeared_called && td1.appeared_family);
+	assert(td2.destroyed_called);
+
+	l_genl_family_destroy(td1.appeared_family);
+
+	l_genl_unref(genl);
 
-	return true;
+	l_main_exit();
 }
 
-static bool check_test_data(struct test_data *data)
+static void test_gets_before_lookup(const void *user_data)
 {
-    return data->group_id != 0 && data->vanished_called;
+	struct l_genl *genl;
+	struct l_idle *idle;
+	struct test_data td1 = { 0 };
+	struct test_data td2 = { 0 };
+
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
+
+	l_log_set_stderr();
+
+	genl = l_genl_new_default();
+
+	l_genl_set_debug(genl, do_debug, "[GENL] ", NULL);
+
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td1,
+					family_destroyed));
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td2,
+					family_destroyed));
+
+	idle = l_idle_create(idle_callback, &td1, NULL);
+
+	l_main_run();
+
+	l_idle_remove(idle);
+
+	assert(td1.appeared_called && td1.appeared_family);
+	assert(td1.appeared_called && td1.appeared_family);
+
+	l_genl_family_destroy(td1.appeared_family);
+	l_genl_family_destroy(td2.appeared_family);
+
+	l_genl_unref(genl);
+
+	l_main_exit();
 }
 
-static void idle_callback(struct l_idle *idle, void *user_data)
+static void test_destroy_before_lookup(const void *user_data)
 {
-	struct test_data *data = user_data;
-	static int count = 0;
+	struct l_genl *genl;
+	struct test_data td1 = { 0 };
+	struct test_data td2 = { 0 };
+
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
+
+	l_log_set_stderr();
+
+	genl = l_genl_new_default();
+
+	l_genl_set_debug(genl, do_debug, "[GENL] ", NULL);
+
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td1,
+					family_destroyed));
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td2,
+					family_destroyed));
 
 	/*
-	 * Exit the event loop if the desired results have been
-	 * obtained, but limit the number of iterations to prevent the
-	 * loop from running indefinitely if the conditions for
-	 * success are never reached.
-	 *
-	 * Allow the main loop to iterate at least four times to allow
-	 * the generic netlink watches and family registration to be
-	 * called and completed, respectively.
+	 * In practice you would never do this. But it effectively simulates
+	 * the genl transport disappearing while you have family lookups
+	 * pending.
 	 */
-	if (check_test_data(data) || ++count > 3)
-		l_main_quit();
+	l_genl_unref(genl);
+	l_genl_unref(genl);
+
+	assert(td1.destroyed_called);
+	assert(td2.destroyed_called);
+
+	l_main_exit();
 }
 
-static bool destroy_test_data(struct test_data *data)
+static void test_family_after_lookup(const void *user_data)
 {
-	bool unregistered =
-			l_genl_family_unregister(data->appeared_family,
-							data->group_id);
+	struct l_genl *genl;
+	struct l_idle *idle;
+	struct test_data td1 = { 0 };
+	struct test_data td2 = { 0 };
+
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
+
+	l_log_set_stderr();
+
+	genl = l_genl_new_default();
+
+	l_genl_set_debug(genl, do_debug, "[GENL] ", NULL);
+
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td1,
+					family_destroyed));
+
+	idle = l_idle_create(idle_callback, &td1, NULL);
+
+	l_main_run();
+
+	l_idle_remove(idle);
+
+	assert(td1.appeared_called);
+
+	l_main_exit();
+
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
+
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td2,
+					family_destroyed));
+
+	idle = l_idle_create(idle_callback, &td2, NULL);
+
+	l_main_run();
+
+	l_idle_remove(idle);
+
+	assert(td2.appeared_called);
+
+	l_genl_family_destroy(td1.appeared_family);
+	l_genl_family_destroy(td2.appeared_family);
 
-	l_genl_family_unref(data->vanished_family);
-	l_genl_family_unref(data->appeared_family);
+	l_genl_unref(genl);
 
-	return unregistered;
+	l_main_exit();
 }
 
-int main(int argc, char *argv[])
+static void message_destroy(void *user_data)
+{
+	struct test_data *td = user_data;
+
+	td->message_destroy++;
+}
+
+static void test_cancel_all_request(const void *user_data)
 {
 	struct l_genl *genl;
+	struct l_genl_msg *msg1, *msg2, *msg3, *msg4;
+	unsigned int msg3_id;
 	struct l_idle *idle;
-	struct test_data data = { .group_id = 0 };
 
-	if (!l_main_init())
-		return -1;
+	struct test_data td1 = { 0 };
+	struct test_data td2 = { 0 };
 
-	l_log_set_stderr();
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
 
 	genl = l_genl_new_default();
 
 	l_genl_set_debug(genl, do_debug, "[GENL] ", NULL);
 
-	assert(prep_family_appeared(genl, &data));
-	assert(prep_family_vanished(genl, &data));
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td1,
+					family_destroyed));
+	assert(l_genl_get_family(genl, "nlctrl", family_appeared, &td2,
+					family_destroyed));
 
-	assert(name_too_long(genl, &data));
-
-	idle = l_idle_create(idle_callback, &data, NULL);
+	idle = l_idle_create(idle_callback, &td1, NULL);
 
 	l_main_run();
 
 	l_idle_remove(idle);
 
-	assert(check_test_data(&data));
-	assert(destroy_test_data(&data));
+	assert(td1.appeared_called);
+	assert(td2.appeared_called);
+
+	/* create some dummy messages to send on both families */
+	msg1 = l_genl_msg_new(1);
+	msg2 = l_genl_msg_new(1);
+	msg3 = l_genl_msg_new(1);
+	msg4 = l_genl_msg_new(1);
+
+	l_genl_family_send(td1.appeared_family, msg1, NULL, &td1,
+					message_destroy);
+	l_genl_family_send(td1.appeared_family, msg2, NULL, &td1,
+					message_destroy);
+	msg3_id = l_genl_family_send(td2.appeared_family, msg3, NULL, &td2,
+					message_destroy);
+	l_genl_family_send(td2.appeared_family, msg4, NULL, &td2,
+					message_destroy);
+
+	/* this should cancel msg1 and msg2 */
+	l_genl_family_destroy(td1.appeared_family);
+
+	assert(td1.message_destroy == 2);
+	assert(td2.message_destroy == 0);
+
+	/* cancel just message 3 */
+	l_genl_family_cancel(td2.appeared_family, msg3_id);
+	assert(td2.message_destroy == 1);
+
+	/* cancel all remaining (just message 4) */
+	l_genl_family_destroy(td2.appeared_family);
+	assert(td2.message_destroy == 2);
 
 	l_genl_unref(genl);
 
 	l_main_exit();
+}
+
+static void genl_destroyed(void *user_data)
+{
+	struct test_data *td = user_data;
+
+	td->genl_destroyed = true;
+}
+
+static void test_genl_disappearing(const void *user_data)
+{
+	struct l_genl *genl;
+	struct test_data td = { 0 };
+
+	if (!l_main_init()) {
+		assert(false);
+		return;
+	}
+
+	genl = l_genl_new_default();
+
+	l_genl_add_destroy_watch(genl, genl_destroyed, &td);
+
+	l_genl_unref(genl);
+
+	assert(td.genl_destroyed);
+
+	l_main_exit();
+}
+
+int main(int argc, char *argv[])
+{
+	l_test_init(&argc, &argv);
+
+	l_test_add("Basic", test_basic, NULL);
+	l_test_add("Gets before lookup", test_gets_before_lookup, NULL);
+	l_test_add("Destroy before lookup", test_destroy_before_lookup, NULL);
+	l_test_add("New family after lookup", test_family_after_lookup, NULL);
+	l_test_add("Cancel all family requests", test_cancel_all_request, NULL);
+	l_test_add("Genl disappearing", test_genl_disappearing, NULL);
 
-	return 0;
+	return l_test_run();
 }
Signature · 1 line
2.17.1
#3 lore raw
In reply to James Prestwood
Quoted 50 lines · James Prestwood

These two files (watchlist.[ch]) were copied from IWD, and public API's changed to l_ prefix.
---

Makefile.am     |   6 +-
ell/ell.h       |   1 +
ell/watchlist.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++
ell/watchlist.h | 129 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 283 insertions(+), 2 deletions(-)

create mode 100644 ell/watchlist.c
create mode 100644 ell/watchlist.h

Makefile.am +2 -1
index 0a8d0f6..31612a7 100644
@@ -48,7 +48,8 @@ pkginclude_HEADERS = ell/ell.h \
 			ell/file.h \
 			ell/dir.h \
 			ell/net.h \
-			ell/dhcp.h
+			ell/dhcp.h \
+			ell/watchlist.h
 

lib_LTLIBRARIES = ell/libell.la

Makefile.am +2 -1
@@ -109,7 +110,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
 			ell/dhcp-private.h \
 			ell/dhcp.c \
 			ell/dhcp-transport.c \
-			ell/dhcp-lease.c
+			ell/dhcp-lease.c \
+			ell/watchlist.c
 
 ell_libell_la_LDFLAGS = -no-undefined \
 			-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
ell/ell.h +1 -0
index ba1928d..0d8e74e 100644
@@ -56,3 +56,4 @@
 #include <ell/dbus-service.h>
 #include <ell/dbus-client.h>
 #include <ell/dhcp.h>
+#include <ell/watchlist.h>
ell/watchlist.c new file +0 -0
new file mode 100644
index 0000000..d300756

ell/watchlist.c line 3

Quoted 3 lines
ell/watchlist.c new file +3 -0
@@ -0,0 +1,149 @@
+/*
+ *
+ *  Wireless daemon for Linux

Change this to match ELL files? Probably update the copyright dates too.

Quoted 14 lines
ell/watchlist.c new file +14 -0
+ *
+ *  Copyright (C) 2016  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public

ell/watchlist.c line 27

Quoted 10 lines
ell/watchlist.c new file +10 -0
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ell/ell.h>

Can this include the relevant header files instead?

Quoted 12 lines
ell/watchlist.c new file +12 -0
+
+static bool watchlist_item_match(const void *a, const void *b)
+{
+	const struct l_watchlist_item *item = a;
+	uint32_t id = L_PTR_TO_UINT(b);
+
+	return item->id == id;
+}
+
+static void __watchlist_item_free(struct l_watchlist *watchlist,
+						struct l_watchlist_item *item)
+{

ell/watchlist.c line 49

Quoted 10 lines
ell/watchlist.c new file +10 -0
+	if (item->destroy)
+		item->destroy(item->notify_data);
+
+	if (watchlist->ops && watchlist->ops->item_free)
+		watchlist->ops->item_free(item);
+	else
+		l_free(item);
+}
+
+struct l_watchlist *l_watchlist_new(const struct l_watchlist_ops *ops)

You'll need to add LIB_EXPORT to functions that are public, and also update ell.sym.

Quoted 106 lines
ell/watchlist.c new file +100 -0
+{
+	struct l_watchlist *watchlist;
+
+	watchlist = l_new(struct l_watchlist, 1);
+	watchlist->items = l_queue_new();
+	watchlist->ops = ops;
+	return watchlist;
+}
+
+void l_watchlist_init(struct l_watchlist *watchlist,
+					const struct l_watchlist_ops *ops)
+{
+	watchlist->next_id = 0;
+	watchlist->items = l_queue_new();
+	watchlist->ops = ops;
+}
+
+unsigned int l_watchlist_link(struct l_watchlist *watchlist,
+					struct l_watchlist_item *item,
+					void *notify, void *notify_data,
+					l_watchlist_item_destroy_func_t destroy)
+{
+	item->id = ++watchlist->next_id;
+	item->notify = notify;
+	item->notify_data = notify_data;
+	item->destroy = destroy;
+
+	l_queue_push_tail(watchlist->items, item);
+
+	return item->id;
+}
+
+unsigned int l_watchlist_add(struct l_watchlist *watchlist, void *notify,
+					void *notify_data,
+					l_watchlist_item_destroy_func_t destroy)
+{
+	struct l_watchlist_item *item;
+
+	item = l_new(struct l_watchlist_item, 1);
+	return l_watchlist_link(watchlist, item, notify, notify_data, destroy);
+}
+
+bool l_watchlist_remove(struct l_watchlist *watchlist, unsigned int id)
+{
+	struct l_watchlist_item *item;
+
+	if (watchlist->in_notify) {
+		item = l_queue_find(watchlist->items, watchlist_item_match,
+							L_UINT_TO_PTR(id));
+		if (!item)
+			return false;
+
+		item->id = 0;	/* Mark stale */
+		watchlist->stale_items = true;
+
+		return true;
+	}
+
+	item = l_queue_remove_if(watchlist->items, watchlist_item_match,
+							L_UINT_TO_PTR(id));
+	if (!item)
+		return false;
+
+	__watchlist_item_free(watchlist, item);
+
+	return true;
+}
+
+static void __watchlist_clear(struct l_watchlist *watchlist)
+{
+	struct l_watchlist_item *item;
+
+	while ((item = l_queue_pop_head(watchlist->items)))
+		__watchlist_item_free(watchlist, item);
+}
+
+void l_watchlist_destroy(struct l_watchlist *watchlist)
+{
+	__watchlist_clear(watchlist);
+	l_queue_destroy(watchlist->items, NULL);
+	watchlist->items = NULL;
+}
+
+void l_watchlist_free(struct l_watchlist *watchlist)
+{
+	__watchlist_clear(watchlist);
+	l_queue_destroy(watchlist->items, NULL);
+	l_free(watchlist);
+}
+
+void __watchlist_prune_stale(struct l_watchlist *watchlist)
+{
+	struct l_watchlist_item *item;
+
+	while ((item = l_queue_remove_if(watchlist->items, watchlist_item_match,
+							L_UINT_TO_PTR(0))))
+		__watchlist_item_free(watchlist, item);
+
+	watchlist->stale_items = false;
+}
ell/watchlist.h new file +0 -0
new file mode 100644
index 0000000..89a6374

ell/watchlist.h line 3

Quoted 3 lines
ell/watchlist.h new file +3 -0
@@ -0,0 +1,129 @@
+/*
+ *
+ *  Wireless daemon for Linux

Update this too.

Quoted 52 lines
ell/watchlist.h new file +52 -0
+ *
+ *  Copyright (C) 2016-2017  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ELL_WATCHLIST_H
+#define __ELL_WATCHLIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*l_watchlist_item_destroy_func_t)(void *data);
+
+struct l_watchlist_item {
+	unsigned int id;
+	void *notify;
+	void *notify_data;
+	l_watchlist_item_destroy_func_t destroy;
+};
+
+struct l_watchlist_ops {
+	void (*item_free)(struct l_watchlist_item *item);
+};
+
+struct l_watchlist {
+	int next_id;
+	struct l_queue *items;
+	bool in_notify : 1;
+	bool stale_items : 1;
+	const struct l_watchlist_ops *ops;
+};
+
+struct l_watchlist *l_watchlist_new(const struct l_watchlist_ops *ops);
+void l_watchlist_init(struct l_watchlist *watchlist,
+				const struct l_watchlist_ops *ops);
+unsigned int l_watchlist_add(struct l_watchlist *watchlist, void *notify,
+				void *notify_data,

ell/watchlist.h line 65

Quoted 10 lines
ell/watchlist.h new file +10 -0
+				l_watchlist_item_destroy_func_t destroy);
+unsigned int l_watchlist_link(struct l_watchlist *watchlist,
+				struct l_watchlist_item *item,
+				void *notify, void *notify_data,
+				l_watchlist_item_destroy_func_t destroy);
+bool l_watchlist_remove(struct l_watchlist *watchlist, unsigned int id);
+void l_watchlist_destroy(struct l_watchlist *watchlist);
+void l_watchlist_free(struct l_watchlist *watchlist);
+
+void __watchlist_prune_stale(struct l_watchlist *watchlist);

Should this be in the public header?

Thanks,
Mat

Quoted 73 lines
ell/watchlist.h new file +64 -0
+
+#define L_WATCHLIST_NOTIFY(watchlist, type, args...)			\
+	do {								\
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)->items);\
+									\
+		(watchlist)->in_notify = true;				\
+		for (; entry; entry = entry->next) {			\
+			struct l_watchlist_item *item = entry->data;	\
+			type t = item->notify;				\
+			if (item->id == 0)				\
+				continue;				\
+			t(args, item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				\
+		if ((watchlist)->stale_items)				\
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)							\
+
+#define L_WATCHLIST_NOTIFY_MATCHES(watchlist, match, match_data, type, args...) \
+	do {								\
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)->items);\
+									\
+		(watchlist)->in_notify = true;				\
+		for (; entry; entry = entry->next) {			\
+			struct l_watchlist_item *item = entry->data;	\
+			type t = item->notify;				\
+									\
+			if (item->id == 0)				\
+				continue;				\
+			if (!match(item, match_data))			\
+				continue;				\
+									\
+			t(args, item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				\
+		if ((watchlist)->stale_items)				\
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)
+
+#define L_WATCHLIST_NOTIFY_NO_ARGS(watchlist, type)			\
+	do {								\
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)->items);\
+									\
+		(watchlist)->in_notify = true;				\
+		for (; entry; entry = entry->next) {			\
+			struct l_watchlist_item *item = entry->data;	\
+			type t = item->notify;				\
+			if (item->id == 0)				\
+				continue;				\
+			t(item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				\
+		if ((watchlist)->stale_items)				\
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_WATCHLIST_H */
\ No newline at end of file
Signature · 6 lines
2.17.1

_______________________________________________
ell mailing list
ell(a)lists.01.org
https://lists.01.org/mailman/listinfo/ell

--
Mat Martineau
Intel OTC

#4 lore raw

Hi Mat,

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+void __watchlist_prune_stale(struct l_watchlist *watchlist);

Should this be in the public header?

Yeah.. I meant to inquire about this. Its needed for the macros below so it does need to be available as a public API, but its not meant to be called directly. Not sure if ELL has anything else like this. Any suggestions?

Quoted 107 lines

Thanks,
Mat

Quoted 102 lines
(quoted diff) +2 -0
+
+#define L_WATCHLIST_NOTIFY(watchlist, type, args...)		
	\
(quoted diff) +1 -0
+	do {								

\

(quoted diff) +2 -0
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)-
Quoted 1 line

items);\

(quoted diff) +1 -0
+									

\

(quoted diff) +1 -0
+		(watchlist)->in_notify = true;				

\

(quoted diff) +1 -0
+		for (; entry; entry = entry->next) {			

\

(quoted diff) +1 -0
+			struct l_watchlist_item *item = entry->data;	

\

(quoted diff) +1 -0
+			type t = item->notify;				

\

ell/watchlist.h new file +5 -0
+			if (item->id == 0)				\
+				continue;				\
+			t(args, item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				

\

(quoted diff) +1 -0
+		if ((watchlist)->stale_items)				

\

(quoted diff) +2 -0
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)							

\

(quoted diff) +2 -0
+
+#define L_WATCHLIST_NOTIFY_MATCHES(watchlist, match, match_data,

type, args...) \

(quoted diff) +1 -0
+	do {								

\

(quoted diff) +2 -0
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)-
Quoted 1 line

items);\

(quoted diff) +1 -0
+									

\

(quoted diff) +1 -0
+		(watchlist)->in_notify = true;				

\

(quoted diff) +1 -0
+		for (; entry; entry = entry->next) {			

\

(quoted diff) +1 -0
+			struct l_watchlist_item *item = entry->data;	

\

(quoted diff) +1 -0
+			type t = item->notify;				

\

(quoted diff) +1 -0
+									

\

ell/watchlist.h new file +3 -0
+			if (item->id == 0)				\
+				continue;				\
+			if (!match(item, match_data))			

\

(quoted diff) +2 -0
+				continue;				\
+									

\

ell/watchlist.h new file +3 -0
+			t(args, item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				

\

(quoted diff) +1 -0
+		if ((watchlist)->stale_items)				

\

ell/watchlist.h new file +4 -0
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)
+
+#define L_WATCHLIST_NOTIFY_NO_ARGS(watchlist, type)		
	\
(quoted diff) +1 -0
+	do {								

\

(quoted diff) +2 -0
+		const struct l_queue_entry *entry =			\
+				l_queue_get_entries((watchlist)-
Quoted 1 line

items);\

(quoted diff) +1 -0
+									

\

(quoted diff) +1 -0
+		(watchlist)->in_notify = true;				

\

(quoted diff) +1 -0
+		for (; entry; entry = entry->next) {			

\

(quoted diff) +1 -0
+			struct l_watchlist_item *item = entry->data;	

\

(quoted diff) +1 -0
+			type t = item->notify;				

\

ell/watchlist.h new file +5 -0
+			if (item->id == 0)				\
+				continue;				\
+			t(item->notify_data);			\
+		}							\
+		(watchlist)->in_notify = false;				

\

(quoted diff) +1 -0
+		if ((watchlist)->stale_items)				

\

ell/watchlist.h new file +8 -0
+			__watchlist_prune_stale(watchlist);		\
+	} while	(false)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_WATCHLIST_H */
\ No newline at end of file
Signature · 1 line
2.17.1
Quoted 9 lines
Quoted 5 lines

_______________________________________________
ell mailing list
ell(a)lists.01.org
https://lists.01.org/mailman/listinfo/ell

--
Mat Martineau
Intel OTC

Thanks,
James

#5 lore raw

Hi James,

Quoted 9 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+void __watchlist_prune_stale(struct l_watchlist *watchlist);

Should this be in the public header?

Yeah.. I meant to inquire about this. Its needed for the macros below so it does need to be available as a public API, but its not meant to be called directly. Not sure if ELL has anything else like this. Any suggestions?

you need to create a watchlist-private.h for these.

Regards

Marcel