I'm writing an emulator of an old computer in Java/Swing, and I think I've identified the design problem that I'm having. As idiosyncratic as this application is, I suspect someone will find a "pattern" to this problem.
I should add that I'm still a beginner at OOP, GUIs, and Design Patterns.
The machine has a GUI thread (Console) - with pushbuttons and switches, and a Model thread (CPU) with which the Console communicate to cause Console events to change the CPU's state. The Console, of course, is event-driven from the AWT event queue. The Console communicates with the CPU by queueing messages on a Priority Blocking Queue, which the CPU receives. As such, the CPU is structured as an event-loop, too. So far, so good.
The problem is that, when you push START on the Console, you want the CPU to start executing whatever program it has in its memory. It still needs to respond to switch throws and button-pushes (such as STOP) from the Console, but it mainly needs to sit there and spin through its instruction fetch-decode-execute loop.
And even that wasn't problematic for a while: I had a method called Cycle() that would execute one specific "cycle" of the current instruction and then return, to be immediately redispatched to execute the next cycle. I placed the call to Cycle() inside the CPU's run loop, and polled the message queue after every cycle. If the CPU was stopped, the run loop would simply wait on the message queue.
Now: I'm implementing the I/O instructions, e.g. Read Card. It's necessary for one of the cycles to send a request for data to the peripheral device in question (itself implemented as a GUI/Model running on separate threads) and then wait for the data to arrive. This completely breaks the whole notion of the CPU being a simple event-loop that receives messages and acts upon them without incurring blocking in the process. This new cycle will block. And if the operator hasn't loaded a card deck into the reader, it can block a long time.
I thought about factoring the instruction fetch-decode-execute loop out into a separate "worker" thread, but I don't think it's a good fit, since worker threads (as I understand them) are intended to run asynchronously to completion, and do not continue interact with their parent thread while running. (In fact, I can't think of why the "worker thread" should ever terminate.) Also, there is currently no synchronization necessary when a cycle needs to access data that can concurrently be modified as a result of console key presses.
So how do I manage to meld "event-driven" processing with a traditional batch-process which needs to explicitly wait for messages before proceeding?