/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/uds-releases/jasper/src/uds/util/eventCount.h#1 $ */ #ifndef EVENT_COUNT_H #define EVENT_COUNT_H #include "timeUtils.h" #include "typeDefs.h" /** * An EventCount is a lock-free equivalent of a condition variable. * * Using an EventCount, a lock-free producer/consumer can wait for a state * change (adding an item to an empty queue, for example) without spinning or * falling back on the use of mutex-based locks. Signalling is cheap when * there are no waiters (a memory fence), and preparing to wait is * also inexpensive (an atomic add instruction). * * A lock-free producer should call eventCountBroadcast() after any mutation * to the lock-free data structure that a consumer might be waiting on. The * consumers should poll for work like this: * * for (;;) { * // Fast path--no additional cost to consumer. * if (lockFreeDequeue(&item)) { * return item; * } * // Two-step wait: get current token and poll state, either cancelling * // the wait or waiting for the token to be signalled. * EventToken token = eventCountPrepare(ec); * if (lockFreeDequeue(&item)) { * eventCountCancel(ec, token); * return item; * } * eventCountWait(ec, token, NULL); * // State has changed, but must check condition again, so loop. * } * * Once eventCountPrepare() is called, the caller should neither dally, sleep, * nor perform long-running or blocking actions before passing the token to * eventCountCancel() or eventCountWait(). The implementation is optimized for * a short polling window, and will not perform well if there are outstanding * tokens that have been signalled but not waited upon. **/ typedef struct eventCount EventCount; typedef unsigned int EventToken; /** * Allocate and initialize an EventCount. * * @param ecPtr a pointer to hold the new EventCount **/ __attribute__((warn_unused_result)) int makeEventCount(EventCount **ecPtr); /** * Free an EventCount. It must no longer be in use. * * @param ec the EventCount to free **/ void freeEventCount(EventCount *ec); /** * Wake all threads that are waiting for the next event. * * @param ec the EventCount to signal **/ void eventCountBroadcast(EventCount *ec); /** * Prepare to wait for the EventCount to change by capturing a token of its * current state. The caller MUST eventually either call eventCountWait() or * eventCountCancel() exactly once for each token obtained. * * @param ec the EventCount on which to prepare to wait * * @return an EventToken to be passed to the next eventCountWait() call **/ EventToken eventCountPrepare(EventCount *ec) __attribute__((warn_unused_result)); /** * Cancel a wait token that has been prepared but not waited upon. This must * be called after eventCountPrepare() when eventCountWait() is not going to * be invoked on the token. * * @param ec the EventCount from which a wait token was obtained * @param token the wait token that will never be passed to eventCountWait() **/ void eventCountCancel(EventCount *ec, EventToken token); /** * Check if the current event count state corresponds to the provided token, * and if it is, wait for a signal that the state has changed. If an optional * timeout is provided, the wait will terminate after the timeout has elapsed. * Timing out automatically cancels the wait token, so callers must not * attempt to cancel the token on timeout. * * @param ec the EventCount on which to wait * @param token the EventToken returned by eventCountPrepare() * @param timeout either NULL or a relative timeout for the wait operation * * @return true if the state has already changed or if signalled, otherwise * false if a timeout was provided and the wait timed out **/ bool eventCountWait(EventCount *ec, EventToken token, const RelTime *timeout); #endif /* EVENT_COUNT_H */