kore/src/runtime.c

296 lines
6.6 KiB
C
Raw Normal View History

/*
2022-01-31 22:02:06 +01:00
* Copyright (c) 2017-2022 Joris Vink <joris@coders.se>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/types.h>
#include "kore.h"
#if !defined(KORE_NO_HTTP)
#include "http.h"
#endif
#if defined(KORE_USE_PYTHON)
#include "python_api.h"
#endif
#if defined(KORE_USE_LUA)
#include "lua_api.h"
#endif
static void native_runtime_execute(void *);
static int native_runtime_onload(void *, int);
static void native_runtime_signal(void *, int);
static void native_runtime_connect(void *, struct connection *);
static void native_runtime_configure(void *, int, char **);
#if !defined(KORE_NO_HTTP)
static int native_runtime_http_request(void *, struct http_request *);
static void native_runtime_http_request_free(void *, struct http_request *);
static void native_runtime_http_body_chunk(void *, struct http_request *,
const void *, size_t);
Rework HTTP and worker processes. The HTTP layer used to make a copy of each incoming header and its value for a request. Stop doing that and make HTTP headers zero-copy all across the board. This change comes with some api function changes, notably the http_request_header() function which now takes a const char ** rather than a char ** out pointer. This commit also constifies several members of http_request, beware. Additional rework how the worker processes deal with the accept lock. Before: if a worker held the accept lock and it accepted a new connection it would release the lock for others and back off for 500ms before attempting to grab the lock again. This approach worked but under high load this starts becoming obvious. Now: - workers not holding the accept lock and not having any connections will wait less long before returning from kore_platform_event_wait(). - workers not holding the accept lock will no longer blindly wait an arbitrary amount in kore_platform_event_wait() but will look at how long until the next lock grab is and base their timeout on that. - if a worker its next_lock timeout is up and failed to grab the lock it will try again in half the time again. - the worker process holding the lock will when releasing the lock double check if it still has space for newer connections, if it does it will keep the lock until it is full. This prevents the lock from bouncing between several non busy worker processes all the time. Additional fixes: - Reduce the number of times we check the timeout list, only do it twice per second rather then every event tick. - Fix solo worker count for TLS (we actually hold two processes, not one). - Make sure we don't accidentally miscalculate the idle time causing new connections under heavy load to instantly drop. - Swap from gettimeofday() to clock_gettime() now that MacOS caught up.
2018-02-14 13:48:49 +01:00
static int native_runtime_validator(void *, struct http_request *,
const void *);
static void native_runtime_wsmessage(void *, struct connection *, u_int8_t,
const void *, size_t);
#endif
struct kore_runtime kore_native_runtime = {
KORE_RUNTIME_NATIVE,
#if !defined(KORE_NO_HTTP)
.http_request = native_runtime_http_request,
.http_request_free = native_runtime_http_request_free,
2021-12-13 10:48:29 +01:00
.http_body_chunk = native_runtime_http_body_chunk,
.validator = native_runtime_validator,
.wsconnect = native_runtime_connect,
.wsmessage = native_runtime_wsmessage,
.wsdisconnect = native_runtime_connect,
#endif
.onload = native_runtime_onload,
.signal = native_runtime_signal,
.connect = native_runtime_connect,
.execute = native_runtime_execute,
.configure = native_runtime_configure
};
static struct kore_runtime *runtimes[] = {
#if defined(KORE_USE_PYTHON)
&kore_python_runtime,
#endif
#if defined(KORE_USE_LUA)
&kore_lua_runtime,
#endif
NULL
};
size_t
kore_runtime_count(void)
{
return ((sizeof(runtimes) / sizeof(runtimes[0])) - 1);
}
void
kore_runtime_resolve(const char *module, const struct stat *st)
{
int i;
if (runtimes[0] == NULL)
return;
for (i = 0; runtimes[i] != NULL; i++) {
if (runtimes[i]->resolve == NULL)
continue;
if (runtimes[i]->resolve(module, st))
break;
}
if (runtimes[i] == NULL)
fatal("No runtime available to run '%s'", module);
}
struct kore_runtime_call *
kore_runtime_getcall(const char *symbol)
{
void *ptr;
struct kore_runtime_call *rcall;
struct kore_runtime *runtime;
ptr = kore_module_getsym(symbol, &runtime);
if (ptr == NULL)
return (NULL);
rcall = kore_malloc(sizeof(*rcall));
rcall->addr = ptr;
rcall->runtime = runtime;
return (rcall);
}
void
kore_runtime_execute(struct kore_runtime_call *rcall)
{
rcall->runtime->execute(rcall->addr);
}
void
kore_runtime_configure(struct kore_runtime_call *rcall, int argc, char **argv)
{
rcall->runtime->configure(rcall->addr, argc, argv);
}
int
kore_runtime_onload(struct kore_runtime_call *rcall, int action)
{
return (rcall->runtime->onload(rcall->addr, action));
}
void
kore_runtime_connect(struct kore_runtime_call *rcall, struct connection *c)
{
rcall->runtime->connect(rcall->addr, c);
}
void
kore_runtime_signal(struct kore_runtime_call *rcall, int sig)
{
rcall->runtime->signal(rcall->addr, sig);
}
#if !defined(KORE_NO_HTTP)
int
kore_runtime_http_request(struct kore_runtime_call *rcall,
struct http_request *req)
{
return (rcall->runtime->http_request(rcall->addr, req));
}
void
kore_runtime_http_request_free(struct kore_runtime_call *rcall,
struct http_request *req)
{
rcall->runtime->http_request_free(rcall->addr, req);
}
void
kore_runtime_http_body_chunk(struct kore_runtime_call *rcall,
struct http_request *req, const void *data, size_t len)
{
rcall->runtime->http_body_chunk(rcall->addr, req, data, len);
}
int
kore_runtime_validator(struct kore_runtime_call *rcall,
Rework HTTP and worker processes. The HTTP layer used to make a copy of each incoming header and its value for a request. Stop doing that and make HTTP headers zero-copy all across the board. This change comes with some api function changes, notably the http_request_header() function which now takes a const char ** rather than a char ** out pointer. This commit also constifies several members of http_request, beware. Additional rework how the worker processes deal with the accept lock. Before: if a worker held the accept lock and it accepted a new connection it would release the lock for others and back off for 500ms before attempting to grab the lock again. This approach worked but under high load this starts becoming obvious. Now: - workers not holding the accept lock and not having any connections will wait less long before returning from kore_platform_event_wait(). - workers not holding the accept lock will no longer blindly wait an arbitrary amount in kore_platform_event_wait() but will look at how long until the next lock grab is and base their timeout on that. - if a worker its next_lock timeout is up and failed to grab the lock it will try again in half the time again. - the worker process holding the lock will when releasing the lock double check if it still has space for newer connections, if it does it will keep the lock until it is full. This prevents the lock from bouncing between several non busy worker processes all the time. Additional fixes: - Reduce the number of times we check the timeout list, only do it twice per second rather then every event tick. - Fix solo worker count for TLS (we actually hold two processes, not one). - Make sure we don't accidentally miscalculate the idle time causing new connections under heavy load to instantly drop. - Swap from gettimeofday() to clock_gettime() now that MacOS caught up.
2018-02-14 13:48:49 +01:00
struct http_request *req, const void *data)
{
return (rcall->runtime->validator(rcall->addr, req, data));
}
void
kore_runtime_wsconnect(struct kore_runtime_call *rcall, struct connection *c)
{
rcall->runtime->wsconnect(rcall->addr, c);
}
void
kore_runtime_wsmessage(struct kore_runtime_call *rcall, struct connection *c,
u_int8_t op, const void *data, size_t len)
{
rcall->runtime->wsmessage(rcall->addr, c, op, data, len);
}
void
kore_runtime_wsdisconnect(struct kore_runtime_call *rcall, struct connection *c)
{
rcall->runtime->wsdisconnect(rcall->addr, c);
}
#endif
static void
native_runtime_execute(void *addr)
{
void (*cb)(void);
*(void **)&(cb) = addr;
cb();
}
static void
native_runtime_configure(void *addr, int argc, char **argv)
{
void (*cb)(int, char **);
*(void **)&(cb) = addr;
cb(argc, argv);
}
static void
native_runtime_connect(void *addr, struct connection *c)
{
void (*cb)(struct connection *);
*(void **)&(cb) = addr;
cb(c);
}
static int
native_runtime_onload(void *addr, int action)
{
int (*cb)(int);
*(void **)&(cb) = addr;
return (cb(action));
}
static void
native_runtime_signal(void *addr, int sig)
{
void (*cb)(int);
*(void **)&(cb) = addr;
cb(sig);
}
#if !defined(KORE_NO_HTTP)
static int
native_runtime_http_request(void *addr, struct http_request *req)
{
int (*cb)(struct http_request *);
*(void **)&(cb) = addr;
return (cb(req));
}
static void
native_runtime_http_request_free(void *addr, struct http_request *req)
{
int (*cb)(struct http_request *);
*(void **)&(cb) = addr;
cb(req);
}
static void
native_runtime_http_body_chunk(void *addr, struct http_request *req,
const void *data, size_t len)
{
void (*cb)(struct http_request *, const void *, size_t);
*(void **)&(cb) = addr;
cb(req, data, len);
}
static int
Rework HTTP and worker processes. The HTTP layer used to make a copy of each incoming header and its value for a request. Stop doing that and make HTTP headers zero-copy all across the board. This change comes with some api function changes, notably the http_request_header() function which now takes a const char ** rather than a char ** out pointer. This commit also constifies several members of http_request, beware. Additional rework how the worker processes deal with the accept lock. Before: if a worker held the accept lock and it accepted a new connection it would release the lock for others and back off for 500ms before attempting to grab the lock again. This approach worked but under high load this starts becoming obvious. Now: - workers not holding the accept lock and not having any connections will wait less long before returning from kore_platform_event_wait(). - workers not holding the accept lock will no longer blindly wait an arbitrary amount in kore_platform_event_wait() but will look at how long until the next lock grab is and base their timeout on that. - if a worker its next_lock timeout is up and failed to grab the lock it will try again in half the time again. - the worker process holding the lock will when releasing the lock double check if it still has space for newer connections, if it does it will keep the lock until it is full. This prevents the lock from bouncing between several non busy worker processes all the time. Additional fixes: - Reduce the number of times we check the timeout list, only do it twice per second rather then every event tick. - Fix solo worker count for TLS (we actually hold two processes, not one). - Make sure we don't accidentally miscalculate the idle time causing new connections under heavy load to instantly drop. - Swap from gettimeofday() to clock_gettime() now that MacOS caught up.
2018-02-14 13:48:49 +01:00
native_runtime_validator(void *addr, struct http_request *req, const void *data)
{
Rework HTTP and worker processes. The HTTP layer used to make a copy of each incoming header and its value for a request. Stop doing that and make HTTP headers zero-copy all across the board. This change comes with some api function changes, notably the http_request_header() function which now takes a const char ** rather than a char ** out pointer. This commit also constifies several members of http_request, beware. Additional rework how the worker processes deal with the accept lock. Before: if a worker held the accept lock and it accepted a new connection it would release the lock for others and back off for 500ms before attempting to grab the lock again. This approach worked but under high load this starts becoming obvious. Now: - workers not holding the accept lock and not having any connections will wait less long before returning from kore_platform_event_wait(). - workers not holding the accept lock will no longer blindly wait an arbitrary amount in kore_platform_event_wait() but will look at how long until the next lock grab is and base their timeout on that. - if a worker its next_lock timeout is up and failed to grab the lock it will try again in half the time again. - the worker process holding the lock will when releasing the lock double check if it still has space for newer connections, if it does it will keep the lock until it is full. This prevents the lock from bouncing between several non busy worker processes all the time. Additional fixes: - Reduce the number of times we check the timeout list, only do it twice per second rather then every event tick. - Fix solo worker count for TLS (we actually hold two processes, not one). - Make sure we don't accidentally miscalculate the idle time causing new connections under heavy load to instantly drop. - Swap from gettimeofday() to clock_gettime() now that MacOS caught up.
2018-02-14 13:48:49 +01:00
int (*cb)(struct http_request *, const void *);
*(void **)&(cb) = addr;
return (cb(req, data));
}
static void
native_runtime_wsmessage(void *addr, struct connection *c, u_int8_t op,
const void *data, size_t len)
{
void (*cb)(struct connection *, u_int8_t, const void *, size_t);
*(void **)&(cb) = addr;
cb(c, op, data, len);
}
#endif