improved the setup for the front-end method so that

printing to stdout and stderr is safe and to better document
the issues
This commit is contained in:
Robby Findler 2011-02-14 13:50:07 -06:00
parent 8e94ce49e4
commit e3c26a2aa4
3 changed files with 75 additions and 16 deletions

View File

@ -1312,7 +1312,7 @@ TODO
(let ([run-on-user-thread (lambda (t)
(queue-user/wait
(λ ()
(with-handlers ((exn? (λ (x) (printf "~s\n" (exn-message x)))))
(with-handlers ((exn? (λ (x) (oprintf "~s\n" (exn-message x)))))
(t)))))])
run-on-user-thread))
@ -1653,10 +1653,39 @@ TODO
(let ([lang (drracket:language-configuration:language-settings-language user-language-settings)]
[drr-evtspace (current-eventspace)]
[s (make-semaphore 0)])
(define-values (sp-err-other-end sp-err) (make-pipe))
(define-values (sp-out-other-end sp-out) (make-pipe))
(define io-chan (make-channel))
;; collect the IO to replay later
(thread
(λ ()
(let loop ([ports (list sp-err-other-end sp-out-other-end)]
[io '()])
(cond
[(null? ports) (channel-put io-chan io)]
[else
(apply sync
(map (λ (port) (handle-evt
port
(λ (_)
(define byte (read-byte port))
(if (eof-object? byte)
(loop (remq port ports) io)
(loop ports (cons (cons port byte)
io))))))
ports))]))))
(run-in-evaluation-thread
(λ ()
(let/ec k
(parameterize ([error-escape-handler (λ () (k (void)))])
;; we set the io ports here to ones that just collect the data
;; since we're blocking the eventspace handler thread (and thus IO to
;; the user's ports can deadlock)
(parameterize ([error-escape-handler (λ () (k (void)))]
[current-output-port sp-out]
[current-error-port sp-err])
(cond
;; this is for backwards compatibility; drracket used to
;; expect this method to be a thunk (but that was a bad decision)
@ -1667,7 +1696,21 @@ TODO
;; this is the backwards compatible case.
(send lang first-opened)])))
(semaphore-post s)))
(semaphore-wait s))
;; wait for the first-opened method to finish up
(semaphore-wait s)
;; close the output ports to get the above thread to terminate
(close-output-port sp-err)
(close-output-port sp-out)
;; duplicate it over to the user's ports, now that there is
;; no danger of deadlock
(for ([i (in-list (reverse (channel-get io-chan)))])
(write-byte (cdr i)
(if (eq? (car i) sp-err-other-end)
(get-err-port)
(get-out-port)))))
(send context enable-evaluation)
(end-edit-sequence)

View File

@ -440,7 +440,7 @@ This method is the same as
}
@defmethod[(on-execute [settings settings]
[run-in-user-thread ((-> void) -> void)])
[run-on-user-thread ((-> void) -> void)])
vod]{
This method is the same as
@method[drracket:language:language<%> on-execute].
@ -628,7 +628,7 @@ default settings obtained via
}
@defmethod*[([(first-opened [settings settings]) void?])]{
@defmethod[(first-opened [settings settings]) void?]{
This method is called after the language is initialized, but
no program has yet been run. It is called from the user's
@ -637,8 +637,8 @@ eventspace's main thread.
See also
@method[drracket:rep:text% initialize-console].
Calling this method should not raise an exception (or otherwise
try to escape). DrRacket calls this method in a @racket[parameterize]
Calling this method should not escape.
DrRacket calls this method in a @racket[parameterize]
where the @racket[error-escape-handler] is set to an escaping
continuation that continues initializing the interactions window.
Thus, raising an exception will report the error in the user's
@ -646,7 +646,14 @@ interactions window as if this were a bug in the user's program.
Escaping in any other way, however, can cause DrRacket to fail
to start up.
Contrary to the method contract space, DrRacket will also invoke this
Also, IO system will deadlock if the @racket[first-opened] method
does IO on the user's IO ports, so the calling context of
@racket[first-opened] sets the @racket[current-output-port] and
@racket[current-error-port] to ports that just collect all of the
IO that happened and then replay it later in the initialization of the
user's program.
Contrary to the method contract spec, DrRacket will also invoke this
method if it has zero arguments, passing nothing; the zero argument
version is for backwards compatibility and is not recommended.
@ -932,7 +939,7 @@ the settings for this language.
}
@defmethod[(on-execute [settings settings]
[run-in-user-thread ((-> any) -> any)])
[run-on-user-thread ((-> any) -> any)])
any]{
The @scheme[on-execute] method is called on DrRacket's
eventspace's main thread before any evaluation happens
@ -1015,13 +1022,16 @@ that error message into the definitions window.}
]
The @scheme[run-in-user-thread] arguments accepts thunks and
runs them on the user's eventspace's main thread. These
thunks must not raise an exceptions (or DrRacket itself will
get stuck). In addition, the output ports are not yet
The @scheme[run-on-user-thread] arguments accepts thunks and
runs them on the user's eventspace's main thread.
The output ports are not yet
functioning, so print outs should be directed to the
original DrRacket output port, if necessary.
This thunk is wrapped in a @racket[with-handlers] that
catches all exceptions matching @racket[exn:fail?] and
then prints out the exception message to the original
output port of the DrRacket process.
}
@defmethod[(order-manuals [manuals (listof bytes?)])

View File

@ -111,7 +111,9 @@ The @scheme[complete-program?] argument determines if the
how it finishes).
}
@defmethod[#:mode augment (on-execute [run-on-user-thread (-> any)]) any]{
@defmethod[(on-execute [run-on-user-thread (-> any)]) any]{
Use @scheme[run-on-user-thread] to initialize the user's parameters, etc.
Called from the DrRacket thread after the language's
@method[drracket:language:language<%> on-execute]
@ -119,8 +121,12 @@ The @scheme[complete-program?] argument determines if the
special values have been setup (the ones registered
via @scheme[drracket:language:add-snip-value]).
Use @scheme[run-on-user-thread] to initialize the user's parameters, etc.
Do not print to @racket[current-output-port] or @racket[current-error-port]
during the dynamic extent of the thunk passed to @racket[run-on-user-thread] becuase
this can deadlock. IO is still, in general, fine, but the @racket[current-error-port]
and @racket[current-output-port] are set to the user's ports that print
into the interactions window and are not in a good state during those calls.
}
@defmethod[(get-error-range)