notjack
2021-4-14 07:09:45

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


wjb
2021-4-14 07:10:53

Ah, the subprocess is not cleaned up


notjack
2021-4-14 07:11:42

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.)


wjb
2021-4-14 07:12:23

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


notjack
2021-4-14 07:15:39

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.


wjb
2021-4-14 07:18:34

Thanks!


lukedeng
2021-4-14 07:18:50

@lukedeng has joined the channel


sschwarzer
2021-4-14 11:02:30

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?


ryanc
2021-4-14 12:41:07

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.


sschwarzer
2021-4-14 13:15:20

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?


popa.bogdanp
2021-4-14 15:11:49

> 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.


ryanc
2021-4-14 15:42:11

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.


jaz
2021-4-14 17:15:17

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


samdphillips
2021-4-14 18:24:40

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.


sschwarzer
2021-4-14 19:36:02

@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:


notjack
2021-4-14 19:40:22

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: &gt; (indented-parenless-list "foo" "bar" "baz") "foo" "bar" "baz" &gt; (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.


samth
2021-4-14 20:15:46

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


notjack
2021-4-14 20:33:50

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


philip.mcgrath
2021-4-14 22:28:02

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.


philip.mcgrath
2021-4-14 22:34:03

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.


philip.mcgrath
2021-4-14 22:35:18

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


nicholas.ung0
2021-4-14 22:37:53

@nicholas.ung0 has joined the channel


philip.mcgrath
2021-4-14 23:20:48

Do .zo files ever contain resolved module paths?


notjack
2021-4-14 23:47:46

I got it working


samth
2021-4-14 23:48:48

For BC or CS?


samth
2021-4-14 23:48:59

But I think the answer is likely yes for both.


philip.mcgrath
2021-4-14 23:49:44

Either, but especially CS.


samth
2021-4-14 23:50:11

raco decompile is the best way to check the contents


philip.mcgrath
2021-4-14 23:53:32

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.


samth
2021-4-14 23:54:33

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


samth
2021-4-14 23:54:59

That’s why downloading and installing precompiled zo files works


philip.mcgrath
2021-4-14 23:55:43

That is what I initially thought/hoped.



mflatt
2021-4-15 01:02:59

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