Re: gerror.c revisited



[resent to the list, which I forgot the first time, only mailing Havoc]
Hi Havoc, 

> I'm doing it this way in gexec.h:
>  #define G_EXEC_ERROR g_exec_error_quark ()
> 
>  GQuark g_exec_error_quark();
> 
> Then the convention for error codes is G_EXEC_ERROR_* members in
> GExecErrorType.
> 
> I guess since you already can't use switch(), it may as well be a
> string. That makes sense to me. Anyone see a problem with it?

The attached patch implements this. I've g_strduped the domain. It should
however be possible to just assign it. But the copy-solution avoids stupid
surprises (for lets say unloaded modules or the like).

> > * I would very much like to see a function
> >   g_print_error (GError **err, gboolean fatal), that prints out the error
> >   in some canonical form and does abort, if fatal is true. Or two
> >   functions, one for error, one for warning (without abort).
> 
> Seems reasonable. On fatal, I would fprintf(stderr, message); then
> exit(1) rather than abort(), though. abort() means "should not
> happen", i.e. g_assert(), and GError should not be used to indicate
> programming bugs.

Ok, actually I don't need that function, if g_set_error is not supposed to
abort (or exit). That will also make Tim happy ;-)

> > * g_set_error should call g_print_error (the_error, TRUE), when err is
> >   NULL. Because if I supply NULL as an err argument, I actually don't want
> >   to know, in the calling code whether it fails, but I'm not calling out
> >   for silent failure, when the error condition is occuring. Or let
> >   g_set_error take a fatal argument as above.
> >
> 
> No, NULL means you want to _ignore_ the error, because the error
> doesn't matter. If the error matters, you need to handle it. :-P If
> the error "should not happen", then don't return it as a GError, just
> print a g_warning().

Ok, I'm not completly convinced, but can't think of any good counter example
either. SO if it's policy, I wont debate it. Or maybe I will ;-) Thinking of
Exceptions as a rough equivalent. They just abort, when not handled, avoiding
silent failure. You say the .xsession-errors mess is bad. But what about a
program, that fails without writing something there. Ok. I'll shut up.

> > +#define glib_error_domain() g_quark_from_static_string ("glib-error-domain")
> > +#define GLIB_ERROR_AGAIN   1   /* Resource temporarily unavailable */
> > +
> 
> I would do this as G_THREAD_ERROR (for the domain) and an enum
> GThreadErrorType with member G_THREAD_ERROR_AGAIN. The convention is
> debatable, but we should use the same one throughout glib.

Ok, done.

> > +  G_THREAD_UF (thread_create, (g_thread_create_proxy, result,
> > +                            stack_size, joinable, bound, priority,
> > +                            &result->system_thread, err));
> >    G_UNLOCK (g_thread_create);
> > +  if (err && *err)
> > +    {
> > +      g_free (result);
> > +      return NULL;
> > +    }
> >    return (GThread*) result;
> >  }
> 
> This code is probably wrong (I'm not sure, it depends on exactly what
> thread_create does). Basically the return value should not be affected
> by whether the user passsed in NULL for err. If you need to know
> whether an error occurred in _your_ code, then create your own error
> variable, check that it's non-NULL, then assign to the user's error if
> needed. I'm pretty sure gexec.c has an example, but something like:

Yes, but this code would work, if g_set_error would exit for NULL.

> GError *local_err = NULL;
> foo (&local_err);
> if (local_err != NULL)
> {
>   if (err)
>    *err = local_err;
>   else
>    g_error_free (local_err);
> 
>   return NULL;
> }

This last part looks like it will be used in a whole lot of functions  and it
seems a bit tedious and easy to get wrong. So I included g_propagate_error for
that purpose, what do you think?
 
