summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/connlist.c
blob: 272e65cac1a9d8181a10f9a94475c2a857069acc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "connlist.h"

conn_list_t conn_list = { NULL, NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER };

static h2o_stream_conn_t **conn_list_get_null_element_unsafe(conn_list_t *list)
{
    struct conn_list_leaf *leaf = list->head;
    while (leaf != NULL) {
        for (int i = 0; i < CONN_LIST_MEMPOOL_SIZE; i++) {
            if (leaf->conn[i] == NULL)
                return &leaf->conn[i];
        }
        leaf = leaf->next;
    }
    return NULL;
}

void conn_list_insert(conn_list_t *list, h2o_stream_conn_t *conn)
{
    pthread_mutex_lock(&list->lock);

    // in case the allocated capacity is not used up
    // we can reuse the null element
    if (list->capacity != list->size) {
        h2o_stream_conn_t **null_element = conn_list_get_null_element_unsafe(list);
        if (unlikely(null_element == NULL)) {
            pthread_mutex_unlock(&list->lock);
            error_report("conn_list_insert: capacity != size but no null element found");
            return;
        }
        *null_element = conn;
        list->size++;
        pthread_mutex_unlock(&list->lock);
        return;
    }

    // if not, we need to allocate a new leaf
    struct conn_list_leaf *old_tail = list->tail;
    list->tail = callocz(1, sizeof(struct conn_list_leaf));
    if (unlikely(old_tail == NULL))
        list->head = list->tail;
    else
        old_tail->next = list->tail;
    
    list->tail->conn[0] = conn;
    list->size++;
    list->capacity += CONN_LIST_MEMPOOL_SIZE;

    pthread_mutex_unlock(&list->lock);
}

typedef struct {
    conn_list_t *list;
    struct conn_list_leaf *leaf;
    int idx;
} conn_list_iter_t;

static inline void conn_list_iter_create_unsafe(conn_list_iter_t *iter, conn_list_t *list)
{
    iter->list = list;
    iter->leaf = list->head;
    iter->idx = 0;
}

static inline int conn_list_iter_next_unsafe(conn_list_iter_t *iter, h2o_stream_conn_t **conn)
{
    if (unlikely(iter->idx == iter->list->capacity))
        return 0;

    if (iter->idx && iter->idx % CONN_LIST_MEMPOOL_SIZE == 0) {
        iter->leaf = iter->leaf->next;
    }

    *conn = iter->leaf->conn[iter->idx++ % CONN_LIST_MEMPOOL_SIZE];
    return 1;
}

void conn_list_iter_all(conn_list_t *list, void (*cb)(h2o_stream_conn_t *conn))
{
    pthread_mutex_lock(&list->lock);
    conn_list_iter_t iter;
    conn_list_iter_create_unsafe(&iter, list);
    h2o_stream_conn_t *conn;
    while (conn_list_iter_next_unsafe(&iter, &conn)) {
        if (conn == NULL)
            continue;
        cb(conn);
    }
    pthread_mutex_unlock(&list->lock);
}

static void conn_list_garbage_collect_unsafe(conn_list_t *list)
{
    if (list->capacity - list->size > CONN_LIST_MEMPOOL_SIZE) {
        struct conn_list_leaf *new_tail = list->head;
        while (new_tail->next != list->tail)
            new_tail = new_tail->next;

        // check if the tail leaf is empty and move the data if not
        for (int i = 0; i < CONN_LIST_MEMPOOL_SIZE; i++) {
            if (list->tail->conn[i] != NULL) {
                h2o_stream_conn_t **null_element = conn_list_get_null_element_unsafe(list);
                if (unlikely(null_element == NULL)) {
                    error_report("conn_list_garbage_collect_unsafe: list->capacity - list->size > CONN_LIST_MEMPOOL_SIZE but no null element found?");
                    return;
                }
                *null_element = list->tail->conn[i];
                list->tail->conn[i] = NULL;
            }
        }

        freez(list->tail);
        new_tail->next = NULL;
        list->tail = new_tail;
        list->capacity -= CONN_LIST_MEMPOOL_SIZE;
    }
}

static inline int conn_list_iter_remove(conn_list_iter_t *iter, h2o_stream_conn_t *conn)
{
    if (unlikely(iter->idx == iter->list->capacity))
        return -1;

    if (iter->idx && iter->idx % CONN_LIST_MEMPOOL_SIZE == 0) {
        iter->leaf = iter->leaf->next;
    }

    if(conn == iter->leaf->conn[iter->idx % CONN_LIST_MEMPOOL_SIZE]) {
        iter->leaf->conn[iter->idx % CONN_LIST_MEMPOOL_SIZE] = NULL;

        iter->idx++;
        return 1;
    }

    iter->idx++;
    return 0;
}

int conn_list_remove_conn(conn_list_t *list, h2o_stream_conn_t *conn)
{
    pthread_mutex_lock(&list->lock);
    conn_list_iter_t iter;
    conn_list_iter_create_unsafe(&iter, list);
    int rc;
    while (!(rc = conn_list_iter_remove(&iter, conn)));
    if (rc == -1) {
        pthread_mutex_unlock(&list->lock);
        error_report("conn_list_remove_conn: conn not found");
        return 0;
    }
    list->size--;
    conn_list_garbage_collect_unsafe(list);
    pthread_mutex_unlock(&list->lock);
    return 1;
}