| 1 | ;;;-*- Mode: Lisp; Package: WOOD -*-
|
|---|
| 2 |
|
|---|
| 3 | ;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|---|
| 4 | ;;
|
|---|
| 5 | ;; disk-cache.lisp
|
|---|
| 6 | ;; Code to support a cached byte I/O stream.
|
|---|
| 7 | ;;
|
|---|
| 8 | ;; Portions Copyright © 2006 Clozure Associates and Anvita eReference (www.Anvita.info)
|
|---|
| 9 | ;; Copyright © 1996 Digitool, Inc.
|
|---|
| 10 | ;; Copyright © 1992-1995 Apple Computer, Inc.
|
|---|
| 11 | ;; All rights reserved.
|
|---|
| 12 | ;; Permission is given to use, copy, and modify this software provided
|
|---|
| 13 | ;; that Digitool is given credit in all derivative works.
|
|---|
| 14 | ;; This software is provided "as is". Digitool makes no warranty or
|
|---|
| 15 | ;; representation, either express or implied, with respect to this software,
|
|---|
| 16 | ;; its quality, accuracy, merchantability, or fitness for a particular
|
|---|
| 17 | ;; purpose.
|
|---|
| 18 | ;;
|
|---|
| 19 |
|
|---|
| 20 | ;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|---|
| 21 | ;;
|
|---|
| 22 | ;; Modification History
|
|---|
| 23 | ;;
|
|---|
| 24 | ;; 02/01/06 gz LispWorks port
|
|---|
| 25 | ;; ------------- 0.96
|
|---|
| 26 | ;; ------------- 0.95
|
|---|
| 27 | ;; ------------- 0.94
|
|---|
| 28 | ;; 03/27/96 bill Dylan changes add a :read-only-p keyword to open-disk-cache
|
|---|
| 29 | ;; ------------- 0.93
|
|---|
| 30 | ;; 05/31/95 bill Shared swapping space:
|
|---|
| 31 | ;; Move the page-size, max-pages, pages & locked-pages slots from the
|
|---|
| 32 | ;; disk-cache structure to the new shared-buffer structure. disk-cache
|
|---|
| 33 | ;; gets a new shared-buffer slot to hold a shared-buffer instance.
|
|---|
| 34 | ;; cons-disk-page works with a NIL disk-cache arg.
|
|---|
| 35 | ;; print-disk-page works if disk-page-stream is NIL.
|
|---|
| 36 | ;; New shared-buffer-pool structure.
|
|---|
| 37 | ;; New get-shared-buffer function, gets or allocates a shared-buffer of
|
|---|
| 38 | ;; a particular page size from a shared-buffer-pool instance.
|
|---|
| 39 | ;; open-disk-cache takes new shared-buffer & shared-buffer-pool keyword
|
|---|
| 40 | ;; args. If shared-buffer-pool is specified, uses get-shared-buffer
|
|---|
| 41 | ;; to get a shared-buffer. Otherwise, if shared-buffer is specified, uses
|
|---|
| 42 | ;; it. Otherwise, conses up a new shared-buffer with the given page-size
|
|---|
| 43 | ;; & swapping-space.
|
|---|
| 44 | ;; close-disk-cache call remove-disk-cache-from-shared-buffer, a new function
|
|---|
| 45 | ;; that removes all references to a disk-cache from the disk-page's in a shared-buffer.
|
|---|
| 46 | ;; add-disk-pages adds the new pages to the disk-cache-shared-buffer.
|
|---|
| 47 | ;; read-disk-page takes a new disk-cache argument and uses it to initialize the
|
|---|
| 48 | ;; disk-page-disk-cache & disk-page-stream slots.
|
|---|
| 49 | ;; flush-disk-page works if disk-page-disk-cache is NIL.
|
|---|
| 50 | ;; get-disk-page, lock-page, unlock-page updated for using the disk-cache-shared-buffer.
|
|---|
| 51 | ;; extend-disk-cache no longer calls add-disk-pages. It lets get-disk-page do so.
|
|---|
| 52 | ;; 05/25/95 Moon New constant: $disk-page-flags_touched-bit, set when a page is
|
|---|
| 53 | ;; referenced.
|
|---|
| 54 | ;; New functions, disk-page-touched? & (setf disk-page-touched?), to access
|
|---|
| 55 | ;; the $disk-page-flags_touched-bit.
|
|---|
| 56 | ;; get-disk-page now uses a 1-bit clock algorithm instead of
|
|---|
| 57 | ;; least-recently-swapped to determine which page to swap out.
|
|---|
| 58 | ;; 05/25/95 bill *default-page-size* moves here from "persistent-heap.lisp".
|
|---|
| 59 | ;; New parameter, *default-swapping-space*, is the default number
|
|---|
| 60 | ;; of bytes to use for swapping space.
|
|---|
| 61 | ;; New parameter, *big-io-buffers*, true if cl:open takes an
|
|---|
| 62 | ;; :elements-per-buffer keyword arg.
|
|---|
| 63 | ;; open-disk-cache defaults its page-size arg to
|
|---|
| 64 | ;; *default-page-size* instead of 512. It errors if the page size
|
|---|
| 65 | ;; is not at least 512. Doesn't pass an :external-format keyword
|
|---|
| 66 | ;; arg to open unless one was passed in. If *big-io-buffers* is
|
|---|
| 67 | ;; true, passes the page-size as the :elements-per-buffer keyword
|
|---|
| 68 | ;; arg to open.
|
|---|
| 69 | ;; extend-disk-cache takes a new, optional, extend-file? arg. If
|
|---|
| 70 | ;; true, calls set-minimum-file-length to extend the length of the file.
|
|---|
| 71 | ;; ------------- 0.9
|
|---|
| 72 | ;; 11/17/95 bill poor man's transactions.
|
|---|
| 73 | ;; open-disk-cache takes an :initial-transaction-p keyword.
|
|---|
| 74 | ;; If nil (NOT the default), errors on any disk writes that
|
|---|
| 75 | ;; happen outside of a start-disk-cache-transaction/commit-disk-cache-transaction
|
|---|
| 76 | ;; pair.
|
|---|
| 77 | ;; 11/03/94 ows open-disk-cache takes a mac-file-creator keyword,
|
|---|
| 78 | ;; which it passes on to open.
|
|---|
| 79 | ;; 10/28/94 Moon Change without-interrupts to with-databases-locked.
|
|---|
| 80 | ;; Remove interlocking from get-disk-page; callers must.
|
|---|
| 81 | ;; Add comment "Must be called inside with-databases-locked"
|
|---|
| 82 | ;; to with-locked-page.
|
|---|
| 83 | ;; 09/21/94 bill without-interrupts as necessary for interlocking
|
|---|
| 84 | ;; 07/26/94 bill get-disk-page allocates a new page if all the pages
|
|---|
| 85 | ;; are locked. Hence, it can't fail unless out of memory.
|
|---|
| 86 | ;; ------------- 0.8
|
|---|
| 87 | ;; 03/27/93 bill with-open-disk-cache
|
|---|
| 88 | ;; ------------- 0.6
|
|---|
| 89 | ;; ------------- 0.5
|
|---|
| 90 | ;; 07/09/92 bill Don't extend the file until flushing a page requires it.
|
|---|
| 91 | ;; Keep a lock count, not just a bit.
|
|---|
| 92 | ;; 03/05/92 bill New file
|
|---|
| 93 | ;;
|
|---|
| 94 |
|
|---|
| 95 | ;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|---|
| 96 | ;;
|
|---|
| 97 | ;; To do:
|
|---|
| 98 | ;;
|
|---|
| 99 | ;; with-databases-locked in just the right places.
|
|---|
| 100 | ;; Add a journaling option.
|
|---|
| 101 | ;; Multi-user support.
|
|---|
| 102 | ;;
|
|---|
| 103 |
|
|---|
| 104 | (in-package :wood)
|
|---|
| 105 |
|
|---|
| 106 | (export '(open-disk-cache close-disk-cache disk-cache-size
|
|---|
| 107 | get-disk-page mark-page-modified extend-disk-cache))
|
|---|
| 108 |
|
|---|
| 109 | ;;;;;;;;;;;;;
|
|---|
| 110 | ;;
|
|---|
| 111 | ;; (open-disk-cache filename &key shared-p page-size max-pages
|
|---|
| 112 | ;; if-exists if-does-not-exist)
|
|---|
| 113 | ;;
|
|---|
| 114 | ;; filename string or pathname
|
|---|
| 115 | ;; shared-p boolean. Open for shared I/O if specified and true.
|
|---|
| 116 | ;; page-size default: 512
|
|---|
| 117 | ;; max-pages default: 200
|
|---|
| 118 | ;; if-exists nil, :error, :supersede, or :overwrite.
|
|---|
| 119 | ;; Default: :overwrite
|
|---|
| 120 | ;; if-does-not-exist Same as for OPEN. default: :error.
|
|---|
| 121 | ;;
|
|---|
| 122 | ;; returns one value, a DISK-CACHE structure
|
|---|
| 123 |
|
|---|
| 124 | ;;;;;;;;;;;;;
|
|---|
| 125 | ;;
|
|---|
| 126 | ;; (close-disk-cache disk-cache)
|
|---|
| 127 | ;;
|
|---|
| 128 | ;; Flushes dirty pages and closes the stream for the given disk-cache.
|
|---|
| 129 |
|
|---|
| 130 | ;;;;;;;;;;;;;
|
|---|
| 131 | ;;
|
|---|
| 132 | ;; (disk-cache-size disk-cache)
|
|---|
| 133 | ;;
|
|---|
| 134 | ;; Return the number of bytes in the file
|
|---|
| 135 |
|
|---|
| 136 | ;;;;;;;;;;;;;
|
|---|
| 137 | ;;
|
|---|
| 138 | ;; (get-disk-page disk-cache address &optional modify-p)
|
|---|
| 139 | ;;
|
|---|
| 140 | ;; disk-cache DISK-CACHE structure, as returned from OPEN-DISK-CACHE.
|
|---|
| 141 | ;; address fixnum. the address from/to you wish to I/O
|
|---|
| 142 | ;; modify-p boolean. True if you plan to write. Default: nil.
|
|---|
| 143 | ;;
|
|---|
| 144 | ;; returns four values:
|
|---|
| 145 | ;; 1) array an array of type (array (signed-byte 8)) containing the byte
|
|---|
| 146 | ;; at address
|
|---|
| 147 | ;; 2) offset fixnum. The offset in the array for the byte at address.
|
|---|
| 148 | ;; 3) length fixnum. The number of bytes of valid data in array at offset.
|
|---|
| 149 | ;; Will be (- page-size (mod address page-size))
|
|---|
| 150 | ;; unless the page is the last one or later.
|
|---|
| 151 | ;; 4) page a disk-page structure that can be passed to mark-page-modified
|
|---|
| 152 |
|
|---|
| 153 | ;;;;;;;;;;;;;
|
|---|
| 154 | ;;
|
|---|
| 155 | ;; (mark-page-modified disk-page)
|
|---|
| 156 | ;;
|
|---|
| 157 | ;; disk-page DISK-PAGE structure as returned in the fourth value from
|
|---|
| 158 | ;; GET-DISK-PAGE.
|
|---|
| 159 | ;;
|
|---|
| 160 | ;; Sometimes you don't know in advance whether you'll modify a page.
|
|---|
| 161 | ;;
|
|---|
| 162 | ;; Returns true if the page was not already marked as modified, NIL
|
|---|
| 163 | ;; otherwise.
|
|---|
| 164 |
|
|---|
| 165 | ;;;;;;;;;;;;;
|
|---|
| 166 | ;;
|
|---|
| 167 | ;; (extend-disk-cache disk-cache new-size)
|
|---|
| 168 | ;;
|
|---|
| 169 | ;; new-size the new size of the file in bytes.
|
|---|
| 170 | ;; If smaller than the current size, this is a NOP.
|
|---|
| 171 |
|
|---|
| 172 |
|
|---|
| 173 | (defstruct (shared-buffer (:constructor cons-shared-buffer (page-size page-count max-pages pages))
|
|---|
| 174 | (:print-function print-shared-buffer))
|
|---|
| 175 | (page-size 512) ; size of a disk-page in bytes
|
|---|
| 176 | page-count ; number of disk pages
|
|---|
| 177 | max-pages ; user's maximum
|
|---|
| 178 | pages ; head of the disk-page chain
|
|---|
| 179 | locked-pages ; head of locked pages chain
|
|---|
| 180 | users ; a list of disk-cache instances
|
|---|
| 181 | )
|
|---|
| 182 |
|
|---|
| 183 | (defun print-shared-buffer (shared-buffer stream level)
|
|---|
| 184 | (declare (ignore level))
|
|---|
| 185 | (print-unreadable-object (shared-buffer stream :type t :identity t)
|
|---|
| 186 | (format stream "~s ~s/~s"
|
|---|
| 187 | (shared-buffer-page-size shared-buffer)
|
|---|
| 188 | (shared-buffer-page-count shared-buffer)
|
|---|
| 189 | (shared-buffer-max-pages shared-buffer))))
|
|---|
| 190 |
|
|---|
| 191 | (defparameter *default-page-size* 512)
|
|---|
| 192 | (defparameter *default-swapping-space* (* 100 1024))
|
|---|
| 193 |
|
|---|
| 194 | (defun make-shared-buffer (&key (page-size *default-page-size*)
|
|---|
| 195 | (swapping-space *default-swapping-space*)
|
|---|
| 196 | max-pages)
|
|---|
| 197 | (if (null max-pages)
|
|---|
| 198 | (setq max-pages (ceiling swapping-space page-size))
|
|---|
| 199 | (setq swapping-space (* max-pages page-size)))
|
|---|
| 200 | (unless (>= page-size 512)
|
|---|
| 201 | (error "Page size must be at least 512"))
|
|---|
| 202 | (unless (eql page-size (expt 2 (1- (integer-length page-size))))
|
|---|
| 203 | (error "page-size must be a power of 2"))
|
|---|
| 204 | (cons-shared-buffer
|
|---|
| 205 | page-size 0 max-pages nil))
|
|---|
| 206 |
|
|---|
| 207 | (defstruct (shared-buffer-pool (:constructor cons-shared-buffer-pool
|
|---|
| 208 | (swapping-space page-size auxiliary-swapping-space)))
|
|---|
| 209 | (swapping-space *default-swapping-space*)
|
|---|
| 210 | (page-size *default-page-size*)
|
|---|
| 211 | (auxiliary-swapping-space *default-swapping-space*)
|
|---|
| 212 | buffers)
|
|---|
| 213 |
|
|---|
| 214 | (defun make-shared-buffer-pool (&key (swapping-space *default-swapping-space*)
|
|---|
| 215 | (page-size *default-page-size*)
|
|---|
| 216 | (auxiliary-swapping-space
|
|---|
| 217 | (min swapping-space *default-swapping-space*)))
|
|---|
| 218 | (cons-shared-buffer-pool swapping-space page-size auxiliary-swapping-space))
|
|---|
| 219 |
|
|---|
| 220 | (defun get-shared-buffer (pool page-size)
|
|---|
| 221 | (or (find page-size (shared-buffer-pool-buffers pool) :key 'shared-buffer-page-size)
|
|---|
| 222 | (let* ((swapping-space (if (eql page-size (shared-buffer-pool-page-size pool))
|
|---|
| 223 | (shared-buffer-pool-swapping-space pool)
|
|---|
| 224 | (shared-buffer-pool-auxiliary-swapping-space pool)))
|
|---|
| 225 | (buffer (make-shared-buffer :page-size page-size
|
|---|
| 226 | :swapping-space swapping-space)))
|
|---|
| 227 | (push buffer (shared-buffer-pool-buffers pool))
|
|---|
| 228 | buffer)))
|
|---|
| 229 |
|
|---|
| 230 | (defstruct (disk-cache (:print-function print-disk-cache))
|
|---|
| 231 | stream ; a stream to a file
|
|---|
| 232 | size ; the length of the file
|
|---|
| 233 | (page-size 512) ; size of a disk-page in bytes
|
|---|
| 234 | (mask -512) ; address mask
|
|---|
| 235 | shared-buffer ; a shared-buffer instance
|
|---|
| 236 | page-hash ; page-address -> disk-page structure
|
|---|
| 237 | dirty-pages ; head of the dirty page chain
|
|---|
| 238 | log ; a LOG structure: see "recovery.lisp"
|
|---|
| 239 | write-hook ; hook to call when a page is written to disk
|
|---|
| 240 | file-eof ; current EOF on disk
|
|---|
| 241 | transaction ; current transaction (just a counter for now)
|
|---|
| 242 | )
|
|---|
| 243 |
|
|---|
| 244 | (defun print-disk-cache (disk-cache stream level)
|
|---|
| 245 | (declare (ignore level))
|
|---|
| 246 | (print-unreadable-object (disk-cache stream :type t :identity t)
|
|---|
| 247 | (prin1 (pathname (disk-cache-stream disk-cache)) stream)))
|
|---|
| 248 |
|
|---|
| 249 | (defun disk-cache-read-only-p (disk-cache)
|
|---|
| 250 | (eql (stream-direction (disk-cache-stream disk-cache))
|
|---|
| 251 | :input))
|
|---|
| 252 |
|
|---|
| 253 | (defstruct (disk-page (:print-function print-disk-page) (:constructor cons-disk-page))
|
|---|
| 254 | disk-cache ; back pointer
|
|---|
| 255 | stream ; the stream (did you guess?)
|
|---|
| 256 | address ; file address of base of this page
|
|---|
| 257 | (flags 0) ; bit 0 = dirty, bit 1 = touched
|
|---|
| 258 | (size 0) ; actual size (smaller for last page)
|
|---|
| 259 | next ; next disk-page in the chain
|
|---|
| 260 | prev ; previous disk-page in the chain
|
|---|
| 261 | next-dirty ; next dirty page
|
|---|
| 262 | prev-dirty ; previous dirty page
|
|---|
| 263 | data ; an (unsigned-byte 8) array
|
|---|
| 264 | (lock-count 0)) ; non-zero means locked that many times.
|
|---|
| 265 |
|
|---|
| 266 | (defconstant $disk-page-flags_dirty-bit 0)
|
|---|
| 267 | (defconstant $disk-page-flags_touched-bit 1)
|
|---|
| 268 |
|
|---|
| 269 | (defun disk-page-dirty (disk-page)
|
|---|
| 270 | (logbitp $disk-page-flags_dirty-bit
|
|---|
| 271 | (the fixnum (disk-page-flags disk-page))))
|
|---|
| 272 |
|
|---|
| 273 | (defun (setf disk-page-dirty) (value disk-page)
|
|---|
| 274 | (with-databases-locked
|
|---|
| 275 | (setf (disk-page-flags disk-page)
|
|---|
| 276 | (if value
|
|---|
| 277 | (%bitset $disk-page-flags_dirty-bit (disk-page-flags disk-page))
|
|---|
| 278 | (%bitclr $disk-page-flags_dirty-bit (disk-page-flags disk-page))))
|
|---|
| 279 | (not (null value))))
|
|---|
| 280 |
|
|---|
| 281 | (declaim (inline disk-page-touched? (setf disk-page-touched?)))
|
|---|
| 282 |
|
|---|
| 283 | (defun disk-page-touched? (disk-page)
|
|---|
| 284 | (declare (optimize (speed 3) (safety 0)))
|
|---|
| 285 | (logbitp $disk-page-flags_touched-bit
|
|---|
| 286 | (the fixnum (disk-page-flags disk-page))))
|
|---|
| 287 |
|
|---|
| 288 | ;; Must be called inside with-databases-locked
|
|---|
| 289 | (defun (setf disk-page-touched?) (value disk-page)
|
|---|
| 290 | (declare (optimize (speed 3) (safety 0)))
|
|---|
| 291 | (setf (disk-page-flags disk-page)
|
|---|
| 292 | (if value
|
|---|
| 293 | (%bitset $disk-page-flags_touched-bit (the fixnum (disk-page-flags disk-page)))
|
|---|
| 294 | (%bitclr $disk-page-flags_touched-bit (the fixnum (disk-page-flags disk-page)))))
|
|---|
| 295 | value)
|
|---|
| 296 |
|
|---|
| 297 | (defun disk-page-locked (disk-page)
|
|---|
| 298 | (let ((count (disk-page-lock-count disk-page)))
|
|---|
| 299 | (unless (eql 0 count)
|
|---|
| 300 | count)))
|
|---|
| 301 |
|
|---|
| 302 | (defun print-disk-page (disk-page stream level)
|
|---|
| 303 | (declare (ignore level))
|
|---|
| 304 | (let* ((disk-page-stream (disk-page-stream disk-page))
|
|---|
| 305 | (path (if disk-page-stream (pathname disk-page-stream) :no-file)))
|
|---|
| 306 | (print-unreadable-object (disk-page stream :type t :identity t)
|
|---|
| 307 | (format stream "~s~@{ ~s~}"
|
|---|
| 308 | (disk-page-address disk-page)
|
|---|
| 309 | (disk-page-size disk-page)
|
|---|
| 310 | (disk-page-dirty disk-page)
|
|---|
| 311 | path))))
|
|---|
| 312 |
|
|---|
| 313 | (defun make-disk-page (disk-cache size)
|
|---|
| 314 | (cons-disk-page :disk-cache disk-cache
|
|---|
| 315 | :stream (and disk-cache (disk-cache-stream disk-cache))
|
|---|
| 316 | :data (make-array size :element-type '(unsigned-byte 8))))
|
|---|
| 317 |
|
|---|
| 318 | (defvar *open-disk-caches* nil)
|
|---|
| 319 |
|
|---|
| 320 | ; New code
|
|---|
| 321 | (defparameter *big-io-buffers*
|
|---|
| 322 | #+ccl (not (null (find :elements-per-buffer (ccl::lfun-keyvect #'open))))
|
|---|
| 323 | #+LispWorks nil)
|
|---|
| 324 |
|
|---|
| 325 | (defun open-disk-cache (filename &key shared-p read-only-p
|
|---|
| 326 | (page-size *default-page-size* page-size-p)
|
|---|
| 327 | max-pages
|
|---|
| 328 | (swapping-space *default-swapping-space*)
|
|---|
| 329 | shared-buffer
|
|---|
| 330 | shared-buffer-pool
|
|---|
| 331 | (if-exists :overwrite)
|
|---|
| 332 | (if-does-not-exist :error)
|
|---|
| 333 | write-hook
|
|---|
| 334 | (initial-transaction-p t))
|
|---|
| 335 | (when shared-buffer-pool
|
|---|
| 336 | (setq shared-buffer (get-shared-buffer shared-buffer-pool page-size)))
|
|---|
| 337 | (if shared-buffer
|
|---|
| 338 | (let ((shared-buffer-page-size (shared-buffer-page-size shared-buffer)))
|
|---|
| 339 | (when (and page-size-p (not (eql page-size shared-buffer-page-size)))
|
|---|
| 340 | (error "Page size different from shared-buffer page size"))
|
|---|
| 341 | (setq page-size shared-buffer-page-size))
|
|---|
| 342 | (setq shared-buffer
|
|---|
| 343 | (make-shared-buffer :page-size page-size
|
|---|
| 344 | :max-pages max-pages
|
|---|
| 345 | :swapping-space swapping-space)))
|
|---|
| 346 | (setq max-pages (shared-buffer-max-pages shared-buffer))
|
|---|
| 347 | (let ((mask (lognot (1- (expt 2 (1- (integer-length page-size)))))))
|
|---|
| 348 | (let* ((stream (open filename
|
|---|
| 349 | :direction (if read-only-p :input (if shared-p :shared :io))
|
|---|
| 350 | :if-exists if-exists
|
|---|
| 351 | :if-does-not-exist if-does-not-exist
|
|---|
| 352 | :element-type '(unsigned-byte 8)
|
|---|
| 353 | #+ccl :sharing #+ccl :external)))
|
|---|
| 354 | (when stream
|
|---|
| 355 | (let* ((size (file-length stream))
|
|---|
| 356 | (disk-cache (make-disk-cache :stream stream
|
|---|
| 357 | :size size
|
|---|
| 358 | :file-eof size
|
|---|
| 359 | :page-size page-size
|
|---|
| 360 | :mask mask
|
|---|
| 361 | :shared-buffer shared-buffer
|
|---|
| 362 | ;remove :max-pages max-pages
|
|---|
| 363 | :write-hook write-hook)))
|
|---|
| 364 | #+wood-fixnum-addresses
|
|---|
| 365 | (unless (fixnump size)
|
|---|
| 366 | (error "File ~s is too large for this compilation of Wood~%~
|
|---|
| 367 | Recompile Wood with :wood-fixnum-addresses removed from *features*"
|
|---|
| 368 | filename))
|
|---|
| 369 | (setf (disk-cache-page-hash disk-cache)
|
|---|
| 370 | (make-disk-page-hash :size (min (ceiling size page-size) max-pages)
|
|---|
| 371 | :page-size page-size))
|
|---|
| 372 | (when initial-transaction-p
|
|---|
| 373 | (setf (disk-cache-transaction disk-cache) 1))
|
|---|
| 374 | (push disk-cache *open-disk-caches*)
|
|---|
| 375 | (push disk-cache (shared-buffer-users shared-buffer))
|
|---|
| 376 | disk-cache)))))
|
|---|
| 377 |
|
|---|
| 378 | (defmacro with-open-disk-cache ((disk-cache filename &rest options) &body body)
|
|---|
| 379 | `(let ((,disk-cache (open-disk-cache ,filename ,@options)))
|
|---|
| 380 | (unwind-protect
|
|---|
| 381 | (progn ,@body)
|
|---|
| 382 | (close-disk-cache ,disk-cache))))
|
|---|
| 383 |
|
|---|
| 384 | (defun make-linked-disk-pages (disk-cache page-size page-count &optional file-length)
|
|---|
| 385 | (when file-length
|
|---|
| 386 | (setq page-count (max 1 (min page-count
|
|---|
| 387 | (floor (+ file-length page-size -1)
|
|---|
| 388 | page-size)))))
|
|---|
| 389 | (let (page last-page)
|
|---|
| 390 | (dotimes (i page-count)
|
|---|
| 391 | (let ((new-page (make-disk-page disk-cache page-size)))
|
|---|
| 392 | (setf (disk-page-next new-page) page)
|
|---|
| 393 | (if page
|
|---|
| 394 | (setf (disk-page-prev page) new-page)
|
|---|
| 395 | (setq last-page new-page))
|
|---|
| 396 | (setq page new-page)))
|
|---|
| 397 | (setf (disk-page-next last-page) page
|
|---|
| 398 | (disk-page-prev page) last-page)
|
|---|
| 399 | (values page page-count)))
|
|---|
| 400 |
|
|---|
| 401 | (defun add-disk-pages (disk-cache count)
|
|---|
| 402 | (let* ((shared-buffer (disk-cache-shared-buffer disk-cache))
|
|---|
| 403 | (old-first-page (shared-buffer-pages shared-buffer))
|
|---|
| 404 | (new-first-page (make-linked-disk-pages
|
|---|
| 405 | disk-cache
|
|---|
| 406 | (disk-cache-page-size disk-cache)
|
|---|
| 407 | count)))
|
|---|
| 408 | (when old-first-page
|
|---|
| 409 | (let ((old-last-page (disk-page-prev old-first-page))
|
|---|
| 410 | (new-last-page (disk-page-prev new-first-page)))
|
|---|
| 411 | (setf (disk-page-next new-last-page) old-first-page
|
|---|
| 412 | (disk-page-prev old-first-page) new-last-page
|
|---|
| 413 | (disk-page-next old-last-page) new-first-page
|
|---|
| 414 | (disk-page-prev new-first-page) old-last-page)))
|
|---|
| 415 | (setf (shared-buffer-pages shared-buffer) new-first-page)
|
|---|
| 416 | (incf (shared-buffer-page-count shared-buffer) count)))
|
|---|
| 417 |
|
|---|
| 418 | (defun close-disk-cache (disk-cache)
|
|---|
| 419 | (flush-disk-cache disk-cache) ; work interruptably
|
|---|
| 420 | (with-databases-locked
|
|---|
| 421 | (flush-disk-cache disk-cache) ; make sure
|
|---|
| 422 | (remove-disk-cache-from-shared-buffer (disk-cache-shared-buffer disk-cache) disk-cache)
|
|---|
| 423 | (close (disk-cache-stream disk-cache))
|
|---|
| 424 | (setq *open-disk-caches* (delq disk-cache *open-disk-caches* 1))
|
|---|
| 425 | (setf (disk-cache-page-hash disk-cache) nil)))
|
|---|
| 426 |
|
|---|
| 427 | (defun remove-disk-cache-from-shared-buffer (shared-buffer disk-cache)
|
|---|
| 428 | (if (null (setf (shared-buffer-users shared-buffer)
|
|---|
| 429 | (delete disk-cache (shared-buffer-users shared-buffer) :test 'eq)))
|
|---|
| 430 | (setf (shared-buffer-page-count shared-buffer) 0
|
|---|
| 431 | (shared-buffer-pages shared-buffer) nil
|
|---|
| 432 | (shared-buffer-locked-pages shared-buffer) nil)
|
|---|
| 433 | (let ((page-hash (disk-cache-page-hash disk-cache)))
|
|---|
| 434 | (when page-hash
|
|---|
| 435 | (let* ((locked-pages nil)
|
|---|
| 436 | (mapper #'(lambda (address page)
|
|---|
| 437 | (unless (eq disk-cache (disk-page-disk-cache page))
|
|---|
| 438 | (error "page in disk-page-hash doesn't belong to disk-cache"))
|
|---|
| 439 | (unless (eql 0 (disk-page-lock-count page))
|
|---|
| 440 | (push page locked-pages)
|
|---|
| 441 | (loop
|
|---|
| 442 | (unless (unlock-page page) (return))))
|
|---|
| 443 | (disk-page-remhash address page-hash)
|
|---|
| 444 | (setf (disk-page-disk-cache page) nil
|
|---|
| 445 | (disk-page-address page) nil))))
|
|---|
| 446 | (declare (dynamic-extent mapper))
|
|---|
| 447 | (disk-page-maphash mapper page-hash)
|
|---|
| 448 | (when locked-pages
|
|---|
| 449 | (cerror "Continue" "Locked pages: ~s" locked-pages)))))))
|
|---|
| 450 |
|
|---|
| 451 | (defun flush-disk-cache (disk-cache)
|
|---|
| 452 | (unless (disk-cache-read-only-p disk-cache)
|
|---|
| 453 | (loop
|
|---|
| 454 | (with-databases-locked
|
|---|
| 455 | (let* ((page (disk-cache-dirty-pages disk-cache)))
|
|---|
| 456 | (unless page (return))
|
|---|
| 457 | (flush-disk-page page))))
|
|---|
| 458 | (with-databases-locked
|
|---|
| 459 | (finish-output (disk-cache-stream disk-cache)))))
|
|---|
| 460 |
|
|---|
| 461 | (defun read-disk-page (disk-cache disk-page address)
|
|---|
| 462 | (flush-disk-page disk-page)
|
|---|
| 463 | (when (> (the fixnum (disk-page-lock-count disk-page)) 0)
|
|---|
| 464 | (error "Attempt to read locked page"))
|
|---|
| 465 | (setf (disk-page-disk-cache disk-page) disk-cache
|
|---|
| 466 | (disk-page-stream disk-page) (disk-cache-stream disk-cache)
|
|---|
| 467 | (disk-page-address disk-page) address)
|
|---|
| 468 | (let* ((size (disk-cache-size disk-cache))
|
|---|
| 469 | (file-eof (disk-cache-file-eof disk-cache))
|
|---|
| 470 | (page-size (min (disk-cache-page-size disk-cache) (- size address))))
|
|---|
| 471 | (when (> file-eof address)
|
|---|
| 472 | (stream-read-bytes (disk-page-stream disk-page)
|
|---|
| 473 | address
|
|---|
| 474 | (disk-page-data disk-page)
|
|---|
| 475 | 0
|
|---|
| 476 | page-size))
|
|---|
| 477 | (setf (disk-page-size disk-page) page-size)))
|
|---|
| 478 |
|
|---|
| 479 | (defun flush-disk-page (disk-page)
|
|---|
| 480 | (when (disk-page-dirty disk-page)
|
|---|
| 481 | (let* ((disk-cache (disk-page-disk-cache disk-page))
|
|---|
| 482 | (write-hook (and disk-cache (disk-cache-write-hook disk-cache))))
|
|---|
| 483 | (when write-hook
|
|---|
| 484 | (funcall write-hook disk-page))
|
|---|
| 485 | (when (or (not write-hook) (disk-page-dirty disk-page)) ; write-hook may have flushed this page
|
|---|
| 486 | (let* ((address (disk-page-address disk-page))
|
|---|
| 487 | (size (disk-page-size disk-page))
|
|---|
| 488 | (end-of-page (+ address size))
|
|---|
| 489 | (stream (disk-page-stream disk-page)))
|
|---|
| 490 | (when (> end-of-page (disk-cache-file-eof disk-cache))
|
|---|
| 491 | (set-minimum-file-length stream end-of-page)
|
|---|
| 492 | (setf (disk-cache-file-eof disk-cache) end-of-page))
|
|---|
| 493 | (stream-write-bytes stream
|
|---|
| 494 | address
|
|---|
| 495 | (disk-page-data disk-page)
|
|---|
| 496 | 0
|
|---|
| 497 | size))
|
|---|
| 498 | (let* ((next (disk-page-next-dirty disk-page))
|
|---|
| 499 | (prev (disk-page-prev-dirty disk-page)))
|
|---|
| 500 | (if (eq next disk-page)
|
|---|
| 501 | (setf next nil)
|
|---|
| 502 | (setf (disk-page-next-dirty prev) next
|
|---|
| 503 | (disk-page-prev-dirty next) prev))
|
|---|
| 504 | (setf (disk-page-next-dirty disk-page) nil
|
|---|
| 505 | (disk-page-prev-dirty disk-page) nil)
|
|---|
| 506 | (when (eq disk-page (disk-cache-dirty-pages disk-cache))
|
|---|
| 507 | (setf (disk-cache-dirty-pages disk-cache) next)))
|
|---|
| 508 | (setf (disk-page-dirty disk-page) nil)))))
|
|---|
| 509 |
|
|---|
| 510 | ; The caller must be inside of with-databases-locked, or the buffer returned
|
|---|
| 511 | ; could be yanked out from under the caller.
|
|---|
| 512 | ; 1-bit-clock page replacement algorithm.
|
|---|
| 513 | (defun get-disk-page (disk-cache address &optional modify-p)
|
|---|
| 514 | (declare (optimize (speed 3)(safety 0)))
|
|---|
| 515 | #+wood-fixnum-addresses
|
|---|
| 516 | (unless (fixnump address)
|
|---|
| 517 | (error "Address is not a fixnum"))
|
|---|
| 518 | (locally
|
|---|
| 519 | #+wood-fixnum-addresses (declare (fixnum address))
|
|---|
| 520 | (let* ((hash (disk-cache-page-hash disk-cache))
|
|---|
| 521 | (base-address (logand address (the fixnum (disk-cache-mask disk-cache))))
|
|---|
| 522 | (page (disk-page-gethash base-address hash))
|
|---|
| 523 | (offset (- address base-address))
|
|---|
| 524 | (size 0))
|
|---|
| 525 | #+wood-fixnum-addresses (declare (fixnum base-address))
|
|---|
| 526 | (declare (fixnum offset size))
|
|---|
| 527 | (block get-the-page
|
|---|
| 528 | (if page
|
|---|
| 529 | (setq size (disk-page-size page))
|
|---|
| 530 | (let ((max-size (disk-cache-size disk-cache))
|
|---|
| 531 | (shared-buffer (disk-cache-shared-buffer disk-cache)))
|
|---|
| 532 | #+wood-fixnum-addresses (declare (fixnum max-size))
|
|---|
| 533 | (when (>= address max-size)
|
|---|
| 534 | (if (> address max-size)
|
|---|
| 535 | (error "~s > size of ~s" address disk-cache)
|
|---|
| 536 | (when (eql address base-address)
|
|---|
| 537 | ; If the address is the beginning of a page, and the end of
|
|---|
| 538 | ; the file, return a pointer off the end of the last page.
|
|---|
| 539 | (setq base-address (logand (1- address) (disk-cache-mask disk-cache))
|
|---|
| 540 | offset (- address base-address)
|
|---|
| 541 | page (disk-page-gethash base-address hash))
|
|---|
| 542 | (when page
|
|---|
| 543 | (setq size (disk-page-size page))
|
|---|
| 544 | (return-from get-the-page)))))
|
|---|
| 545 | ; Keep adding pages till we max out.
|
|---|
| 546 | (when (>= (shared-buffer-page-count shared-buffer)
|
|---|
| 547 | (shared-buffer-max-pages shared-buffer))
|
|---|
| 548 | (setq page (shared-buffer-pages shared-buffer)))
|
|---|
| 549 | (unless page
|
|---|
| 550 | (add-disk-pages disk-cache 1)
|
|---|
| 551 | (setq page (shared-buffer-pages shared-buffer)))
|
|---|
| 552 | ;; Here's the page replacement algorithm, one-bit clock algorithm
|
|---|
| 553 | (loop ; while disk-page-touched?
|
|---|
| 554 | (unless (disk-page-touched? page) (return))
|
|---|
| 555 | (setf (disk-page-touched? page) nil)
|
|---|
| 556 | (setq page (disk-page-next page)))
|
|---|
| 557 | (setf (shared-buffer-pages shared-buffer) (disk-page-next page))
|
|---|
| 558 | (let ((old-address (disk-page-address page)))
|
|---|
| 559 | (when old-address
|
|---|
| 560 | (disk-page-remhash
|
|---|
| 561 | old-address (disk-cache-page-hash (disk-page-disk-cache page)))))
|
|---|
| 562 | (setq size (read-disk-page disk-cache page base-address))
|
|---|
| 563 | (setf (disk-page-gethash base-address hash) page))))
|
|---|
| 564 | (setf (disk-page-touched? page) t)
|
|---|
| 565 | (when modify-p (mark-page-modified page))
|
|---|
| 566 | (values (disk-page-data page)
|
|---|
| 567 | offset
|
|---|
| 568 | (- size offset)
|
|---|
| 569 | page))))
|
|---|
| 570 |
|
|---|
| 571 | (defvar *error-on-non-transaction-writes* t)
|
|---|
| 572 |
|
|---|
| 573 | ; The caller must be inside of with-databases-locked
|
|---|
| 574 | (defun mark-page-modified (disk-page)
|
|---|
| 575 | (declare (optimize (speed 3) (safety 0)))
|
|---|
| 576 | (unless (disk-page-dirty disk-page)
|
|---|
| 577 | ; Link this disk-page as the last one in the dirty cache.
|
|---|
| 578 | (let* ((disk-cache (disk-page-disk-cache disk-page))
|
|---|
| 579 | (dirty-pages (disk-cache-dirty-pages disk-cache)))
|
|---|
| 580 | (when (disk-cache-read-only-p disk-cache)
|
|---|
| 581 | (error "Modifying a read-only database"))
|
|---|
| 582 | (when (and *error-on-non-transaction-writes*
|
|---|
| 583 | (null (disk-cache-transaction disk-cache)))
|
|---|
| 584 | (restart-case
|
|---|
| 585 | (cerror "Let this write proceed"
|
|---|
| 586 | "Write outside of transaction to ~s"
|
|---|
| 587 | (or (disk-cache-pheap disk-cache) disk-cache))
|
|---|
| 588 | (dont-repeat ()
|
|---|
| 589 | :report (lambda (s)
|
|---|
| 590 | (format s "Let this write proceed and don't warn in the future."))
|
|---|
| 591 | (setq *error-on-non-transaction-writes* nil))))
|
|---|
| 592 | (if dirty-pages
|
|---|
| 593 | (let ((prev-dirty (disk-page-prev-dirty dirty-pages)))
|
|---|
| 594 | (setf (disk-page-next-dirty prev-dirty) disk-page
|
|---|
| 595 | (disk-page-prev-dirty disk-page) prev-dirty
|
|---|
| 596 | (disk-page-next-dirty disk-page) dirty-pages
|
|---|
| 597 | (disk-page-prev-dirty dirty-pages) disk-page))
|
|---|
| 598 | (setf (disk-page-next-dirty disk-page) disk-page
|
|---|
| 599 | (disk-page-prev-dirty disk-page) disk-page
|
|---|
| 600 | (disk-cache-dirty-pages disk-cache) disk-page)))
|
|---|
| 601 | (setf (disk-page-dirty disk-page) t)))
|
|---|
| 602 |
|
|---|
| 603 | ; Return the lock count after locking.
|
|---|
| 604 | (defun lock-page (disk-page)
|
|---|
| 605 | (let ((lock-count (disk-page-lock-count disk-page)))
|
|---|
| 606 | (declare (fixnum lock-count))
|
|---|
| 607 | (when (eql 0 lock-count)
|
|---|
| 608 | (let* ((disk-cache (disk-page-disk-cache disk-page))
|
|---|
| 609 | (shared-buffer (disk-cache-shared-buffer disk-cache))
|
|---|
| 610 | (prev (disk-page-prev disk-page))
|
|---|
| 611 | (next (disk-page-next disk-page))
|
|---|
| 612 | (locked (shared-buffer-locked-pages shared-buffer))
|
|---|
| 613 | (prev-locked (if locked (disk-page-prev locked) disk-page)))
|
|---|
| 614 | (when (null locked)
|
|---|
| 615 | (setf (shared-buffer-locked-pages shared-buffer) (setq locked disk-page)))
|
|---|
| 616 | (setf (disk-page-next prev) next
|
|---|
| 617 | (disk-page-prev next) prev
|
|---|
| 618 | (disk-page-next prev-locked) disk-page
|
|---|
| 619 | (disk-page-prev disk-page) prev-locked
|
|---|
| 620 | (disk-page-prev locked) disk-page
|
|---|
| 621 | (disk-page-next disk-page) locked)
|
|---|
| 622 | (when (eq disk-page (shared-buffer-pages shared-buffer))
|
|---|
| 623 | (setf (shared-buffer-pages shared-buffer)
|
|---|
| 624 | (if (eq next disk-page) nil next)))))
|
|---|
| 625 | (setf (disk-page-lock-count disk-page)
|
|---|
| 626 | (the fixnum (1+ lock-count)))))
|
|---|
| 627 |
|
|---|
| 628 | ; Return the lock count or NIL if the page unlocked when this returns.
|
|---|
| 629 | (defun unlock-page (disk-page)
|
|---|
| 630 | (let ((count (disk-page-lock-count disk-page)))
|
|---|
| 631 | (declare (fixnum count))
|
|---|
| 632 | (when (not (eql 0 count))
|
|---|
| 633 | (progn
|
|---|
| 634 | (when (eql count 1)
|
|---|
| 635 | (let* ((disk-cache (disk-page-disk-cache disk-page))
|
|---|
| 636 | (shared-buffer (disk-cache-shared-buffer disk-cache))
|
|---|
| 637 | (prev-locked (disk-page-prev disk-page))
|
|---|
| 638 | (next-locked (disk-page-next disk-page))
|
|---|
| 639 | (pages (shared-buffer-pages shared-buffer))
|
|---|
| 640 | (prev (if pages (disk-page-prev pages) disk-page)))
|
|---|
| 641 | (when (null pages)
|
|---|
| 642 | (setf (shared-buffer-pages shared-buffer) (setq pages disk-page)))
|
|---|
| 643 | (setf (disk-page-next prev-locked) next-locked
|
|---|
| 644 | (disk-page-prev next-locked) prev-locked
|
|---|
| 645 | (disk-page-next prev) disk-page
|
|---|
| 646 | (disk-page-prev disk-page) prev
|
|---|
| 647 | (disk-page-prev pages) disk-page
|
|---|
| 648 | (disk-page-next disk-page) pages)
|
|---|
| 649 | (when (eq disk-page (shared-buffer-locked-pages shared-buffer))
|
|---|
| 650 | (setf (shared-buffer-locked-pages shared-buffer)
|
|---|
| 651 | (if (eq next-locked disk-page) nil next-locked)))))
|
|---|
| 652 | (setf (disk-page-lock-count disk-page) (decf count))
|
|---|
| 653 | (and (not (eql 0 count)) count)))))
|
|---|
| 654 |
|
|---|
| 655 |
|
|---|
| 656 | ;;; Must be called inside with-databases-locked
|
|---|
| 657 | (defmacro with-locked-page ((disk-page-or-disk-cache
|
|---|
| 658 | &optional address modify-p array offset length page)
|
|---|
| 659 | &body body &environment env)
|
|---|
| 660 | (if address
|
|---|
| 661 | (let (ignored-params)
|
|---|
| 662 | (multiple-value-bind (body-tail decls) (parse-body body env nil)
|
|---|
| 663 | (flet ((normalize (param &optional (ignoreable? t))
|
|---|
| 664 | (or param
|
|---|
| 665 | (let ((res (gensym)))
|
|---|
| 666 | (if ignoreable? (push res ignored-params))
|
|---|
| 667 | res))))
|
|---|
| 668 | `(multiple-value-bind (,(normalize array) ,(normalize offset)
|
|---|
| 669 | ,(normalize length) ,(setq page (normalize page nil)))
|
|---|
| 670 | (get-disk-page ,disk-page-or-disk-cache ,address
|
|---|
| 671 | ,@(if modify-p `(,modify-p)))
|
|---|
| 672 | ,@(when ignored-params
|
|---|
| 673 | `((declare (ignore ,@ignored-params))))
|
|---|
| 674 | ,@decls
|
|---|
| 675 | (with-locked-page (,page)
|
|---|
| 676 | ,@body-tail)))))
|
|---|
| 677 | (let ((page-var (gensym)))
|
|---|
| 678 | `(let ((,page-var ,disk-page-or-disk-cache))
|
|---|
| 679 | (unwind-protect
|
|---|
| 680 | (progn
|
|---|
| 681 | (lock-page ,page-var)
|
|---|
| 682 | ,@body)
|
|---|
| 683 | (unlock-page ,page-var))))))
|
|---|
| 684 |
|
|---|
| 685 | (defun lock-page-at-address (disk-cache address)
|
|---|
| 686 | (with-databases-locked
|
|---|
| 687 | (let ((page (nth-value 3 (get-disk-page disk-cache address))))
|
|---|
| 688 | (values (lock-page page) page))))
|
|---|
| 689 |
|
|---|
| 690 | (defun unlock-page-at-address (disk-cache address)
|
|---|
| 691 | (with-databases-locked
|
|---|
| 692 | (let ((page (nth-value 3 (get-disk-page disk-cache address))))
|
|---|
| 693 | (unlock-page page))))
|
|---|
| 694 |
|
|---|
| 695 | (defun extend-disk-cache (disk-cache new-size &optional extend-file?)
|
|---|
| 696 | #+wood-fixnum-addresses
|
|---|
| 697 | (unless (fixnump new-size)
|
|---|
| 698 | (error "New size is not a fixnum"))
|
|---|
| 699 | (with-databases-locked
|
|---|
| 700 | (let ((size (disk-cache-size disk-cache)))
|
|---|
| 701 | (when (> new-size size)
|
|---|
| 702 | ; Update size of last page
|
|---|
| 703 | (when (> size 0)
|
|---|
| 704 | (let* ((page-address (logand (1- size) (disk-cache-mask disk-cache)))
|
|---|
| 705 | (page (disk-page-gethash page-address (disk-cache-page-hash disk-cache))))
|
|---|
| 706 | (when page
|
|---|
| 707 | (setf (disk-page-size page)
|
|---|
| 708 | (min (length (disk-page-data page)) (- new-size page-address))))))
|
|---|
| 709 | ; increase the file size & install the new size
|
|---|
| 710 | (when extend-file?
|
|---|
| 711 | (extend-file-length (disk-cache-stream disk-cache) new-size))
|
|---|
| 712 | (setf (disk-cache-size disk-cache) new-size)))))
|
|---|
| 713 |
|
|---|
| 714 | (defun flush-all-disk-caches ()
|
|---|
| 715 | (dolist (dc *open-disk-caches*)
|
|---|
| 716 | (if (eq :closed (stream-direction (disk-cache-stream dc)))
|
|---|
| 717 | (setq *open-disk-caches* (delq dc *open-disk-caches*))
|
|---|
| 718 | (flush-disk-cache dc))))
|
|---|
| 719 |
|
|---|
| 720 | (register-lisp-cleanup-function 'flush-all-disk-caches)
|
|---|
| 721 |
|
|---|
| 722 | ;;;;;;;;;;;;;;;;;;;;;;;
|
|---|
| 723 | ;;;
|
|---|
| 724 | ;;; Transaction support
|
|---|
| 725 | ;;;
|
|---|
| 726 |
|
|---|
| 727 | ; Not used yet, maybe it's unnecessary.
|
|---|
| 728 | (defmacro with-disk-cache-transaction ((disk-cache) &body body)
|
|---|
| 729 | (let ((thunk (gensym)))
|
|---|
| 730 | `(let ((,thunk #'(lambda () ,@body)))
|
|---|
| 731 | (declare (dynamic-extent ,thunk))
|
|---|
| 732 | (funcall-with-disk-cache-transaction ,disk-cache ,thunk))))
|
|---|
| 733 |
|
|---|
| 734 | (defun funcall-with-disk-cache-transaction (disk-cache thunk)
|
|---|
| 735 | (let ((transaction (start-disk-cache-transaction disk-cache))
|
|---|
| 736 | (done nil))
|
|---|
| 737 | (unwind-protect
|
|---|
| 738 | (multiple-value-prog1
|
|---|
| 739 | (funcall thunk)
|
|---|
| 740 | (setq done t))
|
|---|
| 741 | (if done
|
|---|
| 742 | (commit-disk-cache-transaction transaction)
|
|---|
| 743 | (abort-disk-cache-transaction transaction)))))
|
|---|
| 744 |
|
|---|
| 745 | ; These are dummies for now. Just keep a counter of how many there are.
|
|---|
| 746 | (defun start-disk-cache-transaction (disk-cache)
|
|---|
| 747 | (with-databases-locked
|
|---|
| 748 | (setf (disk-cache-transaction disk-cache)
|
|---|
| 749 | (+ 1 (or (disk-cache-transaction disk-cache) 0)))
|
|---|
| 750 | disk-cache))
|
|---|
| 751 |
|
|---|
| 752 | (defun commit-disk-cache-transaction (transaction &optional (flush t))
|
|---|
| 753 | (let ((disk-cache transaction))
|
|---|
| 754 | (with-databases-locked
|
|---|
| 755 | (let ((count (1- (disk-cache-transaction disk-cache))))
|
|---|
| 756 | (setf (disk-cache-transaction disk-cache)
|
|---|
| 757 | (if (eql count 0) nil count))))
|
|---|
| 758 | (when flush
|
|---|
| 759 | (with-databases-locked
|
|---|
| 760 | (flush-disk-cache disk-cache)))))
|
|---|
| 761 |
|
|---|
| 762 | (defun abort-disk-cache-transaction (transaction &optional (flush t))
|
|---|
| 763 | (commit-disk-cache-transaction transaction flush))
|
|---|
| 764 |
|
|---|
| 765 |
|
|---|
| 766 | #|
|
|---|
| 767 | (setq dc (open-disk-cache "temp.lisp"))
|
|---|
| 768 |
|
|---|
| 769 | ; read a string from dc
|
|---|
| 770 | (defun rc (address size)
|
|---|
| 771 | (declare (optimize (debug 3)))
|
|---|
| 772 | (declare (special dc))
|
|---|
| 773 | (let ((file-size (disk-cache-size dc)))
|
|---|
| 774 | (setq size (max 0 (min size (- file-size address)))))
|
|---|
| 775 | (let ((string (make-string size))
|
|---|
| 776 | (index 0))
|
|---|
| 777 | (loop
|
|---|
| 778 | (when (<= size 0) (return string))
|
|---|
| 779 | (multiple-value-bind (array array-index bytes) (get-disk-page dc address)
|
|---|
| 780 | (dotimes (i (min size bytes))
|
|---|
| 781 | (setf (schar string index) (code-char (aref array array-index)))
|
|---|
| 782 | (incf index)
|
|---|
| 783 | (incf array-index))
|
|---|
| 784 | (decf size bytes)
|
|---|
| 785 | (incf address bytes)))))
|
|---|
| 786 |
|
|---|
| 787 | (defun wc (string address)
|
|---|
| 788 | (declare (special dc))
|
|---|
| 789 | (let* ((length (length string))
|
|---|
| 790 | (min-size (+ address length))
|
|---|
| 791 | (index 0))
|
|---|
| 792 | (when (> min-size (disk-cache-size dc))
|
|---|
| 793 | (extend-disk-cache dc min-size))
|
|---|
| 794 | (loop
|
|---|
| 795 | (when (<= length 0) (return))
|
|---|
| 796 | (multiple-value-bind (array array-index bytes) (get-disk-page dc address t)
|
|---|
| 797 | (dotimes (i (min length bytes))
|
|---|
| 798 | (declare (type (array (unsigned-byte 8)) array))
|
|---|
| 799 | (setf (aref array array-index) (char-code (schar string index)))
|
|---|
| 800 | (incf index)
|
|---|
| 801 | (incf array-index))
|
|---|
| 802 | (incf address bytes)
|
|---|
| 803 | (decf length bytes)))))
|
|---|
| 804 |
|
|---|
| 805 | (close-disk-cache dc)
|
|---|
| 806 |
|
|---|
| 807 | |#
|
|---|
| 808 | ;;; 1 3/10/94 bill 1.8d247
|
|---|
| 809 | ;;; 2 7/26/94 Derek 1.9d027
|
|---|
| 810 | ;;; 3 10/04/94 bill 1.9d071
|
|---|
| 811 | ;;; 4 11/03/94 Moon 1.9d086
|
|---|
| 812 | ;;; 5 11/05/94 kab 1.9d087
|
|---|
| 813 | ;;; 2 2/18/95 Rti 1.10d019
|
|---|
| 814 | ;;; 3 3/23/95 bill 1.11d010
|
|---|
| 815 | ;;; 4 6/02/95 bill 1.11d040
|
|---|
| 816 | ;;; 5 8/01/95 bill 1.11d065
|
|---|
| 817 | ;;; 6 8/18/95 bill 1.11d071
|
|---|
| 818 | ;;; 7 8/25/95 Derek Derek and Neil's massive bug fix upload
|
|---|