source: branches/lispworks/block-io-mcl.lisp@ 22

Last change on this file since 22 was 6, checked in by Gail Zacharias, 17 years ago

Working lispworks version, but now doesn't load in MCL (yet).

  • Property svn:eol-style set to native
File size: 19.7 KB
Line 
1;;;-*- Mode: Lisp; Package: ccl -*-
2
3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4;;
5;; block-io-mcl.lisp
6;; low-level block I/O - MCL version.
7;;
8;; Copyright © 1996-1999 Digitool, Inc.
9;; Copyright © 1992-1995 Apple Computer, Inc.
10;; All rights reserved.
11;; Permission is given to use, copy, and modify this software provided
12;; that Digitool is given credit in all derivative works.
13;; This software is provided "as is". Digitool makes no warranty or
14;; representation, either express or implied, with respect to this software,
15;; its quality, accuracy, merchantability, or fitness for a particular
16;; purpose.
17;;
18
19;;;;;;;;;;;;;;;;;;;;;;;;;;
20;;
21;; Modification history
22;;
23;; 01/29/00 akh fix example at end of this file
24;; 01/10/00 akh fix for possible non fixnum file address
25;; ------------- 0.96
26;; 07/20/96 bill databases-locked-p, with-databases-unlocked, & funcall-with-databases-unlocked
27;; are already part of CCL 4.0. Bind *warn-if-redefine*, etc. appropriately.
28;; 07/06/96 bill provide block-io-mcl, not block-io.
29;; 05/21/96 bill PPC versions of %fread-bytes & %fwrite-bytes now work correctly
30;; if the starting file or buffer position is odd.
31;; They use %copy-ivector-to-ivector instead of local code.
32;; ------------- 0.95
33;; 05/09/96 bill databases-locked-p
34;; funcall-with-databases-locked-p comes out-of-line from with-databases-unlocked.
35;; It now binds *database-locked-p* nil during its body.
36;; 05/03/96 bill multi-process with-databases-locked
37;; 05/01/96 slh don't require lapmacros on PPC
38;; ------------- 0.94 = MCL-PPC 3.9
39;; 03/09/96 bill Eliminate LAP for ppc-target
40;; ------------- 0.93
41;; 07/21/95 bill inhibited-event-dispatch now processes *inhibited-foreground-switch*
42;; 05/31/95 bill wood:with-database-locked now calls new inhibted-event-dispatch
43;; function if event processing happenned while it was inhibited.
44;; This makes interactive response time as good as it can be
45;; given this locking mechanism.
46;; 05/25/95 bill %fread-bytes & %fwrite-bytes use #_BlockMove instead
47;; of a move.b loop; it's faster.
48;; set-minimum-file-length never makes the file shorter.
49;; ------------- 0.9
50;; 03/13/95 bill byte-array-p and ensure-byte-array move here from "disk-cache-accessors.lisp"
51;; byte-array-p updated to work in MCL 3.0.
52;; Former lap uses of $v_subtype changed to calls of ensure-byte-array
53;; 10/25/94 Moon without-interrupts -> with-databases-locked
54;; 09/21/94 bill without-interrupts around part of %fread-bytes and %fwrite-bytes
55;; 01/31/94 bill %fread-bytes & %fwrite-bytes support offsets > 64K and
56;; will read/write more than just the first 512 bytes.
57;; ------------ 0.8
58;; ------------ 0.6
59;; ------------ 0.5
60;; 03/05/92 bill New file
61;;
62
63(in-package :ccl)
64
65(export '(stream-read-bytes stream-write-bytes set-minimum-file-length))
66
67(provide :block-io-mcl)
68
69(defvar *inhibit-event-dispatch* nil)
70(defvar *event-dispatch-inhibited* nil)
71
72; Set non-NIL if a suspend or resume event comes in while
73; *event-dispatch-inhibited* is true. 0 means it was
74; suspend, non-zero means resume.
75(defvar *inhibited-foreground-switch* nil)
76
77;;; This macro provides the interlocking so the WOOD database
78;;; doesn't get screwed up by being used reentrantly by an event
79;;; handler. Change this macro and recompile to change the
80;;; implementation of the interlocking.
81;;; Defined here since WOOD doesn't seem to have
82;;; a file specifically for macros like this.
83#-ccl-3
84(progn
85
86(defmacro wood::with-databases-locked (&body body)
87 ;; The following is surprisingly slow on 68040s
88 ;`(without-interrupts ,@body)
89 ;; So do it this way instead.
90 `(multiple-value-prog1 ; Wish this could be prog1
91 (let ((*inhibit-event-dispatch* t))
92 ,@body)
93 (locally (declare (optimize (speed 3) (safety 0))) ; force inline value cell reference
94 (when (and *event-dispatch-inhibited*
95 (not *inhibit-event-dispatch*))
96 (inhibited-event-dispatch)))))
97
98(defmacro wood::with-databases-unlocked (&body body)
99 `(let ((ccl::*inhibit-event-dispatch* nil))
100 (declare (special ccl::*inhibit-event-dispatch*)) ; fix build problem
101 ,@body))
102
103(defun wood::databases-locked-p (&optional by-locker)
104 (declare (ignore by-locker))
105 (and (boundp 'ccl::*inhibit-event-dispatch*)
106 ccl::*inhibit-event-dispatch*))
107
108) ; end of #-ccl-3 progn
109
110; with-databases-(un)locked is a NOP is CCL 3.0, since store-conditional
111; doesn't exists there yet.
112#+(and ccl-3 (not ppc-target))
113(progn
114
115(defmacro wood:with-databases-locked (&body body)
116 `(progn ,@body))
117
118(defmacro wood:with-databases-unlocked (&body body)
119 `(progn ,@body))
120
121(defun wood::databases-locked-p (&optional by-locker)
122 (declare (ignore by-locker))
123 nil)
124
125) ; end of #+(and ccl-3 (not ppc-target)) progn
126
127#+ppc-target
128(progn
129
130(defvar *database-lock* (make-lock))
131(defvar *database-queue* (make-process-queue "*database-queue*"))
132(defvar *database-locked-p* nil)
133
134(declaim (type lock *database-lock)
135 (type boolean *database-locked-p*))
136
137(declaim (inline lock-databases unlock-databases))
138
139; You should only call this inside a binding of *database-locked-p* to true.
140; Otherwise, another process will steal the lock from you.
141; with-databases-locked uses it correctly.
142; Returns when it has the *database-lock*.
143; A true value means that it is newly grabbed.
144; NIL means that this process already had the lock when lock-databases was called.
145(defun lock-databases ()
146 (declare (type process *current-process*))
147 (let ((process *current-process*)
148 (lock *database-lock*))
149 (declare (type lock lock))
150 (unless (eq (lock.value lock) process)
151 (unless (store-conditional lock nil process)
152 (lock-databases-out-of-line))
153 t)))
154
155(defun unlock-databases ()
156 (declare (type lock *database-lock*)
157 (type process *current-process*))
158 (unless (store-conditional *database-lock* *current-process* nil)
159 (error "~s not held by ~s" '*database-lock* *current-process*)))
160
161(declaim (ftype (function (&optional t)) wood::with-databases-locked-p))
162
163(let ((*warn-if-redefine* nil)
164 (*warn-if-redefine-kernel* nil))
165
166(defun wood::databases-locked-p (&optional by-locker)
167 (without-interrupts
168 (let* ((lock *database-lock*)
169 (locker (lock.value lock)))
170 (cond ((null locker) nil)
171 ((or (process-exhausted-p locker)
172 (not (symbol-value-in-process '*database-locked-p* locker)))
173 (setf (lock.value lock) nil))
174 (by-locker (eq locker by-locker))
175 (t t)))))
176
177)
178
179; This is so hairy because we're trying to avoid an unwind-protect (too slow)
180; yet we still want to notice when the holder of the *database-lock*
181; has thrown out of wood::with-databases-locked.
182(defun lock-databases-out-of-line ()
183 (let ((lock *database-lock*)
184 (queue *database-queue*)
185 (enqueued nil))
186 (declare (type lock lock))
187 ; In case we threw out of a with-databases-locked
188 (unwind-protect
189 (loop
190 (wood::databases-locked-p) ; clear lock.value if it's not really locked
191 (unless enqueued
192 (setq enqueued (process-enqueue-with-timeout queue 30)))
193 (when enqueued
194 (when (store-conditional lock nil *current-process*)
195 (return t))
196 (process-wait-with-timeout "Lock"
197 30
198 #'(lambda (lock)
199 (null (lock.value lock)))
200 lock)))
201 (when enqueued
202 (process-dequeue queue)))))
203
204(defmacro wood::with-databases-locked (&body body)
205 (let ((needs-unlocking-p (gensym)))
206 `(let* ((*database-locked-p* t)
207 (,needs-unlocking-p (lock-databases)))
208 (multiple-value-prog1
209 (progn ,@body)
210 (when ,needs-unlocking-p
211 (unlock-databases))))))
212
213;;; Undo the effect of with-databases-locked temporarily, if possible
214(eval-when (:compile-toplevel :execute :load-toplevel)
215(unless (fboundp 'wood::with-databases-unlocked)
216
217(defmacro wood::with-databases-unlocked (&body body)
218 (let ((thunk (gensym)))
219 `(let ((,thunk #'(lambda () ,@body)))
220 (declare (dynamic-extent ,thunk))
221 (funcall-with-databases-unlocked ,thunk))))
222
223))
224
225(let ((*warn-if-redefine* nil)
226 (*warn-if-redefine-kernel* nil))
227
228(defun funcall-with-databases-unlocked (thunk)
229 (let ((was-locked? nil))
230 (unwind-protect
231 (let ((*database-locked-p* *database-locked-p*))
232 (when (setq was-locked? (wood::databases-locked-p *current-process*))
233 (unlock-databases)
234 (setq *database-locked-p* nil))
235 (funcall thunk))
236 (when was-locked?
237 (lock-databases)))))
238
239)
240
241) ; end of #+ppc-target progn
242
243; Separate function mostly so we can meter it
244(defun inhibited-event-dispatch ()
245 (setq *event-dispatch-inhibited* nil)
246 (let ((switch *inhibited-foreground-switch*))
247 (when switch
248 (setq *inhibited-foreground-switch* nil)
249 (unless (eq *foreground*
250 (setq *foreground* (not (eql switch 0))))
251 (when (fboundp 'establish-*foreground*)
252 (funcall 'establish-*foreground*)))))
253 (event-dispatch))
254
255;; (stream-read-bytes stream address vector offset length)
256;; read length bytes into vector at offset from stream at address.
257;;
258;; (stream-write-bytes stream address vector offset length)
259;; write length bytes from stream at address into vector at offset.
260;; Extend the length of the file if necessary.
261;;
262;; (set-minimum-file-length stream length)
263;; Set the file length of stream to >= length.
264;;
265;; This implementation only supports vectors of type
266;; (array (unsigned-byte 8)), (array (signed-byte 8)), or simple-string
267
268(eval-when (eval compile)
269 #-ppc-target
270 (require 'lapmacros)
271 (require 'lispequ)
272
273;structure of fblock
274;from "ccl:level-1;l1-sysio.lisp"
275
276(let ((*warn-if-redefine* nil))
277
278(def-accessors (fblock) %svref
279 nil ; 'fblock
280 fblock.pb ; a parameter block; nil if closed.
281 fblock.lastchar ; untyi char or nil
282 fblock.dirty ; non-nil when dirty
283 fblock.buffer ; macptr to buffer; nil when closed
284 fblock.bufvec ; buffer vector; nil when closed
285 fblock.bufsize ; size (in 8-bit bytes) of buffer
286 fblock.bufidx ; index of next element to read/write
287 fblock.bufcount ; # of elements in buffer
288 fblock.filepos ; 8-bit position at last read/write
289 fblock.fileeof ; file's logical eof.
290 fblock.stream ; backptr to file stream
291 fblock.element-type ; typespec
292 fblock.nbits-per-element ; # of bits per element
293 fblock.elements-per-buffer ; 512 or whatever
294 fblock.minval ; minimum value of element type or nil: < 0
295 fblock.maxval ; maximum value or nil
296 fblock.element-bit-offset ; for non-arefable n-bit elements
297)
298
299) ; end of let
300
301) ; end of eval-when
302
303; Read length bytes into array at offset from stream at address.
304; Array must be a simple (byte 8) array.
305; stream must be an input stream for 8 bit elements.
306(defmethod stream-read-bytes ((stream input-file-stream)
307 address array offset length)
308 (%fread-bytes (slot-value stream 'fblock)
309 #+:wood-fixnum-addresses
310 (require-type address 'fixnum)
311 #-:wood-fixnum-addresses
312 (require-type address 'integer)
313 array
314 (require-type offset 'fixnum)
315 (require-type length 'fixnum)))
316
317#-ppc-target
318(defun %fread-bytes (fblock address array offset length)
319 (declare (fixnum offset length))
320 #+:wood-fixnum-addresses (declare (fixnum address))
321 (unless (eql 8 (fblock.nbits-per-element fblock))
322 (error "%fread-bytes only implemented for 8-bit bytes"))
323 (unless (>= (length array) (the fixnum (+ offset length)))
324 (error "array too small"))
325 (ensure-byte-array array)
326 (let ((max-length (- (%fsize fblock) address)))
327 #+:wood-fixnum-addresses (declare (fixnum max-length))
328 (if (< max-length length) (setq length max-length))
329 (if (< length 0) (setq length 0)))
330 (let ((bytes length)
331 (bufvec (fblock.bufvec fblock)))
332 (declare (fixnum bytes))
333 (loop
334 (when (<= length 0) (return bytes))
335 (wood::with-databases-locked
336 (%fpos fblock address)
337 (let* ((vec-index (- address (the #+:wood-fixnum-addresses fixnum
338 #-:wood-fixnum-addresses integer
339 (fblock.filepos fblock))))
340 (vec-left (- (the fixnum (fblock.bufcount fblock)) vec-index)))
341 (declare (fixnum vec-index vec-left))
342 ; (print-db vec-index vec-left)
343 (if (> vec-left length) (setq vec-left length))
344 (lap-inline ()
345 (:variable bufvec array offset vec-index vec-left)
346 (move.l (varg bufvec) atemp0)
347 (move.l (varg vec-index) acc)
348 (getint acc)
349 (lea (atemp0 acc.l $v_data) atemp0)
350 (move.l (varg array) atemp1)
351 (move.l (varg offset) acc)
352 (getint acc)
353 (lea (atemp1 acc.l $v_data) atemp1)
354 (move.l (varg vec-left) acc)
355 (getint acc)
356 (dc.w #_BlockMove)
357 )
358 (incf address vec-left)
359 (incf offset vec-left)
360 (decf length vec-left))))))
361
362#+ppc-target
363(defun %fread-bytes (fblock address array offset length)
364 (declare (fixnum offset length))
365 #+:wood-fixnum-addresses (declare (fixnum address))
366 (unless (eql 8 (fblock.nbits-per-element fblock))
367 (error "%fread-bytes only implemented for 8-bit bytes"))
368 (unless (>= (length array) (the fixnum (+ offset length)))
369 (error "array too small"))
370 (ensure-byte-array array)
371 (let ((max-length (- (%fsize fblock) address)))
372 #+:wood-fixnum-addresses (declare (fixnum max-length))
373 (if (< max-length length) (setq length max-length))
374 (if (< length 0) (setq length 0)))
375 (let ((bytes length)
376 (bufvec (fblock.bufvec fblock)))
377 (declare (fixnum bytes))
378 (loop
379 (when (<= length 0) (return bytes))
380 (without-interrupts
381 (%fpos fblock address)
382 (let* ((vec-index (fblock.bufidx fblock))
383 (vec-left (- (the fixnum (fblock.bufcount fblock)) vec-index)))
384 (declare (fixnum vec-index vec-left))
385 (if (> vec-left length) (setq vec-left length))
386 (%copy-ivector-to-ivector bufvec vec-index array offset vec-left)
387 (incf address vec-left)
388 (incf offset vec-left)
389 (decf length vec-left))))))
390
391; same, but other direction
392(defmethod stream-write-bytes ((stream output-file-stream)
393 address array offset length)
394 (%fwrite-bytes (slot-value stream 'fblock)
395 #+:wood-fixnum-addresses (require-type address 'fixnum)
396 #-:wood-fixnum-addresses (require-type address 'integer)
397 array
398 (require-type offset 'fixnum)
399 (require-type length 'fixnum)))
400
401#-ppc-target
402(defun %fwrite-bytes (fblock address array offset length)
403 (declare (fixnum offset length))
404 #+:wood-fixnum-addresses (declare (fixnum address))
405 (unless (eql 8 (fblock.nbits-per-element fblock))
406 (error "%fwrite-bytes only implemented for 8-bit bytes"))
407 (unless (>= (length array) (the fixnum (+ offset length)))
408 (error "array too small"))
409 (ensure-byte-array array)
410 (let ((min-size (+ address length)))
411 #+:wood-fixnum-addresses (declare (fixnum min-size))
412 (when (> min-size (%fsize fblock))
413 (%fsize fblock min-size)))
414 (let ((bytes length)
415 (bufvec (fblock.bufvec fblock)))
416 (declare (fixnum bytes))
417 (loop
418 (when (<= length 0) (return bytes))
419 (wood::with-databases-locked
420 (%fpos fblock address)
421 (let* ((vec-index (- address (the #+:wood-fixnum-addresses fixnum
422 #-:wood-fixnum-addresses integer
423 (fblock.filepos fblock))))
424 (vec-left (- (the fixnum (fblock.elements-per-buffer fblock))
425 vec-index)))
426 (declare (fixnum vec-index vec-left))
427 (if (> vec-left length) (setq vec-left length))
428 (lap-inline ()
429 (:variable bufvec array offset vec-index vec-left)
430 (move.l (varg bufvec) atemp1)
431 (move.l (varg vec-index) acc)
432 (getint acc)
433 (lea (atemp1 acc.l $v_data) atemp1)
434 (move.l (varg array) atemp0)
435 (move.l (varg offset) acc)
436 (getint acc)
437 (lea (atemp0 acc.l $v_data) atemp0)
438 (move.l (varg vec-left) acc)
439 (getint acc)
440 (dc.w #_BlockMove))
441 (let ((index (+ vec-index vec-left))
442 (bufcount (fblock.bufcount fblock)))
443 (declare (fixnum index bufcount))
444 (if (> index bufcount)
445 (setf (fblock.bufcount fblock) index))
446 (setf (fblock.bufidx fblock) index
447 (fblock.dirty fblock) t))
448 (incf address vec-left)
449 (incf offset vec-left)
450 (decf length vec-left))))))
451
452#+ppc-target
453(defun %fwrite-bytes (fblock address array offset length)
454 (declare (fixnum offset length))
455 #+:wood-fixnum-addresses (declare (fixnum address))
456 (unless (eql 8 (fblock.nbits-per-element fblock))
457 (error "%fwrite-bytes only implemented for 8-bit bytes"))
458 (unless (>= (length array) (the fixnum (+ offset length)))
459 (error "array too small"))
460 (ensure-byte-array array)
461 (let ((min-size (+ address length)))
462 #+:wood-fixnum-addresses (declare (fixnum min-size))
463 (when (> min-size (%fsize fblock))
464 (%fsize fblock min-size)))
465 (let ((bytes length)
466 (bufvec (fblock.bufvec fblock)))
467 (declare (fixnum bytes))
468 (loop
469 (when (<= length 0) (return bytes))
470 (wood::with-databases-locked
471 (%fpos fblock address)
472 (let* ((vec-index (fblock.bufidx fblock))
473 (vec-left (- (the fixnum (fblock.elements-per-buffer fblock))
474 vec-index)))
475 (declare (fixnum vec-index vec-left))
476 (if (> vec-left length) (setq vec-left length))
477 (%copy-ivector-to-ivector array offset bufvec vec-index vec-left)
478 (let ((index (+ vec-index vec-left))
479 (bufcount (fblock.bufcount fblock)))
480 (declare (fixnum index bufcount))
481 (if (> index bufcount)
482 (setf (fblock.bufcount fblock) index))
483 (setf (fblock.bufidx fblock) index
484 (fblock.dirty fblock) t))
485 (incf address vec-left)
486 (incf offset vec-left)
487 (decf length vec-left))))))
488
489(defun set-minimum-file-length (stream length)
490 (unless (>= (file-length stream) length)
491 (file-length stream length)))
492
493#|
494(setq s (open "temp.lisp" :direction :io :if-exists :overwrite))
495
496(defun r (address length)
497 (declare (special s))
498 (let ((v (make-string length :element-type 'base-character)))
499 (let ((real-length (stream-read-bytes s address v 0 length)))
500 (if (eql length real-length)
501 (values v length)
502 (let ((res (make-string real-length)))
503 (dotimes (i real-length)
504 (setf (aref res i) (aref v i)))
505 (values res real-length))))))
506
507(defun w (string address &optional
508 (offset 0) (length (- (length string) offset)))
509 (declare (special s))
510 (stream-write-bytes s address string offset length))
511
512|#
513;;; 1 3/10/94 bill 1.8d247
514;;; 2 10/04/94 bill 1.9d071
515;;; 3 11/03/94 Moon 1.9d086
516;;; 2 3/23/95 bill 1.11d010
517;;; 3 6/02/95 bill 1.11d040
518;;; 4 8/01/95 bill 1.11d065
Note: See TracBrowser for help on using the repository browser.