This is a rust library around the psrdada library commonly used in radio astronomy. Unfortunately, the C library is for the most part undocumented, so the behavior presented by this rust library is what the authors have been able to ascertain by reading the original example code. As such, this might not be a 1-to-1 implementation of the original use case.
Use this library if you want a safe abstraction around working with psrdada. As in, use this library if you need to interface with applications that are expecting psrdada buffers. Do not use if you don't have to, as it (psrdada itself) isn't as performant or featureful as other IPC libraries.
The rust library shmem-ipc has excellent performance over shmem, useful for large data transfers (like windows of spectral data). It creates shared ringbuffers, much like psrdada. Interfacing with D-Bus is fine for signaling and headers.
If you need CUDA support, NVSHMEM is a thing that exists, and you should use it. Also, linux has mkfifo which works fine with CUDA as discussed here.
Lastly, there is ipc-channel, which uses the Rust channel API over OS-native IPC abstractions. It's a really nice library.
In short, if you are constructing a pipeline from scratch, don't use psrdada. There are more mature, documented, more performant alternatives.
We are building and linking the psrdada library as part of the build of this crate, which requires you have a working C compiler. See the cc crate for more details.
The most simple way to use this library is to use the top-level push
and pop
methods.
```rust use std::collections::HashMap; use psrdada::builder::DadaClientBuilder;
let key = 0xb0ba; let mut client = DadaClientBuilder::new(key).build().unwrap();
let data = [0u8, 5u8, 10u8]; let header = HashMap::from([ ("foo".toowned(), "bar".toowned()), ("baz".toowned(), "buzz".toowned()), ]);
client.pushdata(&data).unwrap(); // Unsafe as we're not checking if the keys and values are valid unsafe { client.pushheader(&header).unwrap() }; ```
Beyond this, you can split
the DadaClient
into separate clients for headers and data, which can then be read and written to/from.
The original library is intrinsically unsafe as it is written in C. This library tries to ensure at compile time some of the things the
C library checks at runtime. For example, If you try to write to buffer while something else is trying to read (from the same DadaClient
), this
would usually fail a lock. Instead, in this library, we use Rust's borrowing system to ensure you can't build both at the same time.
Take the following code as an example
```rust use std::io::{Read, Write};
use lending_iterator::LendingIterator; use psrdada::builder::DadaClientBuilder;
// Build the paired client let key = 0xb0ba; let mut client = DadaClientBuilder::new(key).build().unwrap();
// Split into individual clients let (, mut dataclient) = client.split();
// Construct the writer (mutable borrow) let mut writer = data_client.writer();
// Grab the next block in the ring (assuming we can) let mut write_block = writer.next().unwrap();
// Write using std::io::Write so you can write chunks at a time writeblock.writeall(&[0u8; 10]).unwrap(); write_block.commit();
// Construct the reader (mutable borrow) let mut reader = data_client.reader();
// Grab the next read block in the ring let mut read_block = reader.next().unwrap();
// Read using std::io::Read let mut buf = [0u8; 10]; readblock.readexact(&mut buf).unwrap(); ```
without that write_block.commit()
line, this code would not compile as there still exist a write in progress.
Additionally, you can only ever split
once, so you'll only ever have a single reader and writer for each type.
ipcio_t
or dada_hdu
.They are wrappers around ipcbuf_t
and have all sorts of undefined behavior.
Specifically, ipcio_t
reimplemented stdlib read
and write
behavior, but in unsafe ways.
Our abstraction presented here reimplements the behavior, but with Rust's compile-time guarantees.
dada_hdu
combines two ipcbuf_t
s, the header and data buffers.
However, doing so breaks CUDA support (for some reason) and messes up the signaling of successful reads.
End of data doesn't prevent us from reading more data or writing more data. It is just a signal we can observe.
The iterator interface we provide will produce None
if we run out of data, trying to be consistent with what that
might mean. Additionally, there is a very specific order in which eod is set and read. It must be set after mark_filled
and before unlock_write
. It must be read after mark_cleared
and before unlock_read
. Any other ordering doesn't work.
psrdada-rs is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See LICENSE-APACHE and LICENSE-MIT for details.