Queue suggestion & ownership/locking concerns
I've been pondering a design issue and I'm wondering if this wonderful and amazing group of developers and professionals has any suggestions.
I have a device. My only access to this device is over USB D2XX. It's reasonably fast I/O but some things of course take time like moving a motor. Other things like turning on or off a heater or checking a sensor is just pretty much the USB I/O overhead.
My question is that this USB port in effect is my bottle neck. I have to cram everything I want to do through this interface and some things I need to do might take several USB transactions. A bit later on there will be "scheduled" items. For example I'll turn on a heater. Then 15 minutes later pretty much to the second I'll turn it back off. I would consider creating two tasks, one that turns it on, another that is scheduled to be run 15 minutes later that turns it off. Both of these would go into the queue.
I'm considering having an instrument class with a thread worker class. Instrument will be a singleton and once initialized the thread worker will start. This thread will live for the life time of the program. It's primary job will be to fetch things to do from a queue, send or execute the USB I/O and move on to the next task.
In some ways this concept reminds me of my old token thread interpreter days. The token thread was the queue, each byte we got we called a function. In my pondering my token thread becomes a list of task objects.
As I mentioned above some things are scheduled so the logic in the thread will have to pre-fetch a thing to do and see if it is legal to do it based on schedule time. There will be immediate tasks that can just happen when they happen and there will be scheduled tasks that pretty much must happen at the right time.
I'm considering creating a task class. For every task I want to do I'll create a task object, push it on to a queue (probably owned by the instrument worker). The instrument thread worker needs to continuously be fetching items from this same queue. If the item is an immediate task it gets done right way. If it is scheduled then it is ignored until the scheduled time is met.
I need to be careful that a whole bunch of immediate tasks don't swap the queue making the scheduled task fall behind. My plan there is to in effect make every task internally a scheduled task. Some are just scheduled for right now. But the very act of pushing a new task on the queue tries to figure out if there is time to do it "right now" and makes sure it will not conflict with a scheduled task.
Here are my specific questions:
Thoughts on the overall design pattern? Are there better ways to achieve what I want to do? I realize this is vague but imagine I have other threads or event loops that need data through this USB interface. I think having some sort of a manager or thread that is taking care of this I/O makes sense.
Ownership of tasks. Some tasks are just on/off. Others need to return data or perform long operations like moving a motor. So my plan was that all tasks would have a state (mutex protected). A task state will go from something like created, to queued (or scheduled), to executed. As part of the task data I would also have flags to tell if the task can be deleted by the instrument worker once complete or if it must be left in existence so some other object that created it can get the results it needs.
My concern is that the instrument worker needs, I think, to own the schedule queue. i'm saying queue but it might be a QList. Not sure of the data container yet. I'll need to have a method that can accept an externally created task object and added it to the queue/list. In some cases the instrument worker will be killing the items on the queue when they are done, in other cases it must leave them alone, filling them with result data and letting some other process handle them.
So I am imagining "push and forget" type tasks where the originator, creates, pushes the task and the flags are set so the instrument worker does the work then kills the task. The other case will be data return tasks like getting a heater temp. The instrument worker will send the commands and stuff the data in the task and set the state to executed but then it just lets it go. The idea would be that some other process is checking for this task to be complete and will handle the data.
I got to thinking about signals. I remember reading that signals are safe across threads. Please correct me if I'm wrong. If that's true than I can define the task object to have a signal when executed. When creating the task I can connect its signal to a slot to take the data and do something with it.
This brings up the question of cleaning up the signals and slots. When you delete one of these tasks that might have signals connected to slots do you have to disconnect?
Anyway I realize this is vague but please understand I'm in idea mode at this point. So far I've done everything I need with simple event passing however simple things like dragging the window across screen cause the event loop to go crazy. I hadn't planned to use events in the end. I just wanted to get something working.
Now that my electronics guy has delivered the first set of device electronics I came to realize my bottle neck will be this single USB connector. Everything must flow through that. So I started to envision queues and lists and tasks. I'd love to hear other ideas and experiences.
Let me know your thoughts please! Thanks in advance!