![]() |
![]() |
#1 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Ctrl+C handling in console application
This is for 68k with Workbench 3.1. I'd like to know why it does not work, rather than there are different ways to do it by upgrading.
Code:
if (err = OpenDevice(SERIALNAME,0,(struct IORequest *)SerialIO,0L)) { if (err == 1) printf("Error: %s is in use\n", SERIALNAME); else printf("Error: %s did not open (%d)\n",SERIALNAME, err); goto done; }
This is understandable. AmigaDOS doesn't clean up the open device and kills the application blindly. What I want to do is stop AmigaDOS from handling the signal and handle it myself and clean up and exit. The following code does do that. I will consistently see "ctrl-c" and the device will be closed and the application will exit cleanly. Code:
LONG user_signal = SIGBREAKF_CTRL_C; serial_signal = 1L << serial_port->mp_SigBit; while (running) { SerialIO->IOSer.io_Command = SDCMD_QUERY; SendIO((struct IORequest *)SerialIO); signals = Wait(serial_signal | user_signal); if (signals & user_signal) { printf("ctrl-c\n"); running = 0; } Code:
timer_signal = 1L << timer_port->mp_SigBit; while (running) { time_request->tr_node.io_Command = TR_ADDREQUEST; time_request->tr_time.tv_secs = 20; time_request->tr_time.tv_micro = 500000L; SendIO((struct IORequest *)time_request); signals = Wait(timer_signal | user_signal); if (signals & user_signal) { printf("ctrl-c\n"); running = 0; } I want to understand why this code does not work. Is there a way of having the Ctrl-c interrupting the Wait and not being processed by AmigaDOS? |
![]() |
![]() |
#2 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Here's the full source code for reference. It is in the state where it works, exits with Ctrl-C and reads commands from the serial port.
https://github.com/rmtew/amiga-exper...sync_read_31.c |
![]() |
![]() |
#3 |
ex. demoscener "Bigmama"
Join Date: Jun 2012
Location: Fyn / Denmark
Posts: 1,664
|
is user_signal correctly initialized in the timer example?
|
![]() |
![]() |
#4 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,050
|
That's not true. AmigaDOS never kills an application.
It is your compiler's runtime which kills your program. You either have to disable Ctrl-C handling on the runtime (that's compiler specific, so I cannot tell you how to do it) or avoid using ANSI C functions. For example you could use Printf instead of printf. |
![]() |
![]() |
#5 |
Coder/webmaster/gamer
Join Date: Oct 2001
Location: Canberra/Australia
Posts: 2,715
|
For SAS/C, you just add these lines, as shown in the RKMs:
int CXBRK(void) { return 0; } /* Disable SAS/Lattice Ctrl-C handling */ void chkabort(void) { return; } /* really */ |
![]() |
![]() |
#6 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Thanks guys. That's points me in the right direction. I now need to work out how vbcc does this - which is in the latest online documentation.
Question 2: how do people conventionally clean up things like the open serial device in the event of control-c and other unforeseen circumstances? Is it to override and catch those signals, like this? Last edited by copse; 02 June 2024 at 22:17. |
![]() |
![]() |
#7 | |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Quote:
![]() All my code is constructed of examples in online autodoc looking web pages and such things as far as I can tell. Some of which contradicts each other - like one says clean up your IO and another never mentions it. All the message and IO handling is a black box to me. I can understand what DoIO and SendIO do, but what messages I should expect and what IO calls I need to do post results or changed circumstances is opaque. |
|
![]() |
![]() |
#8 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Question 3: My sense is that SendIO for the serial query is pointless. That perhaps what I have to do is:
What I want to do is:
At this time I do not know how to Wait until serial data present. |
![]() |
![]() |
#9 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 3,501
|
Many possibilties. Your serial and timer IORequest should either have two separate ports or a single port. The latter is sufficient. You fire off the timerequest by SendIO, and the serial request by SendIO. Then you do a Wait() with the signal mask taking the signal from the reply port of the IORequest(s), and SIGBREAK_CTRL_C. You check the returned bitmask. If it contains SIGBREAK_CTRL_C, you use AbortIO() on both the timer and the serial IO, then WaitIO() on each of the requests to receive the request back, then close the devices and abort the program.
If the returned signal mask is that of the reply port, you use CheckIO() on the serial device IORequest. If CheckIO() indicates that the serial request is ready, you call AbortIO() on the timerequest, then WaitIO() on both requests, and handle whatever data you received. If CheckIO() indicates that the timerequest is ready, you call AbortIO() on the serial request (if you want to?), then WaitIO() on both of them, and then handle whatever needs to be handled on a timeout. Thus, an IORequest can have three states: In your possession, free. (F), queued in the device for processing (D), and ready and processed in your port (R). F -> D is by SendIO(), and you should *only* call SendIO() while a request is in state F ("free") as otherwise you are causing a lot of confusion. D-> R is by the device, and this state transition triggers a signal you can wait on. For serial: When data was received (or send out), for the timer, when the timespan elapsed. D-> R is also (forcefully) possible by AbortIO() saying "I need the request back right now, do not process it anymore". R-> F is done by WaitIO(), which also waits for the request to be in state R. CheckIO() can distinguish between states D (not ready) and R (ready). It should not be called in state F (free). HTHH, Thomas |
![]() |
![]() |
#10 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Thanks Thomas. That breakdown is very handy.
I've also realised I am not thinking good following your hint in that breakdown, as I can of course block on a read of 1 character and wait on that to avoid the polling loop of the wait on the query. In fact this is documented if I search for CMD_READ for the serial device. Thanks again! |
![]() |
![]() |
#11 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
In the case where the Wait returns from either Ctrl-c or serial, I call AbortIO/WaitIO on the timer. At this point the timer signal appears to be set. Is this expected? Should I clear it with SetSignal(0L, timer_signal)? Is there a standard pattern done here?
|
![]() |
![]() |
#12 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 3,501
|
The pattern is to keep the signal bit set, but rather depend on CheckIO() when you received signals whether a request submitted earlier did actually return. A Wait() on a signal set returns immediately, but if you run into CheckIO() later and find that your request is still busy, your program will just loop for another round, which is harmless. If you set the signal to 0 without knowing whether the request is still busy, you could miss a request that just returned, and the next Wait() will wait forever. IOWs, the only thing that should clear signals is Wait(), and not SetSignal().
|
![]() |
![]() |
#13 |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
What might I be missing? This infinite loops and never clears the flag. If I remove the 'if (CheckIO..' line, same result.
Code:
while (running) { ULONG signals2 = SetSignals(0L, 0L); if ((signals2 & time_signal) != 0) printf("time signal still set.\n"); time_request->tr_node.io_Command = TR_ADDREQUEST; time_request->tr_time.tv_secs = 5; SendIO(time_request); signals = WaitIO(serial_and_timer_and_ctrlc); if (CheckIO(time_request) == NULL) AbortIO(time_request); /* Ensure pending is complete/killed. */ WaitIO(time_request); ... } |
![]() |
![]() |
#14 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,050
|
This looks so very complicated.
Hers is a more simple to understand code snippet: Code:
start_timer (timer); start_serial_read(serial,buffer); while (running) { signals_received = Wait(serial_signal | timer_signal | SIGBREAKF_CTRL_C); if (signals_received & seriaL_signal) { WaitIO(serial); process_serial_buffer(buffer); start_serial_read(serial,buffer); } if (signals_received & timer_signal) { WaitIO(timer); printf("timeout\n"); running = FALSE; } if (signals_received & SIGBREAKF_CTRL_C) printf("Ctrl-C\n"); running = FALSE; } } if (CheckIO(serial) == NULL) { AbortIO (serial); WaitIO (serial); } if (CheckIO(timer) == NULL) { AbortIO (timer); WaitIO (timer); } |
![]() |
![]() |
#15 | |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 3,501
|
Quote:
|
|
![]() |
![]() |
#16 |
Registered User
Join Date: Nov 2015
Location: Italy
Posts: 201
|
This IO stuff is tricky and complicated and easy to get wrong. In your simpler code example at the end outside the running loop your cleanup code (checkio, abortio, waitio) may look perfect at first look, but (even if in many cases this may not be a problem) there are cases where your example can end up with iorequests which do not get removed from replyport. Like if the timer and/or the serial request complete just while code is in "running" loop handling the SIGBREAKF_CTRL_C which the user just pressed. The loop exits and the cleanup code never calls WaitIO, because the request(s) are already completed.Having some "timerio_sent" and "serialio_sent" (set to YES before SendIO(), set to NO after WaitIO() or manual removing from replyport) variable may help, as CheckIO() is not enough to know if you have to do some cleanup.
|
![]() |
![]() |
#17 | |
Registered User
Join Date: Jul 2009
Location: Lala Land
Posts: 608
|
Quote:
Please look at it as helping me understand how it works, why it does not work and what the proper way to do what is actually being done is where it would work. Why is the WaitIO not cleaning up the signal seems to be the question as far as I can tell. The fact there's disagreement about how to even do your simpler case right is.. umm.. making me wonder who actually understood this stuff back in the day ![]() |
|
![]() |
![]() |
#18 |
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,581
|
|
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Where did I see that non-recursive directory handling c-example? | hceline | Coders. C/C++ | 9 | 17 November 2023 23:25 |
[blitz] - palette handling - need some help | peceha | Coders. Blitz Basic | 2 | 18 March 2021 18:49 |
Take control of Alt + TAB, Ctrl + Alt + Del and Ctrl + ESC. | Ponki1986 | support.WinUAE | 6 | 13 December 2020 07:54 |
File Handling in BLitzmode | timeslip1974 | Coders. Blitz Basic | 4 | 10 September 2019 17:29 |
Cookie handling | alexh | project.EAB | 1 | 22 October 2007 21:52 |
|
|