Made it async, all is async
This commit is contained in:
parent
df2524b64e
commit
5a63c731f0
310
gnoemoe/gm-net.c
310
gnoemoe/gm-net.c
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include <glib.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
@ -13,6 +12,8 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "gm-net.h"
|
||||
#include "gm-marshal.h"
|
||||
|
@ -21,6 +22,20 @@
|
|||
|
||||
#define GM_NET_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_NET, GmNetPrivate))
|
||||
|
||||
typedef struct _threadinfo {
|
||||
gchar *host;
|
||||
gchar *port;
|
||||
struct addrinfo *addr;
|
||||
struct addrinfo *current;
|
||||
struct addrinfo *tmp;
|
||||
|
||||
pthread_mutex_t *mutex_idle;
|
||||
guint idle_id;
|
||||
gint ret;
|
||||
gchar *message;
|
||||
GmNet *net;
|
||||
} threadinfo;
|
||||
|
||||
struct _GmNetPrivate {
|
||||
int socket; /**< the connection socket, is -1 if not connected */
|
||||
GIOChannel *channel; /**< the channel which is used to 'listen' on the socket via the glib main loop */
|
||||
|
@ -31,6 +46,10 @@ struct _GmNetPrivate {
|
|||
int tn_last; /**< used for telnet */
|
||||
int tn_subneg; /**< used for telnet */
|
||||
|
||||
pthread_t thread;
|
||||
threadinfo *thread_info;
|
||||
pthread_mutex_t mutex_idle;
|
||||
|
||||
struct addrinfo *addr;
|
||||
struct addrinfo *current;
|
||||
|
||||
|
@ -70,10 +89,35 @@ gboolean on_gm_net_connect_check(GIOChannel * source, GIOCondition condition,
|
|||
GmNet *net);
|
||||
gboolean on_gm_net_connect_timeout(GmNet *net);
|
||||
|
||||
static void
|
||||
gm_net_free_thread_info(GmNet *net) {
|
||||
g_free(net->priv->thread_info->message);
|
||||
g_free(net->priv->thread_info->host);
|
||||
g_free(net->priv->thread_info->port);
|
||||
g_free(net->priv->thread_info);
|
||||
|
||||
net->priv->thread_info = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gm_net_close_thread(GmNet *net) {
|
||||
if (net->priv->thread != 0) {
|
||||
pthread_kill(net->priv->thread, SIGKILL);
|
||||
|
||||
if (net->priv->thread_info->idle_id) {
|
||||
g_source_remove(net->priv->thread_info->idle_id);
|
||||
}
|
||||
|
||||
gm_net_free_thread_info(net);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gm_net_finalize(GObject *object) {
|
||||
GmNet *net = GM_NET(object);
|
||||
|
||||
gm_net_close_thread(net);
|
||||
|
||||
g_free(net->priv->current_host);
|
||||
g_free(net->priv->current_port);
|
||||
|
||||
|
@ -146,6 +190,8 @@ gm_net_init(GmNet *net) {
|
|||
net->priv->source = 0;
|
||||
net->priv->connect_timeout_id = 0;
|
||||
net->priv->connect_check_id = 0;
|
||||
|
||||
pthread_mutex_init(&(net->priv->mutex_idle), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -192,6 +238,11 @@ gm_net_clean_disconnection(GmNet *net) {
|
|||
|
||||
g_io_channel_unref(net->priv->channel);
|
||||
|
||||
if (net->priv->source) {
|
||||
g_source_remove(net->priv->source);
|
||||
net->priv->source = 0;
|
||||
}
|
||||
|
||||
net->priv->channel = NULL;
|
||||
net->priv->socket = -1;
|
||||
|
||||
|
@ -262,61 +313,13 @@ gm_net_connect_failed(GmNet *net, gchar *err, gint code) {
|
|||
net->priv->current = NULL;
|
||||
}
|
||||
|
||||
// Don't use set_state here because we weren't really connected before
|
||||
g_signal_emit(net, net_signals[NET_ERROR], 0,
|
||||
_("Could not make connection..."), GM_NET_ERROR_CONNECTING);
|
||||
|
||||
gm_net_set_state(net, GM_NET_STATE_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gm_net_connect_next(GmNet *net) {
|
||||
char host[NI_MAXHOST], port[NI_MAXSERV];
|
||||
int ret, result;
|
||||
struct addrinfo *tmp = net->priv->current;
|
||||
|
||||
if (tmp == NULL) {
|
||||
return;
|
||||
} else {
|
||||
if ((ret = getnameinfo(tmp->ai_addr, tmp->ai_addrlen, host, NI_MAXHOST,
|
||||
port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.ConnectNext: getnameinfo error: %s",
|
||||
gai_strerror(ret));
|
||||
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
|
||||
return;
|
||||
}
|
||||
|
||||
gm_net_set_host(net, host);
|
||||
gm_net_set_port(net, port);
|
||||
gm_net_set_state(net, GM_NET_STATE_TRY_ADDRESS);
|
||||
|
||||
net->priv->socket = socket(tmp->ai_family, tmp->ai_socktype,
|
||||
tmp->ai_protocol);
|
||||
|
||||
if (net->priv->socket < 0) {
|
||||
gm_net_connect_failed(net, strerror(errno), errno);
|
||||
} else {
|
||||
fcntl(net->priv->socket, F_SETFL,
|
||||
fcntl(net->priv->socket, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
if ((result = connect(net->priv->socket, tmp->ai_addr,
|
||||
net->priv->addr->ai_addrlen)) == -1 && errno != EINPROGRESS) {
|
||||
gm_net_connect_failed(net, strerror(errno), errno);
|
||||
} else {
|
||||
net->priv->channel = g_io_channel_unix_new(net->priv->socket);
|
||||
g_io_channel_set_close_on_unref(net->priv->channel, TRUE);
|
||||
|
||||
if (result == 0) {
|
||||
gm_net_connect_succeed(net);
|
||||
} else {
|
||||
net->priv->connect_check_id = g_io_add_watch(net->priv->channel,
|
||||
G_IO_OUT|G_IO_ERR, (GIOFunc)on_gm_net_connect_check, net);
|
||||
net->priv->connect_timeout_id = g_timeout_add(5000,
|
||||
(GSourceFunc)on_gm_net_connect_timeout, net);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gm_net_handle_telnet(GmNet *net, unsigned char *buf, int *len) {
|
||||
int i, j;
|
||||
|
@ -370,6 +373,163 @@ gm_net_handle_telnet(GmNet *net, unsigned char *buf, int *len) {
|
|||
*len = j; //Since j-- is the last written char
|
||||
}
|
||||
|
||||
static gboolean
|
||||
idle_proceed_connect_next(gpointer user_data) {
|
||||
threadinfo *info = (threadinfo *)user_data;
|
||||
GmNet *net = info->net;
|
||||
struct addrinfo *tmp;
|
||||
gint ret = info->ret;
|
||||
gint result;
|
||||
|
||||
info->idle_id = 0;
|
||||
pthread_join(net->priv->thread, NULL);
|
||||
net->priv->thread = 0;
|
||||
|
||||
if (info->ret != 0) {
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.ConnectNext: getnameinfo error: %s",
|
||||
gai_strerror(info->ret));
|
||||
gm_net_free_thread_info(net);
|
||||
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gm_net_set_host(net, info->host);
|
||||
gm_net_set_port(net, info->port);
|
||||
gm_net_set_state(net, GM_NET_STATE_TRY_ADDRESS);
|
||||
|
||||
tmp = info->tmp;
|
||||
gm_net_free_thread_info(net);
|
||||
|
||||
net->priv->socket = socket(tmp->ai_family, tmp->ai_socktype,
|
||||
tmp->ai_protocol);
|
||||
|
||||
if (net->priv->socket < 0) {
|
||||
gm_net_connect_failed(net, strerror(errno), errno);
|
||||
} else {
|
||||
fcntl(net->priv->socket, F_SETFL,
|
||||
fcntl(net->priv->socket, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
if ((result = connect(net->priv->socket, tmp->ai_addr,
|
||||
net->priv->addr->ai_addrlen)) == -1 && errno != EINPROGRESS) {
|
||||
gm_net_connect_failed(net, strerror(errno), errno);
|
||||
} else {
|
||||
net->priv->channel = g_io_channel_unix_new(net->priv->socket);
|
||||
g_io_channel_set_close_on_unref(net->priv->channel, TRUE);
|
||||
|
||||
if (result == 0) {
|
||||
gm_net_connect_succeed(net);
|
||||
} else {
|
||||
net->priv->connect_check_id = g_io_add_watch(net->priv->channel,
|
||||
G_IO_OUT|G_IO_ERR, (GIOFunc)on_gm_net_connect_check, net);
|
||||
net->priv->connect_timeout_id = g_timeout_add(5000,
|
||||
(GSourceFunc)on_gm_net_connect_timeout, net);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
mutex_unlock(void *ptr) {
|
||||
pthread_mutex_unlock((pthread_mutex_t *)ptr);
|
||||
}
|
||||
|
||||
void *
|
||||
nameinfo_thread(void *ptr) {
|
||||
threadinfo *info = (threadinfo *)ptr;
|
||||
char host[NI_MAXHOST], port[NI_MAXSERV];
|
||||
info->tmp = info->current;
|
||||
|
||||
info->ret = getnameinfo(info->tmp->ai_addr, info->tmp->ai_addrlen, host,
|
||||
NI_MAXHOST, port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
|
||||
if (info->ret == 0) {
|
||||
info->host = g_strdup(host);
|
||||
info->port = g_strdup(port);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(info->mutex_idle);
|
||||
pthread_cleanup_push(mutex_unlock, info->mutex_idle);
|
||||
info->idle_id = g_idle_add(idle_proceed_connect_next, info);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gm_net_connect_next(GmNet *net) {
|
||||
threadinfo *info;
|
||||
|
||||
if (net->priv->current == NULL) {
|
||||
return;
|
||||
} else {
|
||||
info = g_new0(threadinfo, 1);
|
||||
info->net = net;
|
||||
info->current = net->priv->current;
|
||||
info->mutex_idle = &(net->priv->mutex_idle);
|
||||
|
||||
net->priv->thread_info = info;
|
||||
pthread_create(&(net->priv->thread), NULL, nameinfo_thread, (void *)info);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
idle_proceed_addrinfo(gpointer user_data) {
|
||||
threadinfo *info = (threadinfo *)user_data;
|
||||
GmNet *net = info->net;
|
||||
|
||||
info->idle_id = 0;
|
||||
pthread_join(net->priv->thread, NULL);
|
||||
net->priv->thread = 0;
|
||||
|
||||
if (info->ret != 0) {
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo failed: %s",
|
||||
gai_strerror(info->ret));
|
||||
gm_net_connect_failed(net, (gchar *)gai_strerror(info->ret),
|
||||
info->ret);
|
||||
gm_net_free_thread_info(net);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
net->priv->addr = info->addr;
|
||||
|
||||
if (info->addr != NULL) {
|
||||
net->priv->current = info->addr;
|
||||
gm_net_free_thread_info(net);
|
||||
gm_net_connect_next(net);
|
||||
} else {
|
||||
gm_net_free_thread_info(net);
|
||||
gm_net_connect_failed(net, _("No addresses available"), 0);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void *
|
||||
addrinfo_thread(void *ptr) {
|
||||
threadinfo *info = (threadinfo *)ptr;
|
||||
struct addrinfo hint;
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
hint.ai_flags = 0;
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
hint.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo: %s : %s",
|
||||
info->host, info->port);
|
||||
|
||||
info->ret = getaddrinfo(info->host, info->port, &hint, &(info->addr));
|
||||
|
||||
pthread_mutex_lock(info->mutex_idle);
|
||||
pthread_cleanup_push(mutex_unlock, info->mutex_idle);
|
||||
info->idle_id = g_idle_add(idle_proceed_addrinfo, info);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Public */
|
||||
GmNet *
|
||||
gm_net_new() {
|
||||
|
@ -385,9 +545,8 @@ gm_net_state(GmNet *net) {
|
|||
|
||||
void
|
||||
gm_net_connect(GmNet *net, const gchar *host, const gchar *port) {
|
||||
struct addrinfo hint;
|
||||
int ret;
|
||||
char shost[NI_MAXHOST], sport[NI_MAXSERV];
|
||||
threadinfo *info;
|
||||
|
||||
if (net->priv->state != GM_NET_STATE_DISCONNECTED) {
|
||||
return;
|
||||
|
@ -401,36 +560,33 @@ gm_net_connect(GmNet *net, const gchar *host, const gchar *port) {
|
|||
|
||||
gm_net_set_state(net, GM_NET_STATE_CONNECTING);
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
hint.ai_flags = 0;
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
hint.ai_protocol = IPPROTO_TCP;
|
||||
info = g_new0(threadinfo, 1);
|
||||
info->host = g_strdup(shost);
|
||||
info->port = g_strdup(sport);
|
||||
info->net = net;
|
||||
info->mutex_idle = &(net->priv->mutex_idle);
|
||||
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo: %s : %s", shost, sport);
|
||||
|
||||
if ((ret = getaddrinfo(shost, sport, &hint, &(net->priv->addr))) != 0) {
|
||||
net->priv->addr = NULL;
|
||||
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo failed: %s", gai_strerror(ret));
|
||||
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (net->priv->addr != NULL) {
|
||||
net->priv->current = net->priv->addr;
|
||||
gm_net_connect_next(net);
|
||||
} else {
|
||||
gm_net_connect_failed(net, _("No addresses available"), 0);
|
||||
}
|
||||
net->priv->thread_info = info;
|
||||
pthread_create(&(net->priv->thread), NULL, addrinfo_thread, (void *)info);
|
||||
}
|
||||
|
||||
void
|
||||
gm_net_disconnect(GmNet *net) {
|
||||
if (net->priv->state == GM_NET_STATE_CONNECTED) {
|
||||
// thread running
|
||||
if (net->priv->state != GM_NET_STATE_DISCONNECTED) {
|
||||
gm_net_close_thread(net);
|
||||
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
|
||||
|
||||
// Remove the watch
|
||||
g_source_remove(net->priv->source);
|
||||
if (net->priv->connect_timeout_id != 0) {
|
||||
g_source_remove(net->priv->connect_timeout_id);
|
||||
net->priv->connect_timeout_id = 0;
|
||||
}
|
||||
|
||||
if (net->priv->connect_check_id != 0) {
|
||||
g_source_remove(net->priv->connect_check_id);
|
||||
net->priv->connect_check_id = 0;
|
||||
}
|
||||
|
||||
gm_net_clean_disconnection(net);
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue