
What happens if you do this: (parameterize ([current-custodian (make-custodian)])
(do-it))

Ah, the subprocess is not cleaned up

Hmm. Sounds like it’s a bug in the custodian layer and not specific to rackunit then. (Rackunit test cases run the code in a new custodian by default.)

Okay. If you think it’s a bug, I’ll file a report on github

It sounds like a bug to me. If it’s not a bug in behavior, then it’s a documentation bug, because it’s very unintuitive and nothing in the docs I skimmed on subprocesses and custodians warned me about it.

Thanks!

@lukedeng has joined the channel

I’m checking the documentation to find out when a port is closed if I don’t explicitly close it. I found this paragraph at https://docs.racket-lang.org/reference/file-ports.html :
> The port produced by https://docs.racket-lang.org/reference/file-ports.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._open-input-file%29%29\|open-input-file should be explicitly closed, either though https://docs.racket-lang.org/reference/port-ops.html#%28def._%28%28quote._~23~25kernel%29._close-input-port%29%29\|close-input-port or indirectly via https://docs.racket-lang.org/reference/custodians.html#%28def._%28%28quote._~23~25kernel%29._custodian-shutdown-all%29%29\|custodian-shutdown-all, to release the OS-level file handle. The input port will not be closed automatically if it is otherwise available for garbage collection (see <https://docs.racket-lang.org/reference/eval-model.html#%28part._gc-model%29|Garbage Collection>); a https://docs.racket-lang.org/reference/willexecutor.html#%28tech._will%29\|will could be associated with an input port to close it more automatically (see <https://docs.racket-lang.org/reference/willexecutor.html|Wills and Executors>). (and the same paragraph, but for output files/ports, further down).
I find the last sentence confusing because I don’t understand the meaning of “otherwise” and “more” in this context. However, the sentence makes sense to me if I ignore these two words. Then my understanding would be that a garbage collection doesn’t close the port automatically, but that I could use (define an-executor (make-will-executor))
(will-register an-executor my-output-port close-output-port)
to make sure that my-output-port
is closed when it’s garbage-collected.
Is this understanding correct?
Are there any related issues I should watch out for?

I think “otherwise” and “more” refer to the fact that custodians manage file-based ports. The custodian keeps (weak, IIRC) references to the ports, and it closes them automatically when the custodian is shut down (but someone needs to tell the custodian to shut down). Note that just registering a will with an executor doesn’t mean that it will get run. Wills are only run when a thread calls will-execute
on the executor.

Oops, that’s more complicated than I thought. I’m looking for a way to make sure that a file is automatically closed when the port value is no longer reachable.
As I understand it now, after registering the will, I’d need to call will-execute
at a point in the program where I (need to) know that the port object is no longer reachable (because if I call will-execute
while the port is still reachable, it will block, and if I call will-try-execute
the port won’t be closed if it’s still reachable?).
So a (still not very simple) alternative would be to • Create a new custodian with make-custodian
• Set it as the current custodian with current-custodian
• Open files etc. • At some later point in the program, when I don’t think I’ll need the file anymore, call custodian-shutdown-all
on the custodian created before I can’t believe it has to be that complicated. :neutral_face: For some context, I’m brand-new to wills and custodians, but still it seems to me the task should be simpler.
What do you think?

> As I understand it now, after registering the will, I’d need to call will-execute
at a point in the program where I (need to) _know_ that the port object is no longer reachable (because if I call will-execute
while the port is still reachable, it will block, and if I call will-try-execute
the port won’t be closed if it’s still reachable?). You can spawn a thread that just calls will-execute
and loops. Here’s an https://github.com/Bogdanp/racket-http-easy/blob/4b05e13f795e3aa918a52547e1d64267b1118e31/http-easy/http-easy/private/session.rkt#L299-L311\|example. You don’t need to know when the port will be GC’d, because the background thread will just wake up whenever will-execute
is ready.

In addition to Bogdan’s example, here’s <https://github.com/racket/racket/blob/master/racket/collects/db/private/generic/prepared.rkt#L104|another from the db library>, for finalizing prepared statements. See the second paragraph of <https://github.com/racket/racket/commit/5a8a0aff022c3f7d2ffb95e78a25ab9d53a4f90f|this commit message> for an explanation of why setting current-namespace to an empty namespace is helpful.

Oh, also: for better performance in cases where (in-alist _)
occurs literally inside a for/whatever
, you can use define-sequence-syntax
.

If your code uses a file port in a predictable open/use/close pattern call-with-input-file
and call-with-output-file
handle the closing without dealing with custodians and other facilities.

@samdphillips I had actually thought about this, but at the moment my idea is that the client code shouldn’t deal with opening and closing the file directly; that will be hidden in a library. I could create my own wrapper on a higher abstraction level, similar to with-handlers
, but it still feels a bit too low-level. I’m not sure yet what design to use; I need to think a bit more about it.
Of course, thanks nonetheless for the suggestion. :slightly_smiling_face:

Pretty printing question: I want to implement a struct like unquoted-printing-string
, except instead of containing a string and printing it without quotes, I’d like it to contain a list and print it without parens, and with indentation if it has to line wrap. So it should work like this: > (indented-parenless-list "foo" "bar" "baz")
"foo" "bar" "baz"
> (indented-parenless-list "fooooooooooooooooooooooooooooooooo"
"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar"
"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz")
"fooooooooooooooooooooooooooooooooo"
"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar"
"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz"
Anyone know how to do that? I’ve tried to grasp the docs on pretty printing and reverse engineer the code for make-constructor-style-printer
but it’s very confusing.

A struct that just prints the elements of a list seems pretty simple.

it’s implementing the “only line break if necessary” part that’s confusing, the racket/pretty
API is not easy to use

One thing to be aware of is that that the underlying file descriptor or Windows equivalent isn’t closed until the Racket-level port is closed (either directly or by a custodian). That means you can run into OS-level limits, because the will won’t be executed until there is a garbage collection (and one that collects the relevant generation): if there’s plenty of memory but you’re opening lots of files, GC (or a sufficiently major GC) may not be triggered. That’s why it’s generally not recommended to rely on GC to release finite non-memory resources—though of course there are many contexts when you don’t really need to worry about file-descriptor limits.

Every time I’ve thought I’ve wanted a will, there has turned out to be a better idea. (Strictly speaking, the one exception is that wills are used to implement ffi/unsafe/alloc
, but that really is about releasing memory outside the Racket heap.) If you can say more about what your library is doing with the file, we may have suggestions.

I have done something like this somewhere; I’ll try to remember where. I remember it being annoying.

@nicholas.ung0 has joined the channel

Do .zo
files ever contain resolved module paths?

I got it working

For BC or CS?

But I think the answer is likely yes for both.

Either, but especially CS.

raco decompile is the best way to check the contents

I was thinking about the issue we discussed with Guix patching paths of .zo
files: if eventually (as we hope) there is support for Racket packages as Guix packages, a similar issue could come up if .zo
files contain resolved module paths.

Compiled files shouldn’t in general have absolute paths in them

That’s why downloading and installing precompiled zo files works

That is what I initially thought/hoped.


Agreeing: resolved module paths (which usually involve complete paths) should generally not be in .zo
files.