better timeout error message, doc revisions
svn: r865
This commit is contained in:
parent
04dd9be199
commit
3c487a849b
|
@ -3,7 +3,7 @@
|
||||||
;; Also replace the "icon.png" and "server-cert.pem" files.
|
;; Also replace the "icon.png" and "server-cert.pem" files.
|
||||||
;; Instead of uncommenting the definition of server:port, you
|
;; Instead of uncommenting the definition of server:port, you
|
||||||
;; can set the PLT_HANDIN_SERVER_PORT environment variable.
|
;; can set the PLT_HANDIN_SERVER_PORT environment variable.
|
||||||
(define name "Course Handin")
|
(define name "Course")
|
||||||
(define collection "handin-client")
|
(define collection "handin-client")
|
||||||
;(define server:port "localhost:7979")
|
;(define server:port "localhost:7979")
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,7 @@
|
||||||
|
|
||||||
(define status
|
(define status
|
||||||
(new message%
|
(new message%
|
||||||
[label (format "Manage ~a account at ~a." handin-name server)]
|
[label (format "Manage ~a handin account at ~a." handin-name server)]
|
||||||
[parent this]
|
[parent this]
|
||||||
[stretchable-width #t]))
|
[stretchable-width #t]))
|
||||||
|
|
||||||
|
@ -515,7 +515,7 @@
|
||||||
|
|
||||||
(define/override (file-menu:between-open-and-revert file-menu)
|
(define/override (file-menu:between-open-and-revert file-menu)
|
||||||
(new menu-item%
|
(new menu-item%
|
||||||
(label (format "Manage ~a Account..." handin-name))
|
(label (format "Manage ~a Handin Account..." handin-name))
|
||||||
(parent file-menu)
|
(parent file-menu)
|
||||||
(callback (lambda (m e) (manage-handin-account))))
|
(callback (lambda (m e) (manage-handin-account))))
|
||||||
(super file-menu:between-open-and-revert file-menu))
|
(super file-menu:between-open-and-revert file-menu))
|
||||||
|
|
|
@ -21,7 +21,8 @@ password and upload the current content of the definitions and
|
||||||
interactions window to the course instructor's server. The "File" menu
|
interactions window to the course instructor's server. The "File" menu
|
||||||
is also extended with a "Manage..." menu item for managing a handin
|
is also extended with a "Manage..." menu item for managing a handin
|
||||||
account (i.e., changing the password, or creating a new account if the
|
account (i.e., changing the password, or creating a new account if the
|
||||||
instructor configures the server to allow new accounts).
|
instructor configures the server to allow new accounts). Students can
|
||||||
|
submit joint work by submitting with a concatenation of usernames.
|
||||||
|
|
||||||
On the instructor's side, the handin server can be configured to check
|
On the instructor's side, the handin server can be configured to check
|
||||||
the student's submission before accepting it.
|
the student's submission before accepting it.
|
||||||
|
@ -100,11 +101,11 @@ To customize the client:
|
||||||
|
|
||||||
* For `name', choose a name for the handin tool as it will
|
* For `name', choose a name for the handin tool as it will
|
||||||
appear in DrScheme's interface (e.g., the "XXX" for the
|
appear in DrScheme's interface (e.g., the "XXX" for the
|
||||||
"Manage XXX Account..." menu item). Again, make the name
|
"Manage XXX Handin Account..." menu item). Again, make the
|
||||||
specific to the course, in case a student installs multiple
|
name specific to the course, in case a student installs
|
||||||
handin tools. It's a good idea to use "Handin" as the last
|
multiple handin tools. Do not use "Handin" as the last part
|
||||||
part of the name, as in "2010 Handin", since the button is
|
of the name, since "Handin" is always added for button and
|
||||||
always named "Handin".
|
menu names.
|
||||||
|
|
||||||
* For `collection', use the name that you chose for your
|
* For `collection', use the name that you chose for your
|
||||||
collection directory (i.e., whatever you changed
|
collection directory (i.e., whatever you changed
|
||||||
|
@ -196,9 +197,11 @@ sub-directories:
|
||||||
"BACKUP-1/handin.scm", etc.; the default is 9
|
"BACKUP-1/handin.scm", etc.; the default is 9
|
||||||
|
|
||||||
'user-regexp : a regular expression that is used to validate
|
'user-regexp : a regular expression that is used to validate
|
||||||
usernames, young students often choose exotic usernames that
|
usernames; young students often choose exotic usernames that
|
||||||
are impossible to remember, and forget capitalization; the
|
are impossible to remember, and forget capitalization, so the
|
||||||
default is fairly strict: #rx"^[a-z][a-z0-9]+$"
|
default is fairly strict: #rx"^[a-z][a-z0-9]+$"; be sure to
|
||||||
|
disallow "+" in a username, since it is used in a submission
|
||||||
|
to specify joint work
|
||||||
|
|
||||||
'user-desc : a plain-words description of the acceptable
|
'user-desc : a plain-words description of the acceptable
|
||||||
username format (according to user-regexp above); #f stands
|
username format (according to user-regexp above); #f stands
|
||||||
|
@ -207,27 +210,29 @@ sub-directories:
|
||||||
|
|
||||||
'username-case-sensitive? : a boolean; when #f, usernames
|
'username-case-sensitive? : a boolean; when #f, usernames
|
||||||
are case-folded for all purposes; defaults to #f
|
are case-folded for all purposes; defaults to #f
|
||||||
(note that you should not set this to #t on Windows, since
|
(note that you should not set this to #t on Windows or when
|
||||||
usernames are used as directory names)
|
using a case-insensitive filesystem, since usernames are used
|
||||||
|
as directory names)
|
||||||
|
|
||||||
'id-regexp : a regular expression that is used to validate a
|
'id-regexp : a regular expression that is used to validate a
|
||||||
"free form" user id (possibly a student id) for a created
|
"free form" user id (possibly a student id) for a created
|
||||||
account; the default is #rx"^.*$"
|
account; the default is #rx"^.*$"
|
||||||
|
|
||||||
'id-desc : a plain-words description of the acceptable id format
|
'id-desc : a plain-words description of the acceptable id format
|
||||||
(according to id-regexp above), eg, "Foo ID Number"; the
|
(according to id-regexp above), eg, "Utah ID Number with
|
||||||
default is #f indicating no description
|
exactly nine digits"; the default is #f indicating no
|
||||||
|
description
|
||||||
|
|
||||||
'email-regexp : a regular expression that is used to validate
|
'email-regexp : a regular expression that is used to validate
|
||||||
emails, the #rx"^[^@<>\"`',]+@[a-zA-Z0-9_.-]+[.][a-zA-Z]+$"
|
emails, the #rx"^[^@<>\"`',]+@[a-zA-Z0-9_.-]+[.][a-zA-Z]+$"
|
||||||
default can be changed to "" if you don't care about emails,
|
default can be changed to "" if you don't care about emails,
|
||||||
or can be further restricted, for example requiring a
|
or can be further restricted, for example requiring a
|
||||||
"@cs.foo.edu" suffix
|
"@cs.utah.edu" suffix
|
||||||
|
|
||||||
'email-desc : a plain-words description of the acceptable email
|
'email-desc : a plain-words description of the acceptable email
|
||||||
format (according to email-regexp above), eg, "Foo CS email";
|
format (according to email-regexp above), eg, "Utah CS email
|
||||||
#f stands for no description; the default is "a valid email
|
address"; #f stands for no description; the default is "a
|
||||||
address"
|
valid email address"
|
||||||
|
|
||||||
'allow-new-users : a boolean indicating whether to allow
|
'allow-new-users : a boolean indicating whether to allow
|
||||||
new-user requests from a client tool; the default is #f
|
new-user requests from a client tool; the default is #f
|
||||||
|
@ -278,20 +283,23 @@ sub-directories:
|
||||||
handins; the lowest numbered such directory represents the latest
|
handins; the lowest numbered such directory represents the latest
|
||||||
handin.
|
handin.
|
||||||
|
|
||||||
Within an "ATTEMPT" or "SUCCESS-n" directory, a file "handin.scm"
|
A cleanup process in the server copies successful submission to
|
||||||
(or some other name if `default-file-name' is set) contains the
|
the student directory -- one level up from the corresponding
|
||||||
actual submission. A `checker' procedure can change this default
|
"SUCCESS-n" directory. This is done only for files and
|
||||||
file name, it can create additional files in an
|
directories that are newer in "SUCCESS-n" than in the submission
|
||||||
"ATTEMPT"/"SUCCESS-n" directory or in the student directory; see
|
root, other files and directories are left intact. If external
|
||||||
below on "checker.ss" for more details.
|
tools add new content to the student directory (eg, a "grade"
|
||||||
|
file, as described below) it will stay there. If the machine
|
||||||
|
crashes or the server is stopped, the cleanup process might not
|
||||||
|
finish. When the server is started, it automatically runs the
|
||||||
|
cleanup process for each student directory.
|
||||||
|
|
||||||
A cleanup process will copy successful submission to the
|
Within a student directory, a file "handin.scm" (or some other
|
||||||
submission root -- one level up from the corresponding "SUCCESS-n"
|
name if `default-file-name' is set) contains the actual
|
||||||
directory. This is done only for files and directories that are
|
submission. A `checker' procedure can change this default file
|
||||||
newer in "SUCCESS-n" than in the submission root, other files and
|
name, and it can create additional files in an "ATTEMPT" directory
|
||||||
directories are left intact. This means that you can have
|
(to be copied by the cleanup process); see below on "checker.ss"
|
||||||
external tools that add new content to the submission directory
|
for more details.
|
||||||
(eg, a "grade" file as described below) and it will stay there.
|
|
||||||
|
|
||||||
For submissions from a normal DrScheme frame, a submission file
|
For submissions from a normal DrScheme frame, a submission file
|
||||||
contains a copy of the student's definitions and interactions
|
contains a copy of the student's definitions and interactions
|
||||||
|
@ -301,6 +309,14 @@ sub-directories:
|
||||||
parts, the file can be parsed with `unpack-submission' from
|
parts, the file can be parsed with `unpack-submission' from
|
||||||
"utils.ss" (see below).
|
"utils.ss" (see below).
|
||||||
|
|
||||||
|
To submit an assignment as a group, students use a concatenation
|
||||||
|
of usernames separated by "+" and any number of spaces (e.g.,
|
||||||
|
"user1+user2"). The same syntax ("user1+user2") is used for the
|
||||||
|
directory for shared submissions, and the usernames are always
|
||||||
|
sorted so that the directory name is deterinistic. Multiple
|
||||||
|
submissions for a particular user in different groups will be
|
||||||
|
rejected.
|
||||||
|
|
||||||
* "inactive/" --- sub-directory for inactive assignments, used by the
|
* "inactive/" --- sub-directory for inactive assignments, used by the
|
||||||
HTTPS status web server.
|
HTTPS status web server.
|
||||||
|
|
||||||
|
@ -312,16 +328,12 @@ sub-directories:
|
||||||
`checker' function can raise an exception; the exception message
|
`checker' function can raise an exception; the exception message
|
||||||
will be relayed back to the student.
|
will be relayed back to the student.
|
||||||
|
|
||||||
To submit an assignment as a group, students use "user1+user2"
|
The first argument is a list of usernames to handle the case of a
|
||||||
etc. The checker function will receive a list of usernames in
|
joint submission (where the submission username was a
|
||||||
this case -- this list is always sorted, so it can check that the
|
concatenation of usernames separated by "+").
|
||||||
list of students are in a list of authorized teams. The same
|
|
||||||
syntax ("user1+user2") is used for the directory for shared
|
|
||||||
submissions; and the usernames are always sorted so the directory
|
|
||||||
name is deterinistic.
|
|
||||||
|
|
||||||
The `checker' function is called with the current directory as
|
The `checker' function is called with the current directory as
|
||||||
"active/<assignment>/<user/s>/ATTEMPT", and the submission is
|
"active/<assignment>/<username(s)>/ATTEMPT", and the submission is
|
||||||
saved in the file "handin". The checker function can change
|
saved in the file "handin". The checker function can change
|
||||||
"handin", and it can create additional files in this directory.
|
"handin", and it can create additional files in this directory.
|
||||||
(Extra files in the current directory will be preserved as it is
|
(Extra files in the current directory will be preserved as it is
|
||||||
|
@ -352,10 +364,13 @@ sub-directories:
|
||||||
was returned by the checker (or the value of the
|
was returned by the checker (or the value of the
|
||||||
`default-file-name' configuration option if there's no checker).
|
`default-file-name' configuration option if there's no checker).
|
||||||
If the submission is from multiple users, then "<user>" is
|
If the submission is from multiple users, then "<user>" is
|
||||||
actually "<user1>+<user2>" etc.
|
actually "<user1>+<user2>" etc. Also, if the cleanup process was
|
||||||
|
interrupted (by a machine failure, etc.), the submission may
|
||||||
|
actually be in "SUCCESS-n" as described above.
|
||||||
|
|
||||||
* "[in]active/<assignment>/<user>/grade" (optional) --- <user>'s grade
|
* "[in]active/<assignment>/<user>/grade" (optional) --- <user>'s
|
||||||
for <assignment>, to be reported by the HTTPS status web server.
|
grade for <assignment>, to be reported by the HTTPS status web
|
||||||
|
server
|
||||||
|
|
||||||
* "[in]active/<assignment>/solution*" --- the solution to the
|
* "[in]active/<assignment>/solution*" --- the solution to the
|
||||||
assignment, made available by the status server to any user who
|
assignment, made available by the status server to any user who
|
||||||
|
@ -380,18 +395,18 @@ button for any network transaction. For handins, "cancel" is
|
||||||
guaranteed to work up to the point that the client sends a "commit"
|
guaranteed to work up to the point that the client sends a "commit"
|
||||||
command; this command is sent only after the server is ready to record
|
command; this command is sent only after the server is ready to record
|
||||||
the submission (having run it through the checker, if any), but before
|
the submission (having run it through the checker, if any), but before
|
||||||
the server writes the "handin.scm" file. Also, the server responds to a
|
renaming "ATTEMPT". Also, the server responds to a commit with "ok"
|
||||||
commit with "ok" only after it has written the file. Thus, when the
|
only after it has written the file. Thus, when the client-side tool
|
||||||
client-side tool reports that the handin was successful, the report is
|
reports that the handin was successful, the report is
|
||||||
reliable. Meanwhile, the tool can also report successful cancels most
|
reliable. Meanwhile, the tool can also report successful cancels most
|
||||||
of the time. In the (normally brief) time between a commit and an "ok"
|
of the time. In the (normally brief) time between a commit and an "ok"
|
||||||
response, the tool gives the student a suitable warning that the
|
response, the tool gives the student a suitable warning that the
|
||||||
cancel is unreliable.
|
cancel is unreliable.
|
||||||
|
|
||||||
To minimize human error, the number of active assignments should be
|
To minimize human error, the number of active assignments should be
|
||||||
limited to 1 whenever possible. When multiple assignments are active,
|
limited to one whenever possible. When multiple assignments are
|
||||||
design a checker to help ensure that the student has selected the
|
active, design a checker to help ensure that the student has selected
|
||||||
correct assignment in the handin dialog.
|
the correct assignment in the handin dialog.
|
||||||
|
|
||||||
A student can download his/her own submissions through a web server
|
A student can download his/her own submissions through a web server
|
||||||
that runs concurrently with the handin server. The starting URL is
|
that runs concurrently with the handin server. The starting URL is
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
|
|
||||||
(define orig-custodian (current-custodian))
|
(define orig-custodian (current-custodian))
|
||||||
|
|
||||||
;; On startup, check that the prefs file is not locked:
|
;; On startup, check that the users file is not locked:
|
||||||
(put-preferences null null
|
(put-preferences null null
|
||||||
(lambda (f)
|
(lambda (f)
|
||||||
(delete-file f)
|
(delete-file f)
|
||||||
|
@ -84,51 +84,41 @@
|
||||||
(define SUCCESS-RE (regexp (format "^~a$" (success-dir "[0-9]+"))))
|
(define SUCCESS-RE (regexp (format "^~a$" (success-dir "[0-9]+"))))
|
||||||
(define SUCCESS-GOOD (map success-dir '(0 1)))
|
(define SUCCESS-GOOD (map success-dir '(0 1)))
|
||||||
|
|
||||||
(define-syntax careful-switch-directory-switch
|
|
||||||
(syntax-rules ()
|
|
||||||
[(_ ?dir body ...)
|
|
||||||
(let ([dir (with-handlers ([void (lambda _ #f)]) (normalize-path ?dir))])
|
|
||||||
(when (and dir (directory-exists? dir))
|
|
||||||
(parameterize ([current-directory (current-directory)])
|
|
||||||
(when (with-handlers ([void (lambda _ #f)])
|
|
||||||
(current-directory dir) #t)
|
|
||||||
body ...))))]))
|
|
||||||
|
|
||||||
(define (cleanup-submission-body)
|
(define (cleanup-submission-body)
|
||||||
;; Find the newest SUCCESS dir -- ignore ATTEMPT, since if it exist it
|
;; Find the newest SUCCESS dir -- ignore ATTEMPT, since if it exist it
|
||||||
;; means that there was a failed submission and the next one will
|
;; means that there was a failed submission and the next one will
|
||||||
;; re-create ATTEMPT.
|
;; re-create ATTEMPT.
|
||||||
(let* ([dirlist (map path->string (directory-list))]
|
(let* ([dirlist (map path->string (directory-list))]
|
||||||
[dir (quicksort
|
[dir (quicksort
|
||||||
(filter (lambda (d)
|
(filter (lambda (d)
|
||||||
(and (directory-exists? d)
|
(and (directory-exists? d)
|
||||||
(regexp-match SUCCESS-RE d)))
|
(regexp-match SUCCESS-RE d)))
|
||||||
dirlist)
|
dirlist)
|
||||||
string<?)]
|
string<?)]
|
||||||
[dir (and (pair? dir) (car dir))])
|
[dir (and (pair? dir) (car dir))])
|
||||||
(when dir
|
(when dir
|
||||||
(unless (member dir SUCCESS-GOOD)
|
(unless (member dir SUCCESS-GOOD)
|
||||||
(LOG "*** USING AN UNEXPECTED SUBMISSION DIRECTORY: ~a"
|
(LOG "*** USING AN UNEXPECTED SUBMISSION DIRECTORY: ~a"
|
||||||
(build-path (current-directory) dir)))
|
(build-path (current-directory) dir)))
|
||||||
;; We have a submission directory -- copy all newer things (extra
|
;; We have a submission directory -- copy all newer things (extra
|
||||||
;; things that exist in the main submission directory but not in
|
;; things that exist in the main submission directory but not in
|
||||||
;; SUCCESS, or things that are newer in the main submission
|
;; SUCCESS, or things that are newer in the main submission
|
||||||
;; directory are kept (but subdirs in SUCCESS will are copied as
|
;; directory are kept (but subdirs in SUCCESS will are copied as
|
||||||
;; is))
|
;; is))
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (f)
|
(lambda (f)
|
||||||
(define dir/f (build-path dir f))
|
(define dir/f (build-path dir f))
|
||||||
(cond [(not (or (file-exists? f) (directory-exists? f)))
|
(cond [(not (or (file-exists? f) (directory-exists? f)))
|
||||||
;; f is in dir but not in the working directory
|
;; f is in dir but not in the working directory
|
||||||
(copy-directory/files dir/f f)]
|
(copy-directory/files dir/f f)]
|
||||||
[(or (<= (file-or-directory-modify-seconds f)
|
[(or (<= (file-or-directory-modify-seconds f)
|
||||||
(file-or-directory-modify-seconds dir/f))
|
(file-or-directory-modify-seconds dir/f))
|
||||||
(and (file-exists? f) (file-exists? dir/f)
|
(and (file-exists? f) (file-exists? dir/f)
|
||||||
(not (= (file-size f) (file-size dir/f)))))
|
(not (= (file-size f) (file-size dir/f)))))
|
||||||
;; f is newer in dir than in the working directory
|
;; f is newer in dir than in the working directory
|
||||||
(delete-directory/files f)
|
(delete-directory/files f)
|
||||||
(copy-directory/files dir/f f)]))
|
(copy-directory/files dir/f f)]))
|
||||||
(directory-list dir)))))
|
(directory-list dir)))))
|
||||||
|
|
||||||
(define cleanup-sema (make-semaphore 1))
|
(define cleanup-sema (make-semaphore 1))
|
||||||
(define (cleanup-submission dir)
|
(define (cleanup-submission dir)
|
||||||
|
@ -168,7 +158,11 @@
|
||||||
(let loop ()
|
(let loop ()
|
||||||
(let loop ([n (+ 20 (random 20))]) ; 10-20 minute delay
|
(let loop ([n (+ 20 (random 20))]) ; 10-20 minute delay
|
||||||
(when (>= n 0)
|
(when (>= n 0)
|
||||||
(let ([new (map directory-list '("active" "inactive"))])
|
(let ([new (map (lambda (x)
|
||||||
|
(if (directory-exists? x)
|
||||||
|
(directory-list x)
|
||||||
|
null))
|
||||||
|
'("active" "inactive"))])
|
||||||
(if (equal? new last-active/inactive)
|
(if (equal? new last-active/inactive)
|
||||||
(begin (sleep 30) (loop (sub1 n)))
|
(begin (sleep 30) (loop (sub1 n)))
|
||||||
(begin (set! last-active/inactive new)
|
(begin (set! last-active/inactive new)
|
||||||
|
@ -389,6 +383,8 @@
|
||||||
;; Try making a watcher:
|
;; Try making a watcher:
|
||||||
(let ([session-cust (make-custodian)]
|
(let ([session-cust (make-custodian)]
|
||||||
[session-channel (make-channel)]
|
[session-channel (make-channel)]
|
||||||
|
[timeout (+ (current-inexact-milliseconds)
|
||||||
|
(* 1000 SESSION-TIMEOUT))]
|
||||||
[status-box (box #f)])
|
[status-box (box #f)])
|
||||||
(let ([watcher
|
(let ([watcher
|
||||||
(with-handlers ([exn:fail:unsupported?
|
(with-handlers ([exn:fail:unsupported?
|
||||||
|
@ -400,25 +396,36 @@
|
||||||
(parameterize ([current-custodian orig-custodian])
|
(parameterize ([current-custodian orig-custodian])
|
||||||
(thread (lambda ()
|
(thread (lambda ()
|
||||||
(let ([session-thread (channel-get session-channel)])
|
(let ([session-thread (channel-get session-channel)])
|
||||||
(let loop ()
|
(let loop ([timed-out? #f])
|
||||||
(if (sync/timeout 3 session-thread)
|
(cond
|
||||||
(begin
|
[(sync/timeout 3 session-thread)
|
||||||
(LOG "session killed while ~s" (unbox status-box))
|
(LOG "session killed ~awhile ~s"
|
||||||
(fprintf w "~s\n"
|
(if timed-out? "(timeout) " "")
|
||||||
(format
|
(unbox status-box))
|
||||||
"handin terminated due to excessive memory computation~a"
|
(fprintf w "~s\n"
|
||||||
(if (unbox status-box)
|
(format
|
||||||
(format " while ~a" (unbox status-box))
|
"handin terminated due to ~a (program doesn't terminate?)~a"
|
||||||
"")))
|
(if timed-out?
|
||||||
(close-output-port w)
|
"time limit"
|
||||||
(channel-put session-channel 'done))
|
"excessive memory use")
|
||||||
(begin
|
(if (unbox status-box)
|
||||||
(collect-garbage)
|
(format " while ~a" (unbox status-box))
|
||||||
(LOG "running ~a (~a ~a)"
|
"")))
|
||||||
(current-memory-use session-cust)
|
(close-output-port w)
|
||||||
(current-memory-use orig-custodian)
|
(channel-put session-channel 'done)]
|
||||||
(current-memory-use))
|
[((current-inexact-milliseconds) . > . timeout)
|
||||||
(loop)))))))))])
|
;; Shutdown here to get the handin-terminated error
|
||||||
|
;; message, instead of relying on
|
||||||
|
;; SESSION-TIMEOUT at the run-server level
|
||||||
|
(custodian-shutdown-all session-cust)
|
||||||
|
(loop #t)]
|
||||||
|
[else
|
||||||
|
(collect-garbage)
|
||||||
|
(LOG "running ~a (~a ~a)"
|
||||||
|
(current-memory-use session-cust)
|
||||||
|
(current-memory-use orig-custodian)
|
||||||
|
(current-memory-use))
|
||||||
|
(loop #f)])))))))])
|
||||||
(if watcher
|
(if watcher
|
||||||
;; Run proc in a thread under session-cust:
|
;; Run proc in a thread under session-cust:
|
||||||
(let ([session-thread
|
(let ([session-thread
|
||||||
|
@ -490,7 +497,7 @@
|
||||||
;; flushes an internal buffer that's not supposed to exist, while
|
;; flushes an internal buffer that's not supposed to exist, while
|
||||||
;; the shutdown gives up immediately.
|
;; the shutdown gives up immediately.
|
||||||
(close-output-port w)))))))
|
(close-output-port w)))))))
|
||||||
SESSION-TIMEOUT
|
(+ SESSION-TIMEOUT 30) ; extra 30 seconds gives watcher thread time to produce a nice message
|
||||||
(lambda (exn)
|
(lambda (exn)
|
||||||
(printf "~a~n" (if (exn? exn)
|
(printf "~a~n" (if (exn? exn)
|
||||||
(exn-message exn)
|
(exn-message exn)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user