
Oh my http
package supports back to Racket 5.3.5, so I probably can’t use your retry
pkg directly. :disappointed:

Does anyone know if its possible to add a callback for the Racket VM to call before the OS level process exits?

Plumbers seem to get called at a programs end, which is not necessarily the process’s end.

exit handlers seem to have the same problem.

And custodians seem to be just for freeing up memory.

you can use the FFI to add an exit handler using atexit https://linux.die.net/man/3/atexit

(for normal termination)

and signal handlers for catching (nice) kills

depending on what you want to do that could have icky concurrency interactions with the VM

(particularly signal handlers, since I think the VM might install some of its own?)

what are you trying to do

@thinkmoore True, and I will if I need to.

But I would ideally like to find a racket solution.

I don’t think there is a racket notion of anything that happens “after the program’s end”

I suppose I could use scheme_add_managed_close_on_exit

Oh sure there are, just not at the ‘process’s’ end, if that makes sense.

But ya, in principle, exit-handler runs at the end of a program.

Same with some plumbers.

doesn’t schme_add_managed_close_on_exit just add something to a custodian? which is still “end of program” (or “on custodian shutdown”)?

But I guess the problem with scheme_add_managed_close_on_exit
is that I would have to make sure the current-custodian is the one associated with the process, and not just a thread.

@thinkmoore yup

There is scheme_atexit_closer

Which looks like its called on exit, but also on custodian closing.

So I would just need to find a way to ensure that its the former case.

@mflatt Do you have any thoughts.

@leif Registering with the custodian or plumber is likely to be right For context, what is it that you want to do on exit?

I have an ffi library that has an init()
and close()
funciton

They init()
function can only be called once.

And once called, close()
must eventually be called.

But once close()
is called, I can no longer use the FFI

What happens if you don’t call close
(e.g., the process is terminated with SIGKILL
)?

Frequently the library will segfault.

Which, IMO, is a terrible design, but that’s the case.

It will segfault when you use it from a different process?

Ah..woops, ya, I was thinking of SIGTRM

yes, if you SIGKILL then it will just die.

Leaving whatever it was doing in some undefined state.

But that’s unavoidable.

But if you send a SIGTRM without calling close()
it will segfault.

The problem with using a plumber is that I can only get the plumber for my current program, as such, if I’m running the program multiple times in one process (like say doing a raco setup
), then the second time I’ve already close()
ed the library.

(i’m using scheme_register_process_global
to ensure that I only call init()
once.

can you have that register with the current plumber?

/custodian

If you’re doing something that spans places, then I recommend atexit()
instead of a custodian after all

@mflatt Okay, thanks.

@thinkmoore Sadly nope, because the current plumber may not be the global plumber for the process.

And in, say, the case of raco setup
, it won’t be.

@mflatt Do places run in the same process? I thought they were a slightly glorified fork()

Same OS process

Ah, alright then.

I guess the only thing I’m worried about now is that atexit
is OS-dependent, but I suspect there is an equivalent function for windows.

@thinkmoore Hmmph: couldn't get "atexit" from #f (/home/travis/racket/bin/racket: undefined symbol: atexit)

I also tried the library libc
, but then I get a can’t find the library libc

Maybe I have to special case this to be libc
on os x and glibc
on linux.

or rather, I get: ffi-lib: couldn't open "libc.so" (/usr/lib/x86_64-linux-gnu/libc.so: invalid ELF header)

I could probably make adjustments to support that, could you open an issue?

@mflatt or @thinkmoore Ya, I can’t get atexit
from libc
. The error I get is: ffi-obj: couldn't get "atexit" from "libc.so.6" (/lib/x86_64-linux-gnu/libc.so.6: undefined symbol: atexit)

However, when I wrote a small C program to test it out: #include <stdlib.h>
void bob(void) {
return;
}
int main() {
atexit(bob);
return 0;
}

And ran ld a.out, I got:

ldd*

linux-vdso.so.1 (0x00007ffd2bb36000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe087a76000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe087e21000)

Thoughts?

FWIW, on osx I seem to be able to find the atexit symbol just fine.

Hmm…it looks like libc
doesn’t actually have an atexit symbol (as determined by nm --dynamic
), but that makes me still wonder how C linked to it.

Okay, time for another approach. @mflatt Do you have to call a close()
type of function on the GUI?

(That is a process level close()
)

It does seem like the linker on Linux does something special with atexit
… oh, well

No, the GUI doesn’t need a close()

I guess you could use scheme_add_managed_close_on_exit()
but keep a reference count: +1 when you would have called init()
in a new place, -1
on exit from a place

Use scheme_make_custodian(NULL)
to get a custodian that acts like the root one

… but I’m not sure that when the main place exits, then it will explicitly shut down other places

You might be able to use (_fun #:in-original-place? #t ...)
to register something in the main place

As I try to think of other strategies, part of the problem is that you can’t (I think) write a Racket callback in a non-main place that will work if that place goes away before the main place

@mflatt Ah, so like, whenever init would have been called, that’s whena new on_exit would have happeend?

happened*

I haven’t figured out anything that can work if the main place exits while some other place is still running. Even if you could reach atexit
, that wouldn’t work if some other place is still using the library while the main place is busy running atexit
callbacks.

Can you assume that places are never forcibly terminated (including by a main-place exit)? In that case, reference counting should work.

I just made an experimental PR on compose-app
(on which retry
depends).


Probably I should just write my own exp backoff retry in the tests for http
.

And, I could update the docs for http
to suggest, “hey if you’re using a newer Racket, retry
might be a great way to use http
” or something to that effect

Lets say for right now sure.

(Does raco setup
use places?)

Basically that’s what’s messing things up right now.

Anyway, I’m trying reference counting.

I’ll let you know how it turns out.

@mflatt Ha, it seems to work on my machines, but on travis CI I’m getting a #<bad-value>

Alternatively you could use Docker to run the test against a real server locally on Travis and not run that test normally

somewhere around 6.3 / 6.4 tends to be the oldest my stuff supports because that’s when the new scribble/example lib was added

@mflatt Do you see anything immediately wrong with this: https://github.com/LeifAndersen/racket-video/blob/master/video/private/init-mlt.rkt

Its how I’m doing the counter.

But it still breaks travis, so I’m clearly doing something wrong here.

The first thing I see is (scheme_register_process_global counter-key (cast (box 0) _racket _pointer)
That’s no good, since (box 0)
is a GCable value. Use (malloc 'raw ...)
.

@mflatt Oh good point, thanks.

That would explain why I was getting the bad value

Btw, I think the docs for malloc are wrong, because the parameter order it gives seems to be different from the one used.

Oh wait, nvm. I just missed the line in the pros.

prose*