aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/aws-c-io/source/message_pool.c
blob: 3090f57e6bf3eba9eb36dbb1874dcec394c166fe (plain) (blame)
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#include <aws/io/message_pool.h>

#include <aws/common/thread.h>

int aws_memory_pool_init(
    struct aws_memory_pool *mempool,
    struct aws_allocator *alloc,
    uint16_t ideal_segment_count,
    size_t segment_size) {

    mempool->alloc = alloc;
    mempool->ideal_segment_count = ideal_segment_count;
    mempool->segment_size = segment_size;
    mempool->data_ptr = aws_mem_calloc(alloc, ideal_segment_count, sizeof(void *));
    if (!mempool->data_ptr) {
        return AWS_OP_ERR;
    }

    aws_array_list_init_static(&mempool->stack, mempool->data_ptr, ideal_segment_count, sizeof(void *));

    for (uint16_t i = 0; i < ideal_segment_count; ++i) {
        void *memory = aws_mem_acquire(alloc, segment_size);
        if (memory) {
            aws_array_list_push_back(&mempool->stack, &memory);
        } else {
            goto clean_up;
        }
    }

    return AWS_OP_SUCCESS;

clean_up:
    aws_memory_pool_clean_up(mempool);
    return AWS_OP_ERR;
}

void aws_memory_pool_clean_up(struct aws_memory_pool *mempool) {
    void *cur = NULL;

    while (aws_array_list_length(&mempool->stack) > 0) {
        /* the only way this fails is not possible since I already checked the length. */
        aws_array_list_back(&mempool->stack, &cur);
        aws_array_list_pop_back(&mempool->stack);
        aws_mem_release(mempool->alloc, cur);
    }

    aws_array_list_clean_up(&mempool->stack);
    aws_mem_release(mempool->alloc, mempool->data_ptr);
}

void *aws_memory_pool_acquire(struct aws_memory_pool *mempool) {
    void *back = NULL;
    if (aws_array_list_length(&mempool->stack) > 0) {
        aws_array_list_back(&mempool->stack, &back);
        aws_array_list_pop_back(&mempool->stack);

        return back;
    }

    void *mem = aws_mem_acquire(mempool->alloc, mempool->segment_size);
    return mem;
}

void aws_memory_pool_release(struct aws_memory_pool *mempool, void *to_release) {
    size_t pool_size = aws_array_list_length(&mempool->stack);

    if (pool_size >= mempool->ideal_segment_count) {
        aws_mem_release(mempool->alloc, to_release);
        return;
    }

    aws_array_list_push_back(&mempool->stack, &to_release);
}

struct message_pool_allocator {
    struct aws_allocator base_allocator;
    struct aws_message_pool *msg_pool;
};

void *s_message_pool_mem_acquire(struct aws_allocator *allocator, size_t size) {
    (void)allocator;
    (void)size;

    /* no one should ever call this ever. */
    AWS_ASSERT(0);
    return NULL;
}

void s_message_pool_mem_release(struct aws_allocator *allocator, void *ptr) {
    struct message_pool_allocator *msg_pool_alloc = allocator->impl;

    aws_message_pool_release(msg_pool_alloc->msg_pool, (struct aws_io_message *)ptr);
}

static size_t MSG_OVERHEAD = sizeof(struct aws_io_message) + sizeof(struct message_pool_allocator);

int aws_message_pool_init(
    struct aws_message_pool *msg_pool,
    struct aws_allocator *alloc,
    struct aws_message_pool_creation_args *args) {

    msg_pool->alloc = alloc;

    size_t msg_data_size = args->application_data_msg_data_size + MSG_OVERHEAD;

    if (aws_memory_pool_init(
            &msg_pool->application_data_pool, alloc, args->application_data_msg_count, msg_data_size)) {
        return AWS_OP_ERR;
    }

    size_t small_blk_data_size = args->small_block_msg_data_size + MSG_OVERHEAD;

    if (aws_memory_pool_init(&msg_pool->small_block_pool, alloc, args->small_block_msg_count, small_blk_data_size)) {
        aws_memory_pool_clean_up(&msg_pool->application_data_pool);
        return AWS_OP_ERR;
    }

    return AWS_OP_SUCCESS;
}