Bye,
Sebastian
-- 
Sebastian Wilhelmi                   |            här ovanför alla molnen
mailto:wilhelmi@ira.uka.de           |     är himmlen så förunderligt blå
http://goethe.ira.uka.de/~wilhelmi   |
Index: gerror.c
===================================================================
RCS file: /cvs/gnome/glib/gerror.c,v
retrieving revision 1.15
diff -u -b -B -r1.15 gerror.c
--- gerror.c	2000/07/26 11:01:59	1.15
+++ gerror.c	2000/08/30 14:19:53
@@ -27,7 +27,7 @@
 #include "glib.h"
 
 static GError* 
-g_error_new_valist(GQuark         domain,
+g_error_new_valist(const gchar   *domain,
                    gint           code,
                    const gchar   *format,
                    va_list        args)
@@ -36,7 +36,7 @@
   
   error = g_new (GError, 1);
   
-  error->domain = domain;
+  error->domain = g_strdup (domain);
   error->code = code;
   error->message = g_strdup_vprintf (format, args);
   
@@ -44,7 +44,7 @@
 }
 
 GError*
-g_error_new (GQuark       domain,
+g_error_new (const gchar *domain,
              gint         code,
              const gchar *format,
              ...)
@@ -63,7 +63,7 @@
 }
 
 GError*
-g_error_new_literal (GQuark         domain,
+g_error_new_literal (const gchar   *domain,
                      gint           code,
                      const gchar   *message)
 {
@@ -74,7 +74,7 @@
 
   err = g_new (GError, 1);
 
-  err->domain = domain;
+  err->domain = g_strdup (domain);
   err->code = code;
   err->message = g_strdup (message);
   
@@ -88,6 +88,8 @@
 
   g_free (error->message);
 
+  g_free (error->domain);
+
   g_free (error);
 }
 
@@ -109,17 +111,18 @@
 
 gboolean
 g_error_matches (const GError *error,
-                 GQuark        domain,
+                 const gchar  *domain,
                  gint          code)
 {
   return error &&
-    error->domain == domain &&
-    error->code == code;
+    error->code == code &&
+    error->domain && domain &&
+    strcmp (error->domain, domain) == 0;
 }
 
 void
 g_set_error (GError     **err,
-             GQuark       domain,
+             const gchar  *domain,
              gint         code,
              const gchar *format,
              ...)
@@ -136,6 +139,21 @@
   va_start (args, format);
   *err = g_error_new_valist (domain, code, format, args);
   va_end (args);
+}
+
+void     
+g_propagate_error (GError       **dest,
+		   GError        *src)
+{
+  if (dest) 
+    {
+      if (*dest != NULL)
+	g_warning ("GError set over the top of a previous GError or uninitialized memory.\n"
+		   "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.");      
+      *dest = src;
+    }
+  else if (src) 
+    g_error_free (src);
 }
 
 void
Index: gerror.h
===================================================================
RCS file: /cvs/gnome/glib/gerror.h,v
retrieving revision 1.2
diff -u -b -B -r1.2 gerror.h
--- gerror.h	2000/07/26 11:01:59	1.2
+++ gerror.h	2000/08/30 14:19:53
@@ -30,17 +30,17 @@
 
 struct _GError
 {
-  GQuark       domain;
+  gchar       *domain;
   gint         code;
   gchar       *message;
 };
 
