refactor: move JSON file loading from module

master
Sean McBride 3 years ago
parent 438b7650ba
commit b79eb44958

@ -244,4 +244,4 @@ module_free_linear_memory(struct module *module, struct wasm_memory *memory)
void module_free(struct module *module); void module_free(struct module *module);
struct module *module_alloc(char *mod_name, char *mod_path, uint32_t stack_sz, uint32_t relative_deadline_us, int port, struct module *module_alloc(char *mod_name, char *mod_path, uint32_t stack_sz, uint32_t relative_deadline_us, int port,
int req_sz, int resp_sz, int admissions_percentile, uint32_t expected_execution_us); int req_sz, int resp_sz, int admissions_percentile, uint32_t expected_execution_us);
int module_alloc_from_json(char *filename); int module_alloc_from_json(const char *json_buf, ssize_t json_buf_size);

@ -5,6 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <sched.h> #include <sched.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
@ -344,6 +345,77 @@ check_versions()
static_assert(__linux__ == 1, "Requires epoll, a Linux-only feature"); static_assert(__linux__ == 1, "Requires epoll, a Linux-only feature");
} }
/**
* Allocates a buffer in memory containing the entire contents of the file provided
* @param file_name file to load into memory
* @param ret_ptr Pointer to set with address of buffer this function allocates. The caller must free this!
* @return size of the allocated buffer or -1 in case of error;
*/
static inline size_t
load_file_into_buffer(const char *file_name, char **file_buffer)
{
/* Use stat to get file attributes and make sure file is present and not empty */
struct stat stat_buffer;
if (stat(file_name, &stat_buffer) < 0) {
fprintf(stderr, "Attempt to stat %s failed: %s\n", file_name, strerror(errno));
goto err;
}
if (stat_buffer.st_size == 0) {
fprintf(stderr, "File %s is unexpectedly empty\n", file_name);
goto err;
}
if (!S_ISREG(stat_buffer.st_mode)) {
fprintf(stderr, "File %s is not a regular file\n", file_name);
goto err;
}
/* Open the file */
FILE *module_file = fopen(file_name, "r");
if (!module_file) {
fprintf(stderr, "Attempt to open %s failed: %s\n", file_name, strerror(errno));
goto err;
}
/* Initialize a Buffer */
*file_buffer = calloc(1, stat_buffer.st_size);
if (*file_buffer == NULL) {
fprintf(stderr, "Attempt to allocate file buffer failed: %s\n", strerror(errno));
goto stat_buffer_alloc_err;
}
/* Read the file into the buffer and check that the buffer size equals the file size */
ssize_t total_chars_read = fread(*file_buffer, sizeof(char), stat_buffer.st_size, module_file);
#ifdef LOG_MODULE_LOADING
debuglog("size read: %d content: %s\n", total_chars_read, *file_buffer);
#endif
if (total_chars_read != stat_buffer.st_size) {
fprintf(stderr, "Attempt to read %s into buffer failed: %s\n", file_name, strerror(errno));
goto fread_err;
}
assert(total_chars_read > 0);
/* Close the file */
if (fclose(module_file) == EOF) {
fprintf(stderr, "Attempt to close buffer containing %s failed: %s\n", file_name, strerror(errno));
goto fclose_err;
};
module_file = NULL;
return total_chars_read;
fclose_err:
/* We will retry fclose when we fall through into stat_buffer_alloc_err */
fread_err:
free(*file_buffer);
stat_buffer_alloc_err:
// Check to ensure we haven't already close this
if (module_file != NULL) {
if (fclose(module_file) == EOF) panic("Failed to close file\n");
}
err:
return (ssize_t)-1;
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -380,8 +452,12 @@ main(int argc, char **argv)
#ifdef LOG_MODULE_LOADING #ifdef LOG_MODULE_LOADING
debuglog("Parsing modules file [%s]\n", argv[1]); debuglog("Parsing modules file [%s]\n", argv[1]);
#endif #endif
if (module_alloc_from_json(argv[1])) panic("failed to initialize module(s) defined in %s\n", argv[1]); const char *json_path = argv[1];
char *json_buf = NULL;
ssize_t json_buf_len = load_file_into_buffer(json_path, &json_buf);
if (unlikely(json_buf_len <= 0)) panic("failed to initialize module(s) defined in %s\n", json_path);
int rc = module_alloc_from_json(json_buf, json_buf_len);
if (unlikely(rc != 0)) panic("failed to initialize module(s) defined in %s\n", json_path);
for (int i = 0; i < runtime_worker_threads_count; i++) { for (int i = 0; i < runtime_worker_threads_count; i++) {
int ret = pthread_join(runtime_worker_threads[i], NULL); int ret = pthread_join(runtime_worker_threads[i], NULL);

@ -4,7 +4,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "debuglog.h" #include "debuglog.h"
@ -238,114 +237,39 @@ err:
goto done; goto done;
} }
/**
* Allocates a buffer in memory containing the entire contents of the file provided
* @param file_name file to load into memory
* @param ret_ptr Pointer to set with address of buffer this function allocates. The caller must free this!
* @return size of the allocated buffer or -1 in case of error;
*/
static inline size_t
load_file_into_buffer(char *file_name, char **file_buffer)
{
/* Use stat to get file attributes and make sure file is present and not empty */
struct stat stat_buffer;
if (stat(file_name, &stat_buffer) < 0) {
fprintf(stderr, "Attempt to stat %s failed: %s\n", file_name, strerror(errno));
goto err;
}
if (stat_buffer.st_size == 0) {
fprintf(stderr, "File %s is unexpectedly empty\n", file_name);
goto err;
}
if (!S_ISREG(stat_buffer.st_mode)) {
fprintf(stderr, "File %s is not a regular file\n", file_name);
goto err;
}
/* Open the file */
FILE *module_file = fopen(file_name, "r");
if (!module_file) {
fprintf(stderr, "Attempt to open %s failed: %s\n", file_name, strerror(errno));
goto err;
}
/* Initialize a Buffer */
*file_buffer = calloc(1, stat_buffer.st_size);
if (*file_buffer == NULL) {
fprintf(stderr, "Attempt to allocate file buffer failed: %s\n", strerror(errno));
goto stat_buffer_alloc_err;
}
/* Read the file into the buffer and check that the buffer size equals the file size */
ssize_t total_chars_read = fread(*file_buffer, sizeof(char), stat_buffer.st_size, module_file);
#ifdef LOG_MODULE_LOADING
debuglog("size read: %d content: %s\n", total_chars_read, *file_buffer);
#endif
if (total_chars_read != stat_buffer.st_size) {
fprintf(stderr, "Attempt to read %s into buffer failed: %s\n", file_name, strerror(errno));
goto fread_err;
}
assert(total_chars_read > 0);
/* Close the file */
if (fclose(module_file) == EOF) {
fprintf(stderr, "Attempt to close buffer containing %s failed: %s\n", file_name, strerror(errno));
goto fclose_err;
};
module_file = NULL;
return total_chars_read;
fclose_err:
/* We will retry fclose when we fall through into stat_buffer_alloc_err */
fread_err:
free(*file_buffer);
stat_buffer_alloc_err:
// Check to ensure we haven't already close this
if (module_file != NULL) {
if (fclose(module_file) == EOF) panic("Failed to close file\n");
}
err:
return (ssize_t)-1;
}
/** /**
* Parses a JSON file and allocates one or more new modules * Parses a JSON file and allocates one or more new modules
* @param file_name The path of the JSON file * @param file_name The path of the JSON file
* @return RC 0 on Success. -1 on Error * @return RC 0 on Success. -1 on Error
*/ */
int int
module_alloc_from_json(char *file_name) module_alloc_from_json(const char *json_buf, ssize_t json_buf_size)
{ {
assert(file_name != NULL); assert(json_buf != NULL);
assert(json_buf_size > 0);
int return_code = -1; int return_code = -1;
jsmntok_t tokens[JSON_MAX_ELEMENT_SIZE * JSON_MAX_ELEMENT_COUNT]; jsmntok_t tokens[JSON_MAX_ELEMENT_SIZE * JSON_MAX_ELEMENT_COUNT];
/* Load file_name into memory */
char *file_buffer = NULL;
ssize_t total_chars_read = load_file_into_buffer(file_name, &file_buffer);
if (total_chars_read <= 0) goto module_alloc_err;
/* Initialize the Jasmine Parser and an array to hold the tokens */ /* Initialize the Jasmine Parser and an array to hold the tokens */
jsmn_parser module_parser; jsmn_parser module_parser;
jsmn_init(&module_parser); jsmn_init(&module_parser);
/* Use Jasmine to parse the JSON */ /* Use Jasmine to parse the JSON */
int total_tokens = jsmn_parse(&module_parser, file_buffer, total_chars_read, tokens, int total_tokens = jsmn_parse(&module_parser, json_buf, json_buf_size, tokens,
sizeof(tokens) / sizeof(tokens[0])); sizeof(tokens) / sizeof(tokens[0]));
if (total_tokens < 0) { if (total_tokens < 0) {
if (total_tokens == JSMN_ERROR_INVAL) { if (total_tokens == JSMN_ERROR_INVAL) {
fprintf(stderr, "Error parsing %s: bad token, JSON string is corrupted\n", file_name); fprintf(stderr, "Error parsing %s: bad token, JSON string is corrupted\n", json_buf);
} else if (total_tokens == JSMN_ERROR_PART) { } else if (total_tokens == JSMN_ERROR_PART) {
fprintf(stderr, "Error parsing %s: JSON string is too short, expecting more JSON data\n", fprintf(stderr, "Error parsing %s: JSON string is too short, expecting more JSON data\n",
file_name); json_buf);
} else if (total_tokens == JSMN_ERROR_NOMEM) { } else if (total_tokens == JSMN_ERROR_NOMEM) {
/* /*
* According to the README at https://github.com/zserge/jsmn, this is a potentially recoverable * According to the README at https://github.com/zserge/jsmn, this is a potentially recoverable
* error. More tokens can be allocated and jsmn_parse can be re-invoked. * error. More tokens can be allocated and jsmn_parse can be re-invoked.
*/ */
fprintf(stderr, "Error parsing %s: Not enough tokens, JSON string is too large\n", file_name); fprintf(stderr, "Error parsing %s: Not enough tokens, JSON string is too large\n", json_buf);
} }
goto json_parse_err; goto json_parse_err;
} }
@ -375,9 +299,8 @@ module_alloc_from_json(char *file_name)
char val[256] = { 0 }; char val[256] = { 0 };
sprintf(val, "%.*s", tokens[j + i + 1].end - tokens[j + i + 1].start, sprintf(val, "%.*s", tokens[j + i + 1].end - tokens[j + i + 1].start,
file_buffer + tokens[j + i + 1].start); json_buf + tokens[j + i + 1].start);
sprintf(key, "%.*s", tokens[j + i].end - tokens[j + i].start, sprintf(key, "%.*s", tokens[j + i].end - tokens[j + i].start, json_buf + tokens[j + i].start);
file_buffer + tokens[j + i].start);
if (strlen(key) == 0) panic("Unexpected encountered empty key\n"); if (strlen(key) == 0) panic("Unexpected encountered empty key\n");
if (strlen(val) == 0) panic("%s field contained empty string\n", key); if (strlen(val) == 0) panic("%s field contained empty string\n", key);
@ -448,20 +371,16 @@ module_alloc_from_json(char *file_name)
module_count++; module_count++;
} }
if (module_count == 0) panic("%s contained no active modules\n", file_name); if (module_count == 0) panic("%s contained no active modules\n", json_buf);
#ifdef LOG_MODULE_LOADING #ifdef LOG_MODULE_LOADING
debuglog("Loaded %d module%s!\n", module_count, module_count > 1 ? "s" : ""); debuglog("Loaded %d module%s!\n", module_count, module_count > 1 ? "s" : "");
#endif #endif
free(file_buffer);
return_code = 0; return_code = 0;
done: done:
return return_code; return return_code;
module_alloc_err: module_alloc_err:
json_parse_err: json_parse_err:
file_load_err:
free(file_buffer);
err: err:
return_code = -1; return_code = -1;
goto done; goto done;

Loading…
Cancel
Save