[aisleriot] Rework scheme exception handling



commit 3d1b9a2bdc3c302cbeb26cd799a278efe56885d3
Author: Christian Persch <chpe gnome org>
Date:   Thu Apr 14 15:12:52 2011 +0200

    Rework scheme exception handling

 src/game.c   |   92 ++++++++++++++++++++++++---------------------------------
 src/game.h   |    7 ++--
 src/window.c |   57 ++++++++++++++++++++++++------------
 3 files changed, 81 insertions(+), 75 deletions(-)
---
diff --git a/src/game.c b/src/game.c
index 4156f41..58b74b0 100644
--- a/src/game.c
+++ b/src/game.c
@@ -1,6 +1,6 @@
 /*
  * Copyright © 1998, 2003 Jonathan Blandford <jrb alum mit edu>
- * Copyright © 2007 Christian Persch
+ * Copyright © 2007, 2011 Christian Persch
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -44,6 +44,11 @@
 
 #define I_(string) g_intern_static_string (string)
 
+struct _AisleriotGameClass
+{
+  GObjectClass parent_class;
+};
+
 struct _AisleriotGame
 {
   GObject parent_instance;
@@ -278,8 +283,6 @@ typedef struct {
   SCM lambda;
   SCM *args;
   gsize n_args;
-  SCM retval;
-  gboolean exception;
 } CallData;
 
 static char *
@@ -396,50 +399,19 @@ game_scm_pre_unwind_handler (void *user_data,
                              SCM tag,
                              SCM throw_args)
 {
-  CallData *data = (CallData *) user_data;
-  AisleriotGame *game = app_game;
-  char *message = NULL;
-  int error_fd;
-  char *error_file = NULL;
-  GError *error = NULL;
-
-  if (data)
-    data->exception = TRUE;
+  GError **error = user_data;
+  char *message;
 
-  g_print ("preunwind handler\n");
-
-  if (game->had_exception)
-    goto out;
+  /* Not interested in errors, or already had an exception */
+  if (error == NULL || *error != NULL)
+    return SCM_UNDEFINED;
 
   message = cscmi_exception_get_backtrace (tag, throw_args);
-  if (!message) {
-    g_warning ("A scheme exception occurred, but there was no exception info\n");
-    goto out;
-  }
-
-  g_print ("scheme exception: \n\n%s\n\n", message);
-    goto out;
-
-  error_fd = g_file_open_tmp ("arcrashXXXXXX", &error_file, &error);
-  if (error_fd >= 0) {
-    close (error_fd);
-    g_file_set_contents (error_file, message, strlen (message), NULL);
-
-    /* Tell the frontend about the problem */
-    g_signal_emit (game, signals[EXCEPTION], 0, error_file);
-    g_free (error_file);
-  } else {
-    g_warning ("A scheme exception occurred, and aisleriot could not create a temporary file to report it: %s",
-               error->message);
-    g_error_free (error);
-  }
-
-out:
-  /* This game is over, but don't count it in the statistics */
-  set_game_state (game, GAME_LOADED);
-
-  g_free (message);
-
+  g_set_error (error,
+               AISLERIOT_GAME_ERROR,
+               GAME_ERROR_EXCEPTION,
+               message ? message
+                       : "A scheme exception occurred, but there was no exception info");
   return SCM_UNDEFINED;
 }
 
@@ -481,15 +453,23 @@ game_scm_call (SCM lambda,
                gsize n_args,
                SCM *retval)
 {
-  CallData data = { lambda, args, n_args, FALSE };
+  CallData data = { lambda, args, n_args };
+  GError *error = NULL;
   SCM rv;
 
   rv = scm_c_catch (SCM_BOOL_T,
                     game_scm_call_lambda, &data,
-                    game_scm_catch_handler, &data,
-                    game_scm_pre_unwind_handler, &data);
-  if (data.exception)
+                    game_scm_catch_handler, NULL,
+                    game_scm_pre_unwind_handler, &error);
+  if (error) {
+    g_signal_emit (app_game, signals[EXCEPTION], 0, error);
+    g_error_free (error);
+
+    /* This game is over, but don't count it in the statistics */
+    set_game_state (app_game, GAME_LOADED);
+
     return FALSE;
+  }
 
   if (retval)
     *retval = rv;
