這個pattern設計是根據kernel的notification chain做一些修改, 主要應用於眾多subscriber註冊於某個有興趣的event ID, 會根據註冊時的權重由大到小串成link-list, 當publisher執行blocking_notif_call(event_id, int val, void *v)時, 就會依序call這些subscriber註冊的callback function, 達到通知的效果.
notification-chain.h
#ifndef NOTIFICATION_CHAIN_H
#define NOTIFICATION_CHAIN_H
enum notif_id {
/* 新的ID請加在這裡 */
notif_id_network,
notif_id_lte,
notif_id_max
};
int notif_init(void);
typedef int (*notif_call_fp)(char const * caller, unsigned long val, void *v);
int _notif_reg(enum notif_id nid,
notif_call_fp notif_call,
char *notif_call_name,
int priority);
#define notif_reg(nid, notif_call, priority) _notif_reg(nid, notif_call, #notif_call, priority);
int notif_unreg(enum notif_id nid, notif_call_fp notif_call);
char * notif_dump(char *buf, int sz);
int _blocking_notif_call(enum notif_id nid,
unsigned long val,
void *v,
char const * caller);
#define blocking_notif_call(nid, val, v) _blocking_notif_call(nid, val, v, __FUNCTION__)
int async_notif_call(enum notif_id nid,
unsigned long val ,
void *v ,
void (*free_v)(void *v));
#endif
notification-chain.c
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
#include "notification-chain.h"
struct notif_block {
notif_call_fp notif_call;
char *notif_call_name;
struct notif_block *next;
int priority;
int called;
};
struct notif_chain_head {
pthread_mutex_t mutex;
pthread_cond_t cond;
struct notif_block *next;
};
struct notif_statistical {
unsigned int alloc;
unsigned int alloced;
unsigned int freed;
};
struct notif_statistical notif_statistical = {};
static struct notif_chain_head *_notif_chain_tab = NULL;
static int _init_notif_chain(struct notif_chain_head **h)
{
int i;
*h = (struct notif_chain_head *) malloc(sizeof(struct notif_chain_head) * notif_id_max);
if (!*h) {
return -1;
}
for (i = 0; i < notif_id_max; i++) {
pthread_mutex_init(&((*h)[i].mutex), NULL);
pthread_cond_init(&((*h)[i].cond), NULL);
(*h)[i].next = NULL;
}
return 0;
}
int notif_init(void)
{
int ret = 0;
if ((ret = _init_notif_chain(&_notif_chain_tab)) < 0) {
return ret;
}
return ret;
}
static int _notif_check_parms(enum notif_id nid,
notif_call_fp notif_call,
char *notif_call_name)
{
if (!_notif_chain_tab) {
return -1;
}
if ((nid < 0) || (nid >= notif_id_max)) {
return -1;
}
if (!notif_call) {
return -1;
}
if (!notif_call_name) {
return -1;
}
return 0;
}
static int _notif_block_alloc(struct notif_block **n)
{
notif_statistical.alloc++;
*n = (struct notif_block*) malloc(sizeof(struct notif_block));
if (!(*n)) {
return -1;
}
memset(*n, 0, sizeof(struct notif_block));
notif_statistical.alloced++;
return 0;
}
static int _notif_block_free(struct notif_block *n)
{
notif_statistical.freed++;
if (n->notif_call_name) {
free(n->notif_call_name);
}
if (n) {
free(n);
}
return 0;
}
int _notif_reg(enum notif_id nid,
notif_call_fp notif_call,
char *notif_call_name,
int priority)
{
struct notif_block **n, *nb;
if (_notif_check_parms(nid, notif_call, notif_call_name) < 0) {
return -1;
}
if (_notif_block_alloc(&nb) < 0) {
return -1;
}
nb->notif_call = notif_call;
nb->notif_call_name = strdup(notif_call_name);
if (!nb->notif_call_name) {
_notif_block_free(nb);
return -1;
}
nb->priority = priority;
pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
n = &(_notif_chain_tab[nid].next);
while ((*n) && ((*n)->priority > nb->priority)) {
n = &((*n)->next);
}
nb->next = *n;
*n = nb;
pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));
return 0;
}
int notif_unreg(enum notif_id nid, notif_call_fp notif_call)
{
struct notif_block **n, *sn = NULL;
if (_notif_check_parms(nid, notif_call, (void *) 0xFF ) < 0) {
return -1;
}
pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
n = &(_notif_chain_tab[nid].next);
while ((*n)) {
if ((*n)->notif_call == notif_call) {
sn = *n;
*n = (*n)->next;
break;
}
n = &((*n)->next);
}
pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));
if (!sn) {
return -1;
}
_notif_block_free(sn);
return 0;
}
int _blocking_notif_call(enum notif_id nid,
unsigned long val,
void *v,
char const * caller)
{
struct notif_block *n;
if (_notif_check_parms(nid, (void*) 0xFF , (void *) 0xFF ) < 0) {
return -1;
}
pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
n = _notif_chain_tab[nid].next;
while (n) {
n->notif_call(caller, val, v);
n->called++;
n = n->next;
}
pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));
return 0;
}
char * notif_dump(char *buf, int sz)
{
int i;
char *p = buf;
struct notif_block *n;
memset(buf, 0, sz);
p += snprintf(p, sz - (buf - p), "\tnotif_fp/notif_fp_name/pri called\n");
for (i = 0; i < notif_id_max; i++) {
pthread_mutex_lock(&(_notif_chain_tab[i].mutex));
n = _notif_chain_tab[i].next;
p += snprintf(p, sz - (buf - p), "notif id: %d\n", i);
while (n) {
p += snprintf(p, sz - (buf - p), "\t%p/%s/%d %d\n",
n->notif_call, n->notif_call_name, n->priority, n->called);
n = n->next;
}
pthread_mutex_unlock(&(_notif_chain_tab[i].mutex));
}
return buf;
}
main.c
#include <stdio.h>
#include <unistd.h>
#include "notification-chain.h"
/* 這裡有用service-reg作為publisher */
#include "service-reg.h"
#define LINK_DOWN 0
#define LINK_UP 1
void *publisher(void *v)
{
int *link_state = (int *) v;
++(*link_state);
(*link_state) %= 2;
blocking_notif_call(notif_id_network, *link_state, NULL);
}
int link_notif_call1(char const *caller, unsigned long val, void *v)
{
printf("%s(#%d): caller:%s, %s\n",
__FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
return 0;
}
int link_notif_call2(char const *caller, unsigned long val, void *v)
{
printf("%s(#%d): caller:%s, %s\n",
__FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
return 0;
}
int link_notif_call3(char const *caller, unsigned long val, void *v)
{
printf("%s(#%d): caller:%s, %s\n",
__FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
return 0;
}
int main(int argc, char *argv[])
{
int link_state = 0;
char buf[1024];
/* 請先call notif_init()做初始化 */
notif_init();
srv_reg("publisher", publisher, &link_state);
srv_start_periodical("publisher", 1, 0);
sleep(2);
printf("reg 1, 10\n");
notif_reg(notif_id_network, link_notif_call1, 10);
sleep(2);
printf("dump\n");
printf("%s\n", notif_dump(buf, sizeof(buf)));
sleep(2);
printf("reg 3, 30\n");
notif_reg(notif_id_network, link_notif_call3, 30);
sleep(2);
printf("reg 2, 20\n");
notif_reg(notif_id_network, link_notif_call2, 20);
sleep(2);
printf("dump\n");
printf("%s\n", notif_dump(buf, sizeof(buf)));
sleep(2);
printf("unreg 3\n");
notif_unreg(notif_id_network, link_notif_call3);
sleep(2);
printf("dump\n");
printf("%s\n", notif_dump(buf, sizeof(buf)));
sleep(2);
printf("unreg 1 lte \n");
notif_unreg(notif_id_lte, link_notif_call1);
sleep(2);
printf("dump\n");
printf("%s\n", notif_dump(buf, sizeof(buf)));
return 0;
}
result
brook@vista:~/notification-chain$ ./demo
reg 1, 10
link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
dump
notif_fp/notif_fp_name/pri called
notif id: 0
0x400fa4/link_notif_call1/10 2
notif id: 1
link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
reg 3, 30
link_notif_call3(#34): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
reg 2, 20
link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
notif_fp/notif_fp_name/pri called
notif id: 0
0x401046/link_notif_call3/30 4
0x400ff5/link_notif_call2/20 2
0x400fa4/link_notif_call1/10 8
notif id: 1
link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 3
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
notif_fp/notif_fp_name/pri called
notif id: 0
0x400ff5/link_notif_call2/20 6
0x400fa4/link_notif_call1/10 12
notif id: 1
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 1 lte
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
notif_fp/notif_fp_name/pri called
notif id: 0
0x400ff5/link_notif_call2/20 10
0x400fa4/link_notif_call1/10 16
notif id: 1