Hi,
I found a specific bug in Gambit-C 4.2.5 that crashes with a "bus error".
Here is the code to repoduce it:
(define (crash) (with-exception-handler (lambda (e) (display (compute-error-message e)) (force-output)) (lambda () (+ 1 a))))
(define (compute-error-message e) (##continuation-capture (lambda (k) (string-append (call-with-output-string "" (lambda (p) (##display-exception-in-context e k p))) "\n\n" (call-with-output-string "" (lambda (p) (##cmd-b 0 k p)))))))
It used to work before. The function (compute-error-message e) is used to get a verbose error message when my program crashes since it can happen in any concurrent thread.
Is the ##cmd still supported?
Thank you, Francois Magnan
Afficher les réponses par date
On 11-Apr-08, at 11:31 AM, François Magnan wrote:
Hi,
I found a specific bug in Gambit-C 4.2.5 that crashes with a "bus error".
Here is the code to repoduce it:
(define (crash) (with-exception-handler (lambda (e) (display (compute-error-message e)) (force-output)) (lambda () (+ 1 a))))
(define (compute-error-message e) (##continuation-capture (lambda (k) (string-append (call-with-output-string "" (lambda (p) (##display-exception-in-context e k p))) "\n\n" (call-with-output-string "" (lambda (p) (##cmd-b 0 k p)))))))
It used to work before. The function (compute-error-message e) is used to get a verbose error message when my program crashes since it can happen in any concurrent thread.
Is the ##cmd still supported?
The API for ##cmd-b changed. You do not need to call these unsafe "##" procedures anymore because the functionality has been exposed with safe (type checking) procedures. Here's what you should do:
(define (compute-error-message e) (continuation-capture (lambda (k) (string-append (call-with-output-string "" (lambda (p) (display-exception-in-context e k p))) "\n\n" (call-with-output-string "" (lambda (p) (display-continuation-backtrace k p)))))))
Marc
On 11-Apr-08, at 8:03 PM, Marc Feeley wrote:
The API for ##cmd-b changed. You do not need to call these unsafe "##" procedures anymore because the functionality has been exposed with safe (type checking) procedures. Here's what you should do:
Actually, this is probably what you meant to do (so that the continuation displayed is the one where the error occurred):
(define (compute-error-message e k) (call-with-output-string "" (lambda (p) (display-exception-in-context e k p) (newline p) (newline p) (display-continuation-backtrace k p))))
(define (crash) (with-exception-handler (lambda (e) (continuation-capture (lambda (k) (display (compute-error-message e k)) (force-output)))) (lambda () (+ 1 a))))
(crash)
Thank you! Now I understand what the prefix "##" means and I will try to avoid using these unsafe procedures.
Francois
On 11-Apr-08, at 8:09 PM, Marc Feeley wrote:
On 11-Apr-08, at 8:03 PM, Marc Feeley wrote:
The API for ##cmd-b changed. You do not need to call these unsafe "##" procedures anymore because the functionality has been exposed with safe (type checking) procedures. Here's what you should do:
Actually, this is probably what you meant to do (so that the continuation displayed is the one where the error occurred):
(define (compute-error-message e k) (call-with-output-string "" (lambda (p) (display-exception-in-context e k p) (newline p) (newline p) (display-continuation-backtrace k p))))
(define (crash) (with-exception-handler (lambda (e) (continuation-capture (lambda (k) (display (compute-error-message e k)) (force-output)))) (lambda () (+ 1 a))))
(crash)
François Magnan francois.magnan@licef.ca writes:
Thank you! Now I understand what the prefix "##" means and I will try to avoid using these unsafe procedures.
Francois
On 11-Apr-08, at 8:09 PM, Marc Feeley wrote:
On 11-Apr-08, at 8:03 PM, Marc Feeley wrote:
The API for ##cmd-b changed. You do not need to call these unsafe "##" procedures anymore because the functionality has been exposed with safe (type checking) procedures. Here's what you should do:
Is there somewhere I can read more about this?
Thanks, Joel
On Mon, Apr 14, 2008 at 02:03:37PM -0400, Joel J. Adamson wrote:
Is there somewhere I can read more about this?
The manual doesn't mention type safety or the namespace feature (which the ## prefix is one instance of) but the Wiki has a page on them. ## means "internal" more than "unsafe". Many of the internal procedures are unsafe, though, and all unsafe procedures are supposed to be internal.
http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Namespaces
Comments from new readers might improve the page. That's why I'm posting to the list.
-- Derek
On 14-Apr-08, at 3:58 PM, Derek Peschel wrote:
On Mon, Apr 14, 2008 at 02:03:37PM -0400, Joel J. Adamson wrote:
Is there somewhere I can read more about this?
The manual doesn't mention type safety or the namespace feature (which the ## prefix is one instance of) but the Wiki has a page on them. ## means "internal" more than "unsafe". Many of the internal procedures are unsafe, though, and all unsafe procedures are supposed to be internal.
http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Namespaces
Comments from new readers might improve the page. That's why I'm posting to the list.
Here's a short explanation of the ## prefix.
Gambit is a "Scheme in Scheme", meaning that most of the code (including the Gambit compiler, the Gambit interpreter and the Gambit runtime system and libraries) is written in Scheme and compiled with the Gambit compiler. OK, so how does this work? Take something simple like the car procedure. How can car be implemented in Scheme? It obviously can't be implemented as:
(define (car x) (car x))
Since this is an infinite recursion. Gambit's implementation of the car procedure corresponds roughly to this:
(define (car x)
(declare (extended-bindings))
(if (pair? x) (##car x) (error "car expects a pair!")))
The ##car procedure which is called by car is a version of car which assumes that the parameter is a pair. In other words ##car is simply an indirection (with offset). Thanks to the extended-bindings declaration the compiler knows that the reference to the ##car variable truly refers to the ##car procedure, so the compiler can inline this operation. The resulting C code for the body of car is roughly the following:
/* x is a ___SCMOBJ, which is of type int */
if (___PAIRP(x)) result = ___CAR(x); else error(...);
where ___PAIRP and ___CAR are defined in include/gambit.h as something like:
#define ___tPAIR 3 #define ___PAIRP(x) (((x) & 3) == ___tPAIR) #define ___CAR(x) (___SCMOBJ*)((x) - ___tPAIR)[1] #define ___CDR(x) (___SCMOBJ*)((x) - ___tPAIR)[2]
The original purpose of the ## prefix is to give a non-conflicting name for the primitive operations, such as ##cons, ##car, ##pair?, which are needed to implement more complex procedures such as append and write. Note that the ## prefix is illegal in Scheme, so these names (which are accepted by the Gambit reader) will never conflict with variable names defined by portable Scheme code. This is fairly common practice, for instance MzScheme uses the #% prefix for these "hidden" names.
The ## prefix has also been used to "keep out of the user's way" some of the runtime's utility procedures which are not meant to be called directly by the user. For performance reasons, these procedures normally do not check the type of their parameters, and they only call other ## procedures. As a general rule for most non-## procedures provided by the runtime system there is a corresponding ## procedure which does the same operation without type checks, and sometimes with slightly different parameters. For example the ##append procedure takes exactly two parameters and it does not require that the first parameter is a proper list (any non-pair object is treated as the empty list), so (##append 11 '(22 . 33)) => (22 . 33). This makes it easy to enforce one of Scheme's requirements that the redefinition of a predefined global variable does not change the behaviour of procedures which would seem to have to call them. For example the naive (and incorrect) definition of list-ref is:
(define (list-ref lst i) (if (= i 0) (car lst) (list-ref lst (- i 1))))
It is incorrect because doing (set! = <) or (set! car cdr) would change the behaviour of list-ref. Instead, list-ref is implemented roughly as
(define (list-ref lst i) (##list-ref lst i))
(define (##list-ref lst i)
(declare (extended-bindings))
(if (##= i 0) (##car lst) (##list-ref lst (##- i 1))))
That way a mutation of any of the global variables =, car, -, list-ref will not change the behaviour of the list-ref procedure:
(let ((lr list-ref)) (set! = <) (set! list-ref vector-ref) (lr '(11 22 33) 1)) => 22
So as a general rule:
1) ## procedures are unsafe (they assume the parameters are of the appropriate type) 2) simple ## procedures are inlinable by the compiler (##car, ##vector- ref, ##fixnum.+, etc) 3) if the runtime system provides procedure P, then ##P does the same operation without checking the parameters
Marc
On 14-Apr-08, at 10:53 PM, Marc Feeley wrote:
It obviously can't be implemented as:
(define (car x) (car x))
Since this is an infinite recursion. Gambit's implementation of the car procedure corresponds roughly to this:
(define (car x)
(declare (extended-bindings))
(if (pair? x) (##car x) (error "car expects a pair!")))
The ##car procedure which is called by car is a version of car which assumes that the parameter is a pair. In other words ##car is simply an indirection (with offset).
And if you are wondering how the ##car procedure is implemented, then it goes roughly like this:
(define (##car x)
(declare (extended-bindings))
(##car x))
Note that this is *not* an infinite recursion because the call to ##car is inlined by the compiler. This approach is used to define all the simple primitive procedures.
Marc
On Mon, Apr 14, 2008 at 11:14:26PM -0400, Marc Feeley wrote:
And if you are wondering how the ##car procedure is implemented, then it goes roughly like this:
Why have a ##car Scheme procedure at all, if the calls to ##car in situations like your other example are inlined by the compiler? The only reason I can think of is "so calls to ##car work from procedures without extended bindings". If that's the reason, are those calls inlined also? That is, does the extended-bindings declaration cause optimization across procedures?
About the ## prefix in general, I thought of another question. Which procedures' interfaces (argument number and types) are guaranteed not to change, and which have no guarantee? Obviously standard procedures are constrained by the standard, but there's the rest of the public namespace to consider, the entire ## namespace, and other namespaces (though in practice I doubt people have to worry about them).
-- Derek
On 22-Apr-08, at 1:51 AM, Derek Peschel wrote:
On Mon, Apr 14, 2008 at 11:14:26PM -0400, Marc Feeley wrote:
And if you are wondering how the ##car procedure is implemented, then it goes roughly like this:
Why have a ##car Scheme procedure at all, if the calls to ##car in situations like your other example are inlined by the compiler? The only reason I can think of is "so calls to ##car work from procedures without extended bindings".
That's one case, but more importantly it allows you to develop/debug with the interpreter (which does not inline ##car, etc) programs that will eventually be compiled.
If that's the reason, are those calls inlined also? That is, does the extended-bindings declaration cause optimization across procedures?
extended-bindings declares to the compiler: "Trust me, at run time the global variable ##car will contain the procedure ##car". The compiler can ignore such declarations and perform a true function call instead of inlining the procedure ##car (in practice the compiler always inlines, but the point is that the inlining is completely transparent to the user, except for making the program run faster).
About the ## prefix in general, I thought of another question. Which procedures' interfaces (argument number and types) are guaranteed not to change, and which have no guarantee? Obviously standard procedures are constrained by the standard, but there's the rest of the public namespace to consider, the entire ## namespace, and other namespaces (though in practice I doubt people have to worry about them).
There are no guarantees in life! Interfaces have changed over the many releases of Gambit. In general, I try to be as backward compatible as possible. The rule of thumb is: if it is documented, then I will try hard to preserve the same interface in the future (i.e. only interface extensions are done).
Marc
Marc Feeley feeley@iro.umontreal.ca writes:
On 14-Apr-08, at 3:58 PM, Derek Peschel wrote:
On Mon, Apr 14, 2008 at 02:03:37PM -0400, Joel J. Adamson wrote:
Is there somewhere I can read more about this?
The manual doesn't mention type safety or the namespace feature (which the ## prefix is one instance of) but the Wiki has a page on them. ## means "internal" more than "unsafe". Many of the internal procedures are unsafe, though, and all unsafe procedures are supposed to be internal.
http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Namespaces
Comments from new readers might improve the page. That's why I'm posting to the list.
Here's a short explanation of the ## prefix.
Gambit is a "Scheme in Scheme", meaning that most of the code (including the Gambit compiler, the Gambit interpreter and the Gambit runtime system and libraries) is written in Scheme and compiled with the Gambit compiler. OK, so how does this work? Take something simple like the car procedure. How can car be implemented in Scheme?
Excellent explanation professor. Perhaps this is the beginning of the namespaces chapter in the manual? I must say Gambit's excellent feature-request responses and cool-and-friendly user community are keeping me using Gambit, despite how often I encounter undocumented procedures or usage; certain elements of style are never documented anywhere, but some things come up so often they seem like they require explanation.
My main question was whether in coding an application I should be using
##define
or
define
and you answered it succinctly.
Thanks, Joel