diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2011-11-18 23:23:15 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2011-11-18 23:23:15 +0100 |
| commit | 8d8236e041af06e187a582e5a64d8e718d8396fc (patch) | |
| tree | f36f62d1423dbe749a8dde1a07fece195457bc0e /src | |
| parent | 7abe3ca427d4460fa010d165d11fa979ff4d2f27 (diff) | |
Improve the update torrent list speed by *A LOT* by using d.multicall.
Instead of one xmlrpc call for each attribute for each torrent.
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.c | 692 |
1 files changed, 389 insertions, 303 deletions
@@ -34,6 +34,7 @@ typedef enum typedef struct { + const gchar* hash; state_t state; gchar* title; gfloat downloaded; /* percent */ @@ -49,9 +50,8 @@ typedef struct GtkTreeIter iter; } torrent_t; -static void torrent_update(const gchar* hash, - torrent_t* torrent, torrent_data_t* data); -static void torrent_data_init(torrent_data_t* data); +static void torrent_update(torrent_t* torrent, const torrent_data_t* data); +static void torrent_data_init(torrent_data_t* data, const gchar* hash); static void torrent_data_free(torrent_data_t* data); typedef struct @@ -79,8 +79,6 @@ typedef struct guint config_try; GHashTable* torrents; - guint64 last_sync_time; - guint last_sync_count; guint sync_timeout; const gchar* item_lock; @@ -118,18 +116,20 @@ typedef enum /* Tell worker to update the list of torrents. * Will generate a MSG_SYNCLIST response */ MSG_UPDATELIST, - /* Worker responding to a MSG_UPDATELIST with added and removed torrents */ + /* Worker responding to a MSG_UPDATELIST with added and removed torrents + * and torrent data for all (except removed) */ MSG_SYNCLIST, - /* Tell worker to update a torrent. - * Will generate a MSG_SYNC response */ - MSG_UPDATE, - /* Worker responding to a MSG_UPDATE with uptodate torrent data */ - MSG_SYNC, - /* Tell worker to send a start command for torrent */ + /* Worker responding to a MSG_START/MSG_STOP/MSG_REHASH with uptodate + * torrent state */ + MSG_SYNCSTATE, + /* Tell worker to send a start command for torrent. + * Generates a MSG_SYNCSTATE as response if no error */ MSG_START, - /* Tell worker to send a stop command for torrent */ + /* Tell worker to send a stop command for torrent. + * Generates a MSG_SYNCSTATE as response if no error */ MSG_STOP, - /* Tell worker to send a rehash command for torrent */ + /* Tell worker to send a rehash command for torrent. + * Generates a MSG_SYNCSTATE as response if no error */ MSG_REHASH, } msg_type_t; @@ -184,54 +184,45 @@ typedef struct typedef struct { msg_t base; - const gchar** added; /* NULL terminated list */ - const gchar** removed; /* NULL terminated list */ + const torrent_data_t* data; /* .hash NULL terminated list */ + const torrent_data_t* added; /* .hash NULL terminated list */ + const gchar** removed; /* NULL terminated list */ } msg_synclist_t; typedef struct { msg_t base; const gchar* hash; - torrent_t torrent; -} msg_update_t; - -typedef struct -{ - msg_t base; - const gchar* hash; - torrent_t torrent; - torrent_data_t data; -} msg_sync_t; + state_t state; +} msg_syncstate_t; typedef struct { msg_t base; gchar* hash; - torrent_t torrent; } msg_start_t; typedef struct { msg_t base; gchar* hash; - torrent_t torrent; } msg_stop_t; typedef struct { msg_t base; gchar* hash; - torrent_t torrent; } msg_rehash_t; typedef struct _hashlist_t { - gchar** data; + torrent_data_t* data; gboolean* mark; gsize size, fill; - const gchar** added; + torrent_data_t* added; const gchar** removed; + gsize removed_size; } hashlist_t; static void msg_free(msg_t* msg); @@ -245,19 +236,12 @@ static msg_t* msg_status(const gchar* format, ...); static msg_t* msg_disconnect(void); static msg_t* msg_quit(void); static msg_t* msg_updatelist(void); -static msg_t* msg_synclist(const gchar** added, const gchar** removed); -static msg_t* msg_update(const gchar* hash, torrent_t* torrent); -static msg_t* msg_sync(const gchar* hash, torrent_t* torrent, - torrent_data_t* data); -static msg_t* msg_start(const gchar* hash, torrent_t* torrent); -static msg_t* msg_stop(const gchar* hash, torrent_t* torrent); -static msg_t* msg_rehash(const gchar* hash, torrent_t* torrent); - -static void hashlist_init(hashlist_t* hlist); -static void hashlist_clear(hashlist_t* hlist); -static void hashlist_free(hashlist_t* hlist); -static gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env, - const xmlrpc_value * const result, gsize count); +static msg_t* msg_synclist(torrent_data_t* data, torrent_data_t* added, + const gchar** removed); +static msg_t* msg_syncstate(const gchar* hash, state_t state); +static msg_t* msg_start(const gchar* hash); +static msg_t* msg_stop(const gchar* hash); +static msg_t* msg_rehash(const gchar* hash); static void do_connect(GtkAction* action, gpointer data); static void do_disconnect(GtkAction* action, gpointer data); @@ -565,15 +549,6 @@ gboolean sync_config(gpointer _data) return FALSE; } -static void update_torrent(gpointer _hash, gpointer _torrent, gpointer _master) -{ - master_t* master = _master; - const gchar* hash = _hash; - torrent_t* torrent = _torrent; - - g_async_queue_push(master->queue, msg_update(hash, torrent)); -} - static gboolean update_list(gpointer _master) { master_t* master = _master; @@ -650,78 +625,65 @@ gboolean incoming_msg(gpointer data) case MSG_SYNCLIST: { msg_synclist_t* m = (msg_synclist_t*)msg; - const gchar** x = m->removed; - for (; *x != NULL; ++x) + const torrent_data_t* data; + const gchar** del = m->removed; + for (; *del != NULL; ++del) { - if (msg->master->item_lock == *x) + if (msg->master->item_lock == *del) { msg->master->item_lock = NULL; listselection_changed(msg->master->listselection, msg->master); } - g_hash_table_remove(msg->master->torrents, *x); + g_hash_table_remove(msg->master->torrents, *del); } - x = m->added; - for (; *x != NULL; ++x) + data = m->data; + for (; data->hash != NULL; ++data) + { + torrent_t* t = g_hash_table_lookup(msg->master->torrents, + data->hash); + if (t != NULL) + { + torrent_update(t, data); + } + if (msg->master->item_lock == data->hash) + { + msg->master->item_lock = NULL; + listselection_changed(msg->master->listselection, msg->master); + } + } + data = m->added; + for (; data->hash != NULL; ++data) { torrent_t* t = g_slice_new(torrent_t); t->store = msg->master->liststore; gtk_list_store_append(t->store, &t->iter); - g_hash_table_insert(msg->master->torrents, (gpointer)*x, t); + g_hash_table_insert(msg->master->torrents, (gpointer)data->hash, t); + torrent_update(t, data); } - msg->master->last_sync_time = g_get_monotonic_time(); - msg->master->last_sync_count = - g_hash_table_size(msg->master->torrents); - g_hash_table_foreach(msg->master->torrents, - update_torrent, msg->master); + msg->master->sync_timeout = g_timeout_add(UPDATE_INTERVAL_MS, + update_list, + msg->master); break; } - case MSG_SYNC: + case MSG_SYNCSTATE: { - msg_sync_t* m = (msg_sync_t*)msg; - g_assert(msg->master->last_sync_count > 0); - - torrent_update(m->hash, &m->torrent, &m->data); + msg_syncstate_t* m = (msg_syncstate_t*)msg; + torrent_t* t = g_hash_table_lookup(msg->master->torrents, m->hash); + if (t != NULL) + { + gtk_list_store_set(t->store, &t->iter, COLUMN_STATE, m->state, -1); + } if (msg->master->item_lock == m->hash) { msg->master->item_lock = NULL; listselection_changed(msg->master->listselection, msg->master); } - - if (--msg->master->last_sync_count == 0) - { - if (msg->master->sync_timeout == 0) - { - guint64 now = g_get_monotonic_time(); - msg->master->last_sync_time += UPDATE_INTERVAL_MS * 1000; - if (now >= msg->master->last_sync_time) - { - g_async_queue_push(msg->master->queue, msg_updatelist()); - } - else - { - now = msg->master->last_sync_time - now; - if (now >= UPDATE_INTERVAL_MS * 1000) - { - g_async_queue_push(msg->master->queue, - msg_updatelist()); - } - else - { - msg->master->sync_timeout = - g_timeout_add((guint)(now / 1000), - update_list, - msg->master); - } - } - } - } break; } case MSG_CONNECT: case MSG_DISCONNECT: case MSG_QUIT: - case MSG_UPDATE: case MSG_UPDATELIST: case MSG_START: case MSG_STOP: @@ -817,38 +779,35 @@ void do_disconnect(GtkAction* action, gpointer data) listselection_changed(master->listselection, master); } -static gboolean get_selected_torrent(master_t* master, const gchar** hash, - torrent_t* torrent) +static const gchar* get_selected_torrent(master_t* master) { GValue value = G_VALUE_INIT; GtkTreeModel* model; + GtkTreeIter iter; + const gchar* hash; if (!master->connected || master->item_lock) { return FALSE; } - if (!gtk_tree_selection_get_selected(master->listselection, &model, - &torrent->iter)) + if (!gtk_tree_selection_get_selected(master->listselection, &model, &iter)) { return FALSE; } - torrent->store = master->liststore; - gtk_tree_model_get_value(model, &torrent->iter, - COLUMN_HASH, &value); - *hash = g_value_get_pointer(&value); + gtk_tree_model_get_value(model, &iter, COLUMN_HASH, &value); + hash = g_value_get_pointer(&value); g_value_unset(&value); - return TRUE; + return hash; } void do_start(GtkAction* action, gpointer data) { master_t* master = data; - torrent_t torrent; const gchar* hash; - if (!get_selected_torrent(master, &hash, &torrent)) + if ((hash = get_selected_torrent(master)) == NULL) { return; } - g_async_queue_push(master->queue, msg_start(hash, &torrent)); + g_async_queue_push(master->queue, msg_start(hash)); master->item_lock = hash; gtk_action_set_sensitive(master->startaction, FALSE); gtk_action_set_sensitive(master->stopaction, FALSE); @@ -858,13 +817,12 @@ void do_start(GtkAction* action, gpointer data) void do_stop(GtkAction* action, gpointer data) { master_t* master = data; - torrent_t torrent; const gchar* hash; - if (!get_selected_torrent(master, &hash, &torrent)) + if ((hash = get_selected_torrent(master)) == NULL) { return; } - g_async_queue_push(master->queue, msg_stop(hash, &torrent)); + g_async_queue_push(master->queue, msg_stop(hash)); master->item_lock = hash; gtk_action_set_sensitive(master->startaction, FALSE); gtk_action_set_sensitive(master->stopaction, FALSE); @@ -874,13 +832,12 @@ void do_stop(GtkAction* action, gpointer data) void do_rehash(GtkAction* action, gpointer data) { master_t* master = data; - torrent_t torrent; const gchar* hash; - if (!get_selected_torrent(master, &hash, &torrent)) + if ((hash = get_selected_torrent(master)) == NULL) { return; } - g_async_queue_push(master->queue, msg_rehash(hash, &torrent)); + g_async_queue_push(master->queue, msg_rehash(hash)); master->item_lock = hash; gtk_action_set_sensitive(master->startaction, FALSE); gtk_action_set_sensitive(master->stopaction, FALSE); @@ -909,7 +866,7 @@ void status(master_t* master, const char* format, ...) g_free(tmp); } -void torrent_update(const gchar* hash, torrent_t* torrent, torrent_data_t* data) +void torrent_update(torrent_t* torrent, const torrent_data_t* data) { gtk_list_store_set(torrent->store, &torrent->iter, COLUMN_STATE, data->state, @@ -922,7 +879,7 @@ void torrent_update(const gchar* hash, torrent_t* torrent, torrent_data_t* data) COLUMN_PROGRESSSORT, data->downloaded < 100.0f ? data->downloaded : 1000.0f + data->seeded, - COLUMN_HASH, hash, + COLUMN_HASH, data->hash, -1); } @@ -931,9 +888,17 @@ static void worker_respond(worker_data_t* data, msg_t* msg); static gint64 get_i64_xmlrpc(xmlrpc_env* env, xmlrpc_value* value); static gboolean get_bool_xmlrpc(xmlrpc_env* env, xmlrpc_value* value); -static void sync_torrent_data(worker_data_t* data, xmlrpc_env* env, - xmlrpc_client* client, xmlrpc_server_info* server, - const gchar* hash, torrent_t* torrent); +static void sync_torrent_state(worker_data_t* data, xmlrpc_env* env, + xmlrpc_client* client, + xmlrpc_server_info* server, + const gchar* hash); + +static void hashlist_init(hashlist_t* hlist); +static void hashlist_clear(hashlist_t* hlist); +static void hashlist_free(hashlist_t* hlist); +static gboolean hashlist_sync(hashlist_t* hlist, worker_data_t* data, + xmlrpc_env * const env, + const xmlrpc_value * const result, gsize count); gpointer worker_main(gpointer _data) { @@ -1053,18 +1018,51 @@ gpointer worker_main(gpointer _data) } case MSG_UPDATELIST: { - xmlrpc_value* params, * result = NULL; + xmlrpc_value* params, * item, * result = NULL; int count; params = xmlrpc_array_new(&env); - xmlrpc_client_call2(&env, client, server, "download_list", + item = xmlrpc_string_new(&env, ""); /* default view */ + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.hash="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.is_active="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.hashing_failed="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.is_hash_checking="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.name="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.size_bytes="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.bytes_done="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.ratio="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.down.rate="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + item = xmlrpc_string_new(&env, "d.up.rate="); + xmlrpc_array_append_item(&env, params, item); + xmlrpc_DECREF(item); + xmlrpc_client_call2(&env, client, server, "d.multicall", params, &result); + xmlrpc_DECREF(params); if (!env.fault_occurred) { count = xmlrpc_array_size(&env, result); } if (env.fault_occurred) { - xmlrpc_DECREF(params); if (result) xmlrpc_DECREF(result); worker_respond(data, msg_status("Failed to update list (%d): %s", @@ -1073,10 +1071,9 @@ gpointer worker_main(gpointer _data) msg_free(msg); continue; } - if (!hashlist_sync(&hashlist, &env, + if (!hashlist_sync(&hashlist, data, &env, result, count > 0 ? (gsize)count : 0)) { - xmlrpc_DECREF(params); xmlrpc_DECREF(result); if (env.fault_occurred) { @@ -1094,9 +1091,11 @@ gpointer worker_main(gpointer _data) continue; } xmlrpc_DECREF(result); - worker_respond(data, msg_synclist(hashlist.added, + worker_respond(data, msg_synclist(hashlist.data, + hashlist.added, hashlist.removed)); + params = xmlrpc_array_new(&env); xmlrpc_client_call2(&env, client, server, "ratio.min", params, &result); data->ratio_min = get_i64_xmlrpc(&env, result) * 10; @@ -1123,14 +1122,6 @@ gpointer worker_main(gpointer _data) msg_free(msg); break; } - case MSG_UPDATE: - { - msg_update_t* m = (msg_update_t*)msg; - sync_torrent_data(data, &env, client, server, m->hash, - &m->torrent); - msg_free(msg); - break; - } case MSG_QUIT: quit = TRUE; msg_free(msg); @@ -1156,8 +1147,7 @@ gpointer worker_main(gpointer _data) } else { - sync_torrent_data(data, &env, client, server, m->hash, - &m->torrent); + sync_torrent_state(data, &env, client, server, m->hash); } msg_free(msg); break; @@ -1198,8 +1188,7 @@ gpointer worker_main(gpointer _data) } else { - sync_torrent_data(data, &env, client, server, m->hash, - &m->torrent); + sync_torrent_state(data, &env, client, server, m->hash); } } msg_free(msg); @@ -1226,8 +1215,7 @@ gpointer worker_main(gpointer _data) } else { - sync_torrent_data(data, &env, client, server, m->hash, - &m->torrent); + sync_torrent_state(data, &env, client, server, m->hash); } msg_free(msg); break; @@ -1236,7 +1224,7 @@ gpointer worker_main(gpointer _data) case MSG_ERROR: case MSG_STATUS: case MSG_SYNCLIST: - case MSG_SYNC: + case MSG_SYNCSTATE: g_assert(FALSE); break; } @@ -1321,17 +1309,10 @@ void msg_free(msg_t* msg) g_slice_free(msg_synclist_t, m); break; } - case MSG_UPDATE: - { - msg_update_t* m = (msg_update_t*)msg; - g_slice_free(msg_update_t, m); - break; - } - case MSG_SYNC: + case MSG_SYNCSTATE: { - msg_sync_t* m = (msg_sync_t*)msg; - torrent_data_free(&m->data); - g_slice_free(msg_sync_t, m); + msg_syncstate_t* m = (msg_syncstate_t*)msg; + g_slice_free(msg_syncstate_t, m); break; } case MSG_START: @@ -1433,58 +1414,47 @@ msg_t* msg_updatelist(void) return &msg->base; } -msg_t* msg_synclist(const gchar** added, const gchar** removed) +msg_t* msg_synclist(torrent_data_t* data, torrent_data_t* added, + const gchar** removed) { msg_synclist_t* msg = g_slice_new(msg_synclist_t); msg->base.type = MSG_SYNCLIST; + msg->data = data; msg->added = added; msg->removed = removed; return &msg->base; } -msg_t* msg_update(const gchar* hash, torrent_t* torrent) +msg_t* msg_syncstate(const gchar* hash, state_t state) { - msg_update_t* msg = g_slice_new(msg_update_t); - msg->base.type = MSG_UPDATE; + msg_syncstate_t* msg = g_slice_new(msg_syncstate_t); + msg->base.type = MSG_SYNCSTATE; msg->hash = hash; - msg->torrent = *torrent; + msg->state = state; return &msg->base; } -msg_t* msg_sync(const gchar* hash, torrent_t* torrent, torrent_data_t* data) -{ - msg_sync_t* msg = g_slice_new(msg_sync_t); - msg->base.type = MSG_SYNC; - msg->hash = hash; - msg->torrent = *torrent; - msg->data = *data; - return &msg->base; -} - -msg_t* msg_start(const gchar* hash, torrent_t* torrent) +msg_t* msg_start(const gchar* hash) { msg_start_t* msg = g_slice_new(msg_start_t); msg->base.type = MSG_START; msg->hash = g_strdup(hash); - msg->torrent = *torrent; return &msg->base; } -msg_t* msg_stop(const gchar* hash, torrent_t* torrent) +msg_t* msg_stop(const gchar* hash) { msg_stop_t* msg = g_slice_new(msg_stop_t); msg->base.type = MSG_STOP; msg->hash = g_strdup(hash); - msg->torrent = *torrent; return &msg->base; } -msg_t* msg_rehash(const gchar* hash, torrent_t* torrent) +msg_t* msg_rehash(const gchar* hash) { msg_rehash_t* msg = g_slice_new(msg_rehash_t); msg->base.type = MSG_REHASH; msg->hash = g_strdup(hash); - msg->torrent = *torrent; return &msg->base; } @@ -1492,29 +1462,30 @@ void hashlist_init(hashlist_t* hlist) { hlist->fill = 0; hlist->size = 10; - hlist->data = g_new0(gchar*, hlist->size); + hlist->data = g_new0(torrent_data_t, hlist->size); hlist->mark = g_new0(gboolean, hlist->size); - hlist->added = (const gchar**)hlist->data; - hlist->removed = (const gchar**)hlist->data + hlist->size - 1; + hlist->added = hlist->data; + hlist->removed_size = 5; + hlist->removed = g_new0(const gchar*, hlist->removed_size); } void hashlist_clear(hashlist_t* hlist) { - gsize i, a, r; - for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a); - r = hlist->removed - (const gchar**)hlist->data; - for (i = 0; i < a; ++i) + torrent_data_t* d = hlist->data; + const gchar** r; + for (; d->hash != NULL; d++) { - g_free(hlist->data[i]); + g_free((gchar*)d->hash); + torrent_data_free(d); } - for (i = r; hlist->data[i]; ++i) + for (r = hlist->removed; *r != NULL; r++) { - g_free(hlist->data[i]); + g_free((gchar*)*r); } - hlist->data[0] = NULL; + hlist->data[0].hash = NULL; hlist->fill = 0; - hlist->added = (const gchar**)hlist->data; - hlist->removed = (const gchar**)hlist->data + hlist->size - 1; + hlist->added = hlist->data; + hlist->removed[0] = NULL; } void hashlist_free(hashlist_t* hlist) @@ -1522,97 +1493,120 @@ void hashlist_free(hashlist_t* hlist) hashlist_clear(hlist); g_free(hlist->data); g_free(hlist->mark); + g_free(hlist->removed); } -gboolean hashlist_resize(hashlist_t* hlist, G_GNUC_UNUSED gsize* a, gsize* r) +static gboolean hashlist_data_resize(hashlist_t* hlist) { gsize ns = hlist->size * 2; - gchar** tmp1; + torrent_data_t* tmp1; gboolean* tmp2; - g_assert(*r == hlist->size - 1); if (ns < 10) ns = 10; - tmp1 = realloc(hlist->data, ns * sizeof(gchar**)); + tmp1 = realloc(hlist->data, ns * sizeof(torrent_data_t)); tmp2 = realloc(hlist->mark, ns * sizeof(gboolean)); if (tmp1 == NULL || tmp2 == NULL) { if (tmp1) hlist->data = tmp1; if (tmp2) hlist->mark = tmp2; - hlist->added = (const gchar**)hlist->data + hlist->fill; - hlist->removed = (const gchar**)hlist->data + *r; + hlist->added = hlist->data + hlist->fill; return FALSE; } - memset(tmp1 + hlist->size, 0, (ns - hlist->size) * sizeof(gchar*)); hlist->data = tmp1; hlist->mark = tmp2; hlist->size = ns; - *r = hlist->size - 1; return TRUE; } -gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env, +static gboolean hashlist_removed_resize(hashlist_t* hlist, gsize need) +{ + gsize ns; + const gchar** tmp; + if (hlist->removed_size > need) + { + return TRUE; + } + ns = hlist->removed_size * 2; + if (ns < need + 1) ns = need + 1; + tmp = realloc(hlist->data, ns * sizeof(const gchar*)); + if (tmp == NULL) + { + hlist->added = hlist->data + hlist->fill; + return FALSE; + } + hlist->removed = tmp; + hlist->removed_size = ns; + return TRUE; +} + +static gboolean hashlist_item_sync(worker_data_t* data, + xmlrpc_env * const env, + const xmlrpc_value * const line, + torrent_data_t* torrent_data); + +gboolean hashlist_sync(hashlist_t* hlist, worker_data_t* data, + xmlrpc_env * const env, const xmlrpc_value * const result, gsize count) { gsize i, j, a, r; gsize found = 0; - g_assert((const gchar**)hlist->data + hlist->fill == hlist->added); - for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a); + g_assert(hlist->data + hlist->fill == hlist->added); + for (a = hlist->added - hlist->data; hlist->data[a].hash; ++a); hlist->fill = a; - hlist->added = (const gchar**)hlist->data + a; - for (r = hlist->removed - (const gchar**)hlist->data; hlist->data[r]; ++r) + hlist->added = hlist->data + a; + for (r = 0; hlist->removed[r]; ++r) { - g_free(hlist->data[r]); - } - g_assert(r == hlist->size - 1); - if (a + 5 > r) - { - gsize ns = a + 10; - gchar** tmp1 = realloc(hlist->data, ns * sizeof(gchar*)); - if (tmp1 != NULL) - { - gboolean* tmp2 = realloc(hlist->mark, ns * sizeof(gboolean)); - hlist->data = tmp1; - if (tmp2 != NULL) - { - hlist->mark = tmp2; - memset(hlist->data + hlist->size, 0, - (ns - hlist->size) * sizeof(gchar*)); - hlist->size = ns; - r = hlist->size - 1; - } - } + g_free((gchar*)hlist->removed[r]); } + hlist->removed[0] = NULL; memset(hlist->mark, 0, hlist->fill * sizeof(gboolean)); for (i = 0; i < count; ++i) { - xmlrpc_value* item; + xmlrpc_value* line, *item; const gchar* hash; - xmlrpc_array_read_item(env, result, i, &item); + xmlrpc_array_read_item(env, result, i, &line); + xmlrpc_array_read_item(env, line, 0, &item); xmlrpc_read_string(env, item, &hash); - xmlrpc_DECREF(item); + if (item) xmlrpc_DECREF(item); if (env->fault_occurred) { - hlist->added = (const gchar**)hlist->data + hlist->fill; - hlist->removed = (const gchar**)hlist->data + r; + hlist->added = hlist->data + hlist->fill; + if (line) xmlrpc_DECREF(line); return FALSE; } if (found == hlist->fill) { - if (a + 1 == r && - !hashlist_resize(hlist, &a, &r)) + if (a == hlist->size && !hashlist_data_resize(hlist)) { free((void*)hash); + xmlrpc_DECREF(line); return FALSE; } - hlist->data[a++] = (gchar*)hash; + torrent_data_init(hlist->data + a, hash); + if (!hashlist_item_sync(data, env, line, hlist->data + a++)) + { + hlist->added = hlist->data + hlist->fill; + hlist->data[a].hash = NULL; + xmlrpc_DECREF(line); + return FALSE; + } + hlist->data[a].hash = NULL; + xmlrpc_DECREF(line); continue; } /* Quick path */ if (i < hlist->fill && !hlist->mark[i] && - strcmp(hlist->data[i], hash) == 0) + strcmp(hlist->data[i].hash, hash) == 0) { + if (!hashlist_item_sync(data, env, line, hlist->data + i)) + { + hlist->added = hlist->data + hlist->fill; + xmlrpc_DECREF(line); + return FALSE; + } hlist->mark[i] = TRUE; found++; free((void*)hash); + xmlrpc_DECREF(line); continue; } for (j = i + 1; j != i; ++j) @@ -1629,13 +1623,19 @@ gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env, { continue; } - if (strcmp(hlist->data[j], hash) == 0) + if (strcmp(hlist->data[j].hash, hash) == 0) { break; } } if (j != i) { + if (!hashlist_item_sync(data, env, line, hlist->data + j)) + { + hlist->added = hlist->data + hlist->fill; + xmlrpc_DECREF(line); + return FALSE; + } hlist->mark[j] = TRUE; found++; free((void*)hash); @@ -1643,34 +1643,49 @@ gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env, else { /* New */ - if (a + 1 == r && - !hashlist_resize(hlist, &a, &r)) + if (a == hlist->size && !hashlist_data_resize(hlist)) { free((void*)hash); + xmlrpc_DECREF(line); return FALSE; } - hlist->data[a++] = (gchar*)hash; + torrent_data_init(hlist->data + a, hash); + if (!hashlist_item_sync(data, env, line, hlist->data + a++)) + { + hlist->data[a].hash = NULL; + hlist->added = hlist->data + hlist->fill; + xmlrpc_DECREF(line); + return FALSE; + } + hlist->data[a].hash = NULL; } + xmlrpc_DECREF(line); } if (found < hlist->fill) { gsize n = hlist->fill - found; - for (j = hlist->fill - 1; n > 0; --j) + if (!hashlist_removed_resize(hlist, n)) + { + hlist->added = hlist->data + hlist->fill; + return FALSE; + } + r = 0; + for (j = hlist->fill - 1; r < n; --j) { if (!hlist->mark[j]) { - hlist->data[--r] = hlist->data[j]; + hlist->removed[r++] = hlist->data[j].hash; + torrent_data_free(hlist->data + j); a--; hlist->fill--; memmove(hlist->data + j, hlist->data + j + 1, - (a - j) * sizeof(gchar*)); - hlist->data[a] = NULL; - n--; + (a - j) * sizeof(torrent_data_t)); + hlist->data[a].hash = NULL; } } + hlist->removed[r] = NULL; } - hlist->added = (const gchar**)hlist->data + hlist->fill; - hlist->removed = (const gchar**)hlist->data + r; + hlist->added = hlist->data + hlist->fill; return TRUE; } @@ -1685,9 +1700,10 @@ void torrent_destroy(gpointer value) g_slice_free(torrent_t, torrent); } -void torrent_data_init(torrent_data_t* data) +void torrent_data_init(torrent_data_t* data, const gchar* hash) { memset(data, 0, sizeof(torrent_data_t)); + data->hash = hash; data->left = -1; } @@ -1851,145 +1867,142 @@ gint liststore_default_compare_func(GtkTreeModel* model, } } -void sync_torrent_data(worker_data_t* data, xmlrpc_env* env, - xmlrpc_client* client, xmlrpc_server_info* server, - const gchar* hash, torrent_t* torrent) +gboolean hashlist_item_sync(worker_data_t* data, xmlrpc_env * const env, + const xmlrpc_value * const line, + torrent_data_t* torrent_data) { - xmlrpc_value* params; - torrent_data_t torrent_data; do { - xmlrpc_value* result = NULL, * item; + xmlrpc_value* item; const gchar* tmpstr; gint64 tmpi64; gboolean tmpb; guint64 done, up_rate, down_rate, ratio; - torrent_data_init(&torrent_data); - params = xmlrpc_array_new(env); - item = xmlrpc_string_new(env, hash); - xmlrpc_array_append_item(env, params, item); - xmlrpc_DECREF(item); - - xmlrpc_client_call2(env, client, server, "d.is_active", params, - &result); - tmpb = get_bool_xmlrpc(env, result); + g_assert(torrent_data->hash != NULL); + /* d.active */ + xmlrpc_array_read_item(env, line, 1, &item); + tmpb = get_bool_xmlrpc(env, item); if (env->fault_occurred) { break; } if (!tmpb) { - xmlrpc_client_call2(env, client, server, "d.hashing_failed", - params, &result); - tmpb = get_bool_xmlrpc(env, result); + /* d.hashing_failed */ + xmlrpc_array_read_item(env, line, 2, &item); + tmpb = get_bool_xmlrpc(env, item); if (env->fault_occurred) { break; } if (tmpb) { - torrent_data.state = STATE_HASHFAILED; + torrent_data->state = STATE_HASHFAILED; } else { - torrent_data.state = STATE_DEAD; + torrent_data->state = STATE_DEAD; } } else { - xmlrpc_client_call2(env, client, server, "d.is_hash_checking", - params, &result); - tmpb = get_bool_xmlrpc(env, result); + /* d.is_hash_checking */ + xmlrpc_array_read_item(env, line, 3, &item); + tmpb = get_bool_xmlrpc(env, item); if (env->fault_occurred) { break; } if (tmpb) { - torrent_data.state = STATE_REHASH; + torrent_data->state = STATE_REHASH; } else { - torrent_data.state = STATE_ACTIVE; + torrent_data->state = STATE_ACTIVE; } } - xmlrpc_client_call2(env, client, server, "d.name", params, &result); + /* d.name */ + xmlrpc_array_read_item(env, line, 4, &item); if (!env->fault_occurred) { - xmlrpc_read_string(env, result, &tmpstr); + xmlrpc_read_string(env, item, &tmpstr); } - if (result) xmlrpc_DECREF(result); + if (item) xmlrpc_DECREF(item); if (env->fault_occurred) { break; } - torrent_data.title = (gchar*)tmpstr; + g_free(torrent_data->title); + torrent_data->title = (gchar*)tmpstr; - xmlrpc_client_call2(env, client, server, "d.size_bytes", params, - &result); - tmpi64 = get_i64_xmlrpc(env, result); + /* d.size_bytes */ + xmlrpc_array_read_item(env, line, 5, &item); + tmpi64 = get_i64_xmlrpc(env, item); if (env->fault_occurred) { break; } - torrent_data.size = (guint64)tmpi64; + torrent_data->size = (guint64)tmpi64; - xmlrpc_client_call2(env, client, server, "d.bytes_done", params, - &result); - tmpi64 = get_i64_xmlrpc(env, result); + /* d.bytes_done */ + xmlrpc_array_read_item(env, line, 6, &item); + tmpi64 = get_i64_xmlrpc(env, item); if (env->fault_occurred) { break; } done = (guint64)tmpi64; - if (done >= torrent_data.size) + if (done >= torrent_data->size) { - torrent_data.downloaded = 100.0f; + torrent_data->downloaded = 100.0f; } - else if (torrent_data.size > 0) + else if (torrent_data->size > 0) { - torrent_data.downloaded = ((gfloat)done * 100.0f) / - (gfloat)torrent_data.size; + torrent_data->downloaded = ((gfloat)done * 100.0f) / + (gfloat)torrent_data->size; } else { - torrent_data.downloaded = 0.0f; + torrent_data->downloaded = 0.0f; } - xmlrpc_client_call2(env, client, server, "d.ratio", params, &result); - tmpi64 = get_i64_xmlrpc(env, result); + /* d.ratio */ + xmlrpc_array_read_item(env, line, 7, &item); + tmpi64 = get_i64_xmlrpc(env, item); if (env->fault_occurred) { break; } ratio = (guint64)tmpi64; - torrent_data.seeded = (gfloat)ratio / 1000.0f; + torrent_data->seeded = (gfloat)ratio / 1000.0f; - xmlrpc_client_call2(env, client, server, "d.down.rate", params, - &result); - tmpi64 = get_i64_xmlrpc(env, result); + /* d.down.rate */ + xmlrpc_array_read_item(env, line, 8, &item); + tmpi64 = get_i64_xmlrpc(env, item); if (env->fault_occurred) { break; } down_rate = (guint64)tmpi64; - torrent_data.down = (gfloat)down_rate / 1000.0f; + torrent_data->down = (gfloat)down_rate / 1000.0f; - xmlrpc_client_call2(env, client, server, "d.up.rate", params, &result); - tmpi64 = get_i64_xmlrpc(env, result); + /* d.up.rate */ + xmlrpc_array_read_item(env, line, 9, &item); + tmpi64 = get_i64_xmlrpc(env, item); if (env->fault_occurred) { break; } up_rate = (guint64)tmpi64; - torrent_data.up = (gfloat)up_rate / 1000.0f; + torrent_data->up = (gfloat)up_rate / 1000.0f; - if (torrent_data.downloaded < 100.0f) + if (torrent_data->downloaded < 100.0f) { if (down_rate >= 512) { - torrent_data.left = (torrent_data.size - done) / + torrent_data->left = (torrent_data->size - done) / (down_rate & ~(guint32)511); } } @@ -2000,7 +2013,7 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env, { if (ratio < data->ratio_max) { - upload_left = (torrent_data.size * + upload_left = (torrent_data->size * (data->ratio_max - ratio)) / 1000; } else @@ -2011,12 +2024,12 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env, if (data->ratio_min > 0) { /* Inexact but good enough */ - guint64 uploaded_bytes = (torrent_data.size * ratio) / 1000; + guint64 uploaded_bytes = (torrent_data->size * ratio) / 1000; if (uploaded_bytes >= data->ratio_upload) { if (ratio < data->ratio_min) { - gint64 tmp = (torrent_data.size * + gint64 tmp = (torrent_data->size * (data->ratio_min - ratio)) / 1000; if (upload_left < 0 || tmp < upload_left) { @@ -2041,24 +2054,97 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env, { if (up_rate >= 512) { - torrent_data.left = (guint64)upload_left / + torrent_data->left = (guint64)upload_left / (up_rate & ~(guint32)511); } } } } while (FALSE); + if (env->fault_occurred) + { + worker_respond(data, + msg_status("Failed to sync torrent %s (%d): %s", + torrent_data->hash, + env->fault_code, env->fault_string)); + env->fault_occurred = FALSE; + torrent_data->state = STATE_DEAD; + return FALSE; + } + return TRUE; +} + +void sync_torrent_state(worker_data_t* data, xmlrpc_env* env, + xmlrpc_client* client, xmlrpc_server_info* server, + const gchar* hash) +{ + xmlrpc_value* params; + state_t state = STATE_DEAD; + do + { + xmlrpc_value* result = NULL, * item; + gboolean tmpb; + params = xmlrpc_array_new(env); + item = xmlrpc_string_new(env, hash); + xmlrpc_array_append_item(env, params, item); + xmlrpc_DECREF(item); + + xmlrpc_client_call2(env, client, server, "d.is_active", params, + &result); + tmpb = get_bool_xmlrpc(env, result); + if (env->fault_occurred) + { + break; + } + if (!tmpb) + { + xmlrpc_client_call2(env, client, server, "d.hashing_failed", + params, &result); + tmpb = get_bool_xmlrpc(env, result); + if (env->fault_occurred) + { + break; + } + if (tmpb) + { + state = STATE_HASHFAILED; + } + else + { + state = STATE_DEAD; + } + } + else + { + xmlrpc_client_call2(env, client, server, "d.is_hash_checking", + params, &result); + tmpb = get_bool_xmlrpc(env, result); + if (env->fault_occurred) + { + break; + } + if (tmpb) + { + state = STATE_REHASH; + } + else + { + state = STATE_ACTIVE; + } + } + } + while (FALSE); xmlrpc_DECREF(params); if (env->fault_occurred) { worker_respond(data, - msg_status("Failed to update torrent %s (%d): %s", + msg_status("Failed to sync torrent %s (%d): %s", hash, env->fault_code, env->fault_string)); env->fault_occurred = FALSE; - torrent_data.state = STATE_DEAD; + state = STATE_DEAD; } - worker_respond(data, msg_sync(hash, torrent, &torrent_data)); + worker_respond(data, msg_syncstate(hash, state)); } void listselection_changed(GtkTreeSelection* selection, |
