question about places and main thread gc

92 views
Skip to first unread message

Nate Griswold

unread,
Sep 13, 2020, 3:56:14 AM9/13/20
to Racket Users
Hi.

I am making an app that basically spawns two racket places and i want to be able to communicate with them from c code.

Will gc happen in the two racket places if i don't keep the main thread (the one that spawned the places) running? I was thinking about whether i should keep the main thread running and block on a stream read, sending it messages, or if i can only call into the main thread when i need something using racket/chezscheme apis. I guess it would be simpler to just talk directly to the main thread when i need to, which is why i'm asking. Otherwise i'm thinking of just using zeromq on the main thread.

Thanks

Nate

George Neuner

unread,
Sep 13, 2020, 1:17:49 PM9/13/20
to Nate Griswold, racket users


On 9/13/2020 3:55 AM, Nate Griswold wrote:

I am making an app that basically spawns two racket places and i want to be able to communicate with them from c code.

Will gc happen in the two racket places if i don't keep the main thread (the one that spawned the places) running?

Exiting the main thread kills the process.  "dynamic"[1] places are (OS level) threads within the same process and they will die with it.  [Technically the main thread is itself a place, but normally we don't use place  terminology when talking about the main thread unless there are other places involved.]


I was thinking about whether i should keep the main thread running and block on a stream read, sending it messages, or if i can only call into the main thread when i need something using racket/chezscheme apis. I guess it would be simpler to just talk directly to the main thread when i need to, which is why i'm asking. Otherwise i'm thinking of just using zeromq on the main thread.

You certainly can talk to any of the places individually ... but you do need the main thread to remain running (even if just waiting on some event) if you want dynamic places to continue running.


Now "distributed"[2,3] places are separate processes - you can start them and they will continue running regardless of what happens to the process that spawned them.

Nate

Hope this helps,
George

[1]  https://docs.racket-lang.org/reference/places.html
[2]  https://docs.racket-lang.org/distributed-places/index.html
[3]  https://pkgd.racket-lang.org/pkgn/search?q=loci

Nate Griswold

unread,
Sep 13, 2020, 4:13:05 PM9/13/20
to George Neuner, racket users
Sorry, i forgot to mention this would be interfacing on the main thread from c

does this still hold true? Like if a c call returns does it kill the places?

Nate

Nate Griswold

unread,
Sep 13, 2020, 5:55:13 PM9/13/20
to George Neuner, racket users
The reason i'm asking is i am dedicating a thread to racket after calling racket_boot on it.

Nate

George Neuner

unread,
Sep 13, 2020, 6:40:46 PM9/13/20
to Nate Griswold, racket users

On 9/13/2020 4:12 PM, Nate Griswold wrote:
Sorry, i forgot to mention this would be interfacing on the main thread from c

does this still hold true? Like if a c call returns does it kill the places?

Nate

I'm not really sure what you are asking:  it sounds like you are wanting to embed Racket into a C program ... if that is the case, then the primary thread is in the C program and any/all Racket threads will be secondary.

I don't know that Racket's place API even works in an embedded scenario.  You certainly can create multiple threads within your C program and run Racket in them, but it's hard to share a Racket environment, and if you create a separate execution environment for each thread, then their operations will be independent of one another.

If you just call a Racket function from C, that function will be executed in the same thread as the caller, and it will return to the caller when it finishes.  Of course that function could have side effects such as signaling a separate Racket thread to terminate.

A lot depends on what you are trying to do and how you structure your solution.


George


Nate Griswold

unread,
Sep 14, 2020, 1:34:23 AM9/14/20
to George Neuner, racket users
Sorry, it's all a bit simpler than i'm making it out to be.

Basically, i want to run racket from c code (embedded i guess, but i'm not compiling any embedded c files). This is because i want to run everything in one process. I want to have actual parallelism so that is why i'm using places. I have one main racket thread that i currently have an infinite loop using zeromq to listen for messages on. This is a dedicated thread. But first on that thread i set up two other racket places to do my concurrent work. The main racket thread then accepts messages and dispatches to the two places depending on what the message is.

So the question is simply: do i need to have my infinite loop on my main thread sitting there waiting for messages (and have custom communication), or is there some better way to do it like just using the standard racket cs calls.

If i understand correctly, in racket cs embedded if i am not currently running anything in the main racket thread then gc cannot happen. But the next time i make a call into racket on that reserved racket thread (which has not been shut down, and by using racket_apply or some such) then gc can happen. But i do not know about the other threads that racket has spawned.

Thanks

Nate

Matthew Flatt

unread,
Sep 14, 2020, 7:47:34 AM9/14/20
to Nate Griswold, racket users
At Mon, 14 Sep 2020 00:34:08 -0500, Nate Griswold wrote:
> If i understand correctly, in racket cs embedded if i am not currently
> running anything in the main racket thread then gc cannot happen. But the
> next time i make a call into racket on that reserved racket thread (which
> has not been shut down, and by using racket_apply or some such) then gc can
> happen. But i do not know about the other threads that racket has spawned.

In Racket CS, you can enable GC without the main thread by deactivating
the thread. At the Racket level, use `#blocking? #t` for a foreign
function to deactivate the current thread while calling the function.
At the C level, you can use `Sdeactivate_thread` and
`Sactivate_therad` from Chez Scheme's API.

Racket BC doesn't have a notation of deactivating a thread. Most GCs
with BC can run in separate places even without the main thread active,
but the main thread is needed when there has been enough shared-space
allocation that all threads must be involved.

One caution for both CS and BC, though: Some foreign-library bindings
use `#:in-original-place?` to make use of the foreign library
single-threaded by routing all calls through the main place. That
requires the main thread to be active.


Matthew

Nate Griswold

unread,
Oct 1, 2020, 7:58:53 AM10/1/20
to Matthew Flatt, racket users
Thanks, Matthew. That helps. I was working on my project again and this came up again, but I still don't quite have my use-case figured out. I have two additional (in addition to main thread place) places that i wanted to send messages to using standard chez and racket c calls (and not relying on something like zeromq or file descriptors). I wanted to allow garbage collection and `#:in-original-place?` dependent ffi libs to work correctly. Then i guess I need to keep my original place in scheme code and *not* `Sdeactivate_thread`ed most of the time to make this work. I had the idea from what you said that i might Sactivate_thread a completely different os-level thread in order to call into scheme using say racket_apply on `place-channel-put`. Can i do this or no? I was thinking no because...

From the docs for `#:in-original-place?`:

"""
If in-original-place? is true, then when a foreign callout procedure with the generated type is called in any Racket place, the procedure is called from the original Racket place. Use this mode for a foreign function that is not thread-safe at the C level, which means that it is not place-safe at the Racket level. Callbacks from place-unsafe code back into Racket at a non-original place typically will not work, since the place of the Racket code may have a different allocator than the original place.
"""

I guess this means os threads use completely different allocators and sharing data among different `Sactivate_thread`ed threads doesn't make any sense at all. Is there any way to do this (command my places using the basic chez and racket funcs like racket_eval and Scons and racket_apply) or should i just use messaging over zeromq or an fd to my main thread? Maybe if place descriptors are guaranteed shared among all os-level threads then i can do it. I guess if `#:in-original-place?` exists there must be some way to do it, but maybe it's not exposed to the user. Also, is there any way to get the place descriptor for the current place or the main place? I didn't see any in the docs. I guess i should just start reading the racket source at this point.

Also maybe i'm missing a simpler solution. Any help would be appreciated. Thanks.

Nate

Nate Griswold

unread,
Oct 1, 2020, 8:38:26 AM10/1/20
to Matthew Flatt, racket users
I looked into it, it seems to be implemented in `src/cs/rumble/foreign.ss` using chez get-thread-id, comparing it to 0 and using a stored ref to the original async callback queue, so looks like this is not exposed to the user. Hm.

Nate

Matthew Flatt

unread,
Oct 1, 2020, 9:21:38 AM10/1/20
to Nate Griswold, racket users
You're right that the main place is tied to the OS thread that is used
to start Racket, so you can't move the place over by activating a
different thread for the same place later. You can start Racket on an
OS thread other than the process's main thread, though.

The question of sharing allocated data among places is complicated. For
BC, different places use different allocators, so it basically doesn't
work (although it's possible to use no-moving objects that are GC
managed but still retained in the original place as long as they're
accessed anywhere else). For CS, there's currently only one allocator
for all places. That probably won't change, but it seems likely that CS
places will become more distinct in some way in a future
implementation, such as having different symbol tables while still
having the same allocator.

You can share memory allocated in 'raw mode among places in both BC and
CS, since that amounts to calling the C library's `malloc`. Going that
direction may end up similar to using something like zeromq, though.

You're right that there's not currently a way exposed to get the
identity of the current place or to check for being in the original
place.

Matthew

At Thu, 1 Oct 2020 07:38:09 -0500, Nate Griswold wrote:
> I looked into it, it seems to be implemented in `src/cs/rumble/foreign.ss`
> using chez get-thread-id, comparing it to 0 and using a stored ref to the
> original async callback queue, so looks like this is not exposed to the
> user. Hm.
>
> Nate
>
>
> On Thu, Oct 1, 2020 at 6:58 AM Nate Griswold <nategr...@gmail.com> wrote:
>
> > Thanks, Matthew. That helps. I was working on my project again and this
> > came up again, but I still don't quite have my use-case figured out. I have
> > two additional (in addition to main thread place) places that i wanted to
> > send messages to using standard chez and racket c calls (and not relying on
> > something like zeromq or file descriptors). I wanted to allow garbage
> > collection and `#:in-original-place?` dependent ffi libs to work correctly.
> > Then i guess I need to keep my original place in scheme code and *not*
> > `Sdeactivate_thread`ed most of the time to make this work. I had the idea
> > from what you said that i might Sactivate_thread a completely different
> > os-level thread in order to call into scheme using say racket_apply on
> > `place-channel-put`. Can i do this or no? I was thinking no because...
> >
> > From the docs for `#:in-original-place?`:
> >
> > """
> > If in-original-place? is true, then when a foreign callout
> >
> <https://docs.racket-lang.org/foreign/foreign_procedures.html#%28tech._callout%
> 29>
> > procedure with the generated type is called in any Racket place
> > <https://docs.racket-lang.org/reference/places.html#%28tech._place%29>,
> > the procedure is called from the original Racket place. Use this mode for a
> > foreign function that is not thread-safe at the C level, which means that
> > it is not place-safe at the Racket level. Callbacks
> >
> <https://docs.racket-lang.org/foreign/foreign_procedures.html#%28tech._callback
> %29>
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-users...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/CAM-xLPp_vT-DadahcHh57d9EB5nw8mH
> NeVzK1Pp42GVLCzbySA%40mail.gmail.com.

Nathaniel Griswold

unread,
Oct 1, 2020, 10:17:18 AM10/1/20
to Matthew Flatt, racket users
Cool. Thanks. I can play around with an activated thread to try and make the calls I want. I guess i wasn’t clear on the difference between an activated manually made thread and a place. Will manually made activated threads always share the same allocator in new versions of racket cs? Will I always be able to share data between these os threads as long as I just use standard synchronization techniques? Is the question of sharing data among manual activated os threads less complicated than places? Is a new manual thread a new racket place or is it not a place at all?

Thanks!

> On Oct 1, 2020, at 8:21 AM, Matthew Flatt <mfl...@cs.utah.edu> wrote:
>
> You're right that the main place is tied to the OS thread that is used

Matthew Flatt

unread,
Oct 1, 2020, 10:37:16 AM10/1/20
to Nathaniel Griswold, racket users
A new manual thread is not a new place. If places in CS change to have
distinct allocators (unlikely) or symbol tables (likely), then I expect
that activation would change to support specifying the place and use
the original place by default. Individual activated threads certainly
would not have separate allocators. It's always fine to use OS-level
synchronization among threads and different places, but beware that
blocking operations can interfere with Racket thread scheduling.

Sharing among activated threads in CS might be simpler than sharing
among places in that you could be guaranteed a shared allocator. Note
that `ffi/unsafe/thread` will let you create new threads of that sort
in CS already (and that won't change) --- it's the same as creating an
OS thread manually and activating it --- but see the caveats in the
documentation for `call-in-os-thread`.

Nathaniel Griswold

unread,
Oct 1, 2020, 10:47:17 AM10/1/20
to Matthew Flatt, racket users
Thanks!!

> On Oct 1, 2020, at 9:37 AM, Matthew Flatt <mfl...@cs.utah.edu> wrote:
>
> A new manual thread is not a new place. If places in CS change to have
Reply all
Reply to author
Forward
0 new messages