Page MenuHomeFreeBSD
Paste P185

wait-free event handlers
ActivePublic

Authored by avg on Jun 22 2018, 7:18 AM.
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Andriy Gapon <avg@freebsd.org>
*
* 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 AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _SYS_WFHANDLER_H_
#define _SYS_WFHANDLER_H_
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
typedef void wfhandler_cb(void *); /* XXX */
struct wfhandler_entry {
SLIST_ENTRY(wfhandler_entry) wfe_link;
wfhandler_cb *wfe_func;
void *wfe_arg;
int wfe_priority;
};
struct wfhandler_list {
struct mtx wfl_lock;
SLIST_HEAD(, wfhandler_entry) wfl_entries;
volatile int wfl_phase;
volatile int wfl_active[2];
};
/* Generic priority levels */
#define WFHANDLER_PRI_FIRST 0
#define WFHANDLER_PRI_ANY 10000
#define WFHANDLER_PRI_LAST 20000
void
wfhandler_list_init(const char *name, struct wfhandler_list *list)
{
mtx_init(&list->wfl_lock, name, NULL, MTX_DEF);
SLIST_INIT(&list->wfl_entries);
list->wfl_phase = 0;
list->wfl_active[0] = 0;
list->wfl_active[1] = 0;
}
void
wfhandler_register(struct wfhandler_list *list,
struct wfhandler_entry *entry)
{
struct wfhandler_entry *elm;
struct wfhandler_entry **prevptr;
/* Serialize modifications. */
mtx_lock(&list->wfl_lock);
LIST_FOREACH_PREVPTR(elm, prevptr, &list->wfl_entries, wfe_link) {
if (elm->wfe_priority > entry->wfe_priority)
break;
}
/*
* A replacement for SLIST_INSERT_AFTER that ensures that the new
* element is linked into the list after its next pointer has been
* setup. Also, the previous element is not required.
* prevptr, a pointer to the previous element's or the list head's
* pointer to the next element, is sufficient.
*/
SLIST_NEXT(entry, wfe_link) = elm;
atomic_store_rel_ptr(prevptr, entry);
mtx_unlock(&list->wfl_lock);
}
void
wfhandler_deregister(struct wfhandler_list *list,
struct wfhandler_entry *entry)
{
struct wfhandler_entry *elm;
struct wfhandler_entry **prevptr;
int phase;
/* Serialize modifications. */
mtx_lock(&list->wfl_lock);
phase = list->wfl_phase;
SLIST_FOREACH_PREVPTR(elm, prevptr, &list->wfl_entries, wfe_link) {
if (elm == entry) {
/*
* Replacement for SLIST_REMOVE_AFTER and
* SLIST_REMOVE_HEAD that does not trash the next
* pointer in the removed element. It also can use
* prevptr instead of the previous element or the list
* head.
*/
*prevptr = SLIST_NEXT(entry, wfe_link);
break;
}
}
KASSERT(elm != NULL, ("entry %p is not on handler list"));
/* Switch new readers to the other busy counter. */
KASSERT(&list->wfl_active[!phase] == 0, ("idle generation is busy"));
atomic_store_rel_int(&list->wfl_phase, !phase);
/*
* Make sure that the list and generation updates are not reordered
* with the check.
*/
atomic_thread_fence_seq_cst();
/*
* Make sure that later operations are not reordered before the check.
*/
while (atomic_load_acq_int(&list->wfl_active[gen]) > 0)
cpu_pause();
mtx_unlock(&list->wfl_lock);
/* This code pokes into sys/queue.h internals. */
TRASHIT(entry->wfe_link.sle_next);
}
void
wfhandler_invoke(struct wfhandler_list *list)
{
struct wfhandler_entry *elm;
int phase;
/*
* Ensure that all loads are after the busy count is updated
* for the current generation.
*/
phase = list->wfl_phase;
atomic_add_acq_int(&list->wfl_active[phase], 1);
SLIST_FOREACH(elm, &list->wfl_entries, wfe_link)
elm->wfe_func(elm->wfe_arg);
/* Ensure that all loads are before the flag is reset. */
atomic_add_rel_int(&list->wfl_active[phase], -1);
}
#endif /* _SYS_WFHANDLER_H_ */

Event Timeline

avg created this paste.Jun 22 2018, 7:18 AM
avg created this object in space S1 Global.
avg created this object with edit policy "avg (Andriy Gapon)".