Is there a way to turn off garbage collection inside of one function?

I have a program where, in one particular section it handles a callback from some hardware that needs to complete as fast as possible. Is there a way to stop the Gc from kicking in during the life of this short function?

Perhaps, do a major GC call right before calling this function? Admittedly, not a fool-proof method.

Not sure but maybe play tricks with the build system to mark it as external ?

external critical_fn = "%whatever_ocaml_opt_generated"

And you’ll have to link .o object file without the `.cmx

Is this “section of code” in Ocaml? If so, it’s tough. But if it’s in C, then I think one can simply acquire the global interpreter lock, and then proceed. If you end up allocating from the ML heap, you might GC (of course) but one presumes that this C code isn’t going to do that, right?

Other threads that want to GC will be blocked, b/c your C function holds that lock. Also though, in a single-threaded program, though it’s good hygiene to properly manage those locks, it isn’t strictly necessary – no other thread is gonna come along and wanna GC, after all.

At least, that’s how I remember it all working.

It’s an OCaml function. I’d really rather not write it in C, if I can help it.

And are you in a multi-threaded context? [ETA: sorry, meant to say “multi-threaded executable”]

Yes, the program is threaded as well.

In that case, there’s not much you can do, yes? The other thread could allocate, causing a GC, and that GC could stop the world. Even your own thread could allocate, causing the same.

There’s been an entire thread of work on realtime GC (and also in an SMP setting) specifically to address this issue.

You can write your code in Rust though, not in C, with the help of ocaml-rs.

1 Like

In that case, there’s not much you can do, yes?

Yeah, it seemed tough when I asked it. Thanks for the feedback.

I’m a little concerned that for my real time needs here, I’ll have to have two programs running; the normal OCaml application, and a separate smaller program written in, say, Rust, to handle the hardware callback. With some clever use of shared memory to tie it all together.

I have noticed this project! It’s nice that there exists a pretty good path now to (along with ctypes) never having to write C ever again.

Honestly, this is probably the best way to do it. Even if Rust isn’t suitable, I’m sure you could use C++ …

Well the idea is to write it in Ocaml (and avoid memory allocation) and to present it to the rest of program as a C function.

Or, maybe, just avoiding the memory allocation should be enough. Depends on how GC is triggered.

Hmm, wouldn’t there be a second problem even starting the callback function if the program is executing another thread, because it would have to wait for the runtime lock?

I think you’re asking in response to my comment that if you have multithreaded code, then you can’t really stop the GC from happening on another thread. My assumption there was that your callback was C code, not ML. In any multithreaded setting, you can’t assume anything about delays incurred by your ML code, both due to GC, and due to being time-sliced out by other threads.

Oh, yeah, that too. Hmm, so I started this asking about GC but there’s actually multiple realtime performance issues.

Determined

  • can’t pause GC
  • may be able to minimize when GC runs in my thread by not allocating, but could still get blocked by other threads running GC
  • can’t control when callback will actually start because it may not be able to pre-empt other threads (whether they’re running GC or not)
  • can’t stop callback from being pre-empted by other threads (whether they run GC or not)
  • strict OCaml based solution isn’t looking good

Way forward

Call into C/C++ Rust via the FFI interface ocaml-rs, release the runtime lock, start a thread to handle the callback. Coordinate via IPC like shared memory (perhaps OCaml bigarrays that can be treated like C-layout arrays).

Missing anything?

EDIT: giving the thing that calls into callback function a higher thread priority might help in the FFI case, since it can prevent other (OCaml) threads from pre-empting it or pre-empt other OCaml threads if CPU is needed