diff options
author | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2011-07-21 16:09:27 +0400 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2011-07-21 16:09:27 +0400 |
commit | 875d1dd367eb433ae77a092148f483e9b9449a47 (patch) | |
tree | 7ecf094891d6d61c4ebe55d77226e560478d2ae6 | |
parent | 5c9372c4a8678c6856360891180b02c2fdf688ee (diff) | |
download | rspamd-875d1dd367eb433ae77a092148f483e9b9449a47.tar.gz rspamd-875d1dd367eb433ae77a092148f483e9b9449a47.zip |
Another fix to avoid race with settings - add reference counter.
-rw-r--r-- | src/settings.c | 71 | ||||
-rw-r--r-- | src/settings.h | 1 |
2 files changed, 50 insertions, 22 deletions
diff --git a/src/settings.c b/src/settings.c index 13e38fc90..b5786d393 100644 --- a/src/settings.c +++ b/src/settings.c @@ -73,7 +73,40 @@ settings_free (gpointer data) if (s->blacklist) { g_hash_table_destroy (s->blacklist); } - g_free (s); + g_slice_free1 (sizeof (struct rspamd_settings), s); +} + +static struct rspamd_settings * +settings_ref (struct rspamd_settings *s) +{ + if (s == NULL) { + s = g_slice_alloc (sizeof (struct rspamd_settings)); + s->metric_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s->reject_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s->metric_actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, settings_actions_free); + s->factors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s->whitelist = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s->blacklist = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s->statfile_alias = NULL; + s->want_spam = FALSE; + s->ref_count = 1; + } + else { + s->ref_count ++; + } + + return s; +} + +static void +settings_unref (struct rspamd_settings *s) +{ + if (s != NULL) { + s->ref_count --; + if (s->ref_count <= 0) { + settings_free (s); + } + } } @@ -127,7 +160,7 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) struct metric_action *new_act; struct rspamd_settings *cur_settings; GList *cur_act; - gchar *cur_name; + gchar *cur_name; void *json_it; double *score; @@ -172,26 +205,20 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) nelts = json_array_size (js); for (i = 0; i < nelts; i++) { - cur_settings = g_malloc (sizeof (struct rspamd_settings)); - cur_settings->metric_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - cur_settings->reject_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - cur_settings->metric_actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, settings_actions_free); - cur_settings->factors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - cur_settings->whitelist = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - cur_settings->blacklist = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - cur_settings->statfile_alias = NULL; - cur_settings->want_spam = FALSE; + cur_settings = settings_ref (NULL); cur_elt = json_array_get (js, i); if (!cur_elt || !json_is_object (cur_elt)) { json_decref (js); msg_err ("loaded json is not an object"); + settings_unref (cur_settings); return; } cur_nm = json_object_get (cur_elt, "name"); if (cur_nm == NULL || !json_is_string (cur_nm)) { json_decref (js); msg_err ("name is not a string or not exists"); + settings_unref (cur_settings); return; } cur_name = g_strdup (json_string_value (cur_nm)); @@ -315,7 +342,7 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) cur_settings->want_spam = TRUE; } } - g_hash_table_insert (((struct json_buf *)data->cur_data)->table, cur_name, cur_settings); + g_hash_table_replace (((struct json_buf *)data->cur_data)->table, cur_name, cur_settings); } json_decref (js); } @@ -342,8 +369,10 @@ read_settings (const gchar *path, struct config_file *cfg, GHashTable * table) void init_settings (struct config_file *cfg) { - cfg->domain_settings = g_hash_table_new_full (rspamd_strcase_hash, rspamd_strcase_equal, g_free, settings_free); - cfg->user_settings = g_hash_table_new_full (rspamd_strcase_hash, rspamd_strcase_equal, g_free, settings_free); + cfg->domain_settings = g_hash_table_new_full (rspamd_strcase_hash, rspamd_strcase_equal, + g_free, (GDestroyNotify)settings_unref); + cfg->user_settings = g_hash_table_new_full (rspamd_strcase_hash, rspamd_strcase_equal, + g_free, (GDestroyNotify)settings_unref); } static gboolean @@ -547,17 +576,15 @@ apply_metric_settings (struct worker_task *task, struct metric *metric, struct m if (check_setting (task, &us, &ds)) { if (us != NULL || ds != NULL) { if (us != NULL) { - g_hash_table_ref (task->cfg->user_settings); - res->user_settings = us; - memory_pool_add_destructor (task->task_pool, (pool_destruct_func)g_hash_table_unref, - task->cfg->user_settings); + res->user_settings = settings_ref (us); + memory_pool_add_destructor (task->task_pool, (pool_destruct_func)settings_unref, + us); } if (ds != NULL) { /* Need to ref hash table to avoid occasional data corruption */ - g_hash_table_ref (task->cfg->domain_settings); - res->domain_settings = ds; - memory_pool_add_destructor (task->task_pool, (pool_destruct_func)g_hash_table_unref, - task->cfg->domain_settings); + res->domain_settings = settings_ref (ds); + memory_pool_add_destructor (task->task_pool, (pool_destruct_func)settings_unref, + ds); } } else { diff --git a/src/settings.h b/src/settings.h index 5d77d429b..fb8f8681e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -13,6 +13,7 @@ struct rspamd_settings { GHashTable *blacklist; /**< hash table of whitelist for this setting */ gchar *statfile_alias; /**< alias for statfile used */ gboolean want_spam; /**< if true disable rspamd checks */ + gint ref_count; /**< reference counter */ }; |