diff --git a/collects/drracket/private/rep.rkt b/collects/drracket/private/rep.rkt index 293ba2606e..c8fd16757e 100644 --- a/collects/drracket/private/rep.rkt +++ b/collects/drracket/private/rep.rkt @@ -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) diff --git a/collects/scribblings/tools/language.scrbl b/collects/scribblings/tools/language.scrbl index 281cd08093..a9ca88f6c1 100644 --- a/collects/scribblings/tools/language.scrbl +++ b/collects/scribblings/tools/language.scrbl @@ -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?)]) diff --git a/collects/scribblings/tools/rep.scrbl b/collects/scribblings/tools/rep.scrbl index bc07584f18..267f4ac9cb 100644 --- a/collects/scribblings/tools/rep.scrbl +++ b/collects/scribblings/tools/rep.scrbl @@ -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)