1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
|
; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
; Part of Scheme 48 1.9. See file COPYING for notices and license.
; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
; This is file vmio.scm.
; *vm-channels* is a vector of descriptors for open channels.
(define *number-of-channels* 100)
(define *vm-channels*)
; Both of the above are needed when writing images.
(define (s48-channels)
*vm-channels*)
(define (s48-channel-count)
*number-of-channels*)
; The channel statuses as fixnums.
(define closed-status
(enter-fixnum (enum channel-status-option closed)))
(define input-status
(enter-fixnum (enum channel-status-option input)))
(define output-status
(enter-fixnum (enum channel-status-option output)))
(define special-input-status
(enter-fixnum (enum channel-status-option special-input)))
(define special-output-status
(enter-fixnum (enum channel-status-option special-output)))
; Predicates for channels.
(define (input-channel? channel)
(= (channel-status channel) input-status))
(define (output-channel? channel)
(= (channel-status channel) output-status))
(define (open? channel)
(not (= (channel-status channel) closed-status)))
; Initialization - create the channel vector and the three standard channels.
(define (initialize-i/o-system+gc)
(set! *number-of-channels*
(max *number-of-channels*
(+ 1
(max (current-input-channel)
(max (current-output-channel)
(current-error-channel))))))
(set! *vm-channels* (make-vector *number-of-channels*
(current-input-channel)))
(set! *pending-channels-head* false)
(set! *pending-channels-tail* false)
(if (null-pointer? *vm-channels*)
(error "out of memory, unable to continue"))
(vector+length-fill! *vm-channels* *number-of-channels* false)
(let ((input-encoding (channel-console-encoding (current-input-channel)))
(output-encoding (channel-console-encoding (current-output-channel)))
(error-encoding (channel-console-encoding (current-error-channel))))
(if (or (null-pointer? input-encoding)
(null-pointer? output-encoding)
(null-pointer? error-encoding))
(error "out of memory, unable to continue"))
(let ((key (ensure-space (* 3 (+ channel-size
(vm-string-size
(string-length "standard output"))
(vm-string-size (string-length input-encoding))
(vm-string-size (string-length output-encoding))
(vm-string-size (string-length error-encoding)))))))
(values (make-initial-channel (current-input-channel)
input-status
"standard input"
key)
(enter-string input-encoding key)
(make-initial-channel (current-output-channel)
output-status
"standard output"
key)
(enter-string output-encoding key)
(make-initial-channel (current-error-channel)
output-status
"standard error"
key)
(enter-string error-encoding key)))))
(define (make-initial-channel channel status name key)
(let ((vm-channel (make-channel status
(enter-string name key)
(enter-fixnum channel)
false ; close-silently?
false ; next
false ; os-status
false ; error?
key)))
(vector-set! *vm-channels* channel vm-channel)
vm-channel))
(define (os-index->channel index)
(vector-ref *vm-channels* index))
; Make a new channel. The os-index is used to handle I/O-completion interrupts
; so we have to ensure that there is at most one channel using each index.
(define (make-registered-channel mode id os-index close-silently? key)
(cond ((not (or (< os-index *number-of-channels*)
(add-more-channels os-index)))
(values false (enum exception out-of-memory)))
((false? (vector-ref *vm-channels* os-index))
(let ((channel (make-channel (enter-fixnum mode)
id
(enter-fixnum os-index)
close-silently?
false ; next
false ; os-status
false ; error?
key)))
(vector-set! *vm-channels* os-index channel)
(values channel
(enum exception out-of-memory)))) ; exception is ignored
(else
(values false (enum exception channel-os-index-already-in-use)))))
; Called from outside the VM. It's up to the caller to be GC-safe.
; Returns FALSE if anything goes wrong.
(define (s48-really-add-channel mode id os-index)
(receive (channel status)
(make-registered-channel (extract-fixnum mode)
id
os-index
false
(ensure-space channel-size))
(if (channel? channel)
channel
(enter-fixnum status))))
; Called from outside to change the os-index of a particular channel.
(define (s48-set-channel-os-index channel os-index)
(cond ((not (or (< os-index *number-of-channels*)
(add-more-channels os-index)))
(enter-fixnum (enum exception out-of-memory)))
((false? (vector-ref *vm-channels* os-index))
(let ((old-index (extract-fixnum (channel-os-index channel))))
(if (vm-eq? (channel-os-status channel)
true) ; operation pending
(enqueue-channel! old-index (channel-abort old-index) false))
(vector-set! *vm-channels* old-index false)
(vector-set! *vm-channels* os-index channel)
(set-channel-os-index! channel (enter-fixnum os-index))
true))
(else
(enter-fixnum (enum exception channel-os-index-already-in-use)))))
; Extend the vector of channels.
(define (add-more-channels index)
(let* ((new-count (max (+ index 1)
(+ *number-of-channels* 8)))
(old-count *number-of-channels*)
(new-vm-channels (make-vector new-count (vector-ref *vm-channels* 0))))
(cond ((null-pointer? new-vm-channels)
#f)
(else
(do ((i 0 (+ i 1)))
((= i *number-of-channels*))
(vector-set! new-vm-channels i (vector-ref *vm-channels* i)))
(do ((i *number-of-channels* (+ i 1)))
((= i new-count))
(vector-set! new-vm-channels i false))
(deallocate *vm-channels*)
(set! *vm-channels* new-vm-channels)
(set! *number-of-channels* new-count)
#t))))
; We abort any operation pending on the channel and then close it, freeing
; up the index. The status from the OS's close function is returned.
(define (close-channel! channel)
(let ((os-index (extract-fixnum (channel-os-index channel))))
(if (vm-eq? (channel-os-status channel)
true) ; operation pending
(enqueue-channel! os-index (channel-abort os-index) false))
(let ((status (if (or (= input-status (channel-status channel))
(= special-input-status (channel-status channel)))
(close-input-channel os-index)
(close-output-channel os-index))))
(vector-set! *vm-channels* os-index false)
(set-channel-status! channel closed-status)
status)))
; Called from outside the VM. Closes the channel at OS-INDEX, should we have
; such.
(define (s48-close-channel os-index)
(if (and (<= 0 os-index)
(< os-index *number-of-channels*)
(channel? (os-index->channel os-index)))
(begin
(close-channel! (os-index->channel os-index))
(unspecific)))) ; CLOSE-CHANNEL! returns an integer
; Called to close an OS channel when we have been unable to make the
; corresponding Scheme channel.
(define (close-channel-index! index name mode)
(let ((status (if (input-channel-status? mode)
(close-input-channel index)
(close-output-channel index))))
(if (error? status)
(channel-close-error status index name))))
(define (input-channel-status? mode)
(or (= mode (enum channel-status-option input))
(= mode (enum channel-status-option special-input))))
(define (channel-close-error status index id)
(write-error-string "Error: ")
(write-error-string (error-string status))
(write-error-newline)
(write-error-string " while closing port ")
(cond
((vm-string? id)
(write-error-string (extract-low-string id)))
((fixnum? id)
(write-error-integer (extract-fixnum index)))
(else
(write-error-string "<strange id>")))
(write-error-newline)
(unspecific))
; Return a list of the open channels, for the opcode of the same name.
; Not that it's important, but the list has the channels in order of
; their os-indexes.
(define (open-channels-list)
(let ((key (ensure-space (* vm-pair-size *number-of-channels*))))
(do ((i (- *number-of-channels* 1) (- i 1))
(res null
(let ((channel (vector-ref *vm-channels* i)))
(if (channel? channel)
(vm-cons channel res key)
res))))
((= i -1)
res))))
;----------------------------------------------------------------
; Handling i/o-completion interrupts
; Currently, because the GC may move buffers, strings, etc. around, the OS
; must buffer the data while waiting for i/o to complete.
;
; Unix: the i/o completion just means that the channel is ready; no characters
; are ever transfered asynchronously.
;
; DOS/Windows: no non-blocking i/o of any kind.
;
; WindowsNT: we will need a fancier GC or something.
; These are a queue of channels with pending interrupts
(define *pending-channels-head* false)
(define *pending-channels-tail* false)
(define (channel-queue-empty?)
(false? *pending-channels-head*))
(define (enqueue-channel! index status error?)
(let ((channel (os-index->channel index)))
(set-channel-os-status! channel (enter-fixnum status))
(set-channel-error?! channel error?)
(cond ((or (not (false? (channel-next channel))) ; already queued (how?)
(eq? channel *pending-channels-head*) ; first and only
(eq? channel *pending-channels-tail*)); last (i.e. no next)
(unspecific)) ; for the type checker
((false? *pending-channels-head*)
(set! *pending-channels-head* channel)
(set! *pending-channels-tail* channel))
(else
(set-channel-next! *pending-channels-tail* channel)
(set! *pending-channels-tail* channel)))))
(define (dequeue-channel!)
(let* ((channel *pending-channels-head*)
(next (channel-next channel)))
(set! *pending-channels-head* next)
(set-channel-next! channel false)
(if (false? next)
(set! *pending-channels-tail* false))
channel))
; See if a the OS has already finished with CHANNEL and return its status
; if it has. If not, call the OS and have it abort the channel's current
; operation.
(define (vm-channel-abort channel)
(let ((head *pending-channels-head*))
(cond ((false? head)
(set-channel-os-status! channel false) ; no longer pending
(enter-fixnum (channel-abort
(extract-fixnum (channel-os-index channel)))))
((vm-eq? channel head)
(dequeue-channel!)
(if (false? *pending-channels-head*)
(pending-interrupts-remove!
(interrupt-bit
(enum interrupt i/o-completion))))
(channel-os-status channel))
(else
(let loop ((ch (channel-next head)) (prev head))
(cond ((false? ch)
(set-channel-os-status! channel false) ; no longer pending
(enter-fixnum (channel-abort
(extract-fixnum (channel-os-index channel)))))
((vm-eq? ch channel)
(if (vm-eq? ch *pending-channels-tail*)
(set! *pending-channels-tail* prev))
(set-channel-next! prev (channel-next ch))
(set-channel-next! ch false)
(channel-os-status ch))
(else
(loop (channel-next ch) ch))))))))
(define (trace-io trace-value)
(set! *pending-channels-head* (trace-value *pending-channels-head*))
(set! *pending-channels-tail* (trace-value *pending-channels-tail*)))
;----------------------------------------------------------------
; Automatically closing channels.
; Make sure all open channel names survive the GC.
(define (trace-channel-names s48-trace-value)
(do ((i 0 (+ i 1)))
((= i *number-of-channels*) #f)
(let ((channel (vector-ref *vm-channels* i)))
(if (and (not (false? channel))
(open? channel))
(set-channel-id! channel
(s48-trace-value (channel-id channel)))))))
; The following is called after the GC finishes.
(define (close-untraced-channels! s48-extant? s48-trace-value)
(do ((i 0 (+ i 1)))
((= i *number-of-channels*) #f)
(let ((channel (vector-ref *vm-channels* i)))
(if (not (false? channel))
(let ((new (cond ((s48-extant? channel)
(s48-trace-value channel))
((open? channel) ; channel was not copied
(close-channel-noisily! channel)
false)
(else
false))))
(vector-set! *vm-channels* i new))))))
(define (close-channel-noisily! channel)
(let ((status (close-channel! channel))
(id (channel-id channel)))
(if (error? status)
(channel-close-error status (channel-os-index channel) id))
(if (false? (channel-close-silently? channel))
(notify-channel-closed channel))))
(define (notify-channel-closed channel)
(let ((id (channel-id channel)))
(write-error-string "Channel closed: ")
(cond
((fixnum? id)
(write-error-integer (extract-fixnum id)))
((vm-string? id)
(write-vm-string id (current-error-port)))
(else
(write-error-string "<strange id>")))
(write-error-string " ")
(write-error-integer (extract-fixnum (channel-os-index channel)))
(write-error-newline)
(unspecific)))
|