openflow build environment setup
This commit is contained in:
648
openflow/include/click/task.hh
Normal file
648
openflow/include/click/task.hh
Normal file
@ -0,0 +1,648 @@
|
||||
// -*- c-basic-offset: 4; related-file-name: "../../lib/task.cc" -*-
|
||||
#ifndef CLICK_TASK_HH
|
||||
#define CLICK_TASK_HH
|
||||
#include <click/element.hh>
|
||||
#include <click/sync.hh>
|
||||
#if HAVE_MULTITHREAD
|
||||
# include <click/atomic.hh>
|
||||
# include <click/ewma.hh>
|
||||
#endif
|
||||
CLICK_DECLS
|
||||
|
||||
#if CLICK_BSDMODULE
|
||||
# include <click/cxxprotect.h>
|
||||
CLICK_CXX_PROTECT
|
||||
# include <sys/lock.h>
|
||||
# include <sys/mutex.h>
|
||||
CLICK_CXX_UNPROTECT
|
||||
# include <click/cxxunprotect.h>
|
||||
#else
|
||||
#define GIANT_REQUIRED
|
||||
#endif
|
||||
|
||||
#define PASS_GT(a, b) ((int)(a - b) > 0)
|
||||
|
||||
typedef bool (*TaskCallback)(Task *, void *);
|
||||
typedef TaskCallback TaskHook CLICK_DEPRECATED;
|
||||
class RouterThread;
|
||||
class TaskList;
|
||||
class Master;
|
||||
|
||||
struct TaskLink {
|
||||
#if !HAVE_TASK_HEAP
|
||||
TaskLink *_prev;
|
||||
TaskLink *_next;
|
||||
#endif
|
||||
#if HAVE_STRIDE_SCHED
|
||||
unsigned _pass;
|
||||
#endif
|
||||
TaskLink() {
|
||||
#if !HAVE_TASK_HEAP
|
||||
_prev = _next = 0;
|
||||
#endif
|
||||
#if HAVE_STRIDE_SCHED
|
||||
_pass = 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class Task : private TaskLink { public:
|
||||
|
||||
#if HAVE_STRIDE_SCHED
|
||||
enum { STRIDE1 = 1U<<16, MAX_STRIDE = 1U<<31 };
|
||||
enum { MAX_TICKETS = 1<<15, DEFAULT_TICKETS = 1<<10 };
|
||||
#endif
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
enum { MAX_UTILIZATION = 1000 };
|
||||
#endif
|
||||
|
||||
/** @brief Construct a task that calls @a f with @a user_data argument.
|
||||
*
|
||||
* @param f callback function
|
||||
* @param user_data argument for callback function
|
||||
*
|
||||
* Constructs a task that, when fired, calls @a f like so:
|
||||
*
|
||||
* @code
|
||||
* bool work_done = f(task, user_data);
|
||||
* @endcode
|
||||
*
|
||||
* where @a task is a pointer to this task. @a f should return true if
|
||||
* the task accomplished some meaningful work, and false if it did not.
|
||||
* For example, a task that polls a network driver for packets should
|
||||
* return true if it emits at least one packet, and false if no packets
|
||||
* were available. */
|
||||
inline Task(TaskCallback f, void *user_data);
|
||||
|
||||
/** @brief Construct a task that calls @a e ->@link Element::run_task(Task*) run_task()@endlink.
|
||||
*
|
||||
* @param e element to call
|
||||
*
|
||||
* Constructs a task that, when fired, calls the element @a e's @link
|
||||
* Element::run_task(Task *) run_task()@endlink method, passing this Task
|
||||
* as an argument.
|
||||
*
|
||||
* @sa Task(TaskCallback, void *) */
|
||||
inline Task(Element *e);
|
||||
|
||||
/** @brief Destroy a task.
|
||||
*
|
||||
* Unschedules the task if necessary. */
|
||||
~Task();
|
||||
|
||||
|
||||
/** @brief Return the task's callback function.
|
||||
*
|
||||
* Returns null if the task was constructed with the Task(Element *)
|
||||
* constructor. */
|
||||
inline TaskCallback callback() const {
|
||||
return _hook;
|
||||
}
|
||||
|
||||
/** @brief Return the task callback function's user data. */
|
||||
inline void *user_data() const {
|
||||
return _thunk;
|
||||
}
|
||||
|
||||
/** @brief Return the task's owning element. */
|
||||
inline Element *element() const {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Return true iff the task has been initialize()d. */
|
||||
inline bool initialized() const;
|
||||
|
||||
/** @brief Return the task's home thread ID.
|
||||
*
|
||||
* This is the @link RouterThread::thread_id() thread_id()@endlink of the
|
||||
* thread on which this Task would run if it were scheduled. This need
|
||||
* not equal the ID of the current thread(), since changes in
|
||||
* home_thread_id() aren't always implemented immediately (because of
|
||||
* locking issues). */
|
||||
inline int home_thread_id() const;
|
||||
|
||||
/** @brief Return the thread on which this task is currently scheduled,
|
||||
* or would be scheduled.
|
||||
*
|
||||
* Usually, task->thread()->@link RouterThread::thread_id()
|
||||
* thread_id()@endlink == task->home_thread_id(). They can differ,
|
||||
* however, if move_thread() was called but the task hasn't yet been moved
|
||||
* to the new thread. */
|
||||
inline RouterThread *thread() const;
|
||||
|
||||
/** @brief Return the router to which this task belongs. */
|
||||
inline Router *router() const {
|
||||
return _owner->router();
|
||||
}
|
||||
|
||||
/** @brief Return the master where this task will be scheduled. */
|
||||
Master *master() const;
|
||||
|
||||
|
||||
/** @brief Initialize the Task, and optionally schedule it.
|
||||
* @param owner specifies the element owning the Task
|
||||
* @param schedule if true, the Task will be scheduled immediately
|
||||
*
|
||||
* This function must be called on every Task before it is used. The
|
||||
* corresponding router's ThreadSched, if any, is used to determine the
|
||||
* task's initial thread assignment. The task initially has the default
|
||||
* number of tickets, and is scheduled iff @a schedule is true.
|
||||
*
|
||||
* An assertion will fail if a Task is initialized twice.
|
||||
*
|
||||
* Most elements call ScheduleInfo::initialize_task() to initialize a Task
|
||||
* object. The ScheduleInfo method additionally sets the task's
|
||||
* scheduling parameters, such as ticket count and thread preference,
|
||||
* based on a router's ScheduleInfo. ScheduleInfo::initialize_task()
|
||||
* calls Task::initialize(). */
|
||||
void initialize(Element *owner, bool schedule);
|
||||
|
||||
/** @brief Initialize the Task, and optionally schedule it.
|
||||
* @param router specifies the router owning the Task
|
||||
* @param schedule if true, the Task will be scheduled immediately
|
||||
*
|
||||
* This function is shorthand for @link Task::initialize(Element *, bool)
|
||||
* Task::initialize@endlink(@a router ->@link Router::root_element
|
||||
* root_element@endlink(), @a scheduled). However, it is better to
|
||||
* explicitly associate tasks with real elements. */
|
||||
void initialize(Router *router, bool schedule);
|
||||
|
||||
|
||||
/** @brief Return true iff the task is currently scheduled to run.
|
||||
*
|
||||
* @note A scheduled task will usually run very soon, but not
|
||||
* always; due to locking issues, the effects of some reschedule()
|
||||
* requests may be delayed. Although a task unscheduled with
|
||||
* strong_unschedule() may appear scheduled(), it will not run
|
||||
* until strong_reschedule() is called. */
|
||||
inline bool scheduled() const {
|
||||
return _status.is_scheduled;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Unschedule the task.
|
||||
*
|
||||
* After unschedule() returns, the task will not run until it is
|
||||
* rescheduled with reschedule().
|
||||
*
|
||||
* @sa reschedule, strong_unschedule */
|
||||
inline void unschedule() {
|
||||
_status.is_scheduled = false;
|
||||
}
|
||||
|
||||
/** @brief Reschedule the task.
|
||||
*
|
||||
* The task is rescheduled on its home thread. It will eventually run,
|
||||
* unless its home thread is quiescent or it has been
|
||||
* strong_unschedule()d.
|
||||
*
|
||||
* @sa unschedule, strong_reschedule */
|
||||
inline void reschedule() {
|
||||
_status.is_scheduled = true;
|
||||
click_fence();
|
||||
if (_pending_nextptr.x < 2)
|
||||
complete_schedule(0);
|
||||
}
|
||||
|
||||
/** @brief Reschedule a task from the task's callback function.
|
||||
*
|
||||
* @warning Only call @a task.fast_reschedule() while @a task is being
|
||||
* fired, i.e., in its callback function. It is an error to call
|
||||
* @task.fast_reschedule() at other times -- the task may not actually be
|
||||
* rescheduled.
|
||||
*
|
||||
* Here's a typical, correct use of fast_reschedule():
|
||||
*
|
||||
* @code
|
||||
* class MyElement : public Element {
|
||||
* ... Task _task; ... bool run_task(Task *t); ...
|
||||
* };
|
||||
* bool MyElement::run_task(Task *) {
|
||||
* do_some_work();
|
||||
* _task.fast_reschedule();
|
||||
* return true;
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* This assumes, however, that run_task() is only called directly by the
|
||||
* driver. If you call run_task() from another context, _task may not
|
||||
* actually be scheduled.
|
||||
*
|
||||
* @code
|
||||
* void MyElement::run_timer(Timer *) {
|
||||
* run_task(); // XXX might not reschedule _task!
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
inline void fast_reschedule() {
|
||||
_status.is_scheduled = true;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Unschedule the Task until strong_reschedule().
|
||||
*
|
||||
* Like unschedule(), but in addition, future reschedule() calls
|
||||
* will not actually schedule the task. Only after strong_reschedule()
|
||||
* will the task run again.
|
||||
* @sa strong_reschedule, unschedule
|
||||
*/
|
||||
inline void strong_unschedule() {
|
||||
_status.is_scheduled = false;
|
||||
_status.is_strong_unscheduled = true;
|
||||
}
|
||||
|
||||
/** @brief Reschedule the Task, undoing a prior strong_unschedule().
|
||||
*
|
||||
* This function undoes any previous strong_unschedule() and
|
||||
* reschedules the task.
|
||||
* @sa reschedule, strong_unschedule
|
||||
*/
|
||||
inline void strong_reschedule() {
|
||||
_status.is_strong_unscheduled = false;
|
||||
reschedule();
|
||||
}
|
||||
|
||||
|
||||
/** @brief Move the Task to a new home thread.
|
||||
*
|
||||
* The home thread ID is set to @a new_thread_id. The task, if it is
|
||||
* currently scheduled, is rescheduled on thread @a new_thread_id
|
||||
* (which generally takes some time to take effect). If @a new_thread_id
|
||||
* is less than zero or greater than the number of threads on the router,
|
||||
* the task is scheduled on a quiescent thread that never actually runs.
|
||||
*/
|
||||
void move_thread(int new_thread_id);
|
||||
|
||||
|
||||
#if HAVE_STRIDE_SCHED
|
||||
inline int tickets() const;
|
||||
inline void set_tickets(int n);
|
||||
inline void adjust_tickets(int delta);
|
||||
#endif
|
||||
|
||||
inline bool fire();
|
||||
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
inline unsigned runs() const;
|
||||
inline unsigned work_done() const;
|
||||
inline unsigned utilization() const;
|
||||
inline void clear_runs();
|
||||
#endif
|
||||
#if HAVE_MULTITHREAD
|
||||
inline int cycles() const;
|
||||
inline unsigned cycle_runs() const;
|
||||
inline void update_cycles(unsigned c);
|
||||
#endif
|
||||
|
||||
/** @cond never */
|
||||
inline TaskCallback hook() const CLICK_DEPRECATED;
|
||||
inline void *thunk() const CLICK_DEPRECATED;
|
||||
/** @endcond never */
|
||||
|
||||
private:
|
||||
|
||||
#if HAVE_TASK_HEAP
|
||||
int _schedpos;
|
||||
#endif
|
||||
|
||||
#if HAVE_STRIDE_SCHED
|
||||
unsigned _stride;
|
||||
int _tickets;
|
||||
#endif
|
||||
|
||||
union Status {
|
||||
struct {
|
||||
int16_t home_thread_id;
|
||||
uint8_t is_scheduled;
|
||||
uint8_t is_strong_unscheduled;
|
||||
};
|
||||
uint32_t status;
|
||||
} _status;
|
||||
|
||||
TaskCallback _hook;
|
||||
void *_thunk;
|
||||
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
unsigned _runs;
|
||||
unsigned _work_done;
|
||||
#endif
|
||||
#if HAVE_MULTITHREAD
|
||||
DirectEWMA _cycles;
|
||||
unsigned _cycle_runs;
|
||||
#endif
|
||||
|
||||
RouterThread *_thread;
|
||||
|
||||
Element *_owner;
|
||||
|
||||
union Pending {
|
||||
Task *t;
|
||||
uintptr_t x;
|
||||
};
|
||||
Pending _pending_nextptr;
|
||||
|
||||
Task(const Task &x);
|
||||
Task &operator=(const Task &x);
|
||||
void cleanup();
|
||||
|
||||
#if CLICK_DEBUG_SCHEDULING
|
||||
public:
|
||||
#endif
|
||||
inline bool on_scheduled_list() const;
|
||||
inline bool on_pending_list() const {
|
||||
return _pending_nextptr.x != 0;
|
||||
}
|
||||
inline bool needs_cleanup() const;
|
||||
#if CLICK_DEBUG_SCHEDULING
|
||||
private:
|
||||
#endif
|
||||
|
||||
void add_pending(bool always);
|
||||
void process_pending(RouterThread* thread);
|
||||
|
||||
void complete_schedule(RouterThread* process_pending_thread);
|
||||
inline void remove_from_scheduled_list();
|
||||
|
||||
static bool error_hook(Task *task, void *user_data);
|
||||
|
||||
friend class RouterThread;
|
||||
friend class Master;
|
||||
};
|
||||
|
||||
|
||||
// need RouterThread's definition for inline functions
|
||||
CLICK_ENDDECLS
|
||||
#include <click/routerthread.hh>
|
||||
CLICK_DECLS
|
||||
|
||||
|
||||
inline
|
||||
Task::Task(TaskCallback f, void *user_data)
|
||||
:
|
||||
#if HAVE_TASK_HEAP
|
||||
_schedpos(-1),
|
||||
#endif
|
||||
#if HAVE_STRIDE_SCHED
|
||||
_stride(0), _tickets(-1),
|
||||
#endif
|
||||
_hook(f), _thunk(user_data),
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
_runs(0), _work_done(0),
|
||||
#endif
|
||||
#if HAVE_MULTITHREAD
|
||||
_cycle_runs(0),
|
||||
#endif
|
||||
_thread(0), _owner(0)
|
||||
{
|
||||
_status.home_thread_id = -2;
|
||||
_status.is_scheduled = _status.is_strong_unscheduled = false;
|
||||
_pending_nextptr.x = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
Task::Task(Element* e)
|
||||
:
|
||||
#if HAVE_TASK_HEAP
|
||||
_schedpos(-1),
|
||||
#endif
|
||||
#if HAVE_STRIDE_SCHED
|
||||
_stride(0), _tickets(-1),
|
||||
#endif
|
||||
_hook(0), _thunk(e),
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
_runs(0), _work_done(0),
|
||||
#endif
|
||||
#if HAVE_MULTITHREAD
|
||||
_cycle_runs(0),
|
||||
#endif
|
||||
_thread(0), _owner(0)
|
||||
{
|
||||
_status.home_thread_id = -2;
|
||||
_status.is_scheduled = _status.is_strong_unscheduled = false;
|
||||
_pending_nextptr.x = 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Task::initialized() const
|
||||
{
|
||||
return _owner != 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Task::on_scheduled_list() const
|
||||
{
|
||||
#if HAVE_TASK_HEAP
|
||||
return _schedpos >= 0;
|
||||
#else
|
||||
return _prev != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
Task::needs_cleanup() const
|
||||
{
|
||||
#if HAVE_TASK_HEAP
|
||||
int a;
|
||||
uintptr_t b;
|
||||
do {
|
||||
a = _schedpos;
|
||||
b = _pending_nextptr.x;
|
||||
click_fence();
|
||||
} while (a != _schedpos || b != _pending_nextptr.x);
|
||||
return a >= 0 || b != 0;
|
||||
#else
|
||||
TaskLink* a;
|
||||
uintptr_t b;
|
||||
do {
|
||||
a = _prev;
|
||||
b = _pending_nextptr.x;
|
||||
click_fence();
|
||||
} while (a != _prev || b != _pending_nextptr.x);
|
||||
return a != 0 || b != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @cond never */
|
||||
/** @brief Return the task's callback function.
|
||||
* @deprecated Use callback() instead. */
|
||||
inline TaskCallback
|
||||
Task::hook() const
|
||||
{
|
||||
return _hook;
|
||||
}
|
||||
|
||||
/** @brief Return the task's callback data.
|
||||
* @deprecated Use user_data() instead. */
|
||||
inline void *
|
||||
Task::thunk() const
|
||||
{
|
||||
return _thunk;
|
||||
}
|
||||
/** @endcond never */
|
||||
|
||||
inline int
|
||||
Task::home_thread_id() const
|
||||
{
|
||||
return _status.home_thread_id;
|
||||
}
|
||||
|
||||
inline RouterThread *
|
||||
Task::thread() const
|
||||
{
|
||||
return _thread;
|
||||
}
|
||||
|
||||
inline void
|
||||
Task::remove_from_scheduled_list()
|
||||
{
|
||||
if (on_scheduled_list()) {
|
||||
#if HAVE_TASK_HEAP
|
||||
Task *back = _thread->_task_heap.back().t;
|
||||
_thread->_task_heap.pop_back();
|
||||
if (_thread->_task_heap.size() > 0)
|
||||
_thread->task_reheapify_from(_schedpos, back);
|
||||
click_fence();
|
||||
_schedpos = -1;
|
||||
#else
|
||||
_next->_prev = _prev;
|
||||
_prev->_next = _next;
|
||||
_next = 0;
|
||||
click_fence();
|
||||
_prev = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_STRIDE_SCHED
|
||||
|
||||
/** @brief Return the task's number of tickets.
|
||||
*
|
||||
* Tasks with larger numbers of tickets are scheduled more often. Tasks are
|
||||
* initialized with tickets() == DEFAULT_TICKETS.
|
||||
*
|
||||
* @sa set_tickets, adjust_tickets
|
||||
*/
|
||||
inline int
|
||||
Task::tickets() const
|
||||
{
|
||||
return _tickets;
|
||||
}
|
||||
|
||||
/** @brief Set the task's ticket count.
|
||||
* @param n the ticket count
|
||||
*
|
||||
* The ticket count @a n is pinned to the range [1, MAX_TICKETS].
|
||||
*
|
||||
* @sa tickets, adjust_tickets
|
||||
*/
|
||||
inline void
|
||||
Task::set_tickets(int n)
|
||||
{
|
||||
if (n > MAX_TICKETS)
|
||||
n = MAX_TICKETS;
|
||||
else if (n < 1)
|
||||
n = 1;
|
||||
_tickets = n;
|
||||
_stride = STRIDE1 / n;
|
||||
assert(_stride < MAX_STRIDE);
|
||||
}
|
||||
|
||||
/** @brief Add @a delta to the Task's ticket count.
|
||||
* @param delta adjustment to the ticket count
|
||||
*
|
||||
* The ticket count cannot be adjusted below 1 or above MAX_TICKETS.
|
||||
*
|
||||
* @sa set_tickets
|
||||
*/
|
||||
inline void
|
||||
Task::adjust_tickets(int delta)
|
||||
{
|
||||
set_tickets(_tickets + delta);
|
||||
}
|
||||
|
||||
#endif /* HAVE_STRIDE_SCHED */
|
||||
|
||||
|
||||
/** @brief Fire the task by calling its callback function.
|
||||
*
|
||||
* This function is generally called by the RouterThread implementation; there
|
||||
* should be no need to call it yourself.
|
||||
*/
|
||||
inline bool
|
||||
Task::fire()
|
||||
{
|
||||
#if CLICK_STATS >= 2
|
||||
click_cycles_t start_cycles = click_get_cycles(),
|
||||
start_child_cycles = _owner->_child_cycles;
|
||||
#endif
|
||||
#if HAVE_MULTITHREAD
|
||||
_cycle_runs++;
|
||||
#endif
|
||||
bool work_done;
|
||||
if (!_hook)
|
||||
work_done = ((Element*)_thunk)->run_task(this);
|
||||
else
|
||||
work_done = _hook(this, _thunk);
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
++_runs;
|
||||
_work_done += work_done;
|
||||
#endif
|
||||
#if CLICK_STATS >= 2
|
||||
click_cycles_t all_delta = click_get_cycles() - start_cycles,
|
||||
own_delta = all_delta - (_owner->_child_cycles - start_child_cycles);
|
||||
_owner->_task_calls += 1;
|
||||
_owner->_task_own_cycles += own_delta;
|
||||
#endif
|
||||
return work_done;
|
||||
}
|
||||
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
inline unsigned
|
||||
Task::runs() const
|
||||
{
|
||||
return _runs;
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
Task::work_done() const
|
||||
{
|
||||
return _work_done;
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
Task::utilization() const
|
||||
{
|
||||
return (_runs ? (MAX_UTILIZATION * _work_done) / _runs : 0);
|
||||
}
|
||||
|
||||
inline void
|
||||
Task::clear_runs()
|
||||
{
|
||||
_runs = _work_done = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_MULTITHREAD
|
||||
inline int
|
||||
Task::cycles() const
|
||||
{
|
||||
return _cycles.unscaled_average();
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
Task::cycle_runs() const
|
||||
{
|
||||
return _cycle_runs;
|
||||
}
|
||||
|
||||
inline void
|
||||
Task::update_cycles(unsigned c)
|
||||
{
|
||||
_cycles.update(c);
|
||||
_cycle_runs = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
CLICK_ENDDECLS
|
||||
#endif
|
||||
Reference in New Issue
Block a user