-GError*  g_error_new           (GQuark         domain,
+GError*  g_error_new           (const gchar   *domain,
                                 gint           code,
                                 const gchar   *format,
                                 ...) G_GNUC_PRINTF (3, 4);
 
-GError*  g_error_new_literal   (GQuark         domain,
+GError*  g_error_new_literal   (const gchar   *domain,
                                 gint           code,
                                 const gchar   *message);
 
@@ -48,17 +48,23 @@
 GError*  g_error_copy          (const GError  *error);
 
 gboolean g_error_matches       (const GError  *error,
-                                GQuark         domain,
+                                const gchar   *domain,
                                 gint           code);
 
 /* if (err) *err = g_error_new(domain, code, format, ...), also has
  * some sanity checks.
  */
 void     g_set_error           (GError       **err,
-                                GQuark         domain,
+                                const gchar   *domain,
                                 gint           code,
                                 const gchar   *format,
                                 ...) G_GNUC_PRINTF (4, 5);
+
+/* if (dest) *dest = src; else g_error_free (src) also has
+ * some sanity checks.
+ */
+void     g_propagate_error     (GError       **dest,
+				GError        *src);
 
 /* if (err && *err) { g_error_free(*err); *err = NULL; } */
 void     g_clear_error         (GError       **err);
Index: glib.h
===================================================================
RCS file: /cvs/gnome/glib/glib.h,v
retrieving revision 1.190
diff -u -b -B -r1.190 glib.h
--- glib.h	2000/08/27 15:33:15	1.190
+++ glib.h	2000/08/30 14:19:54
@@ -941,7 +941,16 @@
   guint len;
 };
 
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#include <gerror.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
 /* IEEE Standard 754 Single Precision Storage Format (gfloat):
  *
  *        31 30           23 22            0
@@ -3035,6 +3044,13 @@
 /* GLib Thread support
  */
 
+#define G_THREAD_ERROR "GThread"
+
+typedef enum
+{
+  G_THREAD_ERROR_AGAIN /* Resource temporarily unavailable */
+} GThreadError;
+
 typedef void		(*GThreadFunc)		(gpointer	value);
 
 typedef enum
@@ -3087,7 +3103,8 @@
 			           gboolean 		 joinable,
 			           gboolean 		 bound,
 			           GThreadPriority 	 priority,
-				   gpointer              thread);
+				   gpointer              thread,
+				   GError              **error);
   void      (*thread_yield)       (void);
   void      (*thread_join)        (gpointer		 thread);
   void      (*thread_exit)        (void);
@@ -3149,10 +3166,11 @@
 			  gulong 		 stack_size,
 			  gboolean 		 joinable,
 			  gboolean 		 bound,
-			  GThreadPriority 	 priority);
+			  GThreadPriority 	 priority,
+			  GError               **error);
 GThread* g_thread_self ();
-void g_thread_join (GThread* thread);
-void g_thread_set_priority (GThread* thread, 
+void g_thread_join (GThread *thread);
+void g_thread_set_priority (GThread         *thread, 
 			    GThreadPriority priority);
 
 /* GStaticMutexes can be statically initialized with the value
@@ -3353,20 +3371,23 @@
 					       gboolean         bound,
 					       GThreadPriority  priority,
 					       gboolean         exclusive,
-					       gpointer         user_data);
+					       gpointer         user_data,
+					       GError         **error);
 
 /* Push new data into the thread pool. This task is assigned to a thread later
  * (when the maximal number of threads is reached for that pool) or now
  * (otherwise). If necessary a new thread will be started. The function
  * returns immediatly */
 void            g_thread_pool_push            (GThreadPool     *pool,
-					       gpointer         data);
+					       gpointer         data,
+					       GError         **error);
 
 /* Set the number of threads, which can run concurrently for that pool, -1
  * means no limit. 0 means has the effect, that the pool won't process
  * requests until the limit is set higher again */
 void            g_thread_pool_set_max_threads (GThreadPool     *pool,
-					       gint             max_threads);
+					       gint             max_threads,
+					       GError         **error);
 gint            g_thread_pool_get_max_threads (GThreadPool     *pool);
 
 /* Get the number of threads assigned to that pool. This number doesn't
@@ -3398,6 +3419,5 @@
 #endif /* __cplusplus */
 
 #include <gunicode.h>
-#include <gerror.h>
 
 #endif /* __G_LIB_H__ */
Index: gthread.c
===================================================================
RCS file: /cvs/gnome/glib/gthread.c,v
retrieving revision 1.6
diff -u -b -B -r1.6 gthread.c
--- gthread.c	2000/07/26 11:01:59	1.6
+++ gthread.c	2000/08/30 14:19:54
@@ -97,7 +97,7 @@
   NULL,                                        /* private_set */
   (void(*)(GThreadFunc, gpointer, gulong, 
 	   gboolean, gboolean, GThreadPriority, 
-	   gpointer))g_thread_fail,            /* thread_create */
+	   gpointer, GError**))g_thread_fail,  /* thread_create */
   NULL,                                        /* thread_yield */
   NULL,                                        /* thread_join */
   NULL,                                        /* thread_exit */
