Commit cb46f83e authored by Rafaël Carré's avatar Rafaël Carré

* #6738 : make history loading faster

general client cleanup / refactor
thread-safe clock updater thread stopping
fix double free on client exit
parent ef005a20
......@@ -250,6 +250,7 @@ sflphone_hung_up (callable_obj_t * c)
#if GTK_CHECK_VERSION(2,10,0)
status_tray_icon_blink (FALSE);
#endif
stop_call_clock(c);
calltree_update_clock();
}
......@@ -385,9 +386,9 @@ void sflphone_fill_ip2ip_profile (void)
ip2ip_profile = (GHashTable *) dbus_get_ip2_ip_details();
}
void sflphone_get_ip2ip_properties (GHashTable **properties)
GHashTable *sflphone_get_ip2ip_properties(void)
{
*properties = ip2ip_profile;
return ip2ip_profile;
}
void
......@@ -416,7 +417,7 @@ sflphone_hang_up()
dbus_hang_up (selectedCall);
call_remove_all_errors (selectedCall);
selectedCall->_state = CALL_STATE_DIALING;
set_timestamp (&selectedCall->_time_stop);
time (&selectedCall->_time_stop);
//if ( (im_window_get_nb_tabs() > 1) && selectedCall->_im_widget &&
// ! (IM_WIDGET (selectedCall->_im_widget)->containText))
......@@ -440,7 +441,7 @@ sflphone_hang_up()
case CALL_STATE_TRANSFERT:
dbus_hang_up (selectedCall);
call_remove_all_errors (selectedCall);
set_timestamp (&selectedCall->_time_stop);
time (&selectedCall->_time_stop);
break;
default:
WARN ("Should not happen in sflphone_hang_up()!");
......@@ -453,33 +454,20 @@ sflphone_hang_up()
calltree_update_call (history, selectedCall, NULL);
if (selectedCall)
stop_call_clock (selectedCall);
calltree_update_clock();
}
void
sflphone_conference_hang_up()
{
conference_obj_t * selectedConf = calltab_get_selected_conf(current_calls);
if (selectedConf) {
dbus_hang_up_conference (selectedConf);
}
}
void
sflphone_pick_up()
{
callable_obj_t * selectedCall = NULL;
selectedCall = calltab_get_selected_call (active_calltree);
callable_obj_t *selectedCall = calltab_get_selected_call (active_calltree);
DEBUG("SFLphone: Pick up");
if (selectedCall) {
if (!selectedCall) {
sflphone_new_call();
return;
}
switch (selectedCall->_state) {
case CALL_STATE_DIALING:
sflphone_place_call (selectedCall);
......@@ -501,19 +489,15 @@ sflphone_pick_up()
dbus_accept (selectedCall);
stop_notification();
break;
case CALL_STATE_HOLD:
sflphone_new_call();
break;
case CALL_STATE_TRANSFERT:
dbus_transfert (selectedCall);
set_timestamp (&selectedCall->_time_stop);
time (&selectedCall->_time_stop);
calltree_remove_call(current_calls, selectedCall, NULL);
calllist_remove_call(current_calls, selectedCall->_callID);
break;
case CALL_STATE_CURRENT:
case CALL_STATE_HOLD:
case CALL_STATE_RECORD:
sflphone_new_call();
break;
case CALL_STATE_RINGING:
sflphone_new_call();
break;
......@@ -521,10 +505,6 @@ sflphone_pick_up()
WARN ("Should not happen in sflphone_pick_up()!");
break;
}
} else {
sflphone_new_call();
}
}
void
......@@ -597,7 +577,7 @@ sflphone_current (callable_obj_t * c)
{
if (c->_state != CALL_STATE_HOLD)
set_timestamp (&c->_time_start);
time (&c->_time_start);
c->_state = CALL_STATE_CURRENT;
calltree_update_call (current_calls, c, NULL);
......@@ -608,7 +588,7 @@ void
sflphone_record (callable_obj_t * c)
{
if (c->_state != CALL_STATE_HOLD)
set_timestamp (&c->_time_start);
time (&c->_time_start);
c->_state = CALL_STATE_RECORD;
calltree_update_call (current_calls, c, NULL);
......@@ -804,7 +784,7 @@ sflphone_keypad (guint keyval, gchar * key)
switch (keyval) {
case 65307: /* ESCAPE */
dbus_hang_up (c);
set_timestamp (&c->_time_stop);
time (&c->_time_stop);
calltree_update_call (history, c, NULL);
break;
default:
......@@ -839,7 +819,7 @@ sflphone_keypad (guint keyval, gchar * key)
case 65293: /* ENTER */
case 65421: /* ENTER numpad */
dbus_transfert (c);
set_timestamp (&c->_time_stop);
time (&c->_time_stop);
calltree_remove_call(current_calls, c, NULL);
calllist_remove_call(current_calls, c->_callID);
break;
......@@ -1066,7 +1046,7 @@ sflphone_add_participant (const gchar* callID, const gchar* confID)
return;
}
set_timestamp(&call->_time_added);
time(&call->_time_added);
iter = calltree_get_gtkiter_from_id(history, (gchar *)confID);
......@@ -1397,7 +1377,7 @@ void sflphone_save_history (void)
ERROR("SFLphone: Error: Unknown type for serialization");
break;
}
gchar *key = convert_timestamp_to_gchar (current->elem.call->_time_start);
gchar *key = g_strdup_printf ("%i", (int) current->elem.call->_time_start);
g_hash_table_replace (result, (gpointer) key,
g_slist_append(g_hash_table_lookup(result, key),(gpointer) value));
......@@ -1412,7 +1392,7 @@ void sflphone_save_history (void)
}
gchar *value = serialize_history_conference_entry(conf);
gchar *key = convert_timestamp_to_gchar(conf->_time_start);
gchar *key = g_strdup_printf ("%i", (int) conf->_time_start);
g_hash_table_replace(result, (gpointer) key,
g_slist_append(g_hash_table_lookup(result, key), (gpointer) value));
......@@ -1427,7 +1407,6 @@ void sflphone_save_history (void)
void
sflphone_srtp_sdes_on (callable_obj_t * c)
{
c->_srtp_state = SRTP_STATE_SDES_SUCCESS;
calltree_update_call (current_calls, c, NULL);
......
......@@ -172,7 +172,7 @@ void sflphone_fill_ip2ip_profile (void);
* @return The internal hash table representing
* the settings for the ip2ip profile.
*/
void sflphone_get_ip2ip_properties (GHashTable **properties);
GHashTable *sflphone_get_ip2ip_properties(void);
/**
* Initialize the accounts data structure
......@@ -314,6 +314,4 @@ void sflphone_fill_conference_list (void);
void sflphone_conference_on_hold (const conference_obj_t * c);
void sflphone_conference_hang_up();
#endif
......@@ -34,66 +34,60 @@
#include <time.h>
#include "contacts/calltree.h"
#include <unistd.h>
#include <assert.h>
gint get_state_callstruct (gconstpointer a, gconstpointer b)
{
callable_obj_t * c = (callable_obj_t*) a;
call_state_t state = *((call_state_t*)b);
if (c->_state == * ( (call_state_t*) b)) {
return 0;
} else {
return 1;
}
return c->_state == state ? 0 : 1;
}
gchar* call_get_peer_name (const gchar *format)
{
const gchar *end, *name;
DEBUG (" callable_obj: %s", format);
end = g_strrstr (format, "<");
if (!end) {
return g_strndup (format, 0);
} else {
name = format;
return g_strndup (name, end - name);
}
const gchar *end = g_strrstr (format, "<");
return g_strndup (format, end ? end - format : 0);
}
gchar* call_get_peer_number (const gchar *format)
{
DEBUG (" callable_obj: %s", format);
gchar * number = g_strrstr (format, "<") + 1;
gchar * end = g_strrstr (format, ">");
gchar *number = g_strrstr (format, "<") + 1;
gchar *end = g_strrstr (format, ">");
if (end && number)
number = g_strndup (number, end - number);
return g_strndup (number, end - number);
else
number = g_strdup (format);
return number;
return g_strdup (format);
}
gchar* call_get_audio_codec (callable_obj_t *obj)
{
if (obj) {
gchar * const audio_codec = dbus_get_current_audio_codec_name (obj);
gchar *ret = NULL;
gchar *audio_codec = NULL;
if (!obj)
goto out;
audio_codec = dbus_get_current_audio_codec_name (obj);
if (!audio_codec)
goto out;
account_t *acc = account_list_get_by_id(obj->_accountID);
if (acc) {
const codec_t * const codec = codec_list_get_by_name (audio_codec, acc->codecs);
if (codec) {
gchar *result = g_markup_printf_escaped ("%s/%i", audio_codec, codec->sample_rate);
g_free (audio_codec);
return result;
}
}
}
if (!acc)
goto out;
const codec_t *const codec = codec_list_get_by_name (audio_codec, acc->codecs);
if (!codec)
goto out;
ret = g_strdup_printf("%s/%i", audio_codec, codec->sample_rate);
out:
g_free(audio_codec);
if (ret == NULL)
return g_strdup("");
return ret;
}
void call_add_error (callable_obj_t * call, gpointer dialog)
......@@ -111,44 +105,16 @@ void call_remove_all_errors (callable_obj_t * call)
g_ptr_array_foreach (call->_error_dialogs, (GFunc) gtk_widget_destroy, NULL);
}
void threaded_clock_incrementer (void *pc)
static void threaded_clock_incrementer (void *pc)
{
callable_obj_t *call = (callable_obj_t *) pc;
while (call->clockStarted) {
int duration;
time_t start, current;
for(;;) {
gdk_threads_enter ();
set_timestamp (& (call->_time_current));
start = call->_time_start;
current = call->_time_current;
if (current == start) {
g_snprintf (call->_timestr, 20, "00:00");
}
duration = (int) difftime (current, start);
int duration = difftime (time(NULL), call->_time_start);
if (duration / 60 == 0) {
if (duration < 10) {
g_snprintf (call->_timestr, 20, "00:0%d", duration);
} else {
g_snprintf (call->_timestr, 20, "00:%d", duration);
}
} else {
if (duration%60 < 10) {
g_snprintf (call->_timestr, 20, "0%d:0%d", duration/60, duration%60);
} else {
g_snprintf (call->_timestr, 20, "%d:%d", duration/60, duration%60);
}
}
g_snprintf (call->_timestr, 20, "%.2d:%.2d", duration % 60, duration / 60);
// Update clock only if call is active (current, hold, recording transfer)
if ( (call->_state != CALL_STATE_INVALID) &&
......@@ -160,23 +126,37 @@ void threaded_clock_incrementer (void *pc)
calltree_update_clock();
}
// gdk_flush();
gdk_threads_leave ();
GTimeVal abs_time;
g_get_current_time(&abs_time);
g_time_val_add(&abs_time, 1000000);
g_mutex_lock(call->mutex);
g_cond_timed_wait(call->cond, call->mutex, &abs_time);
gboolean ret = call->exitClockThread;
g_mutex_unlock(call->mutex);
usleep (1000000);
if (ret == TRUE)
break;
}
DEBUG ("CallableObj: Stopping Thread");
g_thread_exit (NULL);
}
void stop_call_clock (callable_obj_t *c)
{
if (c->_type == CALL && c->clockStarted)
c->clockStarted = 0;
assert(c->_type == CALL);
g_mutex_lock(c->mutex);
c->exitClockThread = TRUE;
g_cond_signal(c->cond);
g_mutex_unlock(c->mutex);
g_thread_join(c->clock_thread);
g_mutex_free(c->mutex);
g_cond_free(c->cond);
c->clock_thread = NULL;
}
callable_obj_t *create_new_call (callable_type_t type, call_state_t state,
......@@ -187,41 +167,26 @@ callable_obj_t *create_new_call (callable_type_t type, call_state_t state,
{
DEBUG ("CallableObj: Create new call (Account: %s)", accountID);
// Allocate memory
callable_obj_t *obj = g_new0 (callable_obj_t, 1);
obj->_error_dialogs = g_ptr_array_new();
// Set fields
obj->_type = type;
obj->_state = state;
if (g_strcasecmp (callID, "") == 0)
{
obj->_callID = g_new0 (gchar, 30);
if (obj->_callID)
g_sprintf (obj->_callID, "%d", rand());
}
else
obj->_callID = g_strdup (callID);
obj->_callID = *callID ? g_strdup (callID) : g_strdup_printf("%d", rand());
obj->_accountID = g_strdup (accountID);
set_timestamp (& (obj->_time_start));
set_timestamp (& (obj->_time_current));
set_timestamp (& (obj->_time_stop));
time (&obj->_time_start);
time (&obj->_time_stop);
obj->_peer_name = g_strdup (peer_name);
obj->_peer_number = g_strdup (peer_number);
obj->_peer_info = get_peer_info (peer_name, peer_number);
obj->clockStarted = 1;
if (obj->_type == CALL) {
GError *err1 = NULL ;
if (!g_thread_create ( (GThreadFunc) threaded_clock_incrementer, (void *) obj, TRUE, &err1)) {
DEBUG ("Thread creation failed!");
g_error_free (err1) ;
}
obj->mutex = g_mutex_new();
obj->cond = g_cond_new();
obj->exitClockThread = FALSE;
obj->clock_thread = g_thread_create((GThreadFunc) threaded_clock_incrementer, (void *) obj, TRUE, NULL);
}
return obj;
......@@ -238,23 +203,21 @@ callable_obj_t *create_new_call_from_details (const gchar *call_id, GHashTable *
if (g_strcasecmp (state_str, "CURRENT") == 0)
state = CALL_STATE_CURRENT;
else if (g_strcasecmp (state_str, "RINGING") == 0)
state = CALL_STATE_RINGING;
else if (g_strcasecmp (state_str, "INCOMING") == 0)
state = CALL_STATE_INCOMING;
else if (g_strcasecmp (state_str, "HOLD") == 0)
state = CALL_STATE_HOLD;
else if (g_strcasecmp (state_str, "BUSY") == 0)
state = CALL_STATE_BUSY;
else
state = CALL_STATE_FAILURE;
return create_new_call (CALL, state, call_id, accountID, peer_name, call_get_peer_number (peer_number));
gchar *number = call_get_peer_number (peer_number);
callable_obj_t *c = create_new_call (CALL, state, call_id, accountID, peer_name, number);
g_free(number);
return c;
}
callable_obj_t *create_history_entry_from_serialized_form (const gchar *entry)
......@@ -268,66 +231,37 @@ callable_obj_t *create_history_entry_from_serialized_form (const gchar *entry)
const gchar *recordfile = "";
const gchar *confID = "";
const gchar *time_added = "";
callable_obj_t *new_call;
history_state_t history_state = MISSED;
gint token = 0;
gchar ** ptr;
gchar ** ptr_orig;
static const gchar * const delim = "|";
ptr = g_strsplit(entry, delim, 10);
ptr_orig = ptr;
while (ptr != NULL && token < 10) {
switch (token) {
case 0:
history_state = get_history_state_from_id (*ptr);
break;
case 1:
peer_number = *ptr;
break;
case 2:
peer_name = *ptr;
break;
case 3:
time_start = *ptr;
break;
case 4:
time_stop = *ptr;
break;
case 5:
callID = *ptr;
break;
case 6:
accountID = *ptr;
break;
case 7:
recordfile = *ptr;
break;
case 8:
confID = *ptr;
break;
case 9:
time_added = *ptr;
break;
default:
break;
}
token++;
ptr++;
gchar **ptr_orig = g_strsplit(entry, "|", 10);
gchar **ptr;
gint token;
for (ptr = ptr_orig, token = 0; ptr && token < 10; token++, ptr++)
switch (token) {
case 0: history_state = get_history_state_from_id (*ptr); break;
case 1: peer_number = *ptr; break;
case 2: peer_name = *ptr; break;
case 3: time_start = *ptr; break;
case 4: time_stop = *ptr; break;
case 5: callID = *ptr; break;
case 6: accountID = *ptr; break;
case 7: recordfile = *ptr; break;
case 8: confID = *ptr; break;
case 9: time_added = *ptr; break;
default: break;
}
if (g_strcasecmp (peer_name, "empty") == 0)
peer_name = "";
new_call = create_new_call (HISTORY_ENTRY, CALL_STATE_DIALING, callID, accountID, peer_name, peer_number);
callable_obj_t *new_call = create_new_call (HISTORY_ENTRY, CALL_STATE_DIALING, callID, accountID, peer_name, peer_number);
new_call->_history_state = history_state;
new_call->_time_start = convert_gchar_to_timestamp (time_start);
new_call->_time_stop = convert_gchar_to_timestamp (time_stop);
new_call->_time_start = atoi(time_start);
new_call->_time_stop = atoi(time_stop);
new_call->_recordfile = g_strdup(recordfile);
new_call->_confID = g_strdup(confID);
new_call->_historyConfID = g_strdup(confID);
new_call->_time_added = convert_gchar_to_timestamp(time_start);
new_call->_time_added = atoi(time_start);
new_call->_record_is_playing = FALSE;
g_strfreev(ptr_orig);
......@@ -336,10 +270,6 @@ callable_obj_t *create_history_entry_from_serialized_form (const gchar *entry)
void free_callable_obj_t (callable_obj_t *c)
{
DEBUG ("CallableObj: Free callable object");
stop_call_clock (c);
g_free (c->_callID);
g_free (c->_confID);
g_free (c->_historyConfID);
......@@ -357,11 +287,6 @@ void free_callable_obj_t (callable_obj_t *c)
calltree_update_clock();
}
void attach_thumbnail (callable_obj_t *call, GdkPixbuf *pixbuf)
{
call->_contact_thumbnail = pixbuf;
}
gchar* get_peer_info (const gchar* const number, const gchar* const name)
{
return g_strconcat ("\"", name, "\" <", number, ">", NULL);
......@@ -369,16 +294,9 @@ gchar* get_peer_info (const gchar* const number, const gchar* const name)
history_state_t get_history_state_from_id (gchar *indice)
{
history_state_t state = atoi(indice);
history_state_t state;
if (g_strcasecmp (indice, "0") ==0)
state = MISSED;
else if (g_strcasecmp (indice, "1") ==0)
state = INCOMING;
else if (g_strcasecmp (indice, "2") ==0)
state = OUTGOING;
else
if (state > LAST)
state = MISSED;
return state;
......@@ -386,34 +304,10 @@ history_state_t get_history_state_from_id (gchar *indice)
gchar* get_call_duration (callable_obj_t *obj)
{
int duration;
time_t start, end;
start = obj->_time_start;
end = obj->_time_stop;
if (start == end)
return g_markup_printf_escaped ("<small>Duration:</small> 0:00");
duration = (int) difftime (end, start);
gchar *result;
if (duration / 60 == 0) {
if (duration < 10)
result = g_markup_printf_escaped ("00:0%i", duration);
else
result = g_markup_printf_escaped ("00:%i", duration);
} else {
if (duration%60 < 10)
result = g_markup_printf_escaped ("%i:0%i" , duration/60 , duration%60);
else
result = g_markup_printf_escaped ("%i:%i" , duration/60 , duration%60);
}
gchar *old_result = result;
result = g_markup_printf_escaped ("<small>Duration:</small> %s", old_result);
g_free(old_result);
return result;
long duration = difftime (obj->_time_stop, obj->_time_start);
if (duration < 0)
duration = 0;
return g_strdup_printf("<small>Duration:</small> %.2ld:%.2ld" , duration/60 , duration%60);
}