[m-dev.] ODBC interface

Simon Taylor stayl at cs.mu.oz.au
Thu Oct 2 10:38:03 AEST 1997


Hi Fergus,

>  But restoring hp -- is that safe?
>  I guess so, but you should document that any heap memory
>  allocated between the MR_setjmp() and the MR_longjmp()
>  will not be valid after the MR_longjmp() call.
>  [Or alternatively we could leave it up to the call to save/restore
>  hp -- again you should document that it is the caller's responsibility
>  to do this.]

Leaving it to the caller is probably best. On an exception the caller 
will probably want to copy out some data (e.g. error messages) before 
reclaiming the memory.

>  In addition, with the new implementation of builtin_aggregate(),
>  the heap and the solutions_heap may have been swapped.
>  If so, then they need to be unswapped.
>  Perhaps the simplest way of handling this is to also save/restore
>  solutions_heap_pointer, heap_zone, and solutions_heap_zone.

This can be left up to the caller as well. It isn't a problem
for the ODBC interface since the threaded odbc__states ensure
that an exception can't happen within builtin_aggregate.

> For grades that support trailing, we also need to save/restore
> MR_trail_ptr and MR_ticket_counter.

Is just saving and restoring the pointers OK, or should it try to 
unwind the trail on an exception?

>  > odbc__rollback(Error) -->
>  > 	odbc__add_message(error(user_requested_rollback) - Error),
>  > 	odbc__throw.
>  
>  Oh-oh, here you are allocating memory on the heap before a throw,
>  and expecting that data to persist after the throw.
>  This will only work in conservative-gc grades.
>  You should document that.

This is already pretty well documented:

%       This requires a compilation grade with conservative garbage
%       collection. Any grade containing .gc in its name, such as asm_fast.gc,
%       will do. See the section "Compilation model options" in the Mercury
%       User's Guide for more information.
%

and also:

        /*
        ** odbc.m allocates memory within may_call_mercury pragma C code,
        ** which is a bit dodgy in non-GC grades. Allowing non-GC grades would
        ** require a bit of fairly error-prone code to save/restore the heap
        ** pointer in the right places. When accurate garbage collection is
        ** implemented and a nicer way of allocating heap space from within
        ** C code is available, this should be revisited.
        */
#ifndef CONSERVATIVE_GC
#error The OBDC interface requires conservative garbage collection. \\
                Use a compilation grade containing .gc.
#endif /* ! CONSERVATIVE_GC */
		 
Here's a diff compared to the versions I sent before:


===================================================================
RCS file: RCS/engine.h,v
retrieving revision 1.1
diff -u -r1.1 engine.h
--- engine.h	1997/10/01 23:05:06	1.1
+++ engine.h	1997/10/02 00:14:43
@@ -51,7 +51,8 @@
 	** to ensure that
 	**	 call C -> setjmp -> call Mercury -> call C -> longjmp 
 	** works correctly. This is used by the exception handling code for
