The Single Threaded Scheduler
Note: The ST Scheduler is no longer in tree, but this is just an example of the Scheduler Interfaces
Reference commit
Now that we have seen how the scheduler interfaces to other things, let’s implement a simple scheduler
As its name would indicate, the Single Threaded (ST) Scheduler has one thread that is going to try and do work on the blocks assigned to it. First, let’s look into the components that make up the ST scheduler
There is more to the ST scheduler when we start looking at interfaces between schedulers, but that has been stripped down as of this commit.
Buffer Management
As mentioned in the buffers section, the scheduler is responsible for allocating the buffers that are associated with the edges of the graph
The buffer manager:
- looks through the edges in the graph
- calls the custom or default buffer factory
- catalogs the buffer with a reference to the edge to be used later
Another key operation of the buffer manager is setting the right number of items in each buffer. Currently much of this logic from GNU Radio has been stripped out (output_multiple, interpolation rates, etc), but it is intended that the get_buffer_num_items
will operate similarly and look upstream and downstream determining how large each buffer should be
Graph Executor
The graph executor is directly analagous to block_executor
in GR, the name change indicating that it is not just a single block in a thread, but a graph.
initialize
Construct with a pointer to the buffer manager, and a list of blocks
run_one_iteration
This is called repeatedly from the thread when it is notified that there is work to be done. In the case of the ST scheduler, it notifies itself!
The basic logic of run_one_iteration
is:
- For each block
- Find the appropriate buffers that correspond to its ports
- Query the input and output buffers for read/write availability
- (is there work to be done)
- If work is to be done, then call the
do_work
method with the constructedwork_input
andwork_output
structs - Update the buffers using the
post_read
andpost_write
methods - Handle tags somewhere in there as well
- Return an enum with the status of each block
The status is similar to that from GNU Radio:
enum class executor_iteration_status {
READY, // We made progress; everything's cool.
READY_NO_OUTPUT, // We consumed some input, but produced no output.
BLKD_IN, // no progress; we're blocked waiting for input data.
BLKD_OUT, // no progress; we're blocked waiting for output buffer space.
DONE, // we're done; don't call me again.
};
If an output port of a block is connected to multiple input ports (of other blocks), a copy method is called since each edge has a unique buffer. This is an over-simplification that needs to be fixed via a buffer_reader
class, but is not yet implemented
p_buf->copy_items(_bufman->get_input_buffer(p),
work_output[output_port_index].n_produced);
Thread Wrapper
The thread wrapper is responsible for spawning the thread and handling messaging from the input queue which normally means calling the graph_executor
Execution is started by placing a NOTIFY_ALL
message into the queue, which causes handle_work_notification
to be executed, and in turn calls graph_executor::run_one_iteration
Execution is stopped by placing an EXIT
message in the queue which gracefully exits the thread