Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find a way to stack allocate sockets #789

Open
yorickpeterse opened this issue Dec 16, 2024 · 2 comments
Open

Find a way to stack allocate sockets #789

yorickpeterse opened this issue Dec 16, 2024 · 2 comments
Labels
feature New things to add to Inko, such as a new standard library module runtime Changes related to the Rust-based runtime library std Changes related to the standard library

Comments

@yorickpeterse
Copy link
Collaborator

Description

#783 introduces the ability to define inline types capable of storing inline, copy and heap types.

Sockets are still heap allocated however. This is because internally the runtime mutates the registered flag atomically, and such changes need to be visible across borrows.

We need to find a way such that we can either get rid of the mutations of this field, while still being able to (de/re)register the socket whenever necessary.

Related work

@yorickpeterse yorickpeterse added feature New things to add to Inko, such as a new standard library module runtime Changes related to the Rust-based runtime library std Changes related to the standard library labels Dec 16, 2024
@yorickpeterse
Copy link
Collaborator Author

This also requires changes to the deadline API: we rely on setting a deadline field on the socket, which won't work if we define it as inline. I'm not a fan of this API to begin with, so perhaps there's a better alternative worth looking into.

yorickpeterse added a commit that referenced this issue Jan 28, 2025
This allows these types to be stored on the stack at the cost of
restricting borrows to method receivers. This in turn reduces the cost
of creating files and sockets, and reduces their size by 16 bytes as
they no longer need to store an object header.

The reason we can't use inline types is because of how borrowing works
for inline types: the stack data is copied and any interior heap data is
borrowed individually. For files this can result in the original "owned"
value being dropped while borrows still exist, which then results in a
runtime "file is closed" error when you attempt to use the file. In the
case of sockets this can result in the owned reference and borrows
observing a different state, as the runtime may need to update the
"registered" field of a socket.

In the case of sockets we also need to be able to update the "deadline"
field, and inline types don't support field assignments as this can lead
to unexpected results (= the assignment is applied to a copy).

Since files and sockets are almost always used through an owned
reference (and if not it's usually easy enough to switch to doing so),
this change shouldn't be too disruptive. One can also use a linear
typing pattern where some type/function takes over ownership of the
value and then returns it back along with its results, e.g:

    let file = ReadOnlyFile.new(...)

    match some_function(file) {
      case (file, result) -> {
        ...
      }
    }

This also means one can't cast values of these types to traits. This is
something that at times may prove useful when writing tests as it allows
one to substitute a file/socket/whatever with some sort of mock object.
However, this often results in poorly written tests that essentially
just test if the mock object works, not if the production code works,
and thus this is something I don't want to promote. If this is truly
necessary, one can always create a custom heap allocated wrapper type.

This fixes #789.

Changelog: changed
@yorickpeterse
Copy link
Collaborator Author

yorickpeterse commented Jan 31, 2025

For deadlines I think we can do the following: instead of storing them in a field, we register them with the timeout worker in some sort of file descriptor => deadline map. When performing an operation we get the deadline from that map (if there is one).

This removes the need for storing the deadline in each socket, but at the cost of socket operations always having to go through a shared (and synchronized) map of some sort. This could prove to be a bottleneck for busy systems. To reduce the cost of that we'd have to stick it behind the same lock that's used to access the timeout heap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New things to add to Inko, such as a new standard library module runtime Changes related to the Rust-based runtime library std Changes related to the standard library
Projects
None yet
Development

No branches or pull requests

1 participant