diff --git a/src/core/Makefile.am b/src/core/Makefile.am index c1e12337..cce6b945 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -46,7 +46,8 @@ libcore_a_SOURCES = \ servers-setup.c \ settings.c \ signals.c \ - special-vars.c + special-vars.c \ + write-buffer.c structure_headers = \ chatnet-rec.h \ @@ -94,6 +95,7 @@ noinst_HEADERS = \ signals.h \ special-vars.h \ window-item-def.h \ + write-buffer.h \ $(structure_headers) EXTRA_DIST = \ diff --git a/src/core/core.c b/src/core/core.c index fd6c96b0..fd7459db 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -32,6 +32,7 @@ #include "chatnets.h" #include "commands.h" #include "expandos.h" +#include "write-buffer.h" #include "log.h" #include "rawlog.h" #include "ignore.h" @@ -65,6 +66,7 @@ void core_init(void) expandos_init(); ignore_init(); servers_init(); + write_buffer_init(); log_init(); rawlog_init(); @@ -86,6 +88,7 @@ void core_deinit(void) rawlog_deinit(); log_deinit(); + write_buffer_deinit(); servers_deinit(); ignore_deinit(); expandos_deinit(); diff --git a/src/core/log.c b/src/core/log.c index 55e0c5f6..58d95b1d 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -25,6 +25,7 @@ #include "misc.h" #include "servers.h" #include "log.h" +#include "write-buffer.h" #include "lib-config/iconfig.h" #include "settings.h" @@ -71,8 +72,8 @@ static void log_write_timestamp(int handle, const char *format, tm = localtime(&stamp); if (strftime(str, sizeof(str), format, tm) > 0) { - write(handle, str, strlen(str)); - if (suffix != NULL) write(handle, suffix, strlen(suffix)); + write_buffer(handle, str, strlen(str)); + if (suffix != NULL) write_buffer(handle, suffix, strlen(suffix)); } } @@ -158,6 +159,7 @@ void log_stop_logging(LOG_REC *log) fcntl(log->handle, F_SETLK, &lock); #endif + write_buffer_flush(); close(log->handle); log->handle = -1; } @@ -214,7 +216,7 @@ void log_write_rec(LOG_REC *log, const char *str) log->last = now; log_write_timestamp(log->handle, log_timestamp, str, now); - write(log->handle, "\n", 1); + write_buffer(log->handle, "\n", 1); signal_emit("log written", 2, log, str); } diff --git a/src/core/rawlog.c b/src/core/rawlog.c index d935d1ac..4e47040c 100644 --- a/src/core/rawlog.c +++ b/src/core/rawlog.c @@ -23,7 +23,7 @@ #include "modules.h" #include "signals.h" #include "misc.h" - +#include "write-buffer.h" #include "settings.h" static int rawlog_lines; @@ -45,7 +45,10 @@ void rawlog_destroy(RAWLOG_REC *rawlog) g_slist_foreach(rawlog->lines, (GFunc) g_free, NULL); g_slist_free(rawlog->lines); - if (rawlog->logging) close(rawlog->handle); + if (rawlog->logging) { + write_buffer_flush(); + close(rawlog->handle); + } g_free(rawlog); } @@ -61,8 +64,8 @@ static void rawlog_add(RAWLOG_REC *rawlog, char *str) } if (rawlog->logging) { - write(rawlog->handle, str, strlen(str)); - write(rawlog->handle, "\n", 1); + write_buffer(rawlog->handle, str, strlen(str)); + write_buffer(rawlog->handle, "\n", 1); } rawlog->lines = g_slist_append(rawlog->lines, str); @@ -125,6 +128,7 @@ void rawlog_open(RAWLOG_REC *rawlog, const char *fname) void rawlog_close(RAWLOG_REC *rawlog) { if (rawlog->logging) { + write_buffer_flush(); close(rawlog->handle); rawlog->logging = 0; } diff --git a/src/core/write-buffer.c b/src/core/write-buffer.c new file mode 100644 index 00000000..762fc24e --- /dev/null +++ b/src/core/write-buffer.c @@ -0,0 +1,184 @@ +/* + write-buffer.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "settings.h" +#include "write-buffer.h" + +#define BUFFER_BLOCK_SIZE 2048 + +typedef struct { + char *active_block; + int active_block_pos; + + GSList *blocks; +} BUFFER_REC; + +static GSList *empty_blocks; +static GHashTable *buffers; +static int block_count; + +static int write_buffer_max_blocks; +static int timeout_tag; + +static void write_buffer_new_block(BUFFER_REC *rec) +{ + char *block; + + if (empty_blocks == NULL) + block = g_malloc(BUFFER_BLOCK_SIZE); + else { + block = empty_blocks->data; + empty_blocks = g_slist_remove(empty_blocks, block); + } + + block_count++; + rec->active_block = block; + rec->active_block_pos = 0; + rec->blocks = g_slist_append(rec->blocks, block); +} + +int write_buffer(int handle, const void *data, int size) +{ + BUFFER_REC *rec; + const char *cdata = data; + int next_size; + + if (write_buffer_max_blocks <= 0) { + /* no write buffer */ + return write(handle, data, size); + } + + if (size <= 0) + return size; + + rec = g_hash_table_lookup(buffers, GINT_TO_POINTER(handle)); + if (rec == NULL) { + rec = g_new0(BUFFER_REC, 1); + write_buffer_new_block(rec); + g_hash_table_insert(buffers, GINT_TO_POINTER(handle), rec); + } + + while (size > 0) { + if (rec->active_block_pos == BUFFER_BLOCK_SIZE) + write_buffer_new_block(rec); + + next_size = size < BUFFER_BLOCK_SIZE-rec->active_block_pos ? + size : BUFFER_BLOCK_SIZE-rec->active_block_pos; + memcpy(rec->active_block+rec->active_block_pos, + cdata, next_size); + + rec->active_block_pos += next_size; + cdata += next_size; + size -= next_size; + } + + if (block_count > write_buffer_max_blocks) + write_buffer_flush(); + + return size; +} + +static int write_buffer_flush_rec(void *handlep, BUFFER_REC *rec) +{ + GSList *tmp; + int handle, size; + + handle = GPOINTER_TO_INT(handlep); + for (tmp = rec->blocks; tmp != NULL; tmp = tmp->next) { + size = tmp->data != rec->active_block ? BUFFER_BLOCK_SIZE : + rec->active_block_pos; + write(handle, tmp->data, size); + } + + empty_blocks = g_slist_concat(empty_blocks, rec->blocks); + g_free(rec); + return TRUE; +} + +void write_buffer_flush(void) +{ + g_slist_foreach(empty_blocks, (GFunc) g_free, NULL); + g_slist_free(empty_blocks); + empty_blocks = NULL; + + g_hash_table_foreach_remove(buffers, + (GHRFunc) write_buffer_flush_rec, NULL); + block_count = 0; +} + +static void read_settings(void) +{ + int msecs; + + if (timeout_tag != -1) + g_source_remove(timeout_tag); + + write_buffer_flush(); + + write_buffer_max_blocks = settings_get_int("write_buffer_kb") * + 1024/BUFFER_BLOCK_SIZE; + + if (settings_get_int("write_buffer_mins") > 0) { + msecs = settings_get_int("write_buffer_mins")*60*1000; + timeout_tag = g_timeout_add(msecs, + (GSourceFunc) write_buffer_flush, + NULL); + } +} + +static void cmd_flushbuffer(void) +{ + write_buffer_flush(); +} + +void write_buffer_init(void) +{ + settings_add_int("misc", "write_buffer_mins", 0); + settings_add_int("misc", "write_buffer_kb", 0); + + buffers = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + + empty_blocks = NULL; + block_count = 0; + + timeout_tag = -1; + read_settings(); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind("flushbuffer", NULL, (SIGNAL_FUNC) cmd_flushbuffer); +} + +void write_buffer_deinit(void) +{ + if (timeout_tag != -1) + g_source_remove(timeout_tag); + + write_buffer_flush(); + g_hash_table_destroy(buffers); + + g_slist_foreach(empty_blocks, (GFunc) g_free, NULL); + g_slist_free(empty_blocks); + + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + command_unbind("flushbuffer", (SIGNAL_FUNC) cmd_flushbuffer); +} diff --git a/src/core/write-buffer.h b/src/core/write-buffer.h new file mode 100644 index 00000000..ef527440 --- /dev/null +++ b/src/core/write-buffer.h @@ -0,0 +1,10 @@ +#ifndef __WRITE_BUFFER_H +#define __WRITE_BUFFER_H + +int write_buffer(int handle, const void *data, int size); +void write_buffer_flush(void); + +void write_buffer_init(void); +void write_buffer_deinit(void); + +#endif