static void egg_task_cache_fetch_cb (GObject *object, GAsyncResult *result, gpointer user_data) { EggTaskCache *self = (EggTaskCache *)object; GTask *task = (GTask *)result; GError *error = NULL; gpointer key = user_data; gpointer ret; g_assert (EGG_IS_TASK_CACHE (self)); g_assert (G_IS_TASK (task)); g_hash_table_remove (self->in_flight, key); ret = g_task_propagate_pointer (task, &error); if (ret != NULL) { egg_task_cache_populate (self, key, ret); egg_task_cache_propagate_pointer (self, key, ret); self->value_destroy_func (ret); } else { egg_task_cache_propagate_error (self, key, error); g_clear_error (&error); } self->key_destroy_func (key); EGG_COUNTER_DEC (in_flight); }
static void egg_task_cache_propagate_error (EggTaskCache *self, gconstpointer key, const GError *error) { GPtrArray *queued; g_assert (EGG_IS_TASK_CACHE (self)); g_assert (error != NULL); if ((queued = g_hash_table_lookup (self->queued, key))) { gint64 count = queued->len; gsize i; /* we can't use steal because we want the key freed */ g_ptr_array_ref (queued); g_hash_table_remove (self->queued, key); for (i = 0; i < queued->len; i++) { GTask *task; task = g_ptr_array_index (queued, i); g_task_return_error (task, g_error_copy (error)); } g_ptr_array_unref (queued); EGG_COUNTER_SUB (queued, count); } }
static void egg_task_cache_propagate_pointer (EggTaskCache *self, gconstpointer key, gpointer value) { GPtrArray *queued = NULL; g_assert (EGG_IS_TASK_CACHE (self)); if ((queued = g_hash_table_lookup (self->queued, key))) { gint64 count = queued->len; gsize i; g_ptr_array_ref (queued); g_hash_table_remove (self->queued, key); for (i = 0; i < queued->len; i++) { GTask *task; task = g_ptr_array_index (queued, i); g_task_return_pointer (task, self->value_copy_func (value), self->value_destroy_func); } g_ptr_array_unref (queued); EGG_COUNTER_SUB (queued, count); } }
/** * egg_task_cache_get_finish: * * Finish a call to egg_task_cache_get_async(). * * Returns: (transfer full): The result from the cache. */ gpointer egg_task_cache_get_finish (EggTaskCache *self, GAsyncResult *result, GError **error) { GTask *task = (GTask *)result; g_return_val_if_fail (EGG_IS_TASK_CACHE (self), NULL); g_return_val_if_fail (G_IS_TASK (result), NULL); g_return_val_if_fail (G_IS_TASK (task), NULL); return g_task_propagate_pointer (task, error); }
/** * egg_task_cache_peek: * @self: An #EggTaskCache * @key: The key for the cache * * Peeks to see @key is contained in the cache and returns the * matching #GObject if it does. * * The reference count of the resulting #GObject is not incremented. * For that reason, it is important to remember that this function * may only be called from the main thread. * * Returns: (type GObject.Object) (nullable) (transfer none): A #GObject or * %NULL if the key was not found in the cache. */ gpointer egg_task_cache_peek (EggTaskCache *self, gconstpointer key) { CacheItem *item; g_return_val_if_fail (EGG_IS_TASK_CACHE (self), NULL); if ((item = g_hash_table_lookup (self->cache, key))) { EGG_COUNTER_INC (hits); return item->value; } return NULL; }
static void ide_clang_service_get_translation_unit_cb (GObject *object, GAsyncResult *result, gpointer user_data) { EggTaskCache *cache = (EggTaskCache *)object; g_autoptr(IdeClangTranslationUnit) ret = NULL; g_autoptr(GTask) task = user_data; GError *error = NULL; g_assert (EGG_IS_TASK_CACHE (cache)); if (!(ret = egg_task_cache_get_finish (cache, result, &error))) g_task_return_error (task, error); else g_task_return_pointer (task, g_object_ref (ret), g_object_unref); }
static CacheItem * cache_item_new (EggTaskCache *self, gconstpointer key, gconstpointer value) { CacheItem *ret; g_assert (EGG_IS_TASK_CACHE (self)); ret = g_slice_new0 (CacheItem); ret->self = self; ret->key = self->key_copy_func ((gpointer)key); ret->value = self->value_copy_func ((gpointer)value); if (self->time_to_live_usec > 0) ret->evict_at = g_get_monotonic_time () + self->time_to_live_usec; return ret; }
static void get_diagnostics_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { EggTaskCache *cache = EGG_TASK_CACHE (source_object); g_autoptr(GTask) task = user_data; g_autoptr(IdeGettextDiagnostics) diags = NULL; GError *error = NULL; g_assert (EGG_IS_TASK_CACHE (cache)); g_assert (G_IS_TASK (task)); diags = egg_task_cache_get_finish (cache, res, &error); if (diags == NULL) g_task_return_error (task, error); else g_task_return_pointer (task, g_steal_pointer (&diags), g_object_unref); }
static void egg_task_cache_populate (EggTaskCache *self, gconstpointer key, gpointer value) { CacheItem *item; g_assert (EGG_IS_TASK_CACHE (self)); item = cache_item_new (self, key, value); if (g_hash_table_contains (self->cache, key)) egg_task_cache_evict (self, key); g_hash_table_insert (self->cache, item->key, item); egg_heap_insert_val (self->evict_heap, item); EGG_COUNTER_INC (cached); if (self->evict_source != NULL) evict_source_rearm (self->evict_source); }
/** * egg_task_cache_get_values: (skip) * * Gets all the values in the cache. * * The caller owns the resulting GPtrArray, which itself owns a reference to the children. * * Returns: (transfer container): The values. */ GPtrArray * egg_task_cache_get_values (EggTaskCache *self) { GPtrArray *ar; GHashTableIter iter; gpointer value; g_return_val_if_fail (EGG_IS_TASK_CACHE (self), NULL); ar = g_ptr_array_new_with_free_func (self->value_destroy_func); g_hash_table_iter_init (&iter, self->cache); while (g_hash_table_iter_next (&iter, NULL, &value)) { CacheItem *item = value; g_ptr_array_add (ar, self->value_copy_func (item->value)); } return ar; }
static gboolean egg_task_cache_evict_full (EggTaskCache *self, gconstpointer key, gboolean check_heap) { CacheItem *item; g_return_val_if_fail (EGG_IS_TASK_CACHE (self), FALSE); if ((item = g_hash_table_lookup (self->cache, key))) { if (check_heap) { gsize i; for (i = 0; i < self->evict_heap->len; i++) { if (item == egg_heap_index (self->evict_heap, gpointer, i)) { egg_heap_extract_index (self->evict_heap, i, NULL); break; } } } g_hash_table_remove (self->cache, key); EGG_COUNTER_DEC (cached); if (self->evict_source != NULL) evict_source_rearm (self->evict_source); return TRUE; } return FALSE; }
void egg_task_cache_get_async (EggTaskCache *self, gconstpointer key, gboolean force_update, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; GPtrArray *queued; gpointer ret; g_return_if_fail (EGG_IS_TASK_CACHE (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); /* * If we have the answer, return it now. */ if (!force_update && (ret = egg_task_cache_peek (self, key))) { g_task_return_pointer (task, self->value_copy_func (ret), self->value_destroy_func); return; } EGG_COUNTER_INC (misses); /* * Always queue the request. If we need to dispatch the worker to * fetch the result, that will happen with another task. */ if (!(queued = g_hash_table_lookup (self->queued, key))) { queued = g_ptr_array_new_with_free_func (g_object_unref); g_hash_table_insert (self->queued, self->key_copy_func ((gpointer)key), queued); } g_ptr_array_add (queued, g_object_ref (task)); EGG_COUNTER_INC (queued); /* * The in_flight hashtable will have a bit set if we have queued * an operation for this key. */ if (!g_hash_table_contains (self->in_flight, key)) { g_autoptr(GTask) fetch_task = NULL; fetch_task = g_task_new (self, cancellable, egg_task_cache_fetch_cb, self->key_copy_func ((gpointer)key)); g_hash_table_insert (self->in_flight, self->key_copy_func ((gpointer)key), GINT_TO_POINTER (TRUE)); self->populate_callback (self, key, g_object_ref (fetch_task), self->populate_callback_data); EGG_COUNTER_INC (in_flight); } }
static void populate_cache (EggTaskCache *cache, gconstpointer key, GTask *task, gpointer user_data) { IdeGettextDiagnosticProvider *self = user_data; g_autoptr(IdeUnsavedFile) unsaved_file = NULL; g_autoptr(GSubprocess) subprocess = NULL; GtkSourceLanguage *language; const gchar *language_id; const gchar *xgettext_lang; const gchar *temp_path; TranslationUnit *unit; IdeFile *file = (IdeFile *)key; GCancellable *cancellable; GError *error = NULL; GPtrArray *args; g_assert (EGG_IS_TASK_CACHE (cache)); g_assert (IDE_IS_FILE (file)); g_assert (IDE_IS_GETTEXT_DIAGNOSTIC_PROVIDER (self)); cancellable = g_task_get_cancellable (task); if (NULL == (unsaved_file = get_unsaved_file (self, file))) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Failed to locate file contents"); return; } if (NULL == (language = ide_file_get_language (file)) || NULL == (language_id = gtk_source_language_get_id (language)) || NULL == (xgettext_lang = id_to_xgettext_language (language_id))) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Failed to determine language type"); return; } if (!ide_unsaved_file_persist (unsaved_file, cancellable, &error)) { g_task_return_error (task, error); return; } temp_path = ide_unsaved_file_get_temp_path (unsaved_file); g_assert (temp_path != NULL); args = g_ptr_array_new (); g_ptr_array_add (args, "xgettext"); g_ptr_array_add (args, "--check=ellipsis-unicode"); g_ptr_array_add (args, "--check=quote-unicode"); g_ptr_array_add (args, "--check=space-ellipsis"); g_ptr_array_add (args, "-k_"); g_ptr_array_add (args, "-kN_"); g_ptr_array_add (args, "-L"); g_ptr_array_add (args, (gchar *)xgettext_lang); g_ptr_array_add (args, "-o"); g_ptr_array_add (args, "-"); g_ptr_array_add (args, (gchar *)temp_path); g_ptr_array_add (args, NULL); #ifdef IDE_ENABLE_TRACE { g_autofree gchar *str = NULL; str = g_strjoinv (" ", (gchar **)args->pdata); IDE_TRACE_MSG ("Launching '%s'", str); } #endif subprocess = g_subprocess_newv ((const gchar * const *)args->pdata, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE, &error); g_ptr_array_free (args, TRUE); if (subprocess == NULL) { g_task_return_error (task, error); return; } unit = g_slice_new0 (TranslationUnit); unit->file = g_object_ref (file); unit->unsaved_file = ide_unsaved_file_ref (unsaved_file); g_task_set_task_data (task, unit, (GDestroyNotify)translation_unit_free); g_subprocess_wait_async (subprocess, cancellable, subprocess_wait_cb, g_object_ref (task)); }