Skip to content

Optimize how blocking I/O interacts with Go scheduler #100

Description

@gavv

Sender and Receiver both have ClockSource field in their config structs, with two possible values:

  • ClockExternal - Receiver.ReadFloats and Sender.WriteFloats are non-blocking; the user is resposible for invoking these methods at appropriate time (in other words, the stream clock is managed by user, thus "external clock")

  • ClockInternal - Receiver.ReadFloats and Sender.WriteFloats are blocking; they will automatically block to align read and write operations with the stream clock (in other words, the stream clock is managed by library, thus "internal clock")

Current implementation of Receiver.ReadFloats and Sender.WriteFloats just invokes corresponding C function; if blocking is needed (for ClockInternal), it is done inside the C code.

However, this does not play perfectly well with the Go scheduler. Scheduler threads will idle until sysmon detects that we've blocked, instead of performing useful work, and then will have to do some extra houskeeping before proceeding.


We could avoid this the following way, for Sender:

  • If ClockSource is ClockInternal, OpenSender creates a channel and background goroutine. Sender.Close asks background goroutine to interrupt and waits until it exits.

  • Background goroutine calls runtime.LockOSThread to detach from Go scheduler and starts reading from channel.

  • Sender.WriteFloats writes frame to channel.

  • Background goroutine reads frames from channel and passes them to the C function.

  • Sender.WriteFloats waits until background goroutine finishes handling the frame.

This way, Sender.WriteFloats will block on a Go channel instead of blocking in C code, which will integrate well with the Go scheduler (it will efficently switch to other goroutines when the channel is blocked). Blocking inside C code will happen on a dedicated goroutine detached from scheduler, which won't harm it.


Receiver should be addressed the same way, but the flow of samples will be the opposite (from C to Go, not from Go to C).


A unit test should be added both to receiver_test.go and sender_test.go for I/O in internal clock mode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedContributions are welcome

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions