Merge branch 'master' of github.com:gwsystems/sledge-serverless-framework into feat-prometheus

master
Sean McBride 3 years ago
commit 69c2b4de29

@ -122,3 +122,6 @@ scratch_storage_delete.install: ../runtime/bin/scratch_storage_delete.wasm.so
.PHONY: scratch_storage_upsert.install .PHONY: scratch_storage_upsert.install
scratch_storage_upsert.install: ../runtime/bin/scratch_storage_upsert.wasm.so scratch_storage_upsert.install: ../runtime/bin/scratch_storage_upsert.wasm.so
.PHONY: depth_to_xyz.install
depth_to_xyz.install: ../runtime/bin/depth_to_xyz.wasm.so

@ -1 +1 @@
Subproject commit 0b9f67d75fd9dab652e1995e7adf91806080523b Subproject commit 726b0804777ef346308f9dfcdf928032c28226a8

@ -59,7 +59,6 @@ BINARY_NAME=sledgert
# CFLAGS += -DLOG_ADMISSIONS_CONTROL # CFLAGS += -DLOG_ADMISSIONS_CONTROL
# CFLAGS += -DLOG_CONTEXT_SWITCHES # CFLAGS += -DLOG_CONTEXT_SWITCHES
# CFLAGS += -DLOG_HTTP_PARSER # CFLAGS += -DLOG_HTTP_PARSER
# CFLAGS += -DLOG_LOCK_OVERHEAD
# CFLAGS += -DLOG_TENANT_LOADING # CFLAGS += -DLOG_TENANT_LOADING
# CFLAGS += -DLOG_PREEMPTION # CFLAGS += -DLOG_PREEMPTION
# CFLAGS += -DLOG_SANDBOX_ALLOCATION # CFLAGS += -DLOG_SANDBOX_ALLOCATION

@ -1,11 +0,0 @@
#pragma once
#include <stdint.h>
#include <threads.h>
extern thread_local uint64_t generic_thread_lock_duration;
extern thread_local uint64_t generic_thread_lock_longest;
extern thread_local uint64_t generic_thread_start_timestamp;
void generic_thread_dump_lock_overhead(void);
void generic_thread_initialize(void);

