Menu

#35 Use BUSY flag to prevent re-entrancy

TBD
open
None
5
2017-05-03
2017-05-03
No

Stephen Heumann wrote on comp.sys.apple2.programmer>

I'm working on a IIgs system extension (an init) that uses TCP/IP. I
would like it to be able to continually poll for incoming data and send
responses as long as GS/OS is running (regardless of the current
application), but I don't see an entirely satisfactory way to do that.

The basic issue is that it doesn't seem to be safe to call Marinetti
using interrupt-based timers (e.g. from a heartbeat task). This may
appear to work as long as nothing else is using Marinetti, but if
something else is, then I get dropped connections and other odd
behavior. I think this is because my code may wind up calling Marinetti
while the other program is also in the middle of a Marinetti call, and
since Marinetti is not (fully) reentrant this corrupts its state.

My understanding is that it's generally safe to make most tool calls in
such situations if the system busy flag is not set (and if it is set
then the scheduler can be used to defer the operation until it's not),
but this doesn't seem to make a difference with Marinetti. As far as I
can tell from looking at the MOSP code, Marinetti never increments the
busy flag, so it's not surprising that this doesn't work.

My current workaround for this issue is to use a run queue task
instead. This more or less works in desktop programs, but it doesn't
work in other environments like textual shells, because they don't call
SystemTask and therefore run queue tasks don't get run. (There can also
be an issue if a desktop program is busy with something and doesn't
call SystemTask for a long time.)

Am I missing some good way to do what I want? If not, should Marinetti
be changed to make this possible? I think at a minimum it would have to
be modified to increment the busy flag around any non-reentrant
sections of code. It seems like this would be useful for a fairly wide
range of programs, including servers and programs that provide some
kind of system service using a TCP/IP connection.

Discussion

  • Andrew Roughan

    Andrew Roughan - 2017-05-03

    Email response from Richard to the above>

    Yeah Marinetti isn’t intentionally reentrant. Much of it probably is, but not enough to be used that way. Certainly a lot of the memory management would have to be spin locked or simply blocked and that would cause all manner of problems, not to mention code changes. And if they are blocked then interrupt code callers are going to get frustrated anyway. I’m actually surprised I didn’t prevent reentrancy from happening, it seems like an obvious thing to protect against in my testing. Maybe it’s mentioned in the manual?

    Marinetti was designed knowing that it had to minimise use inside interrupts. As you know Marinetti buffers incoming data using interrupts, but the processing for the various layers of packet assembly are too much of a performance overhead to be done inside an interrupt. Hence the runq and manual polling were added as the only viable options. At least for a standard speed Apple IIgs.

    Getting back to reentrancy, the busy flag sounds like a good thing to add, based on the description of it below. I’d have to go back and look at it to be sure though, because I’m not sure why I wouldn’t have included it considering it’s a standard thing. I seem to remember the system and interrupts manage the flag but the tools only read it. I don’t know. Maybe there’s a reason I didn’t include it. I know we manage it in the interrupt parts of Marinetti.

    On the other hand _TCPIPPoll was designed for this purpose, non-desktop code to give Marinetti control when it doesn’t have a runq. So CDAs, inits, text apps etc. _TCPIPPoll goes in their event or polling loop. Obviously if there’s some headless code that is only ever called by an interrupt then that’s going to be an problem, so it would depend on what else they could add to get a poll in there that’s not under interrupt.

    Keep in mind of course that as mentioned above, interrupt time is precious. So even if they could call Marinetti from an interrupt (I can’t think of any reason why Marinetti wouldn’t work that way, and possibly why we don’t check the busy flag?), I would still recommend against it. Any of the more common i/o calls you’d not want that much time used inside an interrupt. Marinetti does a lot of memory management for example, and you don’t want the memory manager moving stuff around under interrupt. Interrupt code should do the minimum, even if that’s just setting a flag and exiting (as per Apple’s recommendations), and then leaving the rest of the application to perform the flagged logic outside of the interrupt. Obviously that’s going to depend on the application. But again, that’s why all this logic is not handled inside Marinetti’s interrupts, it would cause data loss at high data rates due to missing interrupts. Marinetti’s serial port interrupt handler is highly optimised to handle incoming data without loss, to the point of rewriting part of the ROM’s interrupt handler. Obviously with fast emulators that becomes less of an issue, but not with a stock IIgs.

    So yeah, busy flag seems like a weird oversight on my part, if the explanation of it is correct (I don’t remember enough myself). But that would only prevent reentrancy, not allow it. A work around might be for the init to check the busy flag instead? And I wouldn’t add anything to Marinetti that would specifically allow interrupt code to call it’s functions, simply because most likely the system would slow down (interrupts + slow speed mode), the mouse would stutter (losing interrupts), and there’d be data loss.

    By the way, the heartbeat interrupt is already a big performance problem, from memory it disables interrupts too much. And it’s always interrupting, even if there’s no events to handle. I did look into using this for Marinetti’s main logic, but bumped into the same problems mentioned above (interrupts disabled for way too long), hence the runq and manual polling solutions.

    Moving their logic outside of the interrupt would be the best option here. Unfortunately in my personal opinion it’s also the only option.

    Let me know how they go and what they’re doing. I’m interested in whether there’s another solution.

     
  • Andrew Roughan

    Andrew Roughan - 2017-05-03

    I wrote to Richard>

    I have suggested to Stephen that setting a scheduler task and using TCPIPPoll would be the best way to proceed but this would require setting the Busy flag. I am going to add this into the ToolStub (INCBUSY / DECBUSY) and provide him with a build so he can test it.

    Creating your own toolset is covered in TB Ref 2 Appendix A:
    "A simple interrupt environment should be supplied. Each function should increment or decrement the busy flag, be reentrant or disable interrupts during execution."

    and the busy flag scheduler usage is covered in TB Ref 2 Chp 19.
    https://archive.org/details/AppleIIGSToolboxReferenceVolume2

    Further response from Richard by email>

    Also I had a quick look at the busy flag to remind myself and I’m not sure a blanket increment in the tool stub is the way to go. The following tech note has more information on this, which I’m assuming you read because it mentions using the scheduler for calling busy flagged code. Note though that from my initial reading, you don’t need to set the busy flag in Marinetti to support the scheduler, as the scheduler is never called inside an interrupt, and thus never inside Marinetti either. Stephen should be able to use this right now without any Marinetti changes. (unless I’ve read it wrong)

    http://www.1000bit.it/support/manuali/apple/technotes/iigs/tn.iigs.057.html

    As far as the tool stub goes, the busy flag should only be incremented when we are not reentrant. This would mean checking all functions and only setting it for those which are. I’d rather do that than flat out deny access to all Marinetti. Obviously this would take more time, but it’s not like there’s an urgency to this. In his case he can use the scheduler, or some other work around. And again, he SHOULD NOT be calling Marinetti from an interrupt anyway, REGARDLESS whether the busy flag is set. If you do add it as a blanket case in the tool stub, then at least mark it with a TODO so we know it needs to be fixed properly at some point.

     
  • Andrew Roughan

    Andrew Roughan - 2017-05-03

    Stephen replied on csa2p>

    Whenever an AFP-over-TCP connection is open, I want to have a way to get control periodically (every couple seconds or so) and be able to send or receive data over the TCP connection. This is necessary to check for new messages from the server and to periodically send a 'tickle' message to the server to keep the connection alive.

    The currently released version uses a run queue task for this, but that only works in desktop programs, and even there run queue tasks may not get run for a while if the program is busy with something. It also offers the 'fake sleep' option as a workaround, but that is really a hack and doesn't work with all servers.
    ...
    I can make a test version of AFPBridge that uses a heartbeat task (or an AppleTalk InstallTimer call) plus the scheduler for its periodic task, and see how it works.

     

Log in to post a comment.

MongoDB Logo MongoDB