#Hard memory bounded networking in a game loop

20 messages · Page 1 of 1 (latest)

jagged oasis
#

I want to have a clear, very well constrained bound on memory usage in MUD (text based online game). My design concept:

  1. Allocate a buffer after first getting data from a network socket.
  2. Parse the text as it arrives, but don't copy it out, keeping references to the buffer.
  3. Respond instantly to some network inputs that dont require any game state to process (negotiation etc).
  4. Read and respond to events that have showed up during each simulation tick (likely, only popping events at a certain frequency as determined by game logic).
  5. If more data shows up then can fit in the buffer, respond accordingly (dropping data or dropping the client as needed).

My problem is that I can't quite identify what, if any, existing data structure setup to use for this. My best guess is a simple u8 vec with a lock, and a tokio(?) networking stack that pushes parsed messages to a queue with indexes instead of references, but that seems horribly unergonomic and will require a lot of manually ensuring buffer bounds aren't broken. Has anyone solved anything like this before?

ebon knoll
#

The data structure for this is technically the boxed slice

#

Box<[u8]>

#

Create one from a Vec<u8> with .into_boxed_slice()

jagged oasis
#

Right, thanks, thats a good point. Is there something higher level?

ebon knoll
#

Not that I can think of, no. What are you looking for in particular?

jagged oasis
#

IDK. Something like BytesMut, but that tracks which chunks have been freed and can reuse them? At least, I cant figure out how to use that here.

ebon knoll
#

...So, bytes::BufMut is implemented for &mut [u8], Vec<u8>, but not Box<[u8]>, fun

jagged oasis
#

like, my logical operations are
(1) read socket to circular buffer
(2) mark parts of the circular buffer as "in use" so that "references" to it can be sent elsewhere (like, cross thread)
(3) mark parts of the circular buffer as "freed", so that reads can start reusing the space
not worried about fragmentation, since frees should be in-order

last oracle
#

You could use an arena allocator

jagged oasis
#

hmm. lemme take a look at the api, cause I am not quite sure how I would read from a socket.

ebon knoll
#

Arenas can generally only free all at once

#

You could use a bounded allocator, but at the cost that running out of memory will abort (unless you go out of your way to use the fallible allocation methods)

jagged oasis
#

ah, that would work if I didn't leave stuff queued

#

my problem is less the allocation, and more trying to minimize copies

#

I mean, worst case I can try to write one myself... probably using Bytes as a foundation?

ebon knoll
#

Bytes could work, yeah, it refcounts by default

#

Depending on what exactly you need, looking into zero-copy crates could be a good idea, and if you want to go extreme the im crate has whole data structures optimized for structural sharing

jagged oasis
#

that sounds good, ill look into it

remote flicker
#

Just as a note: Arc<[AtomicU32]> for the data buffer is also possible.
While you don't get references in the form of &[u8], you can read and write data from there with Relaxed ordering, no problem. And it should be decently correct to pass that buffer to any system interface.