@@ -381,10 +381,11 @@
 		 gulong 		 stack_size,
 		 gboolean 		 joinable,
 		 gboolean 		 bound,
-		 GThreadPriority 	 priority)
+		 GThreadPriority 	 priority,
+		 GError                **error)
 {
   GRealThread* result = g_new (GRealThread, 1);
-
+  GError *local_error = NULL;
   g_return_val_if_fail (thread_func, NULL);
   
   result->thread.joinable = joinable;
@@ -394,10 +395,18 @@
   result->arg = arg;
   result->private_data = NULL; 
   G_LOCK (g_thread_create);
-  G_THREAD_UF (thread_create, (g_thread_create_proxy, result, stack_size, 
-			       joinable, bound, priority,
-			       &result->system_thread));
+  G_THREAD_UF (thread_create, (g_thread_create_proxy, result, 
+			       stack_size, joinable, bound, priority,
+			       &result->system_thread, &local_error));
   G_UNLOCK (g_thread_create);
+
+  if (local_error)
+    {
+      g_free (result);
+      g_propagate_error (error, local_error);
+      return NULL;
+    }
+
   return (GThread*) result;
 }
 
Index: gthreadpool.c
===================================================================
RCS file: /cvs/gnome/glib/gthreadpool.c,v
retrieving revision 1.2
diff -u -b -B -r1.2 gthreadpool.c
--- gthreadpool.c	2000/07/26 11:01:59	1.2
+++ gthreadpool.c	2000/08/30 14:19:54
@@ -55,7 +55,7 @@
 
 static void g_thread_pool_free_internal (GRealThreadPool* pool);
 static void g_thread_pool_thread_proxy (gpointer data);
-static void g_thread_pool_start_thread (GRealThreadPool* pool);
+static void g_thread_pool_start_thread (GRealThreadPool* pool, GError **error);
 static void g_thread_pool_wakeup_and_stop_all (GRealThreadPool* pool);
 
 #define g_thread_should_run(pool, len) \
@@ -162,7 +162,8 @@
 }
 
 static void
-g_thread_pool_start_thread (GRealThreadPool* pool)
+g_thread_pool_start_thread (GRealThreadPool  *pool, 
+			    GError          **error)
 {
   gboolean success = FALSE;
   GThreadPriority priority = pool->pool.priority;
@@ -206,9 +207,19 @@
     }
 
   if (!success)
+    {
+      GError *local_error = NULL;
     /* No thread was found, we have to start one new */
-    g_thread_create (g_thread_pool_thread_proxy, pool, pool->pool.stack_size, 
-		     FALSE, pool->pool.bound, priority);
+      g_thread_create (g_thread_pool_thread_proxy, pool, 
+		       pool->pool.stack_size, FALSE, 
+		       pool->pool.bound, priority, &local_error);
+      
+      if (local_error)
+	{
+	  g_propagate_error (error, local_error);
+	  return;
+	}
+    }
 
   /* See comment in g_thread_pool_thread_proxy as to why this is done
    * here and not there */
@@ -222,7 +233,8 @@
 		   gboolean         bound,
 		   GThreadPriority  priority,
 		   gboolean         exclusive,
-		   gpointer         user_data)
+		   gpointer         user_data,
+		   GError         **error)
 {
   GRealThreadPool *retval;
 
@@ -259,7 +271,18 @@
       g_async_queue_lock (retval->queue);
 
       while (retval->num_threads < retval->max_threads)
-	g_thread_pool_start_thread (retval);
+	{
+	  GError *local_error = NULL;
+	  g_thread_pool_start_thread (retval, &local_error);
+	  if (local_error)
+	    {
+	      g_propagate_error (error, local_error);
+	      g_async_queue_unref_and_unlock (retval->queue);
+	      g_free (retval);
+	      /* FIXME: free all other threads */
+	      return NULL;
+	    }
+	}
 
       g_async_queue_unlock (retval->queue);
     }