void aws_message_pool_clean_up(struct aws_message_pool *msg_pool) {
    aws_memory_pool_clean_up(&msg_pool->application_data_pool);
    aws_memory_pool_clean_up(&msg_pool->small_block_pool);
    AWS_ZERO_STRUCT(*msg_pool);
}

struct message_wrapper {
    struct aws_io_message message;
    struct message_pool_allocator msg_allocator;
    uint8_t buffer_start[1];
};

struct aws_io_message *aws_message_pool_acquire(
    struct aws_message_pool *msg_pool,
    enum aws_io_message_type message_type,
    size_t size_hint) {

    struct message_wrapper *message_wrapper = NULL;
    size_t max_size = 0;
    switch (message_type) {
        case AWS_IO_MESSAGE_APPLICATION_DATA:
            if (size_hint > msg_pool->small_block_pool.segment_size - MSG_OVERHEAD) {
                message_wrapper = aws_memory_pool_acquire(&msg_pool->application_data_pool);
                max_size = msg_pool->application_data_pool.segment_size - MSG_OVERHEAD;
            } else {
                message_wrapper = aws_memory_pool_acquire(&msg_pool->small_block_pool);
                max_size = msg_pool->small_block_pool.segment_size - MSG_OVERHEAD;
            }
            break;
        default:
            AWS_ASSERT(0);
            aws_raise_error(AWS_IO_CHANNEL_UNKNOWN_MESSAGE_TYPE);
            return NULL;
    }

    if (!message_wrapper) {
        return NULL;
    }

    message_wrapper->message.message_type = message_type;
    message_wrapper->message.message_tag = 0;
    message_wrapper->message.user_data = NULL;
    message_wrapper->message.copy_mark = 0;
    message_wrapper->message.on_completion = NULL;
    /* the buffer shares the allocation with the message. It's the bit at the end. */
    message_wrapper->message.message_data.buffer = message_wrapper->buffer_start;
    message_wrapper->message.message_data.len = 0;
    message_wrapper->message.message_data.capacity = size_hint <= max_size ? size_hint : max_size;

    /* set the allocator ptr */
    message_wrapper->msg_allocator.base_allocator.impl = &message_wrapper->msg_allocator;
    message_wrapper->msg_allocator.base_allocator.mem_acquire = s_message_pool_mem_acquire;
    message_wrapper->msg_allocator.base_allocator.mem_realloc = NULL;
    message_wrapper->msg_allocator.base_allocator.mem_release = s_message_pool_mem_release;
    message_wrapper->msg_allocator.msg_pool = msg_pool;

    message_wrapper->message.allocator = &message_wrapper->msg_allocator.base_allocator;
    return &message_wrapper->message;
}

void aws_message_pool_release(struct aws_message_pool *msg_pool, struct aws_io_message *message) {

    memset(message->message_data.buffer, 0, message->message_data.len);
    message->allocator = NULL;

    struct message_wrapper *wrapper = AWS_CONTAINER_OF(message, struct message_wrapper, message);

    switch (message->message_type) {
        case AWS_IO_MESSAGE_APPLICATION_DATA:
            if (message->message_data.capacity > msg_pool->small_block_pool.segment_size - MSG_OVERHEAD) {
                aws_memory_pool_release(&msg_pool->application_data_pool, wrapper);
            } else {
                aws_memory_pool_release(&msg_pool->small_block_pool, wrapper);
            }
            break;
        default:
            AWS_ASSERT(0);
            aws_raise_error(AWS_IO_CHANNEL_UNKNOWN_MESSAGE_TYPE);
    }
}