-	** the ODBC interface.
+	** the ODBC interface, and probably shouldn't be used for anything
+	** else.
 	*/ 
 
 typedef struct {
@@ -62,11 +63,16 @@
 					** used by calls to setjmp and longjmp 
 					*/
 		Word *saved_succip;
-		Word *saved_hp;
 		Word *saved_sp;
 		Word *saved_curfr;
 		Word *saved_maxfr;
+		MR_IF_USE_TRAIL(Word *saved_trail_ptr;)
+		MR_IF_USE_TRAIL(Word *saved_ticket_counter;)
+
+#if NUM_REAL_REGS > 0
 		Word regs[NUM_REAL_REGS];
+#endif /* NUM_REAL_REGS > 0 */
+
 	} MR_jmp_buf;
 
 	/*
@@ -74,39 +80,51 @@
 	**
 	** Save MR_engine_jmp_buf, save the Mercury state, call setjmp(env), 
 	** then fall through.
+	**
 	** When setjmp returns via a call to longjmp, control will pass to
 	** longjmp_label.
-	** Note that the Mercury registers must be valid before the call
-	** to MR_setjmp.
+	**
+	** Notes:
+	** - The Mercury registers must be valid before the call to MR_setjmp.
+	** - The general-purpose registers r1, r2... are not restored and must
+	** be saved by the caller.
+	** - In grades without conservative garbage collection, the caller
+	** must save and restore hp, solutions_heap_pointer, heap_zone 
+	** and solutions_heap_zone.
 	*/
 #define MR_setjmp(setjmp_env, longjmp_label)				\
 	    do {							\
 		(setjmp_env)->mercury_env = MR_engine_jmp_buf;		\
 		save_regs_to_mem((setjmp_env)->regs);			\
 		(setjmp_env)->saved_succip = succip;			\
-		(setjmp_env)->saved_hp = hp;				\
 		(setjmp_env)->saved_sp = sp;				\
 		(setjmp_env)->saved_curfr = curfr;			\
 		(setjmp_env)->saved_maxfr = maxfr;			\
+		MR_IF_USE_TRAIL((setjmp_env)->saved_trail_ptr = 	\
+				MR_trail_ptr);				\
+		MR_IF_USE_TRAIL((setjmp_env)->saved_ticket_counter =	\
+				MR_ticket_counter);			\
 		if (setjmp((setjmp_env)->env)) {			\
 			MR_engine_jmp_buf = (setjmp_env)->mercury_env;	\
 			restore_regs_from_mem((setjmp_env)->regs);	\
 			succip = (setjmp_env)->saved_succip;		\
-			hp = (setjmp_env)->saved_hp;			\
 			sp = (setjmp_env)->saved_sp;			\
 			curfr = (setjmp_env)->saved_curfr;		\
 			maxfr = (setjmp_env)->saved_maxfr;		\
+			MR_IF_USE_TRAIL(MR_trail_ptr = 			\
+					(setjmp_env)->saved_trail_ptr);	\
+			MR_IF_USE_TRAIL(MR_ticket_counter = 		\
+				(setjmp_env)->saved_ticket_counter);	\
 			goto longjmp_label;				\
 		}							\
 	    } while (0)
 
 	/*
-	** MR_longjmp(MR_jmp_buf *env, int return)
-	**
-	** Reset MR_engine_jmp_buf to the value stored in env, restore the
-	** Mercury registers, then call longjmp().
+	** MR_longjmp(MR_jmp_buf *env)
+	** 
+	** Call longjmp(), MR_setjmp() will handle the rest.
 	*/
-#define MR_longjmp(setjmp_env, ret)	longjmp((setjmp_env)->env, ret)
+#define MR_longjmp(setjmp_env)	longjmp((setjmp_env)->env, 1)
 
 	/* 
 	** engine_jmp_buf should only be referred to in engine.c


Index: mercury_trail.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_trail.h,v
retrieving revision 1.10
diff -u -r1.10 mercury_trail.h
--- mercury_trail.h	1997/09/23 16:47:44	1.10
+++ mercury_trail.h	1997/10/02 00:11:58
@@ -16,6 +16,12 @@
 
 #include "memory.h"
 
+#ifdef MR_USE_TRAIL
+#define MR_IF_USE_TRAIL(x) x
+#else /* ! MR_USE_TRAIL */
+#define MR_IF_USE_TRAIL(x)
+#endif /* ! MR_USE_TRAIL */
+
 /*---------------------------------------------------------------------------*/
 /*
 ** The following macros define how to store and retrieve a 'ticket' -


===================================================================
RCS file: RCS/odbc.m,v
retrieving revision 1.4
diff -u -r1.4 odbc.m
--- odbc.m	1997/10/01 23:05:23	1.4
+++ odbc.m	1997/10/01 23:59:56
@@ -23,7 +23,7 @@
 % Notes:
 %
 % 	Binary data is converted to a string of hexadecimal digits.
-
+%
 %	This requires a compilation grade with conservative garbage 
 % 	collection. Any grade containing .gc in its name, such as asm_fast.gc,
 %	will do. See the section "Compilation model options" in the Mercury
@@ -275,11 +275,14 @@
 
 	/*
 	** odbc.m allocates memory within may_call_mercury pragma C code, 
-	** which is a bit dodgy in non-GC grades. Allowing non-GC grades would
-	** require a bit of fairly error-prone code to save/restore the heap 
-	** pointer in the right places. When accurate garbage collection is 
-	** implemented and a nicer way of allocating heap space from within 
-	** C code is available, this should be revisited.
+	** which is a bit dodgy in non-conservative GC grades.
+	** Allowing non-convservative GC grades would require a bit of fairly 
+	** error-prone code to save/restore the heap pointer in the right 
+	** places. When accurate garbage collection is implemented and a 
+	** nicer way of allocating heap space from within C code is available, 
+	** this should be revisited.
+	** Conservative garbage collection also makes restoring the state
+	** after an exception a bit simpler.
 	*/	
 #ifndef CONSERVATIVE_GC
 #error The OBDC interface requires conservative garbage collection. \\
