tech-userlevel archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
RFC: generic notification mechanism
Hi!
dyoung proposed pmem(9) some time ago:
http://mail-index.netbsd.org/tech-kern/2008/03/28/msg000781.html
The producers are the MD bootstrap and bus scanning code.
The consumers of this API are the device drivers.
It is obvious, that device drivers wants to get notified when
a memory page turns into bad state, for example.
I have an generic and light-weight notification mechanism
here (see attachment)
which should be useful for many other things as well.
The documentation needs to be converted into real manpage syntax.
I intend to put it somewhere into src/common/ since I think,
it is useful for userland as well.
Any comments ?
Christoph
/* $NetBSD: */
/*
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* This code has been taken from
* the GGI project (http://www.ggi-project.org) and adopted to NetBSD.
*/
/*
******************************************************************************
LibGG - Channel functions
Copyright (C) 2006 Eric Faurot [eric.faurot%gmail.com@localhost]
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/queue.h>
#include "nb_channel.h"
#ifdef DEBUG
#define DPRINT(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DPRINT(fmt, ...) do { } while(0)
#endif
struct nb_observer {
nbfunc_channel_observe_cb *cb;
void *arg;
LIST_ENTRY(nb_observer) _others;
};
struct nb_channel {
LIST_HEAD(nb_observer_list, nb_observer) observers;
#define NHANDLERS 2
nbfunc_channel_control_cb *handler[NHANDLERS];
void *arg;
};
static void
nbClearChannel(struct nb_channel *channel)
{
struct nb_observer *curr, *next;
DPRINT("%s(channel=%p)\n", __func__, channel);
for (curr = LIST_FIRST(&(channel->bservers));
curr != NULL;
curr = next)
{
next = LIST_NEXT(curr, _others);
DPRINT("! observer cb=%p, arg=%p still registered\n",
curr->cb, curr->arg);
LIST_REMOVE(curr, _others);
curr->cb = NULL;
curr->arg = NULL;
free(curr);
}
}
struct nb_channel *
nbNewChannel(void *arg, nbfunc_channel_control_cb *cb)
{
struct nb_channel *channel;
if ((channel = malloc(sizeof(*channel))) == NULL)
return NULL;
LIST_INIT(&(channel->observers));
channel->arg = arg;
channel->handler[0] = cb;
channel->handler[1] = NULL;
return channel;
}
void
nbDelChannel(struct nb_channel *channel)
{
ASSERT(channel != NULL, "invalid channel\n");
nbClearChannel(channel);
free(channel);
}
int
nbSetController(struct nb_channel *channel, nbfunc_channel_control_cb *cb)
{
channel->handler[1] = cb;
return 0;
}
struct nb_observer *
nbObserve(struct nb_channel *channel, nbfunc_channel_observe_cb *cb, void *arg)
{
struct nb_observer *observer;
DPRINT("%s(channel=%p, cb=%p, arg=%p)\n", channel, cb, arg);
observer = calloc(1, sizeof(*observer));
if (observer == NULL) {
DPRINT("! can not alloc mem for observer.\n");
return NULL;
}
observer->arg = arg;
observer->cb = cb;
LIST_INSERT_HEAD(&(channel->observers), observer, _others);
return observer;
}
void
nbDelObserver(struct nb_observer *observer)
{
ASSERT(observer != NULL, "invalid observer\n");
DPRINT("%s(observer=%p)\n", observer);
LIST_REMOVE(observer, _others);
observer->cb = NULL;
observer->arg = NULL;
free(observer);
}
void
nbBroadcast(struct nb_channel *channel, uint32_t msg, void *data)
{
struct nb_observer *curr, *next;
DPRINT("%s(channel=%p, msg=0x%x, data=%p)\n",
channel, msg, data);
for (curr = LIST_FIRST(&(channel->observers));
curr != NULL;
curr = next)
{
next = LIST_NEXT(curr, _others);
if (curr->cb(curr->arg, msg, data)) {
nbDelObserver(curr);
}
}
}
int
nbControl(struct nb_channel *channel, uint32_t ctl, void *data)
{
int err, i;
DPRINT("%s(channel=%p, ctl=0x%x, data=%p)\n",
channel, ctl, data);
err = ENOENT;
for (i = 0; i < NHANDLERS && err == ENOENT; i++) {
if (channel->handler[i]) {
err = channel->handler[i](channel->arg, ctl, data);
}
}
return err;
}
/* $NetBSD: */
/*
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* This code has been taken from
* the GGI project (http://www.ggi-project.org) and adopted to NetBSD.
*/
/*
******************************************************************************
LibGG - Channel functions
Copyright (C) 2006 Eric Faurot [eric.faurot%gmail.com@localhost]
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************
*/
#include <stdint.h>
struct nb_channel;
typedef int (nbfunc_channel_control_cb)(void *arg, uint32_t ctl, void *data);
typedef int (nbfunc_channel_observe_cb)(void *arg, uint32_t msg, void *data);
struct nb_channel *nbNewChannel(void *, nbfunc_channel_control_cb *);
void nbDelChannel(struct nb_channel *);
int nbSetController(struct nb_channel *, nbfunc_channel_control_cb *);
struct nb_observer *nbObserve(struct nb_channel *,
nbfunc_channel_observe_cb *,
void *);
void nbDelObserver(struct nb_observer *);
void nbBroadcast(struct nb_channel *, uint32_t, void *);
int nbControl(struct nb_channel *, uint32_t, void *);
nbchannel(3)
NAME
nbNewChannel, nbDelChannel, nbSetController, nbObserve, nbDelObserver,
nbBroadcast, nbControl : generic message framework
SYNOPSIS
#include <sys/nbchannel.h>
typedef int (nbfunc_channel_control_cb)(void *arg, uint32_t ctl, void
*data);
typedef int (nbfunc_channel_observe_cb)(void *arg, uint32_t msg, void
*data);
struct nb_channel *nbNewChannel(void *arg, nbfunc_channel_control_cb
*cb);
void nbDelChannel(struct nb_channel *channel);
int nbSetController(struct nb_channel *channel,
nbfunc_channel_control_cb *cb);
struct nb_observer *nbObserve(struct nb_channel *,
nbfunc_channel_observe_cb *,
void *);
void nbDelObserver(struct nb_observer *observer);
void nbBroadcast(struct nb_channel *channel, uint32_t msg, void *data);
int nbControl(struct nb_channel *channel, uint32_t ctl, void *data);
DESCRIPTION
This set of functions provide a simple message-passing infrastructure,
consisting of a general observer pattern and command dispatching imple-
mentation. It is primary intended to support the light-weight coopera-
tion model within kernel subsystems. It is also very useful to any
userland application or library writer.
struct nb_channel defines a channel on which observers can be regis-
tered. An observer is simply an opaque value and a callback receiving
that value as first argument, a flag, and an opaque event-specific mes-
sage. The idea is that if you know the channel you're listening on,
you know the semantics behind the flag and the message. When the chan-
nel is triggered, all observers' callbacks will be fired. Specific
control commands can also sent to a channel, pretty much in the way
fcntl(2) or sysctl(3) work.
nbNewChannel creates a new communication channel. The arg parameter
will be passed as the first argument of controllers callback. Gener-
ally, this will be a pointer to the entity that is managed through this
specific channel. The cb callback is the primary controller that will
be tried when nbControl is called on that channel. If this callback is
NULL, or if it returns ENOENT, then the secondary controller (set by
nbSetController) will be tried. This allows the creator of the
channel to define a set of non-overridable controls, and let some other
code (for example a module) install an additional set of specific con-
trols.
nbSetController installs cb as the secondary controller on a channel,
as discussed above. If one was already set, it is replaced.
nbDelChannel frees a channel as well as observers still registered on
it. If at least one is left, then there is probably a logical error in
the observer code, since it must already have been notified somehow of
the channel going down, and unregistered all callbacks before.
nbObserve registers a new observer on the channel. The callback will be
fired when an event is broadcasted on that channel. The arg parameter
is a user value that will be passed as the first argument of the call-
back.
nbDelObserver unregisters the given observer from its channel and frees
it. Note that within a callback, an observer must not call nbDelOb-
server on itselt to unregister. Instead it must return a non-zero
value.
nbBroadcast triggers all observers registered on the channel. msg and
data will be passed to the observers' callback.
nbControl sends a specific control code identified by ctl to the chan-
nel. This triggers the channel controllers.
RETURN VALUES
nbNewChannel returns a newly allocated channel, or NULL on error.
nbSetController returns 0 or a negative error code on failure.
nbObserve returns a newly constructed observer hook. Normally, the
caller will have to keep a reference to it if he needs to call nbDelOb-
server later.
nbControl returns 0 or a negative error code on failure. The ENOENT
error code means that the requested control code was not recognized.
EXAMPLE
#include <sys/nb_channel.h>
#include <stdio.h>
int update(void *o, uint32_t msg, void *d)
{
printf("update called for observer %p, msg=%i, data=%p\n", o, f ,d);
if (msg == 1) {
return 1; /* unregister */
}
return 0;
}
int control(void *a, uint32_t ctl, void *s)
{
switch(ctl) {
case 0:
printf("You say \"%s\", I say \"Hello\"!\n", );
break;
default:
return GGI_NOFUNC;
}
return 0;
}
int main(void)
{
struct nb_channel *chn;
struct nb_observer *o1, *o2;
chn = nbNewChannel(NULL, control);
o1 = nbObserve(chn, update, (void*)1);
o2 = nbObserve(chn, update, (void*)2);
nbBroadcast(chn, 0, NULL);
nbBroadcast(chn, 1, NULL);
nbControl(chn, 0, "Goodbye");
nbDelChannel(chn);
return 0;
}
Home |
Main Index |
Thread Index |
Old Index