Timer time Event Monitor in Libev

Source: Internet
Author: User

1. Data structure

#define EV_AT (W) ((WT) (W))->at
#define Ev_active (W) ((W) (W))->active

typedef ev_watcher_time *WT;

struct EV_LOOP
{
Ev_tstamp Mn_now
Anhe * Timers
int Timermax
int timercnt

Ev_watcher * rfeeds
}

/* HEAP Entry *//whether the at field in the time monitor is cached.
#if ev_heap_cache_at
/* A HEAP element */
typedef struct {
Ev_tstamp at;
WT W;
} Anhe;

#define ANHE_W (HE). w/* Access Watcher, Read-write */
#define ANHE_AT (HE). AT/* Access cached AT, Read-only */
#define Anhe_at_cache (HE). At = (HE). W->at/* Update at from Watcher */
#else
/* A HEAP element */
typedef WT ANHE;

#define ANHE_W (He) (he)
#define ANHE_AT (He)->at
#define Anhe_at_cache (HE)
#endif

2, Ev_timer_start

void Noinline
Ev_timer_start (ev_p_ ev_timer *w) ev_throw
{
if (Expect_false (Ev_is_active (w)))
Return

Ev_at (w) + = Mn_now;

++timercnt;
Ev_start (Ev_a_ (w) W, timercnt + heap0-1);//w->active = timercnt + heap0-1;
Array_needsize (Anhe, Timers, Timermax, Ev_active (w) + 1, EMPTY2);
Anhe_w (Timers [Ev_active (w)]) = (WT) W;
Anhe_at_cache (Timers [Ev_active (W)]);
Upheap (Timers, Ev_active (w));

Ev_frequent_check;

/*assert ("libev:internal Timer heap corruption", Timers [Ev_active (w)] = = (WT) w)); */
}

Inline_speed void
Upheap (Anhe *heap, int k)
{
Anhe he = heap [K];

for (;;)
{
int p = hparent (k);

if (Upheap_done (P, k) | | Anhe_at (heap [P]) <= anhe_at (He))
Break

heap [k] = heap [P];
Ev_active (Anhe_w (heap [k])) = k;
K = p;
}

heap [k] = he;
Ev_active (Anhe_w (He)) = k;
}

3, Timers_reify

inline_size void
Timers_reify (ev_p)
{
Ev_frequent_check;

if (timercnt && anhe_at (Timers [HEAP0]) < Mn_now)
{
Do
{
Ev_timer *w = (Ev_timer *) anhe_w (timers [HEAP0]);

/*assert (("Libev:inactive Timer on Timer heap detected", Ev_is_active (w))); * *

/* First reschedule or stop timer */
if (w->repeat)
{
Ev_at (w) + = w->repeat;
if (Ev_at (W) < Mn_now)
Ev_at (W) = Mn_now;

Assert (("Libev:negative Ev_timer repeat value found while processing timers", W->repeat > 0.));

Anhe_at_cache (Timers [HEAP0]);
Downheap (timers, timercnt, HEAP0);
}
Else
Ev_timer_stop (Ev_a_ W); /* Nonrepeating:stop Timer */

Ev_frequent_check;
Feed_reverse (Ev_a_ (w) w);
}
while (timercnt && anhe_at (Timers [HEAP0]) < Mn_now);

Feed_reverse_done (Ev_a_ Ev_timer);
}
}

#if ev_use_4heap

#define DHEAP 4
#define HEAP0 (DHEAP-1)/* Index of first element in Heap */
#define Hparent (k) ((((k)-heap0-1)/dheap) + HEAP0)
#define Upheap_done (P,k) ((p) = = (k))

/* away from the root */
Inline_speed void
Downheap (anhe *heap, int N, int k)
{
Anhe he = heap [K];
Anhe *e = heap + N + HEAP0;

for (;;)
{
Ev_tstamp Minat;
Anhe *minpos;
Anhe *pos = heap + dheap * (k-heap0) + HEAP0 + 1;

/* Find minimum child */
if (Expect_true (pos + DHEAP-1 < E))
{
/* Fast path */(Minpos = pos + 0), (Minat = Anhe_at (*minpos));
if (Anhe_at (POS [1]) < Minat) (Minpos = pos + 1), (Minat = Anhe_at (*minpos));
if (Anhe_at (POS [2]) < Minat) (Minpos = pos + 2), (Minat = Anhe_at (*minpos));
if (Anhe_at (POS [3]) < Minat) (Minpos = pos + 3), (Minat = Anhe_at (*minpos));
}
else if (Pos < E)
{
/* Slow Path */(Minpos = pos + 0), (Minat = Anhe_at (*minpos));
if (pos + 1 < E && Anhe_at (pos [1]) < Minat) (Minpos = pos + 1), (Minat = Anhe_at (*minpos));
if (pos + 2 < E && Anhe_at (POS [2]) < Minat) (Minpos = pos + 2), (Minat = Anhe_at (*minpos));
if (pos + 3 < E && Anhe_at (POS [3]) < Minat) (Minpos = pos + 3), (Minat = Anhe_at (*minpos));
}
Else
Break

if (Anhe_at (He) <= Minat)
Break

heap [k] = *minpos;
Ev_active (Anhe_w (*minpos)) = k;

K = Minpos-heap;
}

heap [k] = he;
Ev_active (Anhe_w (He)) = k;
}

