From 6774b4b30f0b43fc7c9942bf4f4f39c953b5ef6e Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 14 Dec 2001 01:54:12 +0000 Subject: [PATCH] Added reference counting to server record. At least now we don't accidentally use a destroyed server record when some /command disconnects the server (shouldn't happen really) or when irc_send_cmd() fails sending data to server and disconnects the server (I don't know if this ever happens, but if it does, it very well could have caused crashes) git-svn-id: http://svn.irssi.org/repos/irssi/trunk@2243 dbcabf3a-b0e7-0310-adc4-f8d773084564 --- src/core/commands.c | 2 ++ src/core/server-rec.h | 5 +++- src/core/servers.c | 54 ++++++++++++++++++++++++++++++--------- src/core/servers.h | 3 +++ src/irc/core/irc.c | 7 ++--- src/perl/common/Server.xs | 8 ++++++ src/perl/module.h | 2 +- 7 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/core/commands.c b/src/core/commands.c index d126c358..7893b1c1 100644 --- a/src/core/commands.c +++ b/src/core/commands.c @@ -313,6 +313,7 @@ void command_runsub(const char *cmd, const char *data, subcmd = g_strconcat("command ", newcmd, NULL); g_strdown(subcmd); + if (server != NULL) server_ref(server); if (!signal_emit(subcmd, 3, args, server, item)) { defcmd = g_strdup_printf("default command %s", cmd); if (!signal_emit(defcmd, 3, data, server, item)) { @@ -321,6 +322,7 @@ void command_runsub(const char *cmd, const char *data, } g_free(defcmd); } + if (server != NULL) server_unref(server); g_free(subcmd); g_free(orig); diff --git a/src/core/server-rec.h b/src/core/server-rec.h index b88d2a41..9f5070f7 100644 --- a/src/core/server-rec.h +++ b/src/core/server-rec.h @@ -3,6 +3,8 @@ int type; /* module_get_uniq_id("SERVER", 0) */ int chat_type; /* chat_protocol_lookup(xx) */ +int refcount; + STRUCT_SERVER_CONNECT_REC *connrec; time_t connect_time; /* connection time */ time_t real_connect_time; /* time when server replied that we really are connected */ @@ -10,7 +12,8 @@ time_t real_connect_time; /* time when server replied that we really are connect char *tag; /* tag name for addressing server */ char *nick; /* current nick */ -unsigned int connected:1; /* connected to server */ +unsigned int connected:1; /* Connected to server */ +unsigned int disconnected:1; /* Disconnected, waiting for refcount to drop zero */ unsigned int connection_lost:1; /* Connection lost unintentionally */ unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */ unsigned int no_reconnect:1; /* Don't reconnect to server */ diff --git a/src/core/servers.c b/src/core/servers.c index 199171fb..725010a8 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -45,23 +45,26 @@ void server_connect_failed(SERVER_REC *server, const char *msg) lookup_servers = g_slist_remove(lookup_servers, server); signal_emit("server connect failed", 2, server, msg); - if (server->connect_tag != -1) + + if (server->connect_tag != -1) { g_source_remove(server->connect_tag); - if (server->handle != NULL) + server->connect_tag = -1; + } + if (server->handle != NULL) { net_sendbuffer_destroy(server->handle, TRUE); + server->handle = NULL; + } if (server->connect_pipe[0] != NULL) { g_io_channel_close(server->connect_pipe[0]); g_io_channel_unref(server->connect_pipe[0]); g_io_channel_close(server->connect_pipe[1]); g_io_channel_unref(server->connect_pipe[1]); + server->connect_pipe[0] = NULL; + server->connect_pipe[1] = NULL; } - MODULE_DATA_DEINIT(server); - server_connect_unref(server->connrec); - g_free_not_null(server->nick); - g_free(server->tag); - g_free(server); + server_unref(server); } /* generate tag from server's address */ @@ -242,6 +245,7 @@ void server_connect_init(SERVER_REC *server) MODULE_DATA_INIT(server); server->type = module_get_uniq_id("SERVER", 0); + server_ref(server); server->nick = g_strdup(server->connrec->nick); if (server->connrec->username == NULL || *server->connrec->username == '\0') { @@ -358,15 +362,41 @@ void server_disconnect(SERVER_REC *server) server->handle = NULL; } - if (server->readtag > 0) + if (server->readtag > 0) { g_source_remove(server->readtag); + server->readtag = -1; + } + + server->disconnected = TRUE; + server_unref(server); +} + +void server_ref(SERVER_REC *server) +{ + g_return_if_fail(IS_SERVER(server)); + + server->refcount++; +} + +void server_unref(SERVER_REC *server) +{ + g_return_if_fail(IS_SERVER(server)); + + if (--server->refcount > 0) + return; + + if (g_slist_find(servers, server) != NULL) { + g_warning("Non-referenced server wasn't disconnected"); + server_disconnect(server); + return; + } MODULE_DATA_DEINIT(server); server_connect_unref(server->connrec); - rawlog_destroy(server->rawlog); - line_split_free(server->buffer); - g_free_not_null(server->version); - g_free_not_null(server->away_reason); + if (server->rawlog != NULL) rawlog_destroy(server->rawlog); + if (server->buffer != NULL) line_split_free(server->buffer); + g_free(server->version); + g_free(server->away_reason); g_free(server->nick); g_free(server->tag); g_free(server); diff --git a/src/core/servers.h b/src/core/servers.h index 667a034e..ddeb2494 100644 --- a/src/core/servers.h +++ b/src/core/servers.h @@ -39,6 +39,9 @@ void servers_deinit(void); /* Disconnect from server */ void server_disconnect(SERVER_REC *server); +void server_ref(SERVER_REC *server); +void server_unref(SERVER_REC *server); + SERVER_REC *server_find_tag(const char *tag); SERVER_REC *server_find_chatnet(const char *chatnet); diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c index d24fb801..f5aea94c 100644 --- a/src/irc/core/irc.c +++ b/src/irc/core/irc.c @@ -375,14 +375,15 @@ static void irc_parse_incoming(SERVER_REC *server) too slowly, so read only a few times from the socket before letting other tasks to run. */ count = 0; - while (irc_receive_line(server, &str, count < MAX_SOCKET_READS) > 0) { + server_ref(server); + while (!server->disconnected && + irc_receive_line(server, &str, count < MAX_SOCKET_READS) > 0) { rawlog_input(server->rawlog, str); signal_emit_id(signal_server_incoming, 2, server, str); count++; - if (g_slist_find(servers, server) == NULL) - break; /* disconnected */ } + server_unref(server); } static void irc_init_server(IRC_SERVER_REC *server) diff --git a/src/perl/common/Server.xs b/src/perl/common/Server.xs index 1ae534cf..5516cc4a 100644 --- a/src/perl/common/Server.xs +++ b/src/perl/common/Server.xs @@ -59,6 +59,14 @@ void server_disconnect(server) Irssi::Server server +void +server_ref(server) + Irssi::Server server + +void +server_unref(server) + Irssi::Server server + int isnickflag(server, flag) Irssi::Server server diff --git a/src/perl/module.h b/src/perl/module.h index c54cd61a..e12482ab 100644 --- a/src/perl/module.h +++ b/src/perl/module.h @@ -22,4 +22,4 @@ extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implemen /* Change this every time when some API changes between irssi's perl module (or irssi itself) and irssi's perl libraries. */ -#define IRSSI_PERL_API_VERSION 20011204 +#define IRSSI_PERL_API_VERSION 20011214