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:
parent
8e94ce49e4
commit
e3c26a2aa4
|
@ -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)
|
||||
|
|
|
@ -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?)])
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user