src/server/daemon/resourcepool.c

changeset 385
a1f4cb076d2f
parent 371
ea836c4f7341
child 415
d938228c382e
equal deleted inserted replaced
210:21274e5950af 385:a1f4cb076d2f
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2022 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "resourcepool.h"
30 #include "request.h"
31 #include "session.h"
32 #include "../public/nsapi.h"
33 #include "../util/atomic.h"
34
35 #define RESOURCE_POOL_MAX_DEFAULT 32
36
37 #define RESOURCE_POOL_MAX_ALLOC 268435455
38
39 static UcxMap *resource_pool_types;
40
41 int init_resource_pools(void) {
42 resource_pool_types = ucx_map_new(4);
43 return resource_pool_types ? 0 : 1;
44 }
45
46 int resourcepool_register_type(const char *type_name, ResourceType *type_info) {
47 if(ucx_map_cstr_put(resource_pool_types, type_name, type_info)) {
48 log_ereport(LOG_CATASTROPHE, "resourcepool_register_type: OOM");
49 return 1;
50 }
51 return 0;
52 }
53
54
55
56 int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node) {
57 ResourceType *restype = ucx_map_sstr_get(resource_pool_types, type);
58 if(!restype) {
59 log_ereport(LOG_MISCONFIG, "Unknown resource pool type: %s", type.ptr);
60 return 1;
61 }
62
63 // convert ConfigNode to pblock
64 // no sub-objects allowed for this specific ConfigNode, therefore
65 // it can be represented as key-value-pairs
66 pblock *param = config_obj2pblock(cfg->pool, node);
67 if(!param) {
68 log_ereport(LOG_FAILURE, "resourcepool_new: OOM");
69 return 1;
70 }
71
72 ResourcePool *respool = pool_malloc(cfg->pool, sizeof(ResourcePool));
73 if(!respool) {
74 log_ereport(LOG_FAILURE, "resourcepool_new: OOM");
75 return 1;
76 }
77 respool->pool = cfg->pool;
78
79 void *respool_data = restype->init(cfg->pool, name.ptr, param);
80 if(!respool_data) {
81 log_ereport(LOG_FAILURE, "Cannot create resource pool data: pool: %s type: %s", name.ptr, type.ptr);
82 return 1;
83 }
84
85 respool->type = restype;
86 respool->data = respool_data;
87 respool->min = 4; // TODO: get from node
88 respool->max = RESOURCE_POOL_MAX_DEFAULT; // TODO: get from node
89
90 respool->numcreated = 0;
91 respool->numresources = 0;
92
93
94 // don't allow too large resource pools
95 // this prevents the need to check malloc integer overflows
96 if(respool->max > RESOURCE_POOL_MAX_ALLOC) {
97 respool->max = RESOURCE_POOL_MAX_ALLOC;
98 log_ereport(LOG_WARN, "Resource pool %s: limit max to %d", name.ptr, respool->max);
99 }
100
101 respool->resalloc = respool->max;
102 respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(ResourceDataPrivate*));
103
104 if(!respool->resources || ucx_map_sstr_put(cfg->resources, name, respool)) {
105 log_ereport(LOG_FAILURE, "Cannot add resource pool: OOM");
106 // the only cleanup we have to do
107 restype->destroy(respool_data);
108 return 1;
109 }
110
111 pthread_mutex_init(&respool->lock, NULL);
112 pthread_cond_init(&respool->available, NULL);
113
114 return 0;
115 }
116
117 static ResourceData* s_resourcepool_lookup(ServerConfiguration *cfg, Request *opt_rq, Session *opt_sn, const char *name, int flags) {
118 NSAPIRequest *request = (NSAPIRequest*)opt_rq;
119 NSAPISession *session = (NSAPISession*)opt_sn;
120 ResourceDataPrivate *resource = NULL;
121
122 // was this resource already used by this request?
123 if(request && request->resources) {
124 resource = ucx_map_cstr_get(request->resources, name);
125 if(resource) {
126 return &resource->data;
127 }
128 }
129
130 ResourcePool *respool = ucx_map_cstr_get(cfg->resources, name);
131 if(!respool) return NULL;
132
133
134 pthread_mutex_lock(&respool->lock);
135 WSBool createResource = FALSE;
136 if(respool->numcreated < respool->min) {
137 createResource = TRUE;
138 }
139
140 if(createResource) {
141 // create a new resource and store it in the resourcepool
142 void *resourceData = respool->type->createresource(respool->data);
143 if(resourceData) {
144 respool->numcreated++;
145
146 resource = pool_malloc(respool->pool, sizeof(ResourceDataPrivate));
147 if(resource) {
148 resource->data.data = respool->type->getresourcedata(resourceData);
149 resource->data.resourcepool = respool;
150 resource->resdata = resourceData;
151 } else {
152 respool->type->freeresource(respool->data, resourceData);
153 log_ereport(LOG_CATASTROPHE, "resourcepool_lookup: OOM");
154 }
155 }
156 // else: respool->type->createresource does logging in case of errors
157 } else if(respool->numresources > 0) {
158 resource = respool->resources[--respool->numresources];
159 } else {
160 // wait for free resource
161 pthread_cond_wait(&respool->available, &respool->lock);
162 if(respool->numresources > 0) {
163 resource = respool->resources[--respool->numresources];
164 }
165 }
166
167 // save the resource in the request object, for caching and also
168 // for cleanup later
169 int err = 0;
170 if(resource) {
171 if(request && session) {
172 if(!request->resources) {
173 request->resources = ucx_map_new_a(&session->allocator, 8);
174 }
175
176 if(request->resources) {
177 if(ucx_map_cstr_put(request->resources, name, resource)) {
178 err = 1;
179 }
180 } else {
181 err = 1;
182 }
183 } // else: lookup is outside of any request context
184
185 if(respool->type->prepare(respool->data, resource->resdata)) {
186 err = -1;
187 }
188 }
189
190 if(err) {
191 // err == 1 caused by OOM
192 log_ereport(LOG_FAILURE, "resourcepool_lookup: OOM");
193 // cleanup
194 resourcepool_destroy_resource(resource);
195 resource = NULL;
196 }
197
198 pthread_mutex_unlock(&respool->lock);
199
200 return (ResourceData*)resource;
201 }
202
203 ResourceData* resourcepool_cfg_lookup(ServerConfiguration *cfg, const char *name, int flags) {
204 return s_resourcepool_lookup(cfg, NULL, NULL, name, flags);
205 }
206
207 ResourceData* resourcepool_lookup(Session *sn, Request *rq, const char *name, int flags) {
208 NSAPISession *session = (NSAPISession*)sn;
209 ServerConfiguration *cfg = session->config;
210 return s_resourcepool_lookup(cfg, rq, sn, name, flags);
211 }
212
213 void resourcepool_free(Session *sn, Request *rq, ResourceData *resource) {
214 ResourceDataPrivate *res = (ResourceDataPrivate*)resource;
215 ResourcePool *respool = resource->resourcepool;
216
217 if(respool->type->finish(respool->data, res->resdata)) {
218 log_ereport(LOG_FAILURE, "resourcepool_free: finish failed");
219 }
220
221 pthread_mutex_lock(&respool->lock);
222
223 if(respool->numresources >= respool->resalloc) {
224 // actually respool->resalloc == respool->max
225 // and numresources should always be smaller
226 // however just be extra safe here
227 respool->resalloc += 8;
228 ResourceDataPrivate **new_res_array = pool_realloc(
229 respool->pool,
230 respool->resources,
231 respool->resalloc * sizeof(ResourceDataPrivate*));
232 if(new_res_array) {
233 respool->resources = new_res_array;
234 } else {
235 log_ereport(LOG_FAILURE, "resourcepool_free: OOM");
236 resourcepool_destroy_resource(res);
237 pthread_mutex_unlock(&respool->lock);
238 return;
239 }
240 }
241
242 respool->resources[respool->numresources++] = res;
243
244 pthread_cond_signal(&respool->available);
245 pthread_mutex_unlock(&respool->lock);
246 }
247
248 void resourcepool_destroy_resource(ResourceDataPrivate *res) {
249 res->data.resourcepool->numcreated--;
250 res->data.resourcepool->type->freeresource(res->data.resourcepool->data, res->resdata);
251 pool_free(res->data.resourcepool->pool, res);
252 }
253
254 void resourcepool_destroy(ResourcePool *respool) {
255 // TODO
256 }

mercurial