@ -38,9 +38,10 @@ enum http_session_state
HTTP_SESSION_EXECUTION_COMPLETE, HTTP_SESSION_EXECUTION_COMPLETE,
HTTP_SESSION_SENDING_RESPONSE_HEADER, HTTP_SESSION_SENDING_RESPONSE_HEADER,
HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED, HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED,
HTTP_SESSION_SENDING_RESPONSE, HTTP_SESSION_SENT_RESPONSE_HEADER,
HTTP_SESSION_SEND_RESPONSE_BLOCKED, HTTP_SESSION_SENDING_RESPONSE_BODY,
HTTP_SESSION_SENT_RESPONSE HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED,
HTTP_SESSION_SENT_RESPONSE_BODY
}; };
struct http_session { struct http_session {
@ -218,6 +219,9 @@ static inline int
http_session_send_response_header(struct http_session *session, void_star_cb on_eagain) http_session_send_response_header(struct http_session *session, void_star_cb on_eagain)
{ {
assert(session != NULL); assert(session != NULL);
assert(session->state == HTTP_SESSION_EXECUTION_COMPLETE
|| session->state == HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED);
session->state = HTTP_SESSION_SENDING_RESPONSE_HEADER;
while (session->response_header_length > session->response_header_written) { while (session->response_header_length > session->response_header_written) {
ssize_t sent = ssize_t sent =
@ -232,6 +236,8 @@ http_session_send_response_header(struct http_session *session, void_star_cb on_
} }
} }
session->state = HTTP_SESSION_SENT_RESPONSE_HEADER;
return 0; return 0;
} }
@ -246,6 +252,10 @@ http_session_send_response_body(struct http_session *session, void_star_cb on_ea
{ {
assert(session != NULL); assert(session != NULL);
assert(session->state == HTTP_SESSION_SENT_RESPONSE_HEADER
|| session->state == HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED);
session->state = HTTP_SESSION_SENDING_RESPONSE_BODY;
while (session->response_buffer_written < session->response_buffer.length) { while (session->response_buffer_written < session->response_buffer.length) {
ssize_t sent = ssize_t sent =
tcp_session_send(session->socket, tcp_session_send(session->socket,
@ -259,6 +269,7 @@ http_session_send_response_body(struct http_session *session, void_star_cb on_ea
} }
} }
session->state = HTTP_SESSION_SENT_RESPONSE_BODY;
return 0; return 0;
} }
@ -467,6 +478,8 @@ DONE:
static inline void static inline void
http_session_send_response(struct http_session *session, void_star_cb on_eagain) http_session_send_response(struct http_session *session, void_star_cb on_eagain)
{ {
assert(session->state == HTTP_SESSION_EXECUTION_COMPLETE);
int rc = http_session_send_response_header(session, on_eagain); int rc = http_session_send_response_header(session, on_eagain);
/* session blocked and registered to epoll so continue to next handle */ /* session blocked and registered to epoll so continue to next handle */
if (unlikely(rc == -EAGAIN)) { if (unlikely(rc == -EAGAIN)) {
@ -475,6 +488,8 @@ http_session_send_response(struct http_session *session, void_star_cb on_eagain)
goto CLOSE; goto CLOSE;
} }
assert(session->state == HTTP_SESSION_SENT_RESPONSE_HEADER);
rc = http_session_send_response_body(session, on_eagain); rc = http_session_send_response_body(session, on_eagain);
/* session blocked and registered to epoll so continue to next handle */ /* session blocked and registered to epoll so continue to next handle */
if (unlikely(rc == -EAGAIN)) { if (unlikely(rc == -EAGAIN)) {
@ -483,6 +498,8 @@ http_session_send_response(struct http_session *session, void_star_cb on_eagain)
goto CLOSE; goto CLOSE;
} }
assert(session->state == HTTP_SESSION_SENT_RESPONSE_BODY);
/* Terminal State Logging for Http Session */ /* Terminal State Logging for Http Session */
session->response_sent_timestamp = __getcycles(); session->response_sent_timestamp = __getcycles();
http_session_perf_log_print_entry(session); http_session_perf_log_print_entry(session);

@ -3,7 +3,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdnoreturn.h> #include <stdnoreturn.h>
#include "generic_thread.h"
#include "http_session.h" #include "http_session.h"
#include "module.h" #include "module.h"

@ -1,68 +1,81 @@
#pragma once #pragma once
#include <assert.h>
#include <spinlock/mcs.h> #include <spinlock/mcs.h>
#include <stdint.h> #include <stdint.h>
#include "arch/getcycles.h" #include "arch/getcycles.h"
#include "runtime.h" #include "runtime.h"
#include "generic_thread.h"
typedef ck_spinlock_mcs_t lock_t;
/* A linked list of nodes */
struct lock_wrapper {
uint64_t longest_held;
uint64_t total_held;
ck_spinlock_mcs_t lock;
};
/* A node on the linked list */
struct lock_node {
struct ck_spinlock_mcs node;
uint64_t time_locked;
};
typedef struct lock_wrapper lock_t;
typedef struct lock_node lock_node_t;
/** /**
* Initializes a lock of type lock_t * Initializes a lock
* @param lock - the address of the lock * @param lock - the address of the lock
*/ */
#define LOCK_INIT(lock) ck_spinlock_mcs_init((lock)) static inline void
lock_init(lock_t *self)
{
self->total_held = 0;
self->longest_held = 0;
ck_spinlock_mcs_init(&self->lock);
}
/** /**
* Checks if a lock is locked * Checks if a lock is locked
* @param lock - the address of the lock * @param lock - the address of the lock
* @returns bool if lock is locked * @returns bool if lock is locked
*/ */
static inline bool
#define LOCK_IS_LOCKED(lock) ck_spinlock_mcs_locked((lock)) lock_is_locked(lock_t *self)
{
return ck_spinlock_mcs_locked(&self->lock);
}
/** /**
* Locks a lock, keeping track of overhead * Locks a lock, keeping track of overhead
* @param lock - the address of the lock * @param lock - the address of the lock
* @param unique_variable_name - a unique prefix to hygienically namespace an associated lock/unlock pair * @param node - node to add to lock
*/ */
static inline void
lock_lock(lock_t *self, lock_node_t *node)
{
assert(node->time_locked == 0);
#define LOCK_LOCK_WITH_BOOKKEEPING(lock, unique_variable_name) \ node->time_locked = __getcycles();
struct ck_spinlock_mcs _hygiene_##unique_variable_name##_node; \ ck_spinlock_mcs_lock(&self->lock, &node->node);
uint64_t _hygiene_##unique_variable_name##_pre = __getcycles(); \ }
ck_spinlock_mcs_lock((lock), &(_hygiene_##unique_variable_name##_node)); \
uint64_t _hygiene_##unique_variable_name##_duration = (__getcycles() - _hygiene_##unique_variable_name##_pre); \
if (_hygiene_##unique_variable_name##_duration > generic_thread_lock_longest) { \
generic_thread_lock_longest = _hygiene_##unique_variable_name##_duration; \
} \
generic_thread_lock_duration += _hygiene_##unique_variable_name##_duration;
/** /**
* Unlocks a lock * Unlocks a lock
* @param lock - the address of the lock * @param lock - the address of the lock
* @param unique_variable_name - a unique prefix to hygienically namespace an associated lock/unlock pair * @param node - node used when calling lock_lock
*/ */
#define LOCK_UNLOCK_WITH_BOOKKEEPING(lock, unique_variable_name) \ static inline void
ck_spinlock_mcs_unlock(lock, &(_hygiene_##unique_variable_name##_node)); lock_unlock(lock_t *self, lock_node_t *node)
{
assert(node->time_locked > 0);
/** ck_spinlock_mcs_unlock(&self->lock, &node->node);
* Locks a lock, keeping track of overhead uint64_t now = __getcycles();
* Assumes the availability of DEFAULT as a hygienic prefix for DEFAULT_node and DEFAULT_pre assert(node->time_locked < now);
* uint64_t duration = now - node->time_locked;
* As such, this API can only be used once in a lexical scope. node->time_locked = 0;
* if (unlikely(duration > self->longest_held)) { self->longest_held = duration; }
* Use LOCK_LOCK_WITH_BOOKKEEPING and LOCK_UNLOCK_WITH_BOOKKEEPING if multiple locks are required self->total_held += duration;
* @param lock - the address of the lock }
*/
#define LOCK_LOCK(lock) LOCK_LOCK_WITH_BOOKKEEPING(lock, DEFAULT)
/**
* Unlocks a lock
* Uses lock node NODE_DEFAULT and timestamp PRE_DEFAULT, so this assumes use of LOCK_LOCK
* This API can only be used once in a lexical scope. If this isn't true, use LOCK_LOCK_WITH_BOOKKEEPING and
* LOCK_UNLOCK_WITH_BOOKKEEPING
* @param lock - the address of the lock
*/
#define LOCK_UNLOCK(lock) LOCK_UNLOCK_WITH_BOOKKEEPING(lock, DEFAULT)

@ -37,7 +37,7 @@ map_init(struct map *restrict map)
{ {
for (int i = 0; i < MAP_BUCKET_COUNT; i++) { for (int i = 0; i < MAP_BUCKET_COUNT; i++) {
map->buckets[i].head = NULL; map->buckets[i].head = NULL;
LOCK_INIT(&map->buckets[i].lock); lock_init(&map->buckets[i].lock);
} }
}; };
@ -67,7 +67,8 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
LOCK_LOCK(&bucket->lock); lock_node_t node = {};
lock_lock(&bucket->lock, &node);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) { for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) { if (node->hash == hash) {
value = node->value; value = node->value;
@ -79,7 +80,7 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
if (value == NULL) *ret_value_len = 0; if (value == NULL) *ret_value_len = 0;
DONE: DONE:
LOCK_UNLOCK(&bucket->lock); lock_unlock(&bucket->lock, &node);
return value; return value;
} }
@ -90,7 +91,8 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_
uint32_t hash = MAP_HASH(key, key_len); uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
LOCK_LOCK(&bucket->lock); lock_node_t node;
lock_lock(&bucket->lock, &node);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) { for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) goto DONE; if (node->hash == hash) goto DONE;
} }
@ -111,7 +113,7 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_
did_set = true; did_set = true;
DONE: DONE:
LOCK_UNLOCK(&bucket->lock); lock_unlock(&bucket->lock, &node);
return did_set; return did_set;
} }
@ -125,7 +127,8 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
uint32_t hash = MAP_HASH(key, key_len); uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
LOCK_LOCK(&bucket->lock); lock_node_t node;
lock_lock(&bucket->lock, &node);
struct map_node *prev = bucket->head; struct map_node *prev = bucket->head;
if (prev != NULL && prev->hash == hash) { if (prev != NULL && prev->hash == hash) {
@ -147,7 +150,7 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
} }
DONE: DONE:
LOCK_UNLOCK(&bucket->lock); lock_unlock(&bucket->lock, &node);
return did_delete; return did_delete;
} }
@ -156,7 +159,8 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
{ {
uint32_t hash = MAP_HASH(key, key_len); uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT]; struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
LOCK_LOCK(&bucket->lock); lock_node_t node;
lock_lock(&bucket->lock, &node);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) { for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) { if (node->hash == hash) {
@ -187,5 +191,5 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
bucket->head = new_node; bucket->head = new_node;
DONE: DONE:
LOCK_UNLOCK(&bucket->lock); lock_unlock(&bucket->lock, &node);
} }

@ -19,10 +19,10 @@ perf_window_initialize(struct perf_window *perf_window)
{ {
assert(perf_window != NULL); assert(perf_window != NULL);
LOCK_INIT(&perf_window->lock); lock_init(&perf_window->lock);
perf_window->count = 0; perf_window->count = 0;
memset(perf_window->by_duration, 0, sizeof(struct execution_node) * PERF_WINDOW_BUFFER_SIZE); memset(perf_window->by_duration, 0, sizeof(struct execution_node) * perf_window_capacity);
memset(perf_window->by_termination, 0, sizeof(uint16_t) * PERF_WINDOW_BUFFER_SIZE); memset(perf_window->by_termination, 0, sizeof(uint16_t) * perf_window_capacity);
} }
@ -36,10 +36,10 @@ perf_window_initialize(struct perf_window *perf_window)
static inline void static inline void
perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx, uint16_t second_by_duration_idx) perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx, uint16_t second_by_duration_idx)
{ {
assert(LOCK_IS_LOCKED(&perf_window->lock)); assert(lock_is_locked(&perf_window->lock));
assert(perf_window != NULL); assert(perf_window != NULL);
assert(first_by_duration_idx >= 0 && first_by_duration_idx < PERF_WINDOW_BUFFER_SIZE); assert(first_by_duration_idx < perf_window_capacity);
assert(second_by_duration_idx >= 0 && second_by_duration_idx < PERF_WINDOW_BUFFER_SIZE); assert(second_by_duration_idx < perf_window_capacity);
uint16_t first_by_termination_idx = perf_window->by_duration[first_by_duration_idx].by_termination_idx; uint16_t first_by_termination_idx = perf_window->by_duration[first_by_duration_idx].by_termination_idx;
uint16_t second_by_termination_idx = perf_window->by_duration[second_by_duration_idx].by_termination_idx; uint16_t second_by_termination_idx = perf_window->by_duration[second_by_duration_idx].by_termination_idx;
@ -51,11 +51,11 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx
uint64_t first_execution_time = perf_window->by_duration[first_by_duration_idx].execution_time; uint64_t first_execution_time = perf_window->by_duration[first_by_duration_idx].execution_time;
uint64_t second_execution_time = perf_window->by_duration[second_by_duration_idx].execution_time; uint64_t second_execution_time = perf_window->by_duration[second_by_duration_idx].execution_time;
/* Swap Indices in Buffer*/ /* Swap indices */
perf_window->by_termination[first_by_termination_idx] = second_by_duration_idx; perf_window->by_termination[first_by_termination_idx] = second_by_duration_idx;
perf_window->by_termination[second_by_termination_idx] = first_by_duration_idx; perf_window->by_termination[second_by_termination_idx] = first_by_duration_idx;
/* Swap by_termination_idx */ /* Swap nodes */
struct execution_node tmp_node = perf_window->by_duration[first_by_duration_idx]; struct execution_node tmp_node = perf_window->by_duration[first_by_duration_idx];
perf_window->by_duration[first_by_duration_idx] = perf_window->by_duration[second_by_duration_idx]; perf_window->by_duration[first_by_duration_idx] = perf_window->by_duration[second_by_duration_idx];
perf_window->by_duration[second_by_duration_idx] = tmp_node; perf_window->by_duration[second_by_duration_idx] = tmp_node;
@ -67,66 +67,73 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx
== second_execution_time); == second_execution_time);
} }
static inline void
perf_window_fill(struct perf_window *perf_window, uint64_t newest_execution_time)
{
for (uint16_t i = 0; i < perf_window_capacity; i++) {
perf_window->by_termination[i] = i;
perf_window->by_duration[i] = (struct execution_node){ .execution_time = newest_execution_time,
.by_termination_idx = i };
}
perf_window->count = perf_window_capacity;
}
/** /**
* Adds a new value to the perf window * Adds newest_execution_time to the perf window
* Not intended to be called directly! * Not intended to be called directly!
* @param perf_window * @param perf_window
* @param value * @param newest_execution_time
*/ */
static inline void static inline void
perf_window_add(struct perf_window *perf_window, uint64_t value) perf_window_add(struct perf_window *perf_window, uint64_t newest_execution_time)
{ {
assert(perf_window != NULL); assert(perf_window != NULL);
/* Assumption: A successful invocation should run for a non-zero amount of time */
assert(newest_execution_time > 0);
uint16_t idx_of_oldest; uint16_t idx_to_replace;
uint64_t previous_execution_time;
bool check_up; bool check_up;
if (unlikely(!LOCK_IS_LOCKED(&perf_window->lock))) panic("lock not held when calling perf_window_add\n"); if (unlikely(!lock_is_locked(&perf_window->lock))) panic("lock not held when calling perf_window_add\n");
/* A successful invocation should run for a non-zero amount of time */
assert(value > 0);
/* If count is 0, then fill entire array with initial execution times */ /* If perf window is empty, fill all elements with newest_execution_time */
if (perf_window->count == 0) { if (perf_window->count == 0) {
for (int i = 0; i < PERF_WINDOW_BUFFER_SIZE; i++) { perf_window_fill(perf_window, newest_execution_time);
perf_window->by_termination[i] = i;
perf_window->by_duration[i] = (struct execution_node){ .execution_time = value,
.by_termination_idx = i };
}
perf_window->count = PERF_WINDOW_BUFFER_SIZE;
goto done; goto done;
} }
/* Otherwise, replace the oldest value, and then sort */ /* If full, replace the oldest execution_time. Save the old execution time to know which direction to swap */
idx_of_oldest = perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE]; idx_to_replace = perf_window->by_termination[perf_window->count % perf_window_capacity];
check_up = value > perf_window->by_duration[idx_of_oldest].execution_time; previous_execution_time = perf_window->by_duration[idx_to_replace].execution_time;
perf_window->by_duration[idx_to_replace].execution_time = newest_execution_time;
perf_window->by_duration[idx_of_oldest].execution_time = value;
if (check_up) { /* At this point, the by_duration array is partially sorted. The node we overwrote needs to be shifted left or
for (uint16_t i = idx_of_oldest; * right. We can determine which direction to shift by comparing with the previous execution time. */
i + 1 < PERF_WINDOW_BUFFER_SIZE if (newest_execution_time > previous_execution_time) {
for (uint16_t i = idx_to_replace;
i + 1 < perf_window_capacity
&& perf_window->by_duration[i + 1].execution_time < perf_window->by_duration[i].execution_time; && perf_window->by_duration[i + 1].execution_time < perf_window->by_duration[i].execution_time;
i++) { i++) {
perf_window_swap(perf_window, i, i + 1); perf_window_swap(perf_window, i, i + 1);
} }
} else { } else {
for (int i = idx_of_oldest; for (uint16_t i = idx_to_replace;
i - 1 >= 0 i >= 1
&& perf_window->by_duration[i - 1].execution_time > perf_window->by_duration[i].execution_time; && perf_window->by_duration[i - 1].execution_time > perf_window->by_duration[i].execution_time;
i--) { i--) {
perf_window_swap(perf_window, i, i - 1); perf_window_swap(perf_window, i, i - 1);
} }
} }
/* The idx that we replaces should still point to the same value */ /* The idx that we replaces should still point to the same newest_execution_time */
assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE]] assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % perf_window_capacity]]
.execution_time .execution_time
== value); == newest_execution_time);
/* The by_duration array should be ordered by execution time */ /* The by_duration array should be ordered by execution time */
#ifndef NDEBUG #ifndef NDEBUG
for (int i = 1; i < PERF_WINDOW_BUFFER_SIZE; i++) { for (int i = 1; i < perf_window_capacity; i++) {
assert(perf_window->by_duration[i - 1].execution_time <= perf_window->by_duration[i].execution_time); assert(perf_window->by_duration[i - 1].execution_time <= perf_window->by_duration[i].execution_time);
} }
#endif #endif
@ -152,10 +159,10 @@ perf_window_get_percentile(struct perf_window *perf_window, uint8_t percentile,
if (unlikely(perf_window->count == 0)) return 0; if (unlikely(perf_window->count == 0)) return 0;
if (likely(perf_window->count >= PERF_WINDOW_BUFFER_SIZE)) int idx = precomputed_index;
return perf_window->by_duration[precomputed_index].execution_time; if (unlikely(perf_window->count < perf_window_capacity)) idx = perf_window->count * percentile / 100;
return perf_window->by_duration[perf_window->count * percentile / 100].execution_time; return perf_window->by_duration[idx].execution_time;
} }
/** /**

@ -1,15 +1,19 @@
#pragma once #pragma once
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include "lock.h" #include "lock.h"
/* Should be Power of 2! */ enum
#define PERF_WINDOW_BUFFER_SIZE 16 {
perf_window_capacity = 32
};
static_assert(perf_window_capacity && !(perf_window_capacity & (perf_window_capacity - 1)),
"perf_window_capacity must be power of 2!");
#if ((PERF_WINDOW_BUFFER_SIZE == 0) || (PERF_WINDOW_BUFFER_SIZE & (PERF_WINDOW_BUFFER_SIZE - 1)) != 0) static_assert(perf_window_capacity <= UINT16_MAX, "perf_window_capacity must be indexable by a 16-bit unsigned int");
#error "PERF_WINDOW_BUFFER_SIZE must be power of 2!"
#endif
/* /*
* The by_duration array sorts the last N executions by execution time * The by_duration array sorts the last N executions by execution time
@ -24,8 +28,8 @@ struct execution_node {
}; };
struct perf_window { struct perf_window {
struct execution_node by_duration[PERF_WINDOW_BUFFER_SIZE]; struct execution_node by_duration[perf_window_capacity];
uint16_t by_termination[PERF_WINDOW_BUFFER_SIZE]; uint16_t by_termination[perf_window_capacity];
uint64_t count; uint64_t count;
lock_t lock; lock_t lock;
}; };

@ -4,7 +4,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "generic_thread.h"
#include "lock.h" #include "lock.h"
#include "ps_list.h" #include "ps_list.h"
@ -26,7 +25,7 @@
{ \ { \
ps_list_head_init(&self->list); \ ps_list_head_init(&self->list); \
self->use_lock = use_lock; \ self->use_lock = use_lock; \
if (use_lock) LOCK_INIT(&self->lock); \ if (use_lock) lock_init(&self->lock); \
} \ } \
\ \
static inline void STRUCT_NAME##_pool_deinit(struct STRUCT_NAME##_pool *self) \ static inline void STRUCT_NAME##_pool_deinit(struct STRUCT_NAME##_pool *self) \
@ -44,7 +43,7 @@
static inline struct STRUCT_NAME *STRUCT_NAME##_pool_remove_nolock(struct STRUCT_NAME##_pool *self) \ static inline struct STRUCT_NAME *STRUCT_NAME##_pool_remove_nolock(struct STRUCT_NAME##_pool *self) \
{ \ { \
assert(self != NULL); \ assert(self != NULL); \
assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); \ assert(!self->use_lock || lock_is_locked(&self->lock)); \
\ \
struct STRUCT_NAME *obj = NULL; \ struct STRUCT_NAME *obj = NULL; \
\ \
@ -66,9 +65,10 @@
bool is_empty = STRUCT_NAME##_pool_is_empty(self); \ bool is_empty = STRUCT_NAME##_pool_is_empty(self); \
if (is_empty) return obj; \ if (is_empty) return obj; \
\ \
LOCK_LOCK(&self->lock); \ lock_node_t node = {}; \
lock_lock(&self->lock, &node); \
obj = STRUCT_NAME##_pool_remove_nolock(self); \ obj = STRUCT_NAME##_pool_remove_nolock(self); \
LOCK_UNLOCK(&self->lock); \ lock_unlock(&self->lock, &node); \
return obj; \ return obj; \
} \ } \
\ \
@ -76,7 +76,7 @@
{ \ { \
assert(self != NULL); \ assert(self != NULL); \
assert(obj != NULL); \ assert(obj != NULL); \
assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); \ assert(!self->use_lock || lock_is_locked(&self->lock)); \
\ \
ps_list_head_add_d(&self->list, obj); \ ps_list_head_add_d(&self->list, obj); \
} \ } \
@ -87,7 +87,8 @@
assert(obj != NULL); \ assert(obj != NULL); \
assert(self->use_lock); \ assert(self->use_lock); \
\ \
LOCK_LOCK(&self->lock); \ lock_node_t node = {}; \
lock_lock(&self->lock, &node); \
STRUCT_NAME##_pool_add_nolock(self, obj); \ STRUCT_NAME##_pool_add_nolock(self, obj); \
LOCK_UNLOCK(&self->lock); \ lock_unlock(&self->lock, &node); \
} }

@ -60,7 +60,7 @@ priority_queue_append(struct priority_queue *priority_queue, void *new_item)
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(new_item != NULL); assert(new_item != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
int rc; int rc;
@ -85,7 +85,7 @@ static inline bool
priority_queue_is_empty(struct priority_queue *priority_queue) priority_queue_is_empty(struct priority_queue *priority_queue)
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
return priority_queue->size == 0; return priority_queue->size == 0;
} }
@ -99,7 +99,7 @@ priority_queue_percolate_up(struct priority_queue *priority_queue)
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(priority_queue->get_priority_fn != NULL); assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
/* If there's only one element, set memoized lookup and early out */ /* If there's only one element, set memoized lookup and early out */
if (priority_queue->size == 1) { if (priority_queue->size == 1) {
@ -135,7 +135,7 @@ priority_queue_find_smallest_child(struct priority_queue *priority_queue, const
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(parent_index >= 1 && parent_index <= priority_queue->size); assert(parent_index >= 1 && parent_index <= priority_queue->size);
assert(priority_queue->get_priority_fn != NULL); assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
int left_child_index = 2 * parent_index; int left_child_index = 2 * parent_index;
int right_child_index = 2 * parent_index + 1; int right_child_index = 2 * parent_index + 1;
@ -167,7 +167,7 @@ priority_queue_percolate_down(struct priority_queue *priority_queue, int parent_
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(priority_queue->get_priority_fn != NULL); assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!listener_thread_is_running()); assert(!listener_thread_is_running());
bool update_highest_value = parent_index == 1; bool update_highest_value = parent_index == 1;
@ -217,7 +217,7 @@ priority_queue_dequeue_if_earlier_nolock(struct priority_queue *priority_queue,
assert(dequeued_element != NULL); assert(dequeued_element != NULL);
assert(priority_queue->get_priority_fn != NULL); assert(priority_queue->get_priority_fn != NULL);
assert(!listener_thread_is_running()); assert(!listener_thread_is_running());
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
int return_code; int return_code;
@ -251,9 +251,10 @@ priority_queue_dequeue_if_earlier(struct priority_queue *priority_queue, void **
{ {
int return_code; int return_code;
LOCK_LOCK(&priority_queue->lock); lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
return_code = priority_queue_dequeue_if_earlier_nolock(priority_queue, dequeued_element, target_deadline); return_code = priority_queue_dequeue_if_earlier_nolock(priority_queue, dequeued_element, target_deadline);
LOCK_UNLOCK(&priority_queue->lock); lock_unlock(&priority_queue->lock, &node);
return return_code; return return_code;
} }
@ -281,7 +282,7 @@ priority_queue_initialize(size_t capacity, bool use_lock, priority_queue_get_pri
priority_queue->get_priority_fn = get_priority_fn; priority_queue->get_priority_fn = get_priority_fn;
priority_queue->use_lock = use_lock; priority_queue->use_lock = use_lock;
if (use_lock) LOCK_INIT(&priority_queue->lock); if (use_lock) lock_init(&priority_queue->lock);
return priority_queue; return priority_queue;
} }
@ -332,7 +333,7 @@ static inline int
priority_queue_length_nolock(struct priority_queue *priority_queue) priority_queue_length_nolock(struct priority_queue *priority_queue)
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
return priority_queue->size; return priority_queue->size;
} }
@ -344,9 +345,10 @@ priority_queue_length_nolock(struct priority_queue *priority_queue)
static inline int static inline int
priority_queue_length(struct priority_queue *priority_queue) priority_queue_length(struct priority_queue *priority_queue)
{ {
LOCK_LOCK(&priority_queue->lock); lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
int size = priority_queue_length_nolock(priority_queue); int size = priority_queue_length_nolock(priority_queue);
LOCK_UNLOCK(&priority_queue->lock); lock_unlock(&priority_queue->lock, &node);
return size; return size;
} }
@ -360,7 +362,7 @@ priority_queue_enqueue_nolock(struct priority_queue *priority_queue, void *value
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(value != NULL); assert(value != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
int rc; int rc;
@ -386,9 +388,10 @@ priority_queue_enqueue(struct priority_queue *priority_queue, void *value)
{ {
int rc; int rc;
LOCK_LOCK(&priority_queue->lock); lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
rc = priority_queue_enqueue_nolock(priority_queue, value); rc = priority_queue_enqueue_nolock(priority_queue, value);
LOCK_UNLOCK(&priority_queue->lock); lock_unlock(&priority_queue->lock, &node);
return rc; return rc;
} }
@ -403,7 +406,7 @@ priority_queue_delete_nolock(struct priority_queue *priority_queue, void *value)
{ {
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(value != NULL); assert(value != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
for (int i = 1; i <= priority_queue->size; i++) { for (int i = 1; i <= priority_queue->size; i++) {
if (priority_queue->items[i] == value) { if (priority_queue->items[i] == value) {
@ -427,9 +430,10 @@ priority_queue_delete(struct priority_queue *priority_queue, void *value)
{ {
int rc; int rc;
LOCK_LOCK(&priority_queue->lock); lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
rc = priority_queue_delete_nolock(priority_queue, value); rc = priority_queue_delete_nolock(priority_queue, value);
LOCK_UNLOCK(&priority_queue->lock); lock_unlock(&priority_queue->lock, &node);
return rc; return rc;
} }
@ -468,7 +472,7 @@ priority_queue_top_nolock(struct priority_queue *priority_queue, void **dequeued
assert(priority_queue != NULL); assert(priority_queue != NULL);
assert(dequeued_element != NULL); assert(dequeued_element != NULL);
assert(priority_queue->get_priority_fn != NULL); assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock)); assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
int return_code; int return_code;
@ -495,9 +499,10 @@ priority_queue_top(struct priority_queue *priority_queue, void **dequeued_elemen
{ {
int return_code; int return_code;
LOCK_LOCK(&priority_queue->lock); lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
return_code = priority_queue_top_nolock(priority_queue, dequeued_element); return_code = priority_queue_top_nolock(priority_queue, dequeued_element);
LOCK_UNLOCK(&priority_queue->lock); lock_unlock(&priority_queue->lock, &node);
return return_code; return return_code;
} }

@ -2,7 +2,6 @@
#include <threads.h> #include <threads.h>
#include "generic_thread.h"
#include "runtime.h" #include "runtime.h"
extern thread_local struct arch_context worker_thread_base_context; extern thread_local struct arch_context worker_thread_base_context;

@ -4,6 +4,7 @@
#include "admissions_control.h" #include "admissions_control.h"
#include "debuglog.h" #include "debuglog.h"
#include "likely.h"
#include "panic.h" #include "panic.h"
#include "runtime.h" #include "runtime.h"

@ -24,7 +24,7 @@ admissions_info_initialize(struct admissions_info *admissions_info, uint8_t perc
if (unlikely(percentile < 50 || percentile > 99)) panic("Invalid admissions percentile"); if (unlikely(percentile < 50 || percentile > 99)) panic("Invalid admissions percentile");
admissions_info->percentile = percentile; admissions_info->percentile = percentile;
admissions_info->control_index = PERF_WINDOW_BUFFER_SIZE * percentile / 100; admissions_info->control_index = perf_window_capacity * percentile / 100;
#ifdef LOG_ADMISSIONS_CONTROL #ifdef LOG_ADMISSIONS_CONTROL
debuglog("Percentile: %u\n", admissions_info->percentile); debuglog("Percentile: %u\n", admissions_info->percentile);
debuglog("Control Index: %d\n", admissions_info->control_index); debuglog("Control Index: %d\n", admissions_info->control_index);
@ -44,12 +44,13 @@ admissions_info_update(struct admissions_info *admissions_info, uint64_t executi
#ifdef ADMISSIONS_CONTROL #ifdef ADMISSIONS_CONTROL
struct perf_window *perf_window = &admissions_info->perf_window; struct perf_window *perf_window = &admissions_info->perf_window;
LOCK_LOCK(&admissions_info->perf_window.lock); lock_node_t node = {};
lock_lock(&admissions_info->perf_window.lock, &node);
perf_window_add(perf_window, execution_duration); perf_window_add(perf_window, execution_duration);
uint64_t estimated_execution = perf_window_get_percentile(perf_window, admissions_info->percentile, uint64_t estimated_execution = perf_window_get_percentile(perf_window, admissions_info->percentile,
admissions_info->control_index); admissions_info->control_index);
admissions_info->estimate = admissions_control_calculate_estimate(estimated_execution, admissions_info->estimate = admissions_control_calculate_estimate(estimated_execution,
admissions_info->relative_deadline); admissions_info->relative_deadline);
LOCK_UNLOCK(&admissions_info->perf_window.lock); lock_unlock(&admissions_info->perf_window.lock, &node);
#endif #endif
} }

@ -28,8 +28,6 @@ current_sandbox_sleep()
struct sandbox *sleeping_sandbox = current_sandbox_get(); struct sandbox *sleeping_sandbox = current_sandbox_get();
assert(sleeping_sandbox != NULL); assert(sleeping_sandbox != NULL);
generic_thread_dump_lock_overhead();
switch (sleeping_sandbox->state) { switch (sleeping_sandbox->state) {
case SANDBOX_RUNNING_SYS: { case SANDBOX_RUNNING_SYS: {
sandbox_sleep(sleeping_sandbox); sandbox_sleep(sleeping_sandbox);
@ -54,8 +52,6 @@ current_sandbox_exit()
struct sandbox *exiting_sandbox = current_sandbox_get(); struct sandbox *exiting_sandbox = current_sandbox_get();
assert(exiting_sandbox != NULL); assert(exiting_sandbox != NULL);
generic_thread_dump_lock_overhead();
switch (exiting_sandbox->state) { switch (exiting_sandbox->state) {
case SANDBOX_RETURNED: case SANDBOX_RETURNED:
sandbox_exit_success(exiting_sandbox); sandbox_exit_success(exiting_sandbox);
@ -107,7 +103,6 @@ current_sandbox_wasm_trap_handler(int trapno)
debuglog("%s", error_message); debuglog("%s", error_message);
worker_thread_epoll_remove_sandbox(sandbox); worker_thread_epoll_remove_sandbox(sandbox);
generic_thread_dump_lock_overhead();
current_sandbox_exit(); current_sandbox_exit();
assert(0); assert(0);
} }
@ -155,7 +150,6 @@ current_sandbox_init()
err: err:
debuglog("%s", error_message); debuglog("%s", error_message);
worker_thread_epoll_remove_sandbox(sandbox); worker_thread_epoll_remove_sandbox(sandbox);
generic_thread_dump_lock_overhead();
current_sandbox_exit(); current_sandbox_exit();
return NULL; return NULL;
} }
@ -179,7 +173,6 @@ done:
sandbox_set_as_returned(sandbox, SANDBOX_RUNNING_SYS); sandbox_set_as_returned(sandbox, SANDBOX_RUNNING_SYS);
/* Cleanup connection and exit sandbox */ /* Cleanup connection and exit sandbox */
generic_thread_dump_lock_overhead();
current_sandbox_exit(); current_sandbox_exit();
assert(0); assert(0);
err: err:

@ -1,39 +0,0 @@
#include <stdint.h>
#include <threads.h>
#include "arch/getcycles.h"
#include "debuglog.h"
extern uint32_t runtime_processor_speed_MHz;
extern uint32_t runtime_quantum_us;
/* Implemented by listener and workers */
thread_local uint64_t generic_thread_lock_duration = 0;
thread_local uint64_t generic_thread_lock_longest = 0;
thread_local uint64_t generic_thread_start_timestamp = 0;
void
generic_thread_initialize()
{
generic_thread_start_timestamp = __getcycles();
generic_thread_lock_longest = 0;
generic_thread_lock_duration = 0;
}
/**
* Reports lock contention
*/
void
generic_thread_dump_lock_overhead()
{
#ifndef NDEBUG
#ifdef LOG_LOCK_OVERHEAD
uint64_t duration = __getcycles() - generic_thread_start_timestamp;
debuglog("Locks consumed %lu / %lu cycles, or %f%%\n", generic_thread_lock_duration, duration,
(double)generic_thread_lock_duration / duration * 100);
debuglog("Longest Held Lock was %lu cycles, or %f quantums\n", generic_thread_lock_longest,
(double)generic_thread_lock_longest / ((uint64_t)runtime_processor_speed_MHz * runtime_quantum_us));
#endif
#endif
}

@ -60,7 +60,8 @@ global_request_scheduler_mtds_add(struct sandbox *sandbox)
struct tenant_global_request_queue *tgrq = sandbox->tenant->tgrq_requests; struct tenant_global_request_queue *tgrq = sandbox->tenant->tgrq_requests;
LOCK_LOCK(&global_lock); lock_node_t node = {};
lock_lock(&global_lock, &node);
struct priority_queue *destination_queue = global_request_scheduler_mtds_default; struct priority_queue *destination_queue = global_request_scheduler_mtds_default;
if (sandbox->tenant->tgrq_requests->mt_class == MT_GUARANTEED) { if (sandbox->tenant->tgrq_requests->mt_class == MT_GUARANTEED) {
@ -84,7 +85,7 @@ global_request_scheduler_mtds_add(struct sandbox *sandbox)
// debuglog("Added the TGRQ back to the Global runqueue - %s to Heapify", QUEUE_NAME); // debuglog("Added the TGRQ back to the Global runqueue - %s to Heapify", QUEUE_NAME);
} }
LOCK_UNLOCK(&global_lock); lock_unlock(&global_lock, &node);
return sandbox; return sandbox;
} }
@ -124,9 +125,9 @@ global_request_scheduler_mtds_remove_with_mt_class(struct sandbox **removed_sand
enum MULTI_TENANCY_CLASS target_mt_class) enum MULTI_TENANCY_CLASS target_mt_class)
{ {
int rc = -ENOENT; int rc = -ENOENT;
;
LOCK_LOCK(&global_lock); lock_node_t node = {};
lock_lock(&global_lock, &node);
/* Avoid unnessary locks when the target_deadline is tighter than the head of the Global runqueue */ /* Avoid unnessary locks when the target_deadline is tighter than the head of the Global runqueue */
uint64_t global_guaranteed_deadline = priority_queue_peek(global_request_scheduler_mtds_guaranteed); uint64_t global_guaranteed_deadline = priority_queue_peek(global_request_scheduler_mtds_guaranteed);
@ -180,7 +181,7 @@ global_request_scheduler_mtds_remove_with_mt_class(struct sandbox **removed_sand
} }
done: done:
LOCK_UNLOCK(&global_lock); lock_unlock(&global_lock, &node);
return rc; return rc;
} }
@ -227,7 +228,7 @@ global_request_scheduler_mtds_initialize()
global_tenant_timeout_queue = priority_queue_initialize(RUNTIME_MAX_TENANT_COUNT, false, global_tenant_timeout_queue = priority_queue_initialize(RUNTIME_MAX_TENANT_COUNT, false,
tenant_timeout_get_priority); tenant_timeout_get_priority);
LOCK_INIT(&global_lock); lock_init(&global_lock);
struct global_request_scheduler_config config = { struct global_request_scheduler_config config = {
.add_fn = global_request_scheduler_mtds_add, .add_fn = global_request_scheduler_mtds_add,
@ -265,7 +266,8 @@ global_request_scheduler_mtds_promote_lock(struct tenant_global_request_queue *t
assert(tgrq != NULL); assert(tgrq != NULL);
// assert(priority_queue_length_nolock(tgrq->sandbox_requests) == 0); // assert(priority_queue_length_nolock(tgrq->sandbox_requests) == 0);
LOCK_LOCK(&global_lock); lock_node_t node = {};
lock_lock(&global_lock, &node);
if (tgrq->mt_class == MT_GUARANTEED) goto done; if (tgrq->mt_class == MT_GUARANTEED) goto done;
if (priority_queue_length_nolock(tgrq->sandbox_requests) == 0) goto done; if (priority_queue_length_nolock(tgrq->sandbox_requests) == 0) goto done;
@ -283,7 +285,7 @@ global_request_scheduler_mtds_promote_lock(struct tenant_global_request_queue *t
if (rc == -ENOSPC) panic("Global Guaranteed queue is full!\n"); if (rc == -ENOSPC) panic("Global Guaranteed queue is full!\n");
done: done:
LOCK_UNLOCK(&global_lock); lock_unlock(&global_lock, &node);
} }
/* /*

@ -3,7 +3,6 @@
#include "arch/getcycles.h" #include "arch/getcycles.h"
#include "global_request_scheduler.h" #include "global_request_scheduler.h"
#include "generic_thread.h"
#include "listener_thread.h" #include "listener_thread.h"
#include "metrics_server.h" #include "metrics_server.h"
#include "module.h" #include "module.h"
@ -85,9 +84,9 @@ listener_thread_register_http_session(struct http_session *http)
accept_evt.events = EPOLLOUT; accept_evt.events = EPOLLOUT;
http->state = HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED; http->state = HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED;
break; break;
case HTTP_SESSION_SENDING_RESPONSE: case HTTP_SESSION_SENDING_RESPONSE_BODY:
accept_evt.events = EPOLLOUT; accept_evt.events = EPOLLOUT;
http->state = HTTP_SESSION_SEND_RESPONSE_BLOCKED; http->state = HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED;
break; break;
default: default:
panic("Invalid HTTP Session State: %d\n", http->state); panic("Invalid HTTP Session State: %d\n", http->state);
@ -272,9 +271,6 @@ on_client_request_received(struct http_session *session)
static void static void
on_client_response_header_sending(struct http_session *session) on_client_response_header_sending(struct http_session *session)
{ {
assert(session->state = HTTP_SESSION_EXECUTION_COMPLETE);
session->state = HTTP_SESSION_SENDING_RESPONSE_HEADER;
int rc = http_session_send_response_header(session, (void_star_cb)listener_thread_register_http_session); int rc = http_session_send_response_header(session, (void_star_cb)listener_thread_register_http_session);
if (likely(rc == 0)) { if (likely(rc == 0)) {
on_client_response_body_sending(session); on_client_response_body_sending(session);
@ -310,6 +306,8 @@ on_client_response_body_sending(struct http_session *session)
static void static void
on_client_response_sent(struct http_session *session) on_client_response_sent(struct http_session *session)
{ {
assert(session->state = HTTP_SESSION_SENT_RESPONSE_BODY);
/* Terminal State Logging for Http Session */ /* Terminal State Logging for Http Session */
session->response_sent_timestamp = __getcycles(); session->response_sent_timestamp = __getcycles();
http_session_perf_log_print_entry(session); http_session_perf_log_print_entry(session);
@ -388,7 +386,7 @@ on_client_socket_epoll_event(struct epoll_event *evt)
assert((evt->events & EPOLLOUT) == EPOLLOUT); assert((evt->events & EPOLLOUT) == EPOLLOUT);
on_client_response_header_sending(session); on_client_response_header_sending(session);
break; break;
case HTTP_SESSION_SEND_RESPONSE_BLOCKED: case HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED:
assert((evt->events & EPOLLOUT) == EPOLLOUT); assert((evt->events & EPOLLOUT) == EPOLLOUT);
on_client_response_body_sending(session); on_client_response_body_sending(session);
break; break;
@ -445,7 +443,6 @@ listener_thread_main(void *dummy)
on_client_socket_epoll_event(&epoll_events[i]); on_client_socket_epoll_event(&epoll_events[i]);
} }
} }
generic_thread_dump_lock_overhead();
} }
panic("Listener thread unexpectedly broke loop\n"); panic("Listener thread unexpectedly broke loop\n");

@ -289,12 +289,6 @@ log_compiletime_config()
pretty_print_key_disabled("Log HTTP Parser"); pretty_print_key_disabled("Log HTTP Parser");
#endif #endif
#ifdef LOG_LOCK_OVERHEAD
pretty_print_key_enabled("Log Lock Overhead");
#else
pretty_print_key_disabled("Log Lock Overhead");
#endif
#ifdef LOG_TENANT_LOADING #ifdef LOG_TENANT_LOADING
pretty_print_key_enabled("Log Tenant Loading"); pretty_print_key_enabled("Log Tenant Loading");
#else #else

@ -0,0 +1 @@
out.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

@ -0,0 +1,20 @@
SLEDGE_BINARY_DIR=../../runtime/bin
HOSTNAME=localhost
default: run
clean:
rm -rf res/*
run:
LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} ${SLEDGE_BINARY_DIR}/sledgert spec.json
debug:
SLEDGE_DISABLE_PREEMPTION=true SLEDGE_NWORKERS=1 LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} gdb ${SLEDGE_BINARY_DIR}/sledgert --eval-command="run spec.json"
valgrind:
SLEDGE_DISABLE_PREEMPTION=true SLEDGE_NWORKERS=1 LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} valgrind --leak-check=full --max-stackframe=11150456 --run-libc-freeres=no --run-cxx-freeres=no ${SLEDGE_BINARY_DIR}/sledgert spec.json
.PHONY: client
client:
cat ./0_depth.png | http "${HOSTNAME}:10000/depth_to_xyz" > ./out.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

@ -0,0 +1,18 @@
[
{
"name": "cmu",
"port": 10000,
"replenishment-period-us": 0,
"max-budget-us": 0,
"routes": [
{
"route": "/depth_to_xyz",
"path": "depth_to_xyz.wasm.so",
"expected-execution-us": 5000,
"relative-deadline-us": 360000,
"http-resp-size": 5335057,
"http-resp-content-type": "img/png"
}
]
}
]

@ -38,16 +38,16 @@ run_samples() {
# Scrape the perf window size from the source if possible # Scrape the perf window size from the source if possible
# TODO: Make a util function # TODO: Make a util function
local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)" local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)"
local -i perf_window_buffer_size local -i perf_window_capacity
if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then
printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../include/perf_window.h\n" printf "Failed to scrape perf_window_capacity from ../../include/perf_window.h\n"
printf "Defaulting to 16\n" printf "Defaulting to 16\n"
perf_window_buffer_size=16 perf_window_capacity=16
fi fi
local -ir perf_window_buffer_size local -ir perf_window_capacity
printf "Running Samples: " printf "Running Samples: "
hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -q 200 -cpus 3 -o csv -m GET "http://${hostname}:10000" 1> /dev/null 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -q 200 -cpus 3 -o csv -m GET "http://${hostname}:10000/empty" 1> /dev/null 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "samples failed" panic "samples failed"
return 1 return 1
@ -79,7 +79,7 @@ run_experiments() {
printf "Running Experiments:\n" printf "Running Experiments:\n"
for conn in ${concurrency[*]}; do for conn in ${concurrency[*]}; do
printf "\t%d Concurrency: " "$conn" printf "\t%d Concurrency: " "$conn"
hey -disable-compression -disable-keepalive -disable-redirects -n "$iterations" -c "$conn" -cpus 2 -o csv -m GET "http://$hostname:10000" > "$results_directory/con$conn.csv" 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$iterations" -c "$conn" -cpus 2 -o csv -m GET "http://$hostname:10000/empty" > "$results_directory/con$conn.csv" 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "experiment failed" panic "experiment failed"
return 1 return 1

@ -41,22 +41,22 @@ run_samples() {
# Scrape the perf window size from the source if possible # Scrape the perf window size from the source if possible
# TODO: Make a util function # TODO: Make a util function
local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)" local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)"
local -i perf_window_buffer_size local -i perf_window_capacity
if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then
printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../../runtime/include/perf_window.h\n" printf "Failed to scrape perf_window_capacity from ../../../runtime/include/perf_window.h\n"
printf "Defaulting to 16\n" printf "Defaulting to 16\n"
perf_window_buffer_size=16 perf_window_capacity=16
fi fi
local -ir perf_window_buffer_size local -ir perf_window_capacity
printf "Running Samples: " printf "Running Samples: "
hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10010/fib2" 1> /dev/null 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10010/fib2" 1> /dev/null 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "fib40 samples failed with $?" panic "fib40 samples failed with $?"
return 1 return 1
} }
hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:100010/fib" 1> /dev/null 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:100010/fib" 1> /dev/null 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "fib10 samples failed with $?" panic "fib10 samples failed with $?"
return 1 return 1

@ -38,22 +38,22 @@ run_samples() {
# Scrape the perf window size from the source if possible # Scrape the perf window size from the source if possible
# TODO: Make a util function # TODO: Make a util function
local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../runtime/include/perf_window_t.h)" local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../runtime/include/perf_window_t.h)"
local -i perf_window_buffer_size local -i perf_window_capacity
if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then
printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../runtime/include/perf_window.h\n" printf "Failed to scrape perf_window_capacity from ../../runtime/include/perf_window.h\n"
printf "Defaulting to 16\n" printf "Defaulting to 16\n"
perf_window_buffer_size=16 perf_window_capacity=16
fi fi
local -ir perf_window_buffer_size local -ir perf_window_capacity
printf "Running Samples: " printf "Running Samples: "
hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "fibonacci_40 samples failed with $?" panic "fibonacci_40 samples failed with $?"
return 1 return 1
} }
hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || {
printf "[ERR]\n" printf "[ERR]\n"
panic "fibonacci_10 samples failed with $?" panic "fibonacci_10 samples failed with $?"
return 1 return 1

Loading…
Cancel
Save