@@ -343,13 +346,14 @@
 	** handling. We need to use MR_setjmp and MR_longjmp because we'll 
 	** be longjmping across C->Mercury calls, so we need to restore 
 	** some state in runtime/engine.c.
-	** Beware: the Mercury registers must be valid 
-	** when odbc_catch is called.
+	** Beware: the Mercury registers must be valid when odbc_catch 
+	** is called. odbc_throw will clobber the general-purpose registers
+	** r1, r2, etc. 
 	*/
 #define odbc_catch(longjmp_label)					\
 			MR_setjmp(&odbc_trans_jmp_buf, longjmp_label)
 
-#define odbc_throw() 	MR_longjmp(&odbc_trans_jmp_buf, 1)
+#define odbc_throw() 	MR_longjmp(&odbc_trans_jmp_buf)
 
 	/* 
 	** odbc_trans_jmp_buf stores information saved by odbc_catch (setjmp)
@@ -432,6 +436,10 @@
 	/*
 	** The Mercury registers must be valid at the call to odbc_catch
 	** in odbc_transaction_c_code().
+	** odbc_transaction_c_code() may clobber the Mercury general-purpose 
+	** registers r1, r2, ..., but that is OK, because this C code is 
+	** declared as 'may_call_mercury', so the compiler assumes that it 
+	** is allowed to clobber those registers.
 	*/
 	save_transient_registers();
 	odbc_transaction_c_code(TypeInfo_for_T, Connection, Closure, 

===================================================================
RCS file: RCS/odbc_test.m,v
retrieving revision 1.1
diff -u -r1.1 odbc_test.m
--- odbc_test.m	1997/10/01 23:05:23	1.1
+++ odbc_test.m	1997/10/02 00:24:24
@@ -72,17 +72,18 @@
 		odbc__state::di, odbc__state::uo) is det.
 
 test_trans(Results) -->
-	{ String = "select * from test" },
-	odbc__execute(String, Results).
+	odbc__execute("select * from test", Results).
 
 :- pred output_results(list(odbc__row)::in,
 		io__state::di, io__state::uo) is det.
 
 output_results(Rows) -->
-	{ WriteRow = lambda([Row::in, IO0::di, IO::uo] is det, (
-			io__write_list(Row, " ", output_attribute, IO0, IO)
-		)) },
-	io__write_list(Rows, "\n", WriteRow).
+	io__write_list(Rows, "\n", output_row).
+
+:- pred output_row(odbc__row::in, io__state::di, io__state::uo) is det.
+
+output_row(Row) -->
+	io__write_list(Row, " ", output_attribute).
 
 :- pred output_attribute(odbc__attribute::in, 
 		io__state::di, io__state::uo) is det.

Simon.



More information about the developers mailing list