From 3c487a849b6b3554831062794296bcc4e17a785d Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 16 Sep 2005 19:48:58 +0000 Subject: [PATCH] better timeout error message, doc revisions svn: r865 --- collects/handin-client/info.ss | 2 +- collects/handin-client/tool.ss | 4 +- collects/handin-server/doc.txt | 109 +++++++++++-------- collects/handin-server/handin-server.ss | 137 +++++++++++++----------- 4 files changed, 137 insertions(+), 115 deletions(-) diff --git a/collects/handin-client/info.ss b/collects/handin-client/info.ss index 118e0f184b..5f0fdf0b2f 100644 --- a/collects/handin-client/info.ss +++ b/collects/handin-client/info.ss @@ -3,7 +3,7 @@ ;; Also replace the "icon.png" and "server-cert.pem" files. ;; Instead of uncommenting the definition of server:port, you ;; can set the PLT_HANDIN_SERVER_PORT environment variable. - (define name "Course Handin") + (define name "Course") (define collection "handin-client") ;(define server:port "localhost:7979") diff --git a/collects/handin-client/tool.ss b/collects/handin-client/tool.ss index 38f73a96c4..823b2313f3 100644 --- a/collects/handin-client/tool.ss +++ b/collects/handin-client/tool.ss @@ -257,7 +257,7 @@ (define status (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] [stretchable-width #t])) @@ -515,7 +515,7 @@ (define/override (file-menu:between-open-and-revert file-menu) (new menu-item% - (label (format "Manage ~a Account..." handin-name)) + (label (format "Manage ~a Handin Account..." handin-name)) (parent file-menu) (callback (lambda (m e) (manage-handin-account)))) (super file-menu:between-open-and-revert file-menu)) diff --git a/collects/handin-server/doc.txt b/collects/handin-server/doc.txt index a721bc93cc..071a85ec54 100644 --- a/collects/handin-server/doc.txt +++ b/collects/handin-server/doc.txt @@ -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 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 -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 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 appear in DrScheme's interface (e.g., the "XXX" for the - "Manage XXX Account..." menu item). Again, make the name - specific to the course, in case a student installs multiple - handin tools. It's a good idea to use "Handin" as the last - part of the name, as in "2010 Handin", since the button is - always named "Handin". + "Manage XXX Handin Account..." menu item). Again, make the + name specific to the course, in case a student installs + multiple handin tools. Do not use "Handin" as the last part + of the name, since "Handin" is always added for button and + menu names. * For `collection', use the name that you chose for your collection directory (i.e., whatever you changed @@ -196,9 +197,11 @@ sub-directories: "BACKUP-1/handin.scm", etc.; the default is 9 'user-regexp : a regular expression that is used to validate - usernames, young students often choose exotic usernames that - are impossible to remember, and forget capitalization; the - default is fairly strict: #rx"^[a-z][a-z0-9]+$" + usernames; young students often choose exotic usernames that + are impossible to remember, and forget capitalization, so the + 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 username format (according to user-regexp above); #f stands @@ -207,27 +210,29 @@ sub-directories: 'username-case-sensitive? : a boolean; when #f, usernames are case-folded for all purposes; defaults to #f - (note that you should not set this to #t on Windows, since - usernames are used as directory names) + (note that you should not set this to #t on Windows or when + using a case-insensitive filesystem, since usernames are used + as directory names) 'id-regexp : a regular expression that is used to validate a "free form" user id (possibly a student id) for a created account; the default is #rx"^.*$" 'id-desc : a plain-words description of the acceptable id format - (according to id-regexp above), eg, "Foo ID Number"; the - default is #f indicating no description + (according to id-regexp above), eg, "Utah ID Number with + exactly nine digits"; the default is #f indicating no + description 'email-regexp : a regular expression that is used to validate emails, the #rx"^[^@<>\"`',]+@[a-zA-Z0-9_.-]+[.][a-zA-Z]+$" default can be changed to "" if you don't care about emails, 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 - format (according to email-regexp above), eg, "Foo CS email"; - #f stands for no description; the default is "a valid email - address" + format (according to email-regexp above), eg, "Utah CS email + address"; #f stands for no description; the default is "a + valid email address" 'allow-new-users : a boolean indicating whether to allow 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 handin. - Within an "ATTEMPT" or "SUCCESS-n" directory, a file "handin.scm" - (or some other name if `default-file-name' is set) contains the - actual submission. A `checker' procedure can change this default - file name, it can create additional files in an - "ATTEMPT"/"SUCCESS-n" directory or in the student directory; see - below on "checker.ss" for more details. + A cleanup process in the server copies successful submission to + the student directory -- one level up from the corresponding + "SUCCESS-n" directory. This is done only for files and + directories that are newer in "SUCCESS-n" than in the submission + root, other files and directories are left intact. If external + 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 - submission root -- one level up from the corresponding "SUCCESS-n" - directory. This is done only for files and directories that are - newer in "SUCCESS-n" than in the submission root, other files and - directories are left intact. This means that you can have - external tools that add new content to the submission directory - (eg, a "grade" file as described below) and it will stay there. + Within a student directory, a file "handin.scm" (or some other + name if `default-file-name' is set) contains the actual + submission. A `checker' procedure can change this default file + name, and it can create additional files in an "ATTEMPT" directory + (to be copied by the cleanup process); see below on "checker.ss" + for more details. For submissions from a normal DrScheme frame, a submission file 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 "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 HTTPS status web server. @@ -312,16 +328,12 @@ sub-directories: `checker' function can raise an exception; the exception message will be relayed back to the student. - To submit an assignment as a group, students use "user1+user2" - etc. The checker function will receive a list of usernames in - this case -- this list is always sorted, so it can check that the - 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 first argument is a list of usernames to handle the case of a + joint submission (where the submission username was a + concatenation of usernames separated by "+"). The `checker' function is called with the current directory as - "active///ATTEMPT", and the submission is + "active///ATTEMPT", and the submission is saved in the file "handin". The checker function can change "handin", and it can create additional files in this directory. (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 `default-file-name' configuration option if there's no checker). If the submission is from multiple users, then "" is - actually "+" etc. + actually "+" 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///grade" (optional) --- 's grade - for , to be reported by the HTTPS status web server. + * "[in]active///grade" (optional) --- 's + grade for , to be reported by the HTTPS status web + server * "[in]active//solution*" --- the solution to the 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" 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 server writes the "handin.scm" file. Also, the server responds to a -commit with "ok" only after it has written the file. Thus, when the -client-side tool reports that the handin was successful, the report is +renaming "ATTEMPT". Also, the server responds to a commit with "ok" +only after it has written the file. Thus, when the client-side tool +reports that the handin was successful, the report is reliable. Meanwhile, the tool can also report successful cancels most 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 cancel is unreliable. To minimize human error, the number of active assignments should be -limited to 1 whenever possible. When multiple assignments are active, -design a checker to help ensure that the student has selected the -correct assignment in the handin dialog. +limited to one whenever possible. When multiple assignments are +active, design a checker to help ensure that the student has selected +the correct assignment in the handin dialog. A student can download his/her own submissions through a web server that runs concurrently with the handin server. The starting URL is diff --git a/collects/handin-server/handin-server.ss b/collects/handin-server/handin-server.ss index cf1ebe0e62..7de305562d 100644 --- a/collects/handin-server/handin-server.ss +++ b/collects/handin-server/handin-server.ss @@ -54,7 +54,7 @@ (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 (lambda (f) (delete-file f) @@ -84,51 +84,41 @@ (define SUCCESS-RE (regexp (format "^~a$" (success-dir "[0-9]+")))) (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) ;; Find the newest SUCCESS dir -- ignore ATTEMPT, since if it exist it - ;; means that there was a failed submission and the next one will - ;; re-create ATTEMPT. - (let* ([dirlist (map path->string (directory-list))] - [dir (quicksort - (filter (lambda (d) - (and (directory-exists? d) - (regexp-match SUCCESS-RE d))) - dirlist) - stringstring (directory-list))] + [dir (quicksort + (filter (lambda (d) + (and (directory-exists? d) + (regexp-match SUCCESS-RE d))) + dirlist) + string= 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) (begin (sleep 30) (loop (sub1 n))) (begin (set! last-active/inactive new) @@ -389,6 +383,8 @@ ;; Try making a watcher: (let ([session-cust (make-custodian)] [session-channel (make-channel)] + [timeout (+ (current-inexact-milliseconds) + (* 1000 SESSION-TIMEOUT))] [status-box (box #f)]) (let ([watcher (with-handlers ([exn:fail:unsupported? @@ -400,25 +396,36 @@ (parameterize ([current-custodian orig-custodian]) (thread (lambda () (let ([session-thread (channel-get session-channel)]) - (let loop () - (if (sync/timeout 3 session-thread) - (begin - (LOG "session killed while ~s" (unbox status-box)) - (fprintf w "~s\n" - (format - "handin terminated due to excessive memory computation~a" - (if (unbox status-box) - (format " while ~a" (unbox status-box)) - ""))) - (close-output-port w) - (channel-put session-channel 'done)) - (begin - (collect-garbage) - (LOG "running ~a (~a ~a)" - (current-memory-use session-cust) - (current-memory-use orig-custodian) - (current-memory-use)) - (loop)))))))))]) + (let loop ([timed-out? #f]) + (cond + [(sync/timeout 3 session-thread) + (LOG "session killed ~awhile ~s" + (if timed-out? "(timeout) " "") + (unbox status-box)) + (fprintf w "~s\n" + (format + "handin terminated due to ~a (program doesn't terminate?)~a" + (if timed-out? + "time limit" + "excessive memory use") + (if (unbox status-box) + (format " while ~a" (unbox status-box)) + ""))) + (close-output-port w) + (channel-put session-channel 'done)] + [((current-inexact-milliseconds) . > . timeout) + ;; 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 ;; Run proc in a thread under session-cust: (let ([session-thread @@ -490,7 +497,7 @@ ;; flushes an internal buffer that's not supposed to exist, while ;; the shutdown gives up immediately. (close-output-port w))))))) - SESSION-TIMEOUT + (+ SESSION-TIMEOUT 30) ; extra 30 seconds gives watcher thread time to produce a nice message (lambda (exn) (printf "~a~n" (if (exn? exn) (exn-message exn)