UNIXworkcode

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 CxMap *resource_pool_types; 40 41 int init_resource_pools(void) { 42 resource_pool_types = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 4); 43 return resource_pool_types ? 0 : 1; 44 } 45 46 int resourcepool_register_type(const char *type_name, ResourceType *type_info) { 47 if(cxMapPut(resource_pool_types, cx_hash_key_str(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, cxstring type, cxstring name, ConfigNode *node) { 57 ResourceType *restype = cxMapGet(resource_pool_types, cx_hash_key_bytes((unsigned const char*)type.ptr, type.length)); 58 if(!restype) { 59 log_ereport(LOG_MISCONFIG, "unknown resource pool type: %s", type.ptr); 60 return 1; 61 } 62 63 char *respool_name = cx_strdup_a(cfg->a, name).ptr; 64 if(!respool_name) { 65 log_ereport(LOG_FAILURE, "resourcepool_new: OOM"); 66 return 1; 67 } 68 69 // convert ConfigNode to pblock 70 // no sub-objects allowed for this specific ConfigNode, therefore 71 // it can be represented as key-value-pairs 72 pblock *param = config_obj2pblock(cfg->pool, node); 73 if(!param) { 74 log_ereport(LOG_FAILURE, "resourcepool_new: OOM"); 75 return 1; 76 } 77 78 ResourcePool *respool = pool_malloc(cfg->pool, sizeof(ResourcePool)); 79 if(!respool) { 80 log_ereport(LOG_FAILURE, "resourcepool_new: OOM"); 81 return 1; 82 } 83 respool->pool = cfg->pool; 84 85 void *respool_data = restype->init(cfg->pool, name.ptr, param); 86 if(!respool_data) { 87 log_ereport(LOG_FAILURE, "Cannot create resource pool data: pool: %s type: %s", name.ptr, type.ptr); 88 return 1; 89 } 90 91 respool->name = respool_name; 92 respool->type = restype; 93 respool->data = respool_data; 94 respool->min = 4; // TODO: get from node 95 respool->max = RESOURCE_POOL_MAX_DEFAULT; // TODO: get from node 96 97 respool->numcreated = 0; 98 respool->numresources = 0; 99 100 101 // don't allow too large resource pools 102 // this prevents the need to check malloc integer overflows 103 if(respool->max > RESOURCE_POOL_MAX_ALLOC) { 104 respool->max = RESOURCE_POOL_MAX_ALLOC; 105 log_ereport(LOG_WARN, "Resource pool %s: limit max to %d", name.ptr, respool->max); 106 } 107 108 respool->resalloc = respool->max; 109 respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(ResourceDataPrivate*)); 110 111 if(!respool->resources || cxMapPut(cfg->resources, cx_hash_key_bytes((unsigned const char*)name.ptr, name.length), respool)) { 112 log_ereport(LOG_FAILURE, "Cannot add resource pool: OOM"); 113 // the only cleanup we have to do 114 restype->destroy(respool_data); 115 return 1; 116 } 117 118 pthread_mutex_init(&respool->lock, NULL); 119 pthread_cond_init(&respool->available, NULL); 120 121 return 0; 122 } 123 124 static ResourceData* s_resourcepool_lookup(ServerConfiguration *cfg, Request *opt_rq, Session *opt_sn, const char *name, int flags) { 125 NSAPIRequest *request = (NSAPIRequest*)opt_rq; 126 NSAPISession *session = (NSAPISession*)opt_sn; 127 ResourceDataPrivate *resource = NULL; 128 129 // was this resource already used by this request? 130 if(request && request->resources) { 131 resource = cxMapGet(request->resources, cx_hash_key_str(name)); 132 if(resource) { 133 return &resource->data; 134 } 135 } 136 137 ResourcePool *respool = cxMapGet(cfg->resources, cx_hash_key_str(name)); 138 if(!respool) return NULL; 139 140 141 pthread_mutex_lock(&respool->lock); 142 WSBool createResource = FALSE; 143 if(respool->numcreated < respool->min) { 144 createResource = TRUE; 145 } 146 147 if(createResource) { 148 // create a new resource and store it in the resourcepool 149 log_ereport(LOG_DEBUG, "resourcepool %s: create resource", name); 150 151 void *resourceData = respool->type->createresource(respool->data); 152 if(resourceData) { 153 respool->numcreated++; 154 155 resource = pool_malloc(respool->pool, sizeof(ResourceDataPrivate)); 156 if(resource) { 157 resource->data.data = respool->type->getresourcedata(resourceData); 158 resource->data.resourcepool = respool; 159 resource->resdata = resourceData; 160 } else { 161 respool->type->freeresource(respool->data, resourceData); 162 log_ereport(LOG_CATASTROPHE, "resourcepool_lookup: OOM"); 163 } 164 } 165 // else: respool->type->createresource does logging in case of errors 166 } else if(respool->numresources > 0) { 167 resource = respool->resources[--respool->numresources]; 168 } else { 169 // wait for free resource 170 pthread_cond_wait(&respool->available, &respool->lock); 171 if(respool->numresources > 0) { 172 resource = respool->resources[--respool->numresources]; 173 } 174 } 175 176 // save the resource in the request object, for caching and also 177 // for cleanup later 178 int err = 0; 179 if(resource) { 180 if(request && session) { 181 if(!request->resources) { 182 request->resources = cxHashMapCreate(pool_allocator(session->sn.pool), CX_STORE_POINTERS, 8); 183 } 184 185 if(request->resources) { 186 if(cxMapPut(request->resources, cx_hash_key_str(name), resource)) { 187 err = 1; 188 } 189 } else { 190 err = 1; 191 } 192 } // else: lookup is outside of any request context 193 194 if(respool->type->prepare(respool->data, resource->resdata)) { 195 err = -1; 196 } 197 } 198 199 if(err) { 200 // err == 1 caused by OOM 201 log_ereport(LOG_FAILURE, "resourcepool_lookup: OOM"); 202 // cleanup 203 resourcepool_destroy_resource(resource); 204 resource = NULL; 205 } 206 207 pthread_mutex_unlock(&respool->lock); 208 209 return (ResourceData*)resource; 210 } 211 212 ResourceData* resourcepool_cfg_lookup(ServerConfiguration *cfg, const char *name, int flags) { 213 return s_resourcepool_lookup(cfg, NULL, NULL, name, flags); 214 } 215 216 ResourceData* resourcepool_lookup(Session *sn, Request *rq, const char *name, int flags) { 217 NSAPISession *session = (NSAPISession*)sn; 218 ServerConfiguration *cfg = session->config; 219 return s_resourcepool_lookup(cfg, rq, sn, name, flags); 220 } 221 222 void resourcepool_free(Session *sn, Request *rq, ResourceData *resource) { 223 NSAPIRequest *nsapi_rq = (NSAPIRequest *)rq; 224 ResourceDataPrivate *res = (ResourceDataPrivate*)resource; 225 ResourcePool *respool = resource->resourcepool; 226 227 if(nsapi_rq && !nsapi_rq->finished) { 228 // request processing still ongoing and SAFs will be executed 229 if(!cxMapRemoveAndGet(nsapi_rq->resources, cx_hash_key_str(respool->name))) { 230 log_ereport(LOG_FAILURE, "resourcepool_free: cannot remove resource from request: potential double free"); 231 } 232 } 233 // else: safe to ignore nsapi_rq->resources 234 235 if(respool->type->finish(respool->data, res->resdata)) { 236 log_ereport(LOG_FAILURE, "resourcepool_free: finish failed"); 237 } 238 239 pthread_mutex_lock(&respool->lock); 240 241 if(respool->numresources >= respool->resalloc) { 242 // actually respool->resalloc == respool->max 243 // and numresources should always be smaller 244 // however just be extra safe here 245 respool->resalloc += 8; 246 ResourceDataPrivate **new_res_array = pool_realloc( 247 respool->pool, 248 respool->resources, 249 respool->resalloc * sizeof(ResourceDataPrivate*)); 250 if(new_res_array) { 251 respool->resources = new_res_array; 252 } else { 253 log_ereport(LOG_FAILURE, "resourcepool_free: OOM"); 254 resourcepool_destroy_resource(res); 255 pthread_mutex_unlock(&respool->lock); 256 return; 257 } 258 } 259 260 respool->resources[respool->numresources++] = res; 261 262 pthread_cond_signal(&respool->available); 263 pthread_mutex_unlock(&respool->lock); 264 } 265 266 void resourcepool_destroy_resource(ResourceDataPrivate *res) { 267 res->data.resourcepool->numcreated--; 268 res->data.resourcepool->type->freeresource(res->data.resourcepool->data, res->resdata); 269 pool_free(res->data.resourcepool->pool, res); 270 } 271 272 void resourcepool_destroy(ResourcePool *respool) { 273 // TODO 274 } 275