From 46045c882eddf0a13dcc2907a6e4a89f082b9acf Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 24 Aug 2009 02:26:13 -0500 Subject: [PATCH] Disposables are now registered in a global disposables set. To take advantage of this, subclass disposable instead of providing a disposed slot and call new-disposable instead of new. tools.disposables defines two words, 'disposable.' and 'leaks', to help track down resource lifetime problems --- basis/alien/destructors/destructors.factor | 5 +-- basis/bootstrap/tools/tools.factor | 1 + basis/cache/cache.factor | 4 +-- basis/concurrency/mailboxes/mailboxes.factor | 4 +-- .../core-foundation/fsevents/fsevents.factor | 8 ++--- basis/core-text/core-text.factor | 19 +++++++---- basis/help/handbook/handbook.factor | 3 +- basis/io/backend/unix/unix.factor | 23 +++++++------ basis/io/backend/windows/windows.factor | 22 +++--------- basis/io/mmap/mmap.factor | 21 ++++++------ basis/io/monitors/linux/linux.factor | 2 +- basis/io/monitors/monitors.factor | 4 +-- basis/io/monitors/recursive/recursive.factor | 2 +- basis/io/ports/ports.factor | 4 +-- .../io/sockets/secure/openssl/openssl.factor | 18 +++++----- basis/io/sockets/secure/secure.factor | 2 +- basis/io/sockets/sockets-tests.factor | 4 +++ basis/opengl/textures/textures.factor | 14 ++++---- basis/pango/layouts/layouts.factor | 4 +-- basis/tools/deploy/shaker/shaker.factor | 13 ++++++- .../deploy/shaker/strip-destructors.factor | 6 ++++ basis/tools/destructors/authors.txt | 1 + .../tools/destructors/destructors-docs.factor | 21 ++++++++++++ basis/tools/destructors/destructors.factor | 31 +++++++++++++++++ basis/windows/com/wrapper/wrapper.factor | 4 +-- basis/windows/uniscribe/uniscribe.factor | 4 +-- core/destructors/destructors-docs.factor | 34 ++++++++++++++----- core/destructors/destructors.factor | 29 ++++++++++++++-- core/io/streams/c/c.factor | 9 +++-- 29 files changed, 216 insertions(+), 100 deletions(-) create mode 100644 basis/tools/deploy/shaker/strip-destructors.factor create mode 100644 basis/tools/destructors/authors.txt create mode 100644 basis/tools/destructors/destructors-docs.factor create mode 100644 basis/tools/destructors/destructors.factor diff --git a/basis/alien/destructors/destructors.factor b/basis/alien/destructors/destructors.factor index 374d6425c4..24a75304b7 100755 --- a/basis/alien/destructors/destructors.factor +++ b/basis/alien/destructors/destructors.factor @@ -16,9 +16,10 @@ N [ F stack-effect out>> length ] WHERE -TUPLE: F-destructor alien disposed ; +TUPLE: F-destructor < disposable alien ; -: ( alien -- destructor ) f F-destructor boa ; inline +: ( alien -- destructor ) + F-destructor new-disposable swap >>alien ; inline M: F-destructor dispose* alien>> F N ndrop ; diff --git a/basis/bootstrap/tools/tools.factor b/basis/bootstrap/tools/tools.factor index e5e7e869c8..6bdfd6241c 100644 --- a/basis/bootstrap/tools/tools.factor +++ b/basis/bootstrap/tools/tools.factor @@ -8,6 +8,7 @@ IN: bootstrap.tools "tools.crossref" "tools.errors" "tools.deploy" + "tools.destructors" "tools.disassembler" "tools.memory" "tools.profiler" diff --git a/basis/cache/cache.factor b/basis/cache/cache.factor index 3dab1acac8..a226500c63 100644 --- a/basis/cache/cache.factor +++ b/basis/cache/cache.factor @@ -3,10 +3,10 @@ USING: kernel assocs math accessors destructors fry sequences ; IN: cache -TUPLE: cache-assoc assoc max-age disposed ; +TUPLE: cache-assoc < disposable assoc max-age ; : ( -- cache ) - H{ } clone 10 f cache-assoc boa ; + cache-assoc new-disposable H{ } clone >>assoc 10 >>max-age ; > notify-all ; : ( -- mailbox ) - f mailbox boa ; + mailbox new-disposable >>threads >>data ; : mailbox-empty? ( mailbox -- bool ) data>> deque-empty? ; diff --git a/basis/core-foundation/fsevents/fsevents.factor b/basis/core-foundation/fsevents/fsevents.factor index 1956cd9c20..4aa531f182 100644 --- a/basis/core-foundation/fsevents/fsevents.factor +++ b/basis/core-foundation/fsevents/fsevents.factor @@ -181,15 +181,15 @@ SYMBOL: event-stream-callbacks } "cdecl" [ (master-event-source-callback) ] alien-callback ; -TUPLE: event-stream info handle disposed ; +TUPLE: event-stream < disposable info handle ; : ( quot paths latency flags -- event-stream ) [ - add-event-source-callback dup - [ master-event-source-callback ] dip + add-event-source-callback + [ master-event-source-callback ] keep ] 3dip dup enable-event-stream - f event-stream boa ; + event-stream new-disposable swap >>handle swap >>info ; M: event-stream dispose* { diff --git a/basis/core-text/core-text.factor b/basis/core-text/core-text.factor index de3b5ac715..4add71b08f 100644 --- a/basis/core-text/core-text.factor +++ b/basis/core-text/core-text.factor @@ -46,7 +46,7 @@ ERROR: not-a-string object ; CTLineCreateWithAttributedString ] with-destructors ; -TUPLE: line line metrics image loc dim disposed ; +TUPLE: line < disposable line metrics image loc dim ; : typographic-bounds ( line -- width ascent descent leading ) 0 0 0 @@ -109,6 +109,8 @@ TUPLE: line line metrics image loc dim disposed ; :: ( font string -- line ) [ + line new-disposable + [let* | open-font [ font cache-font ] line [ string open-font font foreground>> |CFRelease ] @@ -120,7 +122,11 @@ TUPLE: line line metrics image loc dim disposed ; ext [ (loc) (dim) [ + ceiling ] 2map ] dim [ ext loc [ - >integer ] 2map ] metrics [ open-font line compute-line-metrics ] | - line metrics + + line >>line + + metrics >>metrics + dim [ { [ font dim fill-background ] @@ -128,11 +134,12 @@ TUPLE: line line metrics image loc dim disposed ; [ loc set-text-position ] [ [ line ] dip CTLineDraw ] } cleave - ] make-bitmap-image - metrics loc dim line-loc - metrics metrics>dim + ] make-bitmap-image >>image + + metrics loc dim line-loc >>loc + + metrics metrics>dim >>dim ] - f line boa ] with-destructors ; M: line dispose* line>> CFRelease ; diff --git a/basis/help/handbook/handbook.factor b/basis/help/handbook/handbook.factor index 1c63360025..5db362d9bc 100644 --- a/basis/help/handbook/handbook.factor +++ b/basis/help/handbook/handbook.factor @@ -287,9 +287,9 @@ ARTICLE: "handbook-tools-reference" "Developer tools" { $heading "Debugging" } { $subsection "prettyprint" } { $subsection "inspector" } +{ $subsection "tools.inference" } { $subsection "tools.annotations" } { $subsection "tools.deprecation" } -{ $subsection "tools.inference" } { $heading "Browsing" } { $subsection "see" } { $subsection "tools.crossref" } @@ -299,6 +299,7 @@ ARTICLE: "handbook-tools-reference" "Developer tools" { $subsection "profiling" } { $subsection "tools.memory" } { $subsection "tools.threads" } +{ $subsection "tools.destructors" } { $subsection "tools.disassembler" } { $heading "Deployment" } { $subsection "tools.deploy" } ; diff --git a/basis/io/backend/unix/unix.factor b/basis/io/backend/unix/unix.factor index 1a52ce6f34..4b7ef4b40f 100644 --- a/basis/io/backend/unix/unix.factor +++ b/basis/io/backend/unix/unix.factor @@ -4,14 +4,15 @@ USING: alien alien.c-types alien.syntax generic assocs kernel kernel.private math io.ports sequences strings sbufs threads unix vectors io.buffers io.backend io.encodings math.parser continuations system libc namespaces make io.timeouts -io.encodings.utf8 destructors accessors summary combinators -locals unix.time fry io.backend.unix.multiplexers ; +io.encodings.utf8 destructors destructors.private accessors +summary combinators locals unix.time fry +io.backend.unix.multiplexers ; QUALIFIED: io IN: io.backend.unix GENERIC: handle-fd ( handle -- fd ) -TUPLE: fd fd disposed ; +TUPLE: fd < disposable fd ; : init-fd ( fd -- fd ) [ @@ -25,14 +26,16 @@ TUPLE: fd fd disposed ; #! since on OS X 10.3, this operation fails from init-io #! when running the Factor.app (presumably because fd 0 and #! 1 are closed). - f fd boa ; + fd new-disposable swap >>fd ; M: fd dispose dup disposed>> [ drop ] [ - [ cancel-operation ] - [ t >>disposed drop ] - [ fd>> close-file ] - tri + { + [ cancel-operation ] + [ t >>disposed drop ] + [ unregister-disposable ] + [ fd>> close-file ] + } cleave ] if ; M: fd handle-fd dup check-disposed fd>> ; @@ -133,7 +136,7 @@ M: unix io-multiplex ( ms/f -- ) ! pipe to non-blocking, and read from it instead of the real ! stdin. Very crufty, but it will suffice until we get native ! threading support at the language level. -TUPLE: stdin control size data disposed ; +TUPLE: stdin < disposable control size data ; M: stdin dispose* [ @@ -168,7 +171,7 @@ M: stdin refill : data-read-fd ( -- fd ) &: stdin_read *uint ; : ( -- stdin ) - stdin new + stdin new-disposable control-write-fd >>control size-read-fd init-fd >>size data-read-fd >>data ; diff --git a/basis/io/backend/windows/windows.factor b/basis/io/backend/windows/windows.factor index fde5cf9b12..5922e217b0 100755 --- a/basis/io/backend/windows/windows.factor +++ b/basis/io/backend/windows/windows.factor @@ -7,33 +7,21 @@ windows.kernel32 windows.shell32 windows.types windows.winsock splitting continuations math.bitwise accessors init sets assocs ; IN: io.backend.windows -: win32-handles ( -- assoc ) - \ win32-handles [ H{ } clone ] initialize-alien ; - -TUPLE: win32-handle < identity-tuple handle disposed ; - -M: win32-handle hashcode* handle>> hashcode* ; +TUPLE: win32-handle < disposable handle ; : set-inherit ( handle ? -- ) [ handle>> HANDLE_FLAG_INHERIT ] dip >BOOLEAN SetHandleInformation win32-error=0/f ; : new-win32-handle ( handle class -- win32-handle ) - new swap >>handle - dup f set-inherit - dup win32-handles conjoin ; + new-disposable swap >>handle + dup f set-inherit ; : ( handle -- win32-handle ) win32-handle new-win32-handle ; -ERROR: disposing-twice ; - -: unregister-handle ( handle -- ) - win32-handles delete-at* - [ t >>disposed drop ] [ disposing-twice ] if ; - M: win32-handle dispose* ( handle -- ) - [ unregister-handle ] [ handle>> CloseHandle win32-error=0/f ] bi ; + handle>> CloseHandle win32-error=0/f ; TUPLE: win32-file < win32-handle ptr ; @@ -54,7 +42,7 @@ HOOK: add-completion io-backend ( port -- ) |dispose dup add-completion ; -: share-mode ( -- fixnum ) +: share-mode ( -- n ) { FILE_SHARE_READ FILE_SHARE_WRITE diff --git a/basis/io/mmap/mmap.factor b/basis/io/mmap/mmap.factor index 9a4443e8e5..aa3ac624a0 100644 --- a/basis/io/mmap/mmap.factor +++ b/basis/io/mmap/mmap.factor @@ -6,30 +6,29 @@ accessors vocabs.loader combinators alien.c-types math ; IN: io.mmap -TUPLE: mapped-file address handle length disposed ; +TUPLE: mapped-file < disposable address handle length ; HOOK: (mapped-file-reader) os ( path length -- address handle ) HOOK: (mapped-file-r/w) os ( path length -- address handle ) -ERROR: bad-mmap-size path size ; +ERROR: bad-mmap-size n ; > ] bi - dup 0 <= [ bad-mmap-size ] when ; +: prepare-mapped-file ( path quot -- mapped-file path' length ) + [ + [ normalize-path ] [ file-info size>> ] bi + [ dup 0 <= [ bad-mmap-size ] [ 2drop ] if ] + [ nip mapped-file new-disposable swap >>length ] + ] dip 2tri [ >>address ] [ >>handle ] bi* ; inline PRIVATE> : ( path -- mmap ) - prepare-mapped-file - [ (mapped-file-reader) ] keep - f mapped-file boa ; + [ (mapped-file-reader) ] prepare-mapped-file ; : ( path -- mmap ) - prepare-mapped-file - [ (mapped-file-r/w) ] keep - f mapped-file boa ; + [ (mapped-file-r/w) ] prepare-mapped-file ; HOOK: close-mapped-file io-backend ( mmap -- ) diff --git a/basis/io/monitors/linux/linux.factor b/basis/io/monitors/linux/linux.factor index 9097e7e864..9b3688d023 100644 --- a/basis/io/monitors/linux/linux.factor +++ b/basis/io/monitors/linux/linux.factor @@ -12,7 +12,7 @@ SYMBOL: watches SYMBOL: inotify -TUPLE: linux-monitor < monitor wd inotify watches disposed ; +TUPLE: linux-monitor < monitor wd inotify watches ; : ( wd path mailbox -- monitor ) linux-monitor new-monitor diff --git a/basis/io/monitors/monitors.factor b/basis/io/monitors/monitors.factor index cc8cea37d2..d8bb1ed488 100644 --- a/basis/io/monitors/monitors.factor +++ b/basis/io/monitors/monitors.factor @@ -20,7 +20,7 @@ M: object dispose-monitors ; [ dispose-monitors ] [ ] cleanup ] with-scope ; inline -TUPLE: monitor < identity-tuple path queue timeout ; +TUPLE: monitor < disposable path queue timeout ; M: monitor hashcode* path>> hashcode* ; @@ -29,7 +29,7 @@ M: monitor timeout timeout>> ; M: monitor set-timeout (>>timeout) ; : new-monitor ( path mailbox class -- monitor ) - new + new-disposable swap >>queue swap >>path ; inline diff --git a/basis/io/monitors/recursive/recursive.factor b/basis/io/monitors/recursive/recursive.factor index 943345bf18..75dfd234a8 100644 --- a/basis/io/monitors/recursive/recursive.factor +++ b/basis/io/monitors/recursive/recursive.factor @@ -8,7 +8,7 @@ IN: io.monitors.recursive ! Simulate recursive monitors on platforms that don't have them -TUPLE: recursive-monitor < monitor children thread ready disposed ; +TUPLE: recursive-monitor < monitor children thread ready ; : notify? ( -- ? ) monitor tget ready>> promise-fulfilled? ; diff --git a/basis/io/ports/ports.factor b/basis/io/ports/ports.factor index b2d71fd535..49f6166e00 100644 --- a/basis/io/ports/ports.factor +++ b/basis/io/ports/ports.factor @@ -10,14 +10,14 @@ IN: io.ports SYMBOL: default-buffer-size 64 1024 * default-buffer-size set-global -TUPLE: port handle timeout disposed ; +TUPLE: port < disposable handle timeout ; M: port timeout timeout>> ; M: port set-timeout (>>timeout) ; : ( handle class -- port ) - new swap >>handle ; inline + new-disposable swap >>handle ; inline TUPLE: buffered-port < port { buffer buffer } ; diff --git a/basis/io/sockets/secure/openssl/openssl.factor b/basis/io/sockets/secure/openssl/openssl.factor index 07246354e3..8f596da0bd 100644 --- a/basis/io/sockets/secure/openssl/openssl.factor +++ b/basis/io/sockets/secure/openssl/openssl.factor @@ -78,9 +78,9 @@ TUPLE: openssl-context < secure-context aliens sessions ; SSL_CTX_set_verify_depth ] [ drop ] if ; -TUPLE: bio handle disposed ; +TUPLE: bio < disposable handle ; -: ( handle -- bio ) f bio boa ; +: ( handle -- bio ) bio new-disposable swap >>handle ; M: bio dispose* handle>> BIO_free ssl-error ; @@ -94,9 +94,9 @@ M: bio dispose* handle>> BIO_free ssl-error ; SSL_CTX_set_tmp_dh ssl-error ] [ drop ] if ; -TUPLE: rsa handle disposed ; +TUPLE: rsa < disposable handle ; -: ( handle -- rsa ) f rsa boa ; +: ( handle -- rsa ) rsa new-disposable swap >>handle ; M: rsa dispose* handle>> RSA_free ; @@ -109,7 +109,7 @@ M: rsa dispose* handle>> RSA_free ; SSL_CTX_set_tmp_rsa ssl-error ; : ( config ctx -- context ) - openssl-context new + openssl-context new-disposable swap >>handle swap >>config V{ } clone >>aliens @@ -139,7 +139,7 @@ M: openssl-context dispose* [ handle>> SSL_CTX_free ] tri ; -TUPLE: ssl-handle file handle connected disposed ; +TUPLE: ssl-handle < disposable file handle connected ; SYMBOL: default-secure-context @@ -151,8 +151,10 @@ SYMBOL: default-secure-context ] unless* ; : ( fd -- ssl ) - current-secure-context handle>> SSL_new dup ssl-error - f f ssl-handle boa ; + ssl-handle new-disposable + current-secure-context handle>> SSL_new + dup ssl-error >>handle + swap >>file ; M: ssl-handle dispose* [ handle>> SSL_free ] [ file>> dispose ] bi ; diff --git a/basis/io/sockets/secure/secure.factor b/basis/io/sockets/secure/secure.factor index bff2dbaf1a..e654caf0b8 100644 --- a/basis/io/sockets/secure/secure.factor +++ b/basis/io/sockets/secure/secure.factor @@ -29,7 +29,7 @@ ephemeral-key-bits ; "vocab:openssl/cacert.pem" >>ca-file t >>verify ; -TUPLE: secure-context config handle disposed ; +TUPLE: secure-context < disposable config handle ; HOOK: secure-socket-backend ( config -- context ) diff --git a/basis/io/sockets/sockets-tests.factor b/basis/io/sockets/sockets-tests.factor index dc0c698699..a4a3f0702b 100644 --- a/basis/io/sockets/sockets-tests.factor +++ b/basis/io/sockets/sockets-tests.factor @@ -79,6 +79,8 @@ concurrency.promises threads io.streams.string ; ! See what happens if other end is closed [ ] [ "port" set ] unit-test +[ ] [ "datagram3" get dispose ] unit-test + [ ] [ [ "127.0.0.1" 0 utf8 @@ -93,6 +95,8 @@ concurrency.promises threads io.streams.string ; [ "hello" f ] [ "port" get ?promise utf8 [ + 1 seconds input-stream get set-timeout + 1 seconds output-stream get set-timeout "hi\n" write flush readln readln ] with-client ] unit-test diff --git a/basis/opengl/textures/textures.factor b/basis/opengl/textures/textures.factor index 34cb14a442..528aaaa12f 100755 --- a/basis/opengl/textures/textures.factor +++ b/basis/opengl/textures/textures.factor @@ -268,7 +268,7 @@ DEFER: make-texture > ] keep draw-textured-rect ] make-dlist ; : ( image loc -- texture ) - single-texture new swap >>loc swap [ >>image ] [ dim>> >>dim ] bi + single-texture new-disposable swap >>loc swap [ >>image ] [ dim>> >>dim ] bi dup image>> dim>> product 0 = [ dup texture-coords >>texture-coords dup image>> make-texture >>texture @@ -347,7 +347,7 @@ M: single-texture draw-scaled-texture dup texture>> [ draw-textured-rect ] [ 2drop ] if ] if ; -TUPLE: multi-texture grid display-list loc disposed ; +TUPLE: multi-texture < disposable grid display-list loc ; : image-locs ( image-grid -- loc-grid ) [ first [ dim>> first ] map ] [ [ first dim>> second ] map ] bi @@ -373,11 +373,9 @@ TUPLE: multi-texture grid display-list loc disposed ; : ( image-grid loc -- multi-texture ) [ - [ - dup - make-textured-grid-display-list - ] keep - f multi-texture boa + [ multi-texture new-disposable ] 2dip + [ nip >>loc ] [ >>grid ] 2bi + dup grid>> make-textured-grid-display-list >>display-list ] with-destructors ; M: multi-texture draw-scaled-texture nip draw-texture ; diff --git a/basis/pango/layouts/layouts.factor b/basis/pango/layouts/layouts.factor index 25aee74ca4..88c6f17093 100644 --- a/basis/pango/layouts/layouts.factor +++ b/basis/pango/layouts/layouts.factor @@ -60,7 +60,7 @@ pango_layout_iter_free ( PangoLayoutIter* iter ) ; DESTRUCTOR: pango_layout_iter_free -TUPLE: layout font string selection layout metrics ink-rect logical-rect image disposed ; +TUPLE: layout < disposable font string selection layout metrics ink-rect logical-rect image ; SYMBOL: dpi @@ -186,7 +186,7 @@ MEMO: missing-font-metrics ( font -- metrics ) : ( font string -- line ) [ - layout new + layout new-disposable swap unpack-selection swap >>font dup [ string>> ] [ font>> ] bi >>layout diff --git a/basis/tools/deploy/shaker/shaker.factor b/basis/tools/deploy/shaker/shaker.factor index c6b6731321..c587f842ca 100755 --- a/basis/tools/deploy/shaker/shaker.factor +++ b/basis/tools/deploy/shaker/shaker.factor @@ -24,11 +24,12 @@ IN: tools.deploy.shaker : strip-init-hooks ( -- ) "Stripping startup hooks" show { + "alien.strings" "command-line" "cpu.x86" + "destructors" "environment" "libc" - "alien.strings" } [ init-hooks get delete-at ] each deploy-threads? get [ @@ -65,6 +66,13 @@ IN: tools.deploy.shaker run-file ] when ; +: strip-destructors ( -- ) + "libc" vocab [ + "Stripping destructor debug code" show + "vocab:tools/deploy/shaker/strip-destructors.factor" + run-file + ] when ; + : strip-call ( -- ) "Stripping stack effect checking from call( and execute(" show "vocab:tools/deploy/shaker/strip-call.factor" run-file ; @@ -278,6 +286,8 @@ IN: tools.deploy.shaker "mallocs" "libc.private" lookup , + "disposables" "destructors" lookup , + deploy-threads? [ "initial-thread" "threads" lookup , ] unless @@ -478,6 +488,7 @@ SYMBOL: deploy-vocab : strip ( -- ) init-stripper strip-libc + strip-destructors strip-call strip-cocoa strip-debugger diff --git a/basis/tools/deploy/shaker/strip-destructors.factor b/basis/tools/deploy/shaker/strip-destructors.factor new file mode 100644 index 0000000000..86c08ebcb5 --- /dev/null +++ b/basis/tools/deploy/shaker/strip-destructors.factor @@ -0,0 +1,6 @@ +USE: kernel +IN: destructors.private + +: register-disposable ( obj -- ) drop ; inline + +: unregister-disposable ( obj -- ) drop ; inline diff --git a/basis/tools/destructors/authors.txt b/basis/tools/destructors/authors.txt new file mode 100644 index 0000000000..1901f27a24 --- /dev/null +++ b/basis/tools/destructors/authors.txt @@ -0,0 +1 @@ +Slava Pestov diff --git a/basis/tools/destructors/destructors-docs.factor b/basis/tools/destructors/destructors-docs.factor new file mode 100644 index 0000000000..e5a8f0318b --- /dev/null +++ b/basis/tools/destructors/destructors-docs.factor @@ -0,0 +1,21 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: help.markup help.syntax quotations ; +IN: tools.destructors + +HELP: disposables. +{ $description "Print the number of disposable objects of each class." } ; + +HELP: leaks +{ $values + { "quot" quotation } +} +{ $description "Runs a quotation, printing any increases in the number of disposable objects after the quotation returns." } ; + +ARTICLE: "tools.destructors" "Destructor tools" +"The " { $vocab-link "tools.destructors" } " vocabulary provides words for tracking down resource leaks." +{ $subsection disposables. } +{ $subsection leaks } +{ $see-also "destructors" } ; + +ABOUT: "tools.destructors" diff --git a/basis/tools/destructors/destructors.factor b/basis/tools/destructors/destructors.factor new file mode 100644 index 0000000000..4f182c6777 --- /dev/null +++ b/basis/tools/destructors/destructors.factor @@ -0,0 +1,31 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: assocs classes destructors fry kernel math namespaces +prettyprint sequences sets sorting ; +IN: tools.destructors + +alist sort-keys simple-table. ; + +PRIVATE> + +: disposables. ( -- ) + disposable-tally (disposables.) ; + +: leaks ( quot -- ) + disposable-tally [ call disposable-tally ] dip subtract-values + (disposables.) ; inline diff --git a/basis/windows/com/wrapper/wrapper.factor b/basis/windows/com/wrapper/wrapper.factor index beac4b6c27..81ae923d26 100755 --- a/basis/windows/com/wrapper/wrapper.factor +++ b/basis/windows/com/wrapper/wrapper.factor @@ -6,7 +6,7 @@ destructors fry math.parser generalizations sets specialized-arrays.alien specialized-arrays.direct.alien ; IN: windows.com.wrapper -TUPLE: com-wrapper callbacks vtbls disposed ; +TUPLE: com-wrapper < disposable callbacks vtbls ; [ +live-wrappers+ get adjoin ] bi ; : ( implementations -- wrapper ) - (make-callbacks) f f com-wrapper boa + com-wrapper new-disposable swap (make-callbacks) >>vtbls dup allocate-wrapper ; M: com-wrapper dispose* diff --git a/basis/windows/uniscribe/uniscribe.factor b/basis/windows/uniscribe/uniscribe.factor index 7c5c26c2da..457f4bc9f0 100755 --- a/basis/windows/uniscribe/uniscribe.factor +++ b/basis/windows/uniscribe/uniscribe.factor @@ -7,7 +7,7 @@ windows.offscreen windows.gdi32 windows.ole32 windows.types windows.fonts opengl.textures locals windows.errors ; IN: windows.uniscribe -TUPLE: script-string font string metrics ssa size image disposed ; +TUPLE: script-string < disposable font string metrics ssa size image ; : line-offset>x ( n script-string -- x ) 2dup string>> length = [ @@ -89,7 +89,7 @@ TUPLE: script-string font string metrics ssa size image disposed ; TEXTMETRIC>metrics ; : ( font string -- script-string ) - [ script-string new ] 2dip + [ script-string new-disposable ] 2dip [ >>font ] [ >>string ] bi* [ { diff --git a/core/destructors/destructors-docs.factor b/core/destructors/destructors-docs.factor index ed7d433026..8a0c36b99a 100644 --- a/core/destructors/destructors-docs.factor +++ b/core/destructors/destructors-docs.factor @@ -8,8 +8,8 @@ HELP: dispose $nl "No further operations can be performed on a disposable object after this call." $nl -"Disposing an object which has already been disposed should have no effect, and in particular it should not fail with an error. To help implement this pattern, add a " { $slot "disposed" } " slot to your object and implement the " { $link dispose* } " method instead." } -{ $notes "You must close disposable objects after you are finished working with them, to avoid leaking operating system resources. A convenient way to automate this is by using the " { $link with-disposal } " word." +"Disposing an object which has already been disposed should have no effect, and in particular it should not fail with an error. To help implement this pattern, inherit from the " { $link disposable } " class and implement the " { $link dispose* } " method instead." } +{ $notes "You must dispose of disposable objects after you are finished working with them, to avoid leaking operating system resources. A convenient way to automate this is by using the " { $link with-disposal } " word." $nl "The default implementation assumes the object has a " { $snippet "disposed" } " slot. If the slot is set to " { $link f } ", it calls " { $link dispose* } " and sets the slot to " { $link t } "." } ; @@ -51,6 +51,9 @@ HELP: dispose-each { "seq" sequence } } { $description "Attempts to dispose of each element of a sequence and collects all of the errors into a sequence. If any errors are thrown during disposal, the last error is rethrown after all objects have been disposed." } ; +HELP: disposables +{ $var-description "Global variable holding all disposable objects which have not been disposed of yet. The " { $link new-disposable } " word adds objects here, and the " { $link dispose } " method on disposables removes them. The " { $link "tools.destructors" } " vocabulary provides some words for working with this data." } ; + ARTICLE: "destructors-anti-patterns" "Resource disposal anti-patterns" "Words which create objects corresponding to external resources should always be used with " { $link with-disposal } ". The following code is wrong:" { $code @@ -58,12 +61,9 @@ ARTICLE: "destructors-anti-patterns" "Resource disposal anti-patterns" } "The reason being that if " { $snippet "do stuff" } " throws an error, the resource will not be disposed of. The most important case where this can occur is with I/O streams, and the correct solution is to always use " { $link with-input-stream } " and " { $link with-output-stream } "; see " { $link "stdio" } " for details." ; -ARTICLE: "destructors" "Deterministic resource disposal" -"Operating system resources such as streams, memory mapped files, and so on are not managed by Factor's garbage collector and must be released when you are done with them. Failing to release a resource can lead to reduced performance and instability." -$nl -"Disposable object protocol:" +ARTICLE: "destructors-using" "Using destructors" +"Disposing of an object:" { $subsection dispose } -{ $subsection dispose* } "Utility word for scoped disposal:" { $subsection with-disposal } "Utility word for disposing multiple objects:" @@ -71,7 +71,23 @@ $nl "Utility words for more complex disposal patterns:" { $subsection with-destructors } { $subsection &dispose } -{ $subsection |dispose } -{ $subsection "destructors-anti-patterns" } ; +{ $subsection |dispose } ; + +ARTICLE: "destructors-extending" "Writing new destructors" +"Superclass for disposable objects:" +{ $subsection disposable } +"Parametrized constructor for disposable objects:" +{ $subsection new-disposable } +"Generic disposal word:" +{ $subsection dispose* } +"Global set of disposable objects:" +{ $subsection disposables } ; + +ARTICLE: "destructors" "Deterministic resource disposal" +"Operating system resources such as streams, memory mapped files, and so on are not managed by Factor's garbage collector and must be released when you are done with them. Failing to release a resource can lead to reduced performance and instability." +{ $subsection "destructors-using" } +{ $subsection "destructors-extending" } +{ $subsection "destructors-anti-patterns" } +{ $see-also "tools.destructors" } ; ABOUT: "destructors" diff --git a/core/destructors/destructors.factor b/core/destructors/destructors.factor index 9a470d53c1..39f0e9f2b9 100644 --- a/core/destructors/destructors.factor +++ b/core/destructors/destructors.factor @@ -1,10 +1,30 @@ -! Copyright (C) 2007, 2008 Doug Coleman, Slava Pestov. +! Copyright (C) 2007, 2009 Doug Coleman, Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors continuations kernel namespaces make -sequences vectors ; +sequences vectors sets assocs init ; IN: destructors -TUPLE: disposable disposed ; +SYMBOL: disposables + +[ H{ } clone disposables set-global ] "destructors" add-init-hook + + + +TUPLE: disposable < identity-tuple disposed id ; + +M: disposable hashcode* nip id>> ; + +: new-disposable ( class -- disposable ) + new \ disposable counter >>id + dup register-disposable ; inline GENERIC: dispose* ( disposable -- ) @@ -18,6 +38,9 @@ GENERIC: dispose ( disposable -- ) M: object dispose dup disposed>> [ drop ] [ t >>disposed dispose* ] if ; +M: disposable dispose + [ unregister-disposable ] [ call-next-method ] bi ; + : dispose-each ( seq -- ) [ [ [ dispose ] curry [ , ] recover ] each diff --git a/core/io/streams/c/c.factor b/core/io/streams/c/c.factor index 7a7ac5a97c..aebc709a9e 100755 --- a/core/io/streams/c/c.factor +++ b/core/io/streams/c/c.factor @@ -6,7 +6,10 @@ io.encodings.utf8 alien.strings continuations destructors byte-arrays accessors combinators ; IN: io.streams.c -TUPLE: c-stream handle disposed ; +TUPLE: c-stream < disposable handle ; + +: new-c-stream ( handle class -- c-stream ) + new-disposable swap >>handle ; inline M: c-stream dispose* handle>> fclose ; @@ -20,7 +23,7 @@ M: c-stream stream-seek TUPLE: c-writer < c-stream ; -: ( handle -- stream ) f c-writer boa ; +: ( handle -- stream ) c-writer new-c-stream ; M: c-writer stream-element-type drop +byte+ ; @@ -32,7 +35,7 @@ M: c-writer stream-flush dup check-disposed handle>> fflush ; TUPLE: c-reader < c-stream ; -: ( handle -- stream ) f c-reader boa ; +: ( handle -- stream ) c-reader new-c-stream ; M: c-reader stream-element-type drop +byte+ ; -- 2.34.1