@@ -269,7 +292,8 @@
 
 void 
 g_thread_pool_push (GThreadPool     *pool,
-		    gpointer         data)
+		    gpointer         data,
+		    GError         **error)
 {
   GRealThreadPool *real = (GRealThreadPool*) pool;
 
@@ -286,15 +310,23 @@
   if (!pool->exclusive && g_async_queue_length_unlocked (real->queue) >= 0)
     {
       /* No thread is waiting in the queue */
-      g_thread_pool_start_thread (real);
+      GError *local_error = NULL;
+      g_thread_pool_start_thread (real, &local_error);
+      if (local_error)
+	{
+	  g_propagate_error (error, local_error);
+	  g_async_queue_unlock (real->queue);
+	  return;
     }
+    }
   g_async_queue_push_unlocked (real->queue, data);
   g_async_queue_unlock (real->queue);
 }
 
 void
 g_thread_pool_set_max_threads (GThreadPool     *pool,
-			       gint             max_threads)
+			       gint             max_threads,
+			       GError         **error)
 {
   GRealThreadPool *real = (GRealThreadPool*) pool;
   gint to_start;
@@ -314,7 +346,12 @@
     to_start = g_async_queue_length_unlocked (real->queue);
   
   for ( ; to_start > 0; to_start--)
-    g_thread_pool_start_thread (real);
+    {
+      GError *local_error = NULL;
+      g_thread_pool_start_thread (real, &local_error);
+      if (local_error)
+	break;
+    }
     
   g_async_queue_unlock (real->queue);
 }
Index: gthread/gthread-posix.c
===================================================================
RCS file: /cvs/gnome/glib/gthread/gthread-posix.c,v
retrieving revision 1.17
diff -u -b -B -r1.17 gthread-posix.c
--- gthread/gthread-posix.c	2000/07/26 11:02:01	1.17
+++ gthread/gthread-posix.c	2000/08/30 14:19:54
@@ -232,9 +232,11 @@
 			    gboolean joinable,
 			    gboolean bound,
 			    GThreadPriority priority,
-			    gpointer thread)
+			    gpointer thread,
+			    GError **error)
 {
   pthread_attr_t attr;
+  gint ret;
 
   g_return_if_fail (thread_func);
 
@@ -274,11 +276,23 @@
 # endif /* G_THREADS_IMPL_DCE */
 #endif /* HAVE_PRIORITIES */
 
-  posix_check_for_error (pthread_create (thread, &attr, 
-                                         (void* (*)(void*))thread_func,
-                                         arg));
+  ret = pthread_create (thread, &attr, (void* (*)(void*))thread_func, arg);
   
+#ifdef G_THREADS_IMPL_DCE
+  if (ret == -1) 
+    ret = errno;
+  else
+    ret = 0;
+#endif /* G_THREADS_IMPL_DCE */
+
   posix_check_for_error (pthread_attr_destroy (&attr));
+
+  if (ret)
+    {
+      g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, 
+		   "Error creating thread: %s", g_strerror (ret));
+      return;
+    }
 
 #ifdef G_THREADS_IMPL_DCE
   if (!joinable)
Index: tests/thread-test.c
===================================================================
RCS file: /cvs/gnome/glib/tests/thread-test.c,v
retrieving revision 1.3
diff -u -b -B -r1.3 thread-test.c
--- tests/thread-test.c	2000/04/19 09:29:19	1.3
+++ tests/thread-test.c	2000/08/30 14:19:54
@@ -27,7 +27,7 @@
   g_assert (G_TRYLOCK (test_g_mutex));
   thread = g_thread_create (test_g_mutex_thread, 
 			    GINT_TO_POINTER (42),
-			    0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL);
+			    0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL, NULL);
   g_usleep (G_MICROSEC);
   test_g_mutex_int = 42;
   G_UNLOCK (test_g_mutex);
