From c5889b606ab2bfe0f8a67402a26744c2ba6a4161 Mon Sep 17 00:00:00 2001 From: vraatikka Date: Tue, 18 Jun 2013 21:17:20 +0300 Subject: [PATCH] First version of log_writer and test. Includes objects : logfile, filewriter. Routines : skygw_message_t, simple_mutex. Actual file handling is missing and some clean-up routines as well. Compiles and runs but doesn't do much. --- log_writer/log_writer.cc | 417 ++++++++++++++++++++++++++++++++++++++ log_writer/log_writer.h | 41 ++++ log_writer/makefile | 38 ++++ log_writer/test/makefile | 34 ++++ log_writer/test/testlog.c | 17 ++ 5 files changed, 547 insertions(+) create mode 100644 log_writer/log_writer.cc create mode 100644 log_writer/log_writer.h create mode 100644 log_writer/makefile create mode 100644 log_writer/test/makefile create mode 100644 log_writer/test/testlog.c diff --git a/log_writer/log_writer.cc b/log_writer/log_writer.cc new file mode 100644 index 000000000..165ca8900 --- /dev/null +++ b/log_writer/log_writer.cc @@ -0,0 +1,417 @@ +#include +#include +#include +#include +#include +#include +#include + +#define log_fname_prefix "skygw_log" +#define log_fname_tail ".out" + +const int nfiles = 10; +const size_t log_file_size = (64*MB); + + +/** Writer thread structure */ +struct filewriter_st { + skygw_chk_t fwr_chk_top; + slist_cursor_t* fwr_logfile_cursor; + skygw_message_t* fwr_logmes; + skygw_message_t* fwr_clientmes; + simple_mutex_t* fwr_logfile_mutex; + pthread_t fwr_tid; + skygw_thread_t* fwr_thread; + skygw_chk_t fwr_chk_tail; +}; + +struct logfile_st { + skygw_chk_t lf_chk_top; + logfile_id_t lf_id; + char* lf_directory; + char* lf_name_prefix; + char* lf_name_tail; + int lf_nfiles_max; + size_t lf_file_size; + /** This must be protected */ + slist_cursor_t* lf_writebuf_cursor; + slist_cursor_t* lf_files_cursor; + int lf_npending_writes; + skygw_chk_t lf_chk_tail; +}; + + +static bool logfile_write_ex( + logfile_t* logfile, + char* logstr, + bool force_flush); + + +static logfile_t* logfile_init_nomutex(logfile_id_t logfile_id); + +static char* get_logfile_directory( + logfile_id_t logfile_id); + +static char* get_logfile_name_prefix( + logfile_id_t logfile_id); + +static char* get_logfile_name_tail( + logfile_id_t logfile_id); + +static int get_logfile_nfiles( + logfile_id_t logfile_id); + +static size_t get_logfile_file_size( + logfile_id_t logfile_id); + +static filewriter_t* filewriter_init(void); + +static void* thr_filewriter_fun(void* data); + +static skygw_message_t* filewriter_get_logmes(filewriter_t* filewriter); + +static skygw_message_t* filewriter_get_clientmes(filewriter_t* filewriter); + +bool logfile_write( + logfile_t* logfile, + char* logstr) +{ + CHK_LOGFILE(logfile); + return logfile_write_ex(logfile, logstr, FALSE); +} + +bool logfile_write_flush( + logfile_t* logfile, + char* logstr) +{ + CHK_LOGFILE(logfile); + return logfile_write_ex(logfile, logstr, TRUE); +} + + +static bool logfile_write_ex( + logfile_t* logfile, + char* logstr, + bool force_flush) +{ + bool succp; + CHK_LOGFILE(logfile); +#if 0 + /** Attempt to get buffer for log writing */ + lf_buf = logfile_get_buffer(logfile); + ss_dassert(lf_buf != NULL); + /** + * Add string to buffer. + * Is it possible for this to fail ? */ + succp = logfile_write_to_buf(lf_buf, logstr); + + if (force_flush) { + /** Send flush message to filewriter */ + logfile_force_flush(logfile); + } +#else + succp = TRUE; +#endif + return succp; +} + + +/** + * @node Create logfile of a specified type for log writing. + * + * Parameters: + * @param logfile_id - in, use + * Specifies the type of logfile. Types are listed in + * filewriter.h + * + * @return pointer to logfile object. + * + * + * @details (write detailed description here) + * + */ +logfile_t* logfile_init( + logfile_id_t logfile_id) +{ + filewriter_t* filewriter; + logfile_t* logfile; + + /** If filewriter doesn't exists, this triggers its creation */ + filewriter = get_or_create_filewriter(logfile_id); + CHK_FILEWRITER(filewriter); + /** + Pitäisikö ensin luoda uusi logfile ja lisätä se sitten + filewriterin listalle? + Selkeämpi? + */ + filewriter_enter_logfilemutex(filewriter); + + /** Protected attempt to get logfile if it exists already */ + logfile = filewriter_get_logfile(logfile_id); + + if (logfile == NULL) { + logfile = logfile_init_nomutex(logfile_id); + } + CHK_LOGFILE(logfile); + filewriter_exit_logfilemutex(filewriter); + + return logfile; +} + +static logfile_t* logfile_init_nomutex( + logfile_id_t logfile_id) +{ + logfile_t* logfile; + + logfile = (logfile_t *)malloc(sizeof(logfile_t)); + + if (logfile == NULL) { + goto return_with_logfile; + } + + logfile->lf_chk_top = CHK_NUM_LOGFILE; + logfile->lf_chk_tail = CHK_NUM_LOGFILE; + logfile->lf_id = logfile_id; + logfile->lf_directory = get_logfile_directory(logfile_id); + logfile->lf_name_prefix = get_logfile_name_prefix(logfile_id); + logfile->lf_name_tail = get_logfile_name_tail(logfile_id); + logfile->lf_nfiles_max = get_logfile_nfiles(logfile_id); + logfile->lf_file_size = get_logfile_file_size(logfile_id); + /** filewriter reads and removes frop top, clients add to tail */ + logfile->lf_writebuf_cursor = slist_init(); + /** only filewriter reads or modifies */ + logfile->lf_files_cursor = slist_init(); + logfile->lf_npending_writes = 0; + +return_with_logfile: + return logfile; +} + +/** + * @node Filewriter is an object which is managed by file writer thread. + * A filewriter is returned or - if it doesn't exist - created prior return. + * + * Parameters: + * @param logfile_id - in, use + * Logfile id is used only if there are multiple file writer threads. + * NOTE that logfile creation is not triggered in this function. + * + * + * @param WRITER - + * + * + * @return pointer to filewriter which is initialized only and so it has no + * logfile set at this phase. + * + * + * @details (write detailed description here) + * + */ +filewriter_t* get_or_create_filewriter( + logfile_id_t logfile_id /** NOT USED WITH 1 WRITER */) +{ + /** global filewriter pointer */ + static filewriter_t* filewriter; + static int a; + static int b; + static bool file_writer_initialized = FALSE; + int my_a = 0; + int wait_usec; + bool just_wait = FALSE; + + while (filewriter == NULL) { + /** Someone else came before you, wait until filewriter has value */ + if (just_wait) { + wait_usec = (rand()%10); + usleep(wait_usec); + continue; + } + + if (my_a == a) { + /** No-one has came since you read a last time, go on */ + + if (a == b) { + a += 1; + my_a += 1; + } else { + /** Someone's still in loop, wait until loop is empty */ + wait_usec = (rand()%10); + usleep(wait_usec); + continue; + } + } else { + just_wait = TRUE; + continue; + } + + if (my_a != a) { + /** Someone updated a after you. Inc. b and retry. */ + my_a = a; + b += 1; + wait_usec = (rand()%100); + usleep(wait_usec); + continue; + } + + /** Only one get this far. It is safe to initialize filewriter */ + ss_info_dassert(file_writer_initialized == FALSE, + "File writer is already initialized. " + "Concurrency problem\n"); + file_writer_initialized = TRUE; + /** + * Create filewriter struct and thread to run with it. + * Wait until thread sends ack. + */ + filewriter = filewriter_init(); + skygw_message_wait(filewriter->fwr_clientmes); + } + CHK_FILEWRITER(filewriter); + ss_info_dassert(skygw_thread_get_state(filewriter->fwr_thread) == + THR_RUNNING, + "File writer thread is not running but filewriter " + "is being returned."); + return filewriter; +} + + +static filewriter_t* filewriter_init(void) +{ + filewriter_t* filewriter; + + filewriter = (filewriter_t *)malloc(sizeof(filewriter_t)); + filewriter->fwr_chk_top = CHK_NUM_FILEWRITER; + filewriter->fwr_chk_tail = CHK_NUM_FILEWRITER; + filewriter->fwr_logfile_cursor = slist_init(); + filewriter->fwr_logmes = skygw_message_init(); + filewriter->fwr_clientmes = skygw_message_init(); + filewriter->fwr_logfile_mutex = simple_mutex_init("logfile"); + filewriter->fwr_thread = + skygw_thread_init("File writer thr", + thr_filewriter_fun, + (void *)filewriter); + skygw_thread_start(filewriter->fwr_thread); + return filewriter; +} + +void filewriter_enter_logfilemutex( + filewriter_t* fwr) +{ + int i; + int err; + + for (i=0; i<100; i++) { + err = simple_mutex_lock(fwr->fwr_logfile_mutex, FALSE); + + if (err == 0) { + break; + } + usleep(200); + } + ss_info_dassert(err == 0, "Can't enter logfilemutex"); +} + +void filewriter_exit_logfilemutex( + filewriter_t* fwr) +{ + int err; + + err = simple_mutex_unlock(fwr->fwr_logfile_mutex); + ss_info_dassert(err == 0, "Can't exit logfilemutex"); +} + + +static skygw_message_t* filewriter_get_logmes( + filewriter_t* filewriter) +{ + CHK_FILEWRITER(filewriter); + + return filewriter->fwr_logmes; +} + +static skygw_message_t* filewriter_get_clientmes( + filewriter_t* filewriter) +{ + CHK_FILEWRITER(filewriter); + + return filewriter->fwr_clientmes; +} + +static void* thr_filewriter_fun( + void* data) +{ + skygw_thread_t* thr; + filewriter_t* fwr; + + thr = (skygw_thread_t *)data; + fwr = (filewriter_t *)skygw_thread_get_data(thr); + + skygw_thread_set_state(thr, THR_RUNNING); + skygw_message_send(fwr->fwr_clientmes); + + while(!skygw_thread_must_exit(thr)) { + skygw_message_wait(fwr->fwr_logmes); + /** Read files whose prefix and tail match with those specified in + * logfiles and insert names + */ + /** Do what is needed and inform client then */ + /** Go wait messages from client it timely alarm */ + } + skygw_thread_set_state(thr, THR_EXIT); + skygw_message_send(fwr->fwr_clientmes); + return NULL; +} + +logfile_t* filewriter_get_logfile( + logfile_id_t id) +{ + return NULL; +} + +bool filewriter_writebuf( + filewriter_t* fw, + void* buf) +{ + return TRUE; +} + +static char* get_logfile_directory( + logfile_id_t logfile_id) +{ + return "/tmp/"; +} + +static char* get_logfile_name_prefix( + logfile_id_t logfile_id) +{ + return "skygw"; +} + +static char* get_logfile_name_tail( + logfile_id_t logfile_id) +{ + return ".msg"; +} + +static int get_logfile_nfiles( + logfile_id_t logfile_id) +{ + return 3; +} + +static size_t get_logfile_file_size( + logfile_id_t logfile_id) +{ + return 3*KB; +} + +void logfile_done( + logfile_id_t id) +{ + fprintf(stderr, "logfile_done\n"); +} + +bool logfile_flush( + logfile_t* logfile) +{ + fprintf(stderr, "logfile_flush\n"); +} diff --git a/log_writer/log_writer.h b/log_writer/log_writer.h new file mode 100644 index 000000000..86feeacb7 --- /dev/null +++ b/log_writer/log_writer.h @@ -0,0 +1,41 @@ +typedef struct filewriter_st filewriter_t; + +EXTERN_C_BLOCK_BEGIN + +typedef enum { + LOGFILE_TRACE = 0, + LOGFILE_MESSAGE, + LOGFILE_ERROR +} logfile_id_t; + +typedef struct logfile_st logfile_t; + +bool logfile_write( + logfile_t* logfile, + char* logstr); + +bool logfile_write_flush( + logfile_t* logfile, + char* logstr); + +bool logfile_flush( + logfile_t* logfile); + +logfile_t* logfile_init( + logfile_id_t logfile_id); + +void logfile_done( + logfile_id_t id); + +EXTERN_C_BLOCK_END + +filewriter_t* get_or_create_filewriter( + logfile_id_t logfile_id /** NOT USED WITH 1 WRITER */); + +void filewriter_enter_logfilemutex( + filewriter_t* fwr); + +logfile_t* filewriter_get_logfile(logfile_id_t id); + +void filewriter_exit_logfilemutex(filewriter_t* fwr); + diff --git a/log_writer/makefile b/log_writer/makefile new file mode 100644 index 000000000..398057a39 --- /dev/null +++ b/log_writer/makefile @@ -0,0 +1,38 @@ +include ../build_gateway.inc +include ../makefile.inc + +CC = gcc +CPP = g++ + +LOG_WRITER_PATH := $(shell pwd) + +makeall: clean all + +clean: + make -C ../utils clean + - $(DEL) *.o + - $(DEL) *.so + - $(DEL) *.so.1.0.1 + - $(DEL) *~ + +all: utils lib + +utils: + make -C $(ROOT_PATH)/utils clean all + $(COPY) $(ROOT_PATH)/utils/skygw_utils.o ./ + +lib: libcomp liblink + +libcomp: + $(CPP) -c $(CFLAGS) \ + -I$(MARIADB_SRC_PATH)/include/ \ + -I../utils/ -I./ \ + -fPIC ./log_writer.cc -o log_writer.o + +liblink: + $(CPP) -shared \ + -Wl,-soname,liblog_writer.so \ + -o liblog_writer.so.1.0.1 log_writer.o \ + $(LDLIBS) $(CPP_LDLIBS) + $(DEL) ./liblog_writer.so + $(LINK) ./liblog_writer.so.1.0.1 ./liblog_writer.so diff --git a/log_writer/test/makefile b/log_writer/test/makefile new file mode 100644 index 000000000..479d19f77 --- /dev/null +++ b/log_writer/test/makefile @@ -0,0 +1,34 @@ +include ../../build_gateway.inc +include ../../makefile.inc + +CC = gcc +CPP = g++ + +TESTPATH := $(shell pwd) +LOG_WRITER_PATH := $(ROOT_PATH)/log_writer +TESTAPP = $(TESTPATH)/testlog + +runtest: makeall testall + +makeall: clean all + +clean: + - $(DEL) *.o + - $(DEL) testlog + - $(DEL) *~ + +all: testcomp testall + +testcomp: + $(CC) $(CFLAGS) \ + -L$(LOG_WRITER_PATH) \ + -Wl,-rpath,$(LOG_WRITER_PATH)/ \ + -o testlog -DSS_DEBUG \ + -I$(SOLIDDB_SRC_PATH)/include \ + -I$(MARIADB_SRC_PATH)/include \ + -I$(LOG_WRITER_PATH) -I$(ROOT_PATH)/utils testlog.c \ + -llog_writer $(LDLIBS) \ + $(LOG_WRITER_PATH)/skygw_utils.o + +testall: + - $(LAUNCH_DEBUGGER) $(TESTAPP) $(BACKGR) diff --git a/log_writer/test/testlog.c b/log_writer/test/testlog.c new file mode 100644 index 000000000..15f1c29e4 --- /dev/null +++ b/log_writer/test/testlog.c @@ -0,0 +1,17 @@ +#include +#include +#include + +int main(int argc, char** argv) +{ + logfile_t* tracelog = logfile_init(LOGFILE_TRACE); + logfile_t* messagelog = logfile_init(LOGFILE_MESSAGE); + + logfile_write(tracelog, "My name is trace"); + logfile_write_flush(messagelog, "I'm the message!"); + logfile_flush(tracelog); + + logfile_done(LOGFILE_TRACE); + logfile_done(LOGFILE_MESSAGE); + return 0; +}