Пример #1
0
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);
}
Пример #2
0
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);
    }
}
Пример #3
0
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);
    }
}
Пример #4
0
/**
 * 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);
}
Пример #5
0
/**
 * 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;
}
Пример #6
0
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);
}
Пример #7
0
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;
}
Пример #8
0
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);
}
Пример #9
0
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);
}
Пример #10
0
/**
 * 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;
}
Пример #11
0
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;
}
Пример #12
0
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);
    }
}
Пример #13
0
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));
}