#else/* 4HEAP */

#define HEAP0 1
#define Hparent (k) ((k) >> 1)
#define Upheap_done (P,K) (! ( P))

/* away from the root */
Inline_speed void
Downheap (anhe *heap, int N, int k)
{
Anhe he = heap [K];

for (;;)
{
int c = k << 1;

if (c >= N + HEAP0)
Break

c + = C + 1 < N + HEAP0 && anhe_at (heap [C]) > Anhe_at (heap [C + 1])
? 1:0;

if (Anhe_at (He) <= anhe_at (heap [c]))
Break

heap [k] = heap [C];
Ev_active (Anhe_w (heap [k])) = k;

K = c;
}

heap [k] = he;
Ev_active (Anhe_w (He)) = k;
}
#endif

Inline_speed void
Feed_reverse (ev_p_ w W)
{
Array_needsize (W, Rfeeds, Rfeedmax, rfeedcnt + 1, EMPTY2);
rfeeds [rfeedcnt++] = w;
}

inline_size void
Feed_reverse_done (ev_p_ int revents)
{
Do
Ev_feed_event (Ev_a_ rfeeds [--rfeedcnt], revents);
while (RFEEDCNT);
}

void Noinline
Ev_feed_event (Ev_p_ void *w, int revents) Ev_throw
{
W w_ = (w) w;
int pri = ABSPRI (w_);

if (Expect_false (w_->pending))
pendings [pri][w_->pending-1].events |= revents;
Else
{
w_->pending = ++pendingcnt [pri];
Array_needsize (anpending, Pendings [pri], Pendingmax [pri], w_->pending, EMPTY2);
pendings [PRI][W_->PENDING-1].W = W_;
pendings [pri][w_->pending-1].events = revents;
}

Pendingpri = NUMPRI-1;
}

4, Ev_run
int Ev_run (EV_P_ int flags)
{
waittime = Max_blocktime;

if (timercnt)
{
Ev_tstamp to = Anhe_at (timers [HEAP0])-mn_now;
if (Waittime > To) waittime = to;

。。。。。
}
Timers_reify (ev_a);
ev_invoke_pending;
}

Libev in the management of timers, the heap is used to store ev_timer, in addition to the minimum 2 fork heap, there are 4 fork heap, which can be set by macro definition to use which.

For n-fork heaps, when the array is used to store the element labeled X, the subscript range for the child's node is [Nx+1, Nx+n]. For example, the 2-fork heap, the subscript is the element of X, the children of the node subscript is 2x+1 and 2x+2.

According to the theorem, for a 4-fork heap, the subscript is an element of x, and the subscript range of its children's nodes is [4x+1, 4x+4]. It can also be concluded that the subscript of its parent node is (x-1)/4. In Libev's code, however, when you use array A to store the heap, the first element of the 4-fork heap is stored in the first element of the a[3],2 fork Heap in a[1].

Therefore, for the implementation of the 4 fork heap in Libev, the element labeled K (corresponding to the subscript in the normal implementation is k-3), the child node's subscript range is [4 (k-3) +1+3, 4 (k-3) +4+3]; its parent node subscript is ((k-3-1)/4) +3.

For the 2-fork heap implementation in Libev, the subscript is the element of K (corresponding to the normal implementation, its subscript is k-1), the child node subscript range is [2 (k-1) +1+1, 2 (k-1) +2+1], that is [2k, 2k+1]; its parent node subscript is ((k-1-1)/2) +1, namely K/2.

The DOWNHEAP and UPHEAP functions use the above principles to compare, exchange, and eventually form a heap with sub-nodes or parent nodes.

First,Ev_timer_start adds the time Monitor to the timers (via Upheap), and Loop->timer is the smallest heap in an array form. According to Timer->at, the top of the heap is the monitor with the smallest time, and the timer->active is the subscript of the array.

In ev_run , the timeout is calculated first so that it is not greater than the minimum time.

Finally, the time listener that is removed to thetimers_reify is added to the pendings queue. If it is repeat, then update the next trigger time, call the downheap operation to move the node down to the appropriate location, otherwise directly delete the watcher.

Ev_prepare, Ev_check, Ev_idle

From a character perspective, these three types of watcher are actually an extension point of the event loop. With these three watcher, developers have the opportunity to get callbacks at special moments in the event loop.

    • The ev_prepare is triggered before the event loop is blocked.

    • The ev_check is triggered after the event loop is blocked. The triggering of Ev_check is prioritized by priority. It is guaranteed that the Ev_check is the first watcher to be triggered when the block at the same priority level is closed. So, if you want to ensure that Ev_check is the first to be executed, you can set it to the highest priority.

    • Ev_idle is triggered when no other watcher is triggered. Ev_idle are also prioritized by priority. Its semantics is that no watcher is triggered at the current priority and higher priority, then it is triggered, regardless of whether any other watcher is triggered at a lower priority.

These three types of watcher provide a very convenient extension mechanism for external developers, on the basis of which developers can do a lot of interesting things, but also have more control over the event loop. Exactly what you can do and how you do it depends on the imagination and creativity of the developers:)

Timer time Event Monitor in Libev

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.