@@ -62,7 +62,7 @@
   g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
   thread = g_thread_create (test_g_static_rec_mutex_thread, 
 			    GINT_TO_POINTER (42),
-			    0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL);
+			    0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL, NULL);
   g_usleep (G_MICROSEC);
   g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
   g_usleep (G_MICROSEC);
@@ -136,7 +136,7 @@
       threads[i] = g_thread_create (test_g_static_private_thread, 
 				    GINT_TO_POINTER (i),
 				    0, TRUE, TRUE, 
-				    G_THREAD_PRIORITY_NORMAL);      
+				    G_THREAD_PRIORITY_NORMAL, NULL);      
     }
   for (i = 0; i < THREADS; i++)
     {
@@ -213,7 +213,7 @@
     {
       threads[i] = g_thread_create (test_g_static_rw_lock_thread, 
 				    0, 0, TRUE, TRUE, 
-				    G_THREAD_PRIORITY_NORMAL);      
+				    G_THREAD_PRIORITY_NORMAL, NULL);      
     }
   g_usleep (G_MICROSEC);
   test_g_static_rw_lock_run = FALSE;
@@ -238,10 +238,21 @@
 main (int   argc,
       char *argv[])
 {
+  int i;
+  GError *err = NULL;
+  g_thread_init (NULL);
   /* Only run the test, if threads are enabled and a default thread
      implementation is available */
+  for (i = 0; i < 10000; i++)
+    {
+      g_thread_create (test_g_static_rw_lock_thread, 
+		       0, 0, TRUE, TRUE, 
+		       G_THREAD_PRIORITY_NORMAL, &err);      
+      g_print ("%d\n",i);
+      if (err)
+	g_error ("%s, %d, %s.\n", err->domain, err->code, err->message);
+    }
 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
-  g_thread_init (NULL);
   run_all_tests ();
 
   /* Now we rerun all tests, but this time we fool the system into
Index: tests/threadpool-test.c
===================================================================
RCS file: /cvs/gnome/glib/tests/threadpool-test.c,v
retrieving revision 1.1
diff -u -b -B -r1.1 threadpool-test.c
--- tests/threadpool-test.c	2000/04/28 12:24:53	1.1
+++ tests/threadpool-test.c	2000/08/30 14:19:54
@@ -33,17 +33,17 @@
   g_thread_init (NULL);
   
   pool1 = g_thread_pool_new (thread_pool_func, 3, 0, FALSE, 
-                            G_THREAD_PRIORITY_NORMAL, FALSE, NULL);
+                            G_THREAD_PRIORITY_NORMAL, FALSE, NULL, NULL);
   pool2 = g_thread_pool_new (thread_pool_func, 5, 0, FALSE, 
-			     G_THREAD_PRIORITY_LOW, FALSE, NULL);
+			     G_THREAD_PRIORITY_LOW, FALSE, NULL, NULL);
   pool3 = g_thread_pool_new (thread_pool_func, 7, 0, FALSE, 
-                             G_THREAD_PRIORITY_LOW, TRUE, NULL);
+                             G_THREAD_PRIORITY_LOW, TRUE, NULL, NULL);
 
   for (i = 0; i < RUNS; i++)
     {
-      g_thread_pool_push (pool1, GUINT_TO_POINTER (1));
-      g_thread_pool_push (pool2, GUINT_TO_POINTER (1));
-      g_thread_pool_push (pool3, GUINT_TO_POINTER (1));
+      g_thread_pool_push (pool1, GUINT_TO_POINTER (1), NULL);
+      g_thread_pool_push (pool2, GUINT_TO_POINTER (1), NULL);
+      g_thread_pool_push (pool3, GUINT_TO_POINTER (1), NULL);
     } 
   
   g_thread_pool_free (pool1, FALSE, TRUE);



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