A QUEUE can function as either a list head (pointer to the first list
element) or a list node. So the QUEUE fields in uv_loop_t are list
heads that point to list nodes that are embedded in uv_handle_t or
uv_req_t structs.
> 2. The below example shows how the API of QUEUE is used:
>
> static void uv__run_pending(uv_loop_t* loop) {
> QUEUE* q;
> uv__io_t* w;
>
> while (!QUEUE_EMPTY(&loop->pending_queue)) {
> q = QUEUE_HEAD(&loop->pending_queue);
> QUEUE_REMOVE(q);
> QUEUE_INIT(q);
>
> w = QUEUE_DATA(q, uv__io_t, pending_queue);
> w->cb(loop, w, UV__POLLOUT);
> }
> }
>
>
> I think the use of QUEUE_EMPTY and QUEUE_HEAD is not hard to guess and
> QUEUE_INIT just sets both QUEUE items to "q".
>
> But how can I understand QUEUE_REMOVE and QUEUE_DATA?
>
> #define QUEUE_REMOVE(q)
> \
> do {
> \
> QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q);
> \
> QUEUE_NEXT_PREV(q) = QUEUE_PREV(q);
> \
> }
> \
> while (0)
>
>
> #define QUEUE_DATA(ptr, type, field)
> \
> ((type *) ((char *) (ptr) - ((long) &((type *) 0)->field)))
The short answer is 'magic'. :-)
The longer answer is that QUEUE_REMOVE() unlinks the node from the
list by making its prev element point to its next element and vice
versa.
QUEUE_DATA() is just a container_of() macro: it calculates and returns
the address of the embedding struct.
I should mention that none of this is really specific to libuv. The
QUEUE type is very similar to the Linux kernel's struct list_head or
nginx's ngx_queue_t (which we used to use until some time ago.)