samedi 24 avril 2021

Designing shared ownership of a USB handle in Rust

During implementation of a low-level USB driver in Rust, I've bumped into a design problem. I have the following components that need to play together:

MyDriver high-level API

A high-level public API which serves as the main entry point for driver calls:

pub struct MyDriver {
    device_handle: MyDeviceHandle,
    device_type: Box<dyn DeviceType>
}

Device Handle

The device handle wraps around the rusb DeviceHandle and implement some driver-specific logic around libusb calls.

extern crate rusb;

use rusb::{DeviceHandle, GlobalContext};

pub struct MyDeviceHandle {
    usb_handle: DeviceHandle<GlobalContext>
}

Common Library Functions

Most common functions are handled easily just by passing them to the USB handle:

impl MyDriver {
    pub fn foo(self) {
        self.usb_handle.handle_foo();
    }
}

Device-specific Functions

This is where things get tricky. Some library functions depend on the active device found at runtime. So I created a common trait for that, and the crux is that each DeviceType also requires access to MyDeviceHandle in order to implement its logic.

impl DeviceType for DeviceA {
    pub fn goo(self, usb_handle: &MyDeviceHandle) {
        usb_handle.one_thing();
    }
}

impl DeviceType for DeviceB {
    pub fn goo(self, usb_handle: &MyDeviceHandle)
        usb_handle.other_thing();
    }
}

Currently the only way to have MyDeviceHandle available to each DeviceType is to pass a reference to it in each implemented function.

Is there a nice way to have a struct that has some shared ownership of MyDeviceHandle that can be instantiated upon construction and then re-used from each function?

Aucun commentaire:

Enregistrer un commentaire