@@ -1248,6 +1228,7 @@ aisleriot_game_class_init (AisleriotGameClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GType param_types[] = { G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE };
+  GType error_types[] = { G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE };
   GType ptr_types[] = { G_TYPE_POINTER };
 
   gobject_class->constructor = aisleriot_game_constructor;
@@ -1310,9 +1291,9 @@ aisleriot_game_class_init (AisleriotGameClass *klass)
                    (GSignalFlags) (G_SIGNAL_RUN_LAST),
                    NULL,
                    NULL, NULL,
-                   g_cclosure_marshal_VOID__STRING,
+                   g_cclosure_marshal_VOID__BOXED,
                    G_TYPE_NONE,
-                   1, param_types);
+                   1, error_types);
 
   g_object_class_install_property
     (gobject_class,
@@ -2418,12 +2399,17 @@ aisleriot_game_set_click_to_move (AisleriotGame *game,
 void
 aisleriot_game_generate_exception (AisleriotGame *game)
 {
-  CallData data = { SCM_EOL, NULL, 0, FALSE };
+  GError *error = NULL;
 
   scm_c_catch (SCM_BOOL_T,
                (scm_t_catch_body) scm_c_eval_string, (void *) "(/ 1 0)",
-               game_scm_catch_handler, &data,
-               game_scm_pre_unwind_handler, &data);
+               game_scm_catch_handler, NULL,
+               game_scm_pre_unwind_handler, &error);
+
+  if (error) {
+    g_signal_emit (app_game, signals[EXCEPTION], 0, error);
+    g_error_free (error);
+  }
 }
 
 /**
diff --git a/src/game.h b/src/game.h
index 6d4a0a1..551cbc4 100644
--- a/src/game.h
+++ b/src/game.h
@@ -111,11 +111,12 @@ char *ar_slot_get_hint_string (ArSlot *slot,
 #define AISLERIOT_GAME_OPTIONS_MAX (0x7FFFFFFF) /* 31 bits, since we're using int not guint */
 
 typedef struct _AisleriotGame AisleriotGame;
-typedef GObjectClass AisleriotGameClass;
+typedef struct _AisleriotGameClass AisleriotGameClass;
 
 enum {
-  GAME_ERROR_GENERIC = 0,
-  GAME_ERROR_FALLBACK = 1
+  GAME_ERROR_EXCEPTION = 0,
+  GAME_ERROR_GENERIC   = 1,
+  GAME_ERROR_FALLBACK  = 2
 };
 
 typedef enum {
diff --git a/src/window.c b/src/window.c
index a240d81..ca5b046 100644
--- a/src/window.c
+++ b/src/window.c
@@ -92,7 +92,7 @@ enum
   ACTION_LEAVE_FULLSCREEN,
   LAST_ACTION
 };
-  
+
 struct _AisleriotWindowPrivate
 {
   AisleriotGame *game;
@@ -1677,14 +1677,18 @@ game_exception_response_cb (GtkWidget *dialog,
                             AisleriotWindow *window)
 {
   AisleriotWindowPrivate *priv = window->priv;
-  const char *error_file;
+  GError *error;
+  gboolean did_report;
 
-  error_file = g_object_get_data (G_OBJECT (dialog), "error-file");
-  g_assert (error_file != NULL);
+  error = g_object_get_data (G_OBJECT (dialog), "error");
+  g_assert (error != NULL);
 
+  did_report = FALSE;
   if (response == GTK_RESPONSE_ACCEPT) {
     GError *err = NULL;
     char pidstr[64];
+    int fd;
+    char *error_file;
     const char * const argv[] = {
       "bug-buddy",
       "--package", "gnome-games",
@@ -1698,20 +1702,34 @@ game_exception_response_cb (GtkWidget *dialog,
 
     g_snprintf (pidstr, sizeof (pidstr), "%d", getpid ());
 
-    if (!g_spawn_async (
-                              NULL /* working dir */,
-                              (char **) argv,
-                              NULL /* envp */,
-                              G_SPAWN_SEARCH_PATH,
-                              NULL, NULL,
-                              NULL,
-                              &err)) {
-      g_warning ("Failed to launch bug buddy: %s\n", err->message);
+    fd = g_file_open_tmp ("arcrashXXXXXX", &error_file, &err);
+    if (fd >= 0) {
+      close (fd);
+
+      g_file_set_contents (error_file, error->message, strlen (error->message), NULL);
+
+      if (g_spawn_async (NULL /* working dir */,
+                         (char **) argv,
+                         NULL /* envp */,
+                         G_SPAWN_SEARCH_PATH,
+                         NULL, NULL,
+                         NULL,
+                         &err)) {
+        did_report = TRUE;
+      } else {
+        g_warning ("Failed to launch bug buddy: %s\n", err->message);
+        g_error_free (err);
+      }
+
+      g_free (error_file);
+    } else {
+      g_warning ("Failed to create temp file: %s\n", err->message);
       g_error_free (err);
     }
+  }
 
-    /* FIXMEchpe: can't unlink now since bug buddy still needs it... what to do? */
-    /* unlink (error_file); */
+  if (!did_report) {
+    g_printerr ("Aisleriot " VERSION " scheme exception occurred\n-- 8< --\n%s\n-- >8 --\n", error->message);
   }
 
   gtk_widget_destroy (dialog);
@@ -1724,12 +1742,12 @@ game_exception_response_cb (GtkWidget *dialog,
 
 static void
 game_exception_cb (AisleriotGame *game,
-                   const char *error_file,
+                   const GError *error,
                    AisleriotWindow *window)
 {
   GtkWidget *dialog;
 
-  g_return_if_fail (error_file != NULL);
+  g_return_if_fail (error != NULL);
 
   dialog = gtk_message_dialog_new (GTK_WINDOW (window),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -1755,8 +1773,9 @@ game_exception_cb (AisleriotGame *game,
 
   g_signal_connect (dialog, "response",
                     G_CALLBACK (game_exception_response_cb), window);
-  g_object_set_data_full (G_OBJECT (dialog), "error-file",
-                          g_strdup (error_file), g_free);
+  g_object_set_data_full (G_OBJECT (dialog), "error",
+                          g_error_copy (error),
+                          (GDestroyNotify) g_error_free);
 
   gtk_widget